Webserver+3d print

Dependents:   Nucleo

Files at this revision

API Documentation at this revision

Comitter:
Sergunb
Date:
Sat Feb 04 18:15:49 2017 +0000
Commit message:
nothing else

Changed in this revision

common/compiler_port.h Show annotated file Show diff for this revision Revisions of this file
common/cpu_endian.c Show annotated file Show diff for this revision Revisions of this file
common/cpu_endian.h Show annotated file Show diff for this revision Revisions of this file
common/date_time.c Show annotated file Show diff for this revision Revisions of this file
common/date_time.h Show annotated file Show diff for this revision Revisions of this file
common/debug.c Show annotated file Show diff for this revision Revisions of this file
common/debug.h Show annotated file Show diff for this revision Revisions of this file
common/error.h Show annotated file Show diff for this revision Revisions of this file
common/fs_port.h Show annotated file Show diff for this revision Revisions of this file
common/fs_port_config.h Show annotated file Show diff for this revision Revisions of this file
common/fs_port_fatfs.c Show annotated file Show diff for this revision Revisions of this file
common/os_port.h Show annotated file Show diff for this revision Revisions of this file
common/os_port_chibios.c Show annotated file Show diff for this revision Revisions of this file
common/os_port_chibios.h Show annotated file Show diff for this revision Revisions of this file
common/os_port_cmsis_rtos.c Show annotated file Show diff for this revision Revisions of this file
common/os_port_cmsis_rtos.h Show annotated file Show diff for this revision Revisions of this file
common/os_port_cmsis_rtos2.c Show annotated file Show diff for this revision Revisions of this file
common/os_port_cmsis_rtos2.h Show annotated file Show diff for this revision Revisions of this file
common/os_port_embos.c Show annotated file Show diff for this revision Revisions of this file
common/os_port_embos.h Show annotated file Show diff for this revision Revisions of this file
common/os_port_freertos.c Show annotated file Show diff for this revision Revisions of this file
common/os_port_freertos.h Show annotated file Show diff for this revision Revisions of this file
common/os_port_none.c Show annotated file Show diff for this revision Revisions of this file
common/os_port_none.h Show annotated file Show diff for this revision Revisions of this file
common/os_port_rtx.c Show annotated file Show diff for this revision Revisions of this file
common/os_port_rtx.h Show annotated file Show diff for this revision Revisions of this file
common/os_port_sys_bios.c Show annotated file Show diff for this revision Revisions of this file
common/os_port_sys_bios.h Show annotated file Show diff for this revision Revisions of this file
common/os_port_ucos2.c Show annotated file Show diff for this revision Revisions of this file
common/os_port_ucos2.h Show annotated file Show diff for this revision Revisions of this file
common/os_port_ucos3.c Show annotated file Show diff for this revision Revisions of this file
common/os_port_ucos3.h Show annotated file Show diff for this revision Revisions of this file
common/os_port_windows.c Show annotated file Show diff for this revision Revisions of this file
common/os_port_windows.h Show annotated file Show diff for this revision Revisions of this file
common/path.c Show annotated file Show diff for this revision Revisions of this file
common/path.h Show annotated file Show diff for this revision Revisions of this file
common/resource_manager.c Show annotated file Show diff for this revision Revisions of this file
common/resource_manager.h Show annotated file Show diff for this revision Revisions of this file
common/str.c Show annotated file Show diff for this revision Revisions of this file
common/str.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/aes.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/aes.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/aria.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/aria.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/asn1.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/asn1.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/base64.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/base64.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/camellia.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/camellia.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/chacha.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/chacha.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/chacha20_poly1305.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/chacha20_poly1305.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/cipher_mode_cbc.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/cipher_mode_cbc.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/cipher_mode_ccm.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/cipher_mode_ccm.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/cipher_mode_cfb.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/cipher_mode_cfb.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/cipher_mode_ctr.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/cipher_mode_ctr.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/cipher_mode_ecb.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/cipher_mode_ecb.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/cipher_mode_gcm.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/cipher_mode_gcm.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/cipher_mode_ofb.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/cipher_mode_ofb.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/crypto.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/des.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/des.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/des3.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/des3.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/dh.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/dh.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/dsa.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/dsa.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/ec.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/ec.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/ec_curves.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/ec_curves.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/ecdh.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/ecdh.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/ecdsa.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/ecdsa.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/hmac.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/hmac.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/idea.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/idea.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/keccak.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/keccak.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/md2.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/md2.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/md4.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/md4.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/md5.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/md5.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/mpi.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/mpi.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/mpi_asm_ccs_cortex_m3.asm Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/mpi_asm_gcc_cortex_a9.S Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/mpi_asm_gcc_cortex_m3.S Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/mpi_asm_gcc_mips.S Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/mpi_asm_iar_cortex_m3.s Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/mpi_asm_keil_arm7.s Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/mpi_asm_keil_cortex_m3.s Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/oid.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/oid.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/pem.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/pem.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/pkcs5.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/pkcs5.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/poly1305.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/poly1305.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/rc4.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/rc4.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/rc6.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/rc6.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/ripemd128.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/ripemd128.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/ripemd160.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/ripemd160.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/rsa.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/rsa.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/seed.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/seed.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha1.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha1.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha224.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha224.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha256.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha256.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha384.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha384.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha3_224.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha3_224.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha3_256.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha3_256.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha3_384.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha3_384.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha3_512.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha3_512.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha512.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha512.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha512_224.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha512_224.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha512_256.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/sha512_256.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/tiger.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/tiger.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/whirlpool.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/whirlpool.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/x509.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/x509.h Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/yarrow.c Show annotated file Show diff for this revision Revisions of this file
cyclone_crypto/yarrow.h Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/ssl_common.c Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/ssl_common.h Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls.c Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls.h Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls_cache.c Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls_cache.h Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls_cipher_suites.c Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls_cipher_suites.h Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls_client.c Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls_client.h Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls_client_misc.c Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls_client_misc.h Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls_common.c Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls_common.h Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls_misc.c Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls_misc.h Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls_record.c Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls_record.h Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls_server.c Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls_server.h Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls_server_misc.c Show annotated file Show diff for this revision Revisions of this file
cyclone_ssl/tls_server_misc.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/bsd_socket.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/bsd_socket.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/ethernet.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/ethernet.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/ip.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/ip.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/net.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/net.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/net_legacy.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/net_mem.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/net_mem.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/nic.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/nic.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/ping.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/ping.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/raw_socket.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/raw_socket.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/socket.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/socket.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/tcp.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/tcp.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/tcp_fsm.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/tcp_fsm.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/tcp_misc.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/tcp_misc.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/tcp_timer.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/tcp_timer.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/udp.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/core/udp.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dhcp/dhcp_client.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dhcp/dhcp_client.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dhcp/dhcp_common.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dhcp/dhcp_common.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dhcp/dhcp_debug.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dhcp/dhcp_debug.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dhcp/dhcp_server.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dhcp/dhcp_server.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dhcpv6/dhcpv6_client.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dhcpv6/dhcpv6_client.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dhcpv6/dhcpv6_common.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dhcpv6/dhcpv6_common.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dhcpv6/dhcpv6_debug.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dhcpv6/dhcpv6_debug.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dhcpv6/dhcpv6_relay.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dhcpv6/dhcpv6_relay.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dns/dns_cache.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dns/dns_cache.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dns/dns_client.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dns/dns_client.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dns/dns_common.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dns/dns_common.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dns/dns_debug.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dns/dns_debug.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dns_sd/dns_sd.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/dns_sd/dns_sd.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/a2fxxxm3_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/a2fxxxm3_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/am335x_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/am335x_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/aps3_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/aps3_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ar8031.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ar8031.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/avr32_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/avr32_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/bcm43362_driver.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/bcm43362_driver.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/dm9000.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/dm9000.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/dm9161.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/dm9161.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/dp83620.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/dp83620.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/dp83848.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/dp83848.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/enc28j60.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/enc28j60.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/enc624j600.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/enc624j600.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/f28m35x_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/f28m35x_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/fm4_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/fm4_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8031.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8031.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8041.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8041.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8051.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8051.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8061.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8061.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8081.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8081.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8091.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8091.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8721.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8721.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8851.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8851.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8873.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8873.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8895.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz8895.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz9031.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/ksz9031.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lan8710.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lan8710.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lan8720.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lan8720.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lan8740.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lan8740.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lan8742.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lan8742.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lan9303.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lan9303.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lm3s_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lm3s_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lpc175x_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lpc175x_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lpc176x_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lpc176x_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lpc178x_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lpc178x_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lpc18xx_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lpc18xx_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lpc23xx_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lpc23xx_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lpc43xx_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/lpc43xx_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/m2sxxx_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/m2sxxx_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/mcf5225x_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/mcf5225x_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/mk6x_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/mk6x_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/mk7x_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/mk7x_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/mkv5x_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/mkv5x_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/mrf24wg_driver.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/mrf24wg_driver.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/omapl138_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/omapl138_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/pcap_driver.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/pcap_driver.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/pic32mx_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/pic32mx_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/pic32mz_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/pic32mz_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/rtl8211.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/rtl8211.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/rx63n_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/rx63n_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/rza1_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/rza1_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/s7g2_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/s7g2_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/sam3x_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/sam3x_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/sam4e_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/sam4e_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/sam7x_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/sam7x_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/sam9263_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/sam9263_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/sama5d2_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/sama5d2_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/sama5d3_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/sama5d3_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/sama5d3_gigabit_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/sama5d3_gigabit_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/same70_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/same70_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/samv71_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/samv71_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/st802rt1a.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/st802rt1a.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/stm32f107_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/stm32f107_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/stm32f2x7_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/stm32f2x7_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/stm32f4x7_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/stm32f4x7_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/stm32f4x9_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/stm32f4x9_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/stm32f7xx_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/stm32f7xx_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/str912_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/str912_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/tm4c129_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/tm4c129_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/upd60611.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/upd60611.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/wilc1000_driver.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/wilc1000_driver.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/xmc4500_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/xmc4500_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/xmc4700_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/xmc4700_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/xmc4800_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/xmc4800_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/zynq7000_eth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/drivers/zynq7000_eth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ftp/ftp_client.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ftp/ftp_client.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ftp/ftp_server.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ftp/ftp_server.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ftp/ftp_server_commands.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ftp/ftp_server_commands.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ftp/ftp_server_events.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ftp/ftp_server_events.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ftp/ftp_server_misc.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ftp/ftp_server_misc.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/http/http_server.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/http/http_server.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/http/http_server_auth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/http/http_server_auth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/http/http_server_misc.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/http/http_server_misc.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/http/mime.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/http/mime.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/http/ssi.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/http/ssi.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/icecast/icecast_client.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/icecast/icecast_client.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv4/arp.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv4/arp.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv4/auto_ip.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv4/auto_ip.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv4/icmp.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv4/icmp.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv4/igmp.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv4/igmp.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv4/ipv4.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv4/ipv4.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv4/ipv4_frag.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv4/ipv4_frag.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv4/ipv4_routing.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/icmpv6.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/icmpv6.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/ipv6.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/ipv6.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/ipv6_frag.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/ipv6_frag.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/ipv6_misc.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/ipv6_misc.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/ipv6_pmtu.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/ipv6_pmtu.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/ipv6_routing.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/ipv6_routing.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/mld.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/mld.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/ndp.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/ndp.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/ndp_cache.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/ndp_cache.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/ndp_misc.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/ndp_misc.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/ndp_router_adv.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/ndp_router_adv.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/slaac.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ipv6/slaac.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mdns/mdns_client.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mdns/mdns_client.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mdns/mdns_common.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mdns/mdns_common.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mdns/mdns_responder.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mdns/mdns_responder.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mibs/mib2_impl.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mibs/mib2_impl.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mibs/mib2_module.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mibs/mib2_module.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mibs/mib_common.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mibs/mib_common.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mqtt/mqtt_client.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mqtt/mqtt_client.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mqtt/mqtt_client_misc.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mqtt/mqtt_client_misc.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mqtt/mqtt_client_packet.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mqtt/mqtt_client_packet.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mqtt/mqtt_client_transport.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mqtt/mqtt_client_transport.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/mqtt/mqtt_common.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/netbios/nbns_client.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/netbios/nbns_client.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/netbios/nbns_common.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/netbios/nbns_common.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/netbios/nbns_responder.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/netbios/nbns_responder.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/chap.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/chap.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/ipcp.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/ipcp.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/ipv6cp.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/ipv6cp.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/lcp.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/lcp.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/pap.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/pap.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/ppp.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/ppp.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/ppp_debug.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/ppp_debug.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/ppp_fsm.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/ppp_fsm.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/ppp_hdlc.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/ppp_hdlc.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/ppp_misc.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/ppp/ppp_misc.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/smtp/smtp_client.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/smtp/smtp_client.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/snmp/snmp_agent.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/snmp/snmp_agent.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/snmp/snmp_agent_dispatch.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/snmp/snmp_agent_dispatch.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/snmp/snmp_agent_misc.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/snmp/snmp_agent_misc.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/snmp/snmp_agent_pdu.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/snmp/snmp_agent_pdu.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/snmp/snmp_common.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/snmp/snmp_common.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/snmp/snmp_usm.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/snmp/snmp_usm.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/sntp/sntp_client.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/sntp/sntp_client.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/std_services/chargen.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/std_services/chargen.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/std_services/daytime.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/std_services/daytime.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/std_services/discard.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/std_services/discard.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/std_services/echo.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/std_services/echo.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/tftp/tftp_common.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/tftp/tftp_server.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/tftp/tftp_server.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/web_socket/web_socket.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/web_socket/web_socket.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/web_socket/web_socket_auth.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/web_socket/web_socket_auth.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/web_socket/web_socket_frame.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/web_socket/web_socket_frame.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/web_socket/web_socket_misc.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/web_socket/web_socket_misc.h Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/web_socket/web_socket_transport.c Show annotated file Show diff for this revision Revisions of this file
cyclone_tcp/web_socket/web_socket_transport.h Show annotated file Show diff for this revision Revisions of this file
ftp_client_demo/src/FreeRTOSConfig.h Show annotated file Show diff for this revision Revisions of this file
ftp_client_demo/src/debug.c Show annotated file Show diff for this revision Revisions of this file
ftp_client_demo/src/main.c Show annotated file Show diff for this revision Revisions of this file
ftp_client_demo/src/net_config.h Show annotated file Show diff for this revision Revisions of this file
ftp_client_demo/src/os_port_config.h Show annotated file Show diff for this revision Revisions of this file
ftp_client_demo/src/stm32f7xx_hal_conf.h Show annotated file Show diff for this revision Revisions of this file
ftp_client_demo/src/stm32f7xx_it.c Show annotated file Show diff for this revision Revisions of this file
ftp_client_demo/src/stm32f7xx_it.h Show annotated file Show diff for this revision Revisions of this file
ftp_client_demo/src/system_stm32f7xx.c Show annotated file Show diff for this revision Revisions of this file
mqtt_client_demo/src/FreeRTOSConfig.h Show annotated file Show diff for this revision Revisions of this file
mqtt_client_demo/src/crypto_config.h Show annotated file Show diff for this revision Revisions of this file
mqtt_client_demo/src/debug.c Show annotated file Show diff for this revision Revisions of this file
mqtt_client_demo/src/main.c Show annotated file Show diff for this revision Revisions of this file
mqtt_client_demo/src/net_config.h Show annotated file Show diff for this revision Revisions of this file
mqtt_client_demo/src/os_port_config.h Show annotated file Show diff for this revision Revisions of this file
mqtt_client_demo/src/stm32f7xx_hal_conf.h Show annotated file Show diff for this revision Revisions of this file
mqtt_client_demo/src/stm32f7xx_it.c Show annotated file Show diff for this revision Revisions of this file
mqtt_client_demo/src/stm32f7xx_it.h Show annotated file Show diff for this revision Revisions of this file
mqtt_client_demo/src/system_stm32f7xx.c Show annotated file Show diff for this revision Revisions of this file
mqtt_client_demo/src/tls_config.h Show annotated file Show diff for this revision Revisions of this file
web_server_demo/resources/www/css/demo.css Show annotated file Show diff for this revision Revisions of this file
web_server_demo/resources/www/css/reset.css Show annotated file Show diff for this revision Revisions of this file
web_server_demo/resources/www/favicon.ico Show annotated file Show diff for this revision Revisions of this file
web_server_demo/resources/www/index.html Show annotated file Show diff for this revision Revisions of this file
web_server_demo/resources/www/index.shtm Show annotated file Show diff for this revision Revisions of this file
web_server_demo/src/FreeRTOSConfig.h Show annotated file Show diff for this revision Revisions of this file
web_server_demo/src/debug.c Show annotated file Show diff for this revision Revisions of this file
web_server_demo/src/main.c Show annotated file Show diff for this revision Revisions of this file
web_server_demo/src/net_config.h Show annotated file Show diff for this revision Revisions of this file
web_server_demo/src/os_port_config.h Show annotated file Show diff for this revision Revisions of this file
web_server_demo/src/stm32f7xx_hal_conf.h Show annotated file Show diff for this revision Revisions of this file
web_server_demo/src/stm32f7xx_it.c Show annotated file Show diff for this revision Revisions of this file
web_server_demo/src/stm32f7xx_it.h Show annotated file Show diff for this revision Revisions of this file
web_server_demo/src/system_stm32f7xx.c Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/compiler_port.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,141 @@
+/**
+ * @file compiler_port.h
+ * @brief Compiler specific definitions
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _COMPILER_PORT_H
+#define _COMPILER_PORT_H
+
+//Dependencies
+#include <stddef.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+//Types
+typedef char char_t;
+typedef signed int int_t;
+typedef unsigned int uint_t;
+typedef uint32_t systime_t;
+
+#if !defined(R_TYPEDEFS_H) && !defined(USE_CHIBIOS_2)
+   typedef int bool_t;
+#endif
+
+#if defined(__linux__)
+   #define PRIuSIZE "zu"
+   #define PRIuTIME "lu"
+#elif defined(_WIN32)
+   #define PRIuSIZE "Iu"
+   #define PRIuTIME "lu"
+#elif defined(__XC32)
+   #define PRIuSIZE "u"
+   #define PRIuTIME "u"
+#elif defined(__CWCC__)
+   #define PRIu8 "u"
+   #define PRIu16 "u"
+   #define PRIu32 "u"
+   #define PRIx8 "x"
+   #define PRIx16 "x"
+   #define PRIx32 "x"
+   #define PRIX8 "X"
+   #define PRIX16 "X"
+   #define PRIX32 "X"
+   #define PRIuSIZE "u"
+   #define PRIuTIME "u"
+#else
+   #define PRIuSIZE "u"
+   #define PRIuTIME "lu"
+#endif
+
+#if defined(__CC_ARM)
+   #undef PRIu8
+   #define PRIu8 "u"
+   #undef PRIu16
+   #define PRIu16 "u"
+#endif
+
+//CodeWarrior compiler?
+#if defined(__CWCC__)
+   typedef uint32_t time_t;
+   int strcasecmp(const char *s1, const char *s2);
+   int strncasecmp(const char *s1, const char *s2, size_t n);
+   char *strtok_r(char *s, const char *delim, char **last);
+//TI ARM C compiler?
+#elif defined(__TI_ARM__)
+   int strcasecmp(const char *s1, const char *s2);
+   int strncasecmp(const char *s1, const char *s2, size_t n);
+   char *strtok_r(char *s, const char *delim, char **last);
+#endif
+
+//Microchip XC32 compiler?
+#if defined(__XC32)
+   #define sprintf _sprintf
+   int sprintf(char * str, const char * format, ...);
+   int strcasecmp(const char *s1, const char *s2);
+   int strncasecmp(const char *s1, const char *s2, size_t n);
+   char *strtok_r(char *s, const char *delim, char **last);
+#endif
+
+//GCC compiler?
+#if defined(__GNUC__)
+   #undef __start_packed
+   #define __start_packed
+   #undef __end_packed
+   #define __end_packed __attribute__((__packed__))
+//Keil MDK-ARM compiler?
+#elif defined(__CC_ARM)
+   #pragma anon_unions
+   #undef __start_packed
+   #define __start_packed __packed
+   #undef __end_packed
+   #define __end_packed
+//IAR C compiler?
+#elif defined(__IAR_SYSTEMS_ICC__)
+   #undef __start_packed
+   #define __start_packed __packed
+   #undef __end_packed
+   #define __end_packed
+//CodeWarrior compiler?
+#elif defined(__CWCC__)
+   #undef __start_packed
+   #define __start_packed
+   #undef __end_packed
+   #define __end_packed
+//TI ARM C compiler?
+#elif defined(__TI_ARM__)
+   #undef __start_packed
+   #define __start_packed
+   #undef __end_packed
+   #define __end_packed __attribute__((__packed__))
+//Win32 compiler?
+#elif defined(_WIN32)
+   #undef interface
+   #undef __start_packed
+   #define __start_packed
+   #undef __end_packed
+   #define __end_packed
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/cpu_endian.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,150 @@
+/**
+ * @file cpu_endian.c
+ * @brief Byte order conversion
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Dependencies
+#include "cpu_endian.h"
+
+
+/**
+ * @brief Reverse the byte order of a 16-bit word
+ * @param[in] value 16-bit value
+ * @return 16-bit value with byte order swapped
+ **/
+
+uint16_t swapInt16(uint16_t value)
+{
+   return SWAPINT16(value);
+}
+
+
+/**
+ * @brief Reverse the byte order of a 32-bit word
+ * @param[in] value 32-bit value
+ * @return 32-bit value with byte order swapped
+ **/
+
+uint32_t swapInt32(uint32_t value)
+{
+   return SWAPINT32(value);
+}
+
+
+/**
+ * @brief Reverse the byte order of a 64-bit word
+ * @param[in] value 64-bit value
+ * @return 64-bit value with byte order swapped
+ **/
+
+uint64_t swapInt64(uint64_t value)
+{
+   return SWAPINT64(value);
+}
+
+
+/**
+ * @brief Reverse bit order in a 4-bit word
+ * @param[in] value 4-bit value
+ * @return 4-bit value with bit order reversed
+ **/
+
+uint8_t reverseInt4(uint8_t value)
+{
+   value = ((value & 0x0C) >> 2) | ((value & 0x03) << 2);
+   value = ((value & 0x0A) >> 1) | ((value & 0x05) << 1);
+
+   return value;
+}
+
+
+/**
+ * @brief Reverse bit order in a byte
+ * @param[in] value 8-bit value
+ * @return 8-bit value with bit order reversed
+ **/
+
+uint8_t reverseInt8(uint8_t value)
+{
+   value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4);
+   value = ((value & 0xCC) >> 2) | ((value & 0x33) << 2);
+   value = ((value & 0xAA) >> 1) | ((value & 0x55) << 1);
+
+   return value;
+}
+
+
+/**
+ * @brief Reverse bit order in a 16-bit word
+ * @param[in] value 16-bit value
+ * @return 16-bit value with bit order reversed
+ **/
+
+uint16_t reverseInt16(uint16_t value)
+{
+   value = ((value & 0xFF00) >> 8) | ((value & 0x00FF) << 8);
+   value = ((value & 0xF0F0) >> 4) | ((value & 0x0F0F) << 4);
+   value = ((value & 0xCCCC) >> 2) | ((value & 0x3333) << 2);
+   value = ((value & 0xAAAA) >> 1) | ((value & 0x5555) << 1);
+
+   return value;
+}
+
+
+/**
+ * @brief Reverse bit order in a 32-bit word
+ * @param[in] value 32-bit value
+ * @return 32-bit value with bit order reversed
+ **/
+
+uint32_t reverseInt32(uint32_t value)
+{
+   value = ((value & 0xFFFF0000UL) >> 16) | ((value & 0x0000FFFFUL) << 16);
+   value = ((value & 0xFF00FF00UL) >> 8) | ((value & 0x00FF00FFUL) << 8);
+   value = ((value & 0xF0F0F0F0UL) >> 4) | ((value & 0x0F0F0F0FUL) << 4);
+   value = ((value & 0xCCCCCCCCUL) >> 2) | ((value & 0x33333333UL) << 2);
+   value = ((value & 0xAAAAAAAAUL) >> 1) | ((value & 0x55555555UL) << 1);
+
+   return value;
+}
+
+
+/**
+ * @brief Reverse bit order in a 64-bit word
+ * @param[in] value 64-bit value
+ * @return 64-bit value with bit order reversed
+ **/
+
+uint64_t reverseInt64(uint64_t value)
+{
+   value = ((value & 0xFFFFFFFF00000000ULL) >> 32) | ((value & 0x00000000FFFFFFFFULL) << 32);
+   value = ((value & 0xFFFF0000FFFF0000ULL) >> 16) | ((value & 0x0000FFFF0000FFFFULL) << 16);
+   value = ((value & 0xFF00FF00FF00FF00ULL) >> 8) | ((value & 0x00FF00FF00FF00FFULL) << 8);
+   value = ((value & 0xF0F0F0F0F0F0F0F0ULL) >> 4) | ((value & 0x0F0F0F0F0F0F0F0FULL) << 4);
+   value = ((value & 0xCCCCCCCCCCCCCCCCULL) >> 2) | ((value & 0x3333333333333333ULL) << 2);
+   value = ((value & 0xAAAAAAAAAAAAAAAAULL) >> 1) | ((value & 0x5555555555555555ULL) << 1);
+
+   return value;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/cpu_endian.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,410 @@
+/**
+ * @file cpu_endian.h
+ * @brief Byte order conversion
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _CPU_ENDIAN_H
+#define _CPU_ENDIAN_H
+
+//Dependencies
+#include "os_port.h"
+
+//Undefine conflicting definitions
+#ifdef HTONS
+   #undef HTONS
+#endif
+
+#ifdef HTONL
+   #undef HTONL
+#endif
+
+#ifdef htons
+   #undef htons
+#endif
+
+#ifdef htonl
+   #undef htonl
+#endif
+
+#ifdef NTOHS
+   #undef NTOHS
+#endif
+
+#ifdef NTOHL
+   #undef NTOHL
+#endif
+
+#ifdef ntohs
+   #undef ntohs
+#endif
+
+#ifdef ntohl
+   #undef ntohl
+#endif
+
+#ifdef HTOLE16
+   #undef HTOLE16
+#endif
+
+#ifdef HTOLE32
+   #undef HTOLE32
+#endif
+
+#ifdef HTOLE64
+   #undef HTOLE64
+#endif
+
+#ifdef htole16
+   #undef htole16
+#endif
+
+#ifdef htole32
+   #undef htole32
+#endif
+
+#ifdef htole64
+   #undef htole64
+#endif
+
+#ifdef LETOH16
+   #undef LETOH16
+#endif
+
+#ifdef LETOH32
+   #undef LETOH32
+#endif
+
+#ifdef LETOH64
+   #undef LETOH64
+#endif
+
+#ifdef letoh16
+   #undef letoh16
+#endif
+
+#ifdef letoh32
+   #undef letoh32
+#endif
+
+#ifdef letoh64
+   #undef letoh64
+#endif
+
+#ifdef HTOBE16
+   #undef HTOBE16
+#endif
+
+#ifdef HTOBE32
+   #undef HTOBE32
+#endif
+
+#ifdef HTOBE64
+   #undef HTOBE64
+#endif
+
+#ifdef htobe16
+   #undef htobe16
+#endif
+
+#ifdef htobe32
+   #undef htobe32
+#endif
+
+#ifdef htobe64
+   #undef htobe64
+#endif
+
+#ifdef BETOH16
+   #undef BETOH16
+#endif
+
+#ifdef BETOH32
+   #undef BETOH32
+#endif
+
+#ifdef BETOH64
+   #undef BETOH64
+#endif
+
+#ifdef betoh16
+   #undef betoh16
+#endif
+
+#ifdef betoh32
+   #undef betoh32
+#endif
+
+#ifdef betoh64
+   #undef betoh64
+#endif
+
+//Load unaligned 16-bit integer (little-endian encoding)
+#define LOAD16LE(p) ( \
+   ((uint16_t)(((uint8_t *)(p))[0]) << 0) | \
+   ((uint16_t)(((uint8_t *)(p))[1]) << 8))
+
+//Load unaligned 16-bit integer (big-endian encoding)
+#define LOAD16BE(p) ( \
+   ((uint16_t)(((uint8_t *)(p))[0]) << 8) | \
+   ((uint16_t)(((uint8_t *)(p))[1]) << 0))
+
+//Load unaligned 24-bit integer (little-endian encoding)
+#define LOAD24LE(p) ( \
+   ((uint32_t)(((uint8_t *)(p))[0]) << 0)| \
+   ((uint32_t)(((uint8_t *)(p))[1]) << 8) | \
+   ((uint32_t)(((uint8_t *)(p))[2]) << 16))
+
+//Load unaligned 24-bit integer (big-endian encoding)
+#define LOAD24BE(p) ( \
+   ((uint32_t)(((uint8_t *)(p))[0]) << 16) | \
+   ((uint32_t)(((uint8_t *)(p))[1]) << 8) | \
+   ((uint32_t)(((uint8_t *)(p))[2]) << 0))
+
+//Load unaligned 32-bit integer (little-endian encoding)
+#define LOAD32LE(p) ( \
+   ((uint32_t)(((uint8_t *)(p))[0]) << 0) | \
+   ((uint32_t)(((uint8_t *)(p))[1]) << 8) | \
+   ((uint32_t)(((uint8_t *)(p))[2]) << 16) | \
+   ((uint32_t)(((uint8_t *)(p))[3]) << 24))
+
+//Load unaligned 32-bit integer (big-endian encoding)
+#define LOAD32BE(p) ( \
+   ((uint32_t)(((uint8_t *)(p))[0]) << 24) | \
+   ((uint32_t)(((uint8_t *)(p))[1]) << 16) | \
+   ((uint32_t)(((uint8_t *)(p))[2]) << 8) | \
+   ((uint32_t)(((uint8_t *)(p))[3]) << 0))
+
+//Load unaligned 64-bit integer (little-endian encoding)
+#define LOAD64LE(p) ( \
+   ((uint64_t)(((uint8_t *)(p))[0]) << 0) | \
+   ((uint64_t)(((uint8_t *)(p))[1]) << 8) | \
+   ((uint64_t)(((uint8_t *)(p))[2]) << 16) | \
+   ((uint64_t)(((uint8_t *)(p))[3]) << 24) | \
+   ((uint64_t)(((uint8_t *)(p))[4]) << 32) | \
+   ((uint64_t)(((uint8_t *)(p))[5]) << 40) | \
+   ((uint64_t)(((uint8_t *)(p))[6]) << 48) | \
+   ((uint64_t)(((uint8_t *)(p))[7]) << 56))
+
+//Load unaligned 64-bit integer (big-endian encoding)
+#define LOAD64BE(p) ( \
+   ((uint64_t)(((uint8_t *)(p))[0]) << 56) | \
+   ((uint64_t)(((uint8_t *)(p))[1]) << 48) | \
+   ((uint64_t)(((uint8_t *)(p))[2]) << 40) | \
+   ((uint64_t)(((uint8_t *)(p))[3]) << 32) | \
+   ((uint64_t)(((uint8_t *)(p))[4]) << 24) | \
+   ((uint64_t)(((uint8_t *)(p))[5]) << 16) | \
+   ((uint64_t)(((uint8_t *)(p))[6]) << 8) | \
+   ((uint64_t)(((uint8_t *)(p))[7]) << 0))
+
+//Store unaligned 16-bit integer (little-endian encoding)
+#define STORE16LE(a, p) \
+   ((uint8_t *)(p))[0] = ((uint16_t)(a) >> 0) & 0xFF, \
+   ((uint8_t *)(p))[1] = ((uint16_t)(a) >> 8) & 0xFF
+
+//Store unaligned 32-bit integer (big-endian encoding)
+#define STORE16BE(a, p) \
+   ((uint8_t *)(p))[0] = ((uint16_t)(a) >> 8) & 0xFF, \
+   ((uint8_t *)(p))[1] = ((uint16_t)(a) >> 0) & 0xFF
+
+//Store unaligned 24-bit integer (little-endian encoding)
+#define STORE24LE(a, p) \
+   ((uint8_t *)(p))[0] = ((uint32_t)(a) >> 0) & 0xFF, \
+   ((uint8_t *)(p))[1] = ((uint32_t)(a) >> 8) & 0xFF, \
+   ((uint8_t *)(p))[2] = ((uint32_t)(a) >> 16) & 0xFF
+
+//Store unaligned 24-bit integer (big-endian encoding)
+#define STORE24BE(a, p) \
+   ((uint8_t *)(p))[0] = ((uint32_t)(a) >> 16) & 0xFF, \
+   ((uint8_t *)(p))[1] = ((uint32_t)(a) >> 8) & 0xFF, \
+   ((uint8_t *)(p))[2] = ((uint32_t)(a) >> 0) & 0xFF
+
+//Store unaligned 32-bit integer (little-endian encoding)
+#define STORE32LE(a, p) \
+   ((uint8_t *)(p))[0] = ((uint32_t)(a) >> 0) & 0xFF, \
+   ((uint8_t *)(p))[1] = ((uint32_t)(a) >> 8) & 0xFF, \
+   ((uint8_t *)(p))[2] = ((uint32_t)(a) >> 16) & 0xFF, \
+   ((uint8_t *)(p))[3] = ((uint32_t)(a) >> 24) & 0xFF
+
+//Store unaligned 32-bit integer (big-endian encoding)
+#define STORE32BE(a, p) \
+   ((uint8_t *)(p))[0] = ((uint32_t)(a) >> 24) & 0xFF, \
+   ((uint8_t *)(p))[1] = ((uint32_t)(a) >> 16) & 0xFF, \
+   ((uint8_t *)(p))[2] = ((uint32_t)(a) >> 8) & 0xFF, \
+   ((uint8_t *)(p))[3] = ((uint32_t)(a) >> 0) & 0xFF
+
+//Store unaligned 64-bit integer (little-endian encoding)
+#define STORE64LE(a, p) \
+   ((uint8_t *)(p))[0] = ((uint64_t)(a) >> 0) & 0xFF, \
+   ((uint8_t *)(p))[1] = ((uint64_t)(a) >> 8) & 0xFF, \
+   ((uint8_t *)(p))[2] = ((uint64_t)(a) >> 16) & 0xFF, \
+   ((uint8_t *)(p))[3] = ((uint64_t)(a) >> 24) & 0xFF, \
+   ((uint8_t *)(p))[4] = ((uint64_t)(a) >> 32) & 0xFF, \
+   ((uint8_t *)(p))[5] = ((uint64_t)(a) >> 40) & 0xFF, \
+   ((uint8_t *)(p))[6] = ((uint64_t)(a) >> 48) & 0xFF, \
+   ((uint8_t *)(p))[7] = ((uint64_t)(a) >> 56) & 0xFF
+
+//Store unaligned 64-bit integer (big-endian encoding)
+#define STORE64BE(a, p) \
+   ((uint8_t *)(p))[0] = ((uint64_t)(a) >> 56) & 0xFF, \
+   ((uint8_t *)(p))[1] = ((uint64_t)(a) >> 48) & 0xFF, \
+   ((uint8_t *)(p))[2] = ((uint64_t)(a) >> 40) & 0xFF, \
+   ((uint8_t *)(p))[3] = ((uint64_t)(a) >> 32) & 0xFF, \
+   ((uint8_t *)(p))[4] = ((uint64_t)(a) >> 24) & 0xFF, \
+   ((uint8_t *)(p))[5] = ((uint64_t)(a) >> 16) & 0xFF, \
+   ((uint8_t *)(p))[6] = ((uint64_t)(a) >> 8) & 0xFF, \
+   ((uint8_t *)(p))[7] = ((uint64_t)(a) >> 0) & 0xFF
+
+//Swap a 16-bit integer
+#define SWAPINT16(x) ( \
+   (((x) & 0x00FF) << 8) | \
+   (((x) & 0xFF00) >> 8))
+
+//Swap a 32-bit integer
+#define SWAPINT32(x) ( \
+   (((x) & 0x000000FFUL) << 24) | \
+   (((x) & 0x0000FF00UL) << 8) | \
+   (((x) & 0x00FF0000UL) >> 8) | \
+   (((x) & 0xFF000000UL) >> 24))
+
+//Swap a 64-bit integer
+#define SWAPINT64(x) ( \
+   (((x) & 0x00000000000000FFULL) << 56) | \
+   (((x) & 0x000000000000FF00ULL) << 40) | \
+   (((x) & 0x0000000000FF0000ULL) << 24) | \
+   (((x) & 0x00000000FF000000ULL) << 8) | \
+   (((x) & 0x000000FF00000000ULL) >> 8) | \
+   (((x) & 0x0000FF0000000000ULL) >> 24) | \
+   (((x) & 0x00FF000000000000ULL) >> 40) | \
+   (((x) & 0xFF00000000000000ULL) >> 56))
+
+//Big-endian machine?
+#ifdef _CPU_BIG_ENDIAN
+
+//Host byte order to network byte order
+#define HTONS(value) (value)
+#define HTONL(value) (value)
+#define htons(value) ((uint16_t) (value))
+#define htonl(value) ((uint32_t) (value))
+
+//Network byte order to host byte order
+#define NTOHS(value) (value)
+#define NTOHL(value) (value)
+#define ntohs(value) ((uint16_t) (value))
+#define ntohl(value) ((uint32_t) (value))
+
+//Host byte order to little-endian byte order
+#define HTOLE16(value) SWAPINT16(value)
+#define HTOLE32(value) SWAPINT32(value)
+#define HTOLE64(value) SWAPINT64(value)
+#define htole16(value) swapInt16((uint16_t) (value))
+#define htole32(value) swapInt32((uint32_t) (value))
+#define htole64(value) swapInt64((uint64_t) (value))
+
+//Little-endian byte order to host byte order
+#define LETOH16(value) SWAPINT16(value)
+#define LETOH32(value) SWAPINT32(value)
+#define LETOH64(value) SWAPINT64(value)
+#define letoh16(value) swapInt16((uint16_t) (value))
+#define letoh32(value) swapInt32((uint32_t) (value))
+#define letoh64(value) swapInt64((uint64_t) (value))
+
+//Host byte order to big-endian byte order
+#define HTOBE16(value) (value)
+#define HTOBE32(value) (value)
+#define HTOBE64(value) (value)
+#define htobe16(value) ((uint16_t) (value))
+#define htobe32(value) ((uint32_t) (value))
+#define htobe64(value) ((uint64_t) (value))
+
+//Big-endian byte order to host byte order
+#define BETOH16(value) (value)
+#define BETOH32(value) (value)
+#define BETOH64(value) (value)
+#define betoh16(value) ((uint16_t) (value))
+#define betoh32(value) ((uint32_t) (value))
+#define betoh64(value) ((uint64_t) (value))
+
+//Little-endian machine?
+#else
+
+//Host byte order to network byte order
+#define HTONS(value) SWAPINT16(value)
+#define HTONL(value) SWAPINT32(value)
+#define htons(value) swapInt16((uint16_t) (value))
+#define htonl(value) swapInt32((uint32_t) (value))
+
+//Network byte order to host byte order
+#define NTOHS(value) SWAPINT16(value)
+#define NTOHL(value) SWAPINT32(value)
+#define ntohs(value) swapInt16((uint16_t) (value))
+#define ntohl(value) swapInt32((uint32_t) (value))
+
+//Host byte order to little-endian byte order
+#define HTOLE16(value) (value)
+#define HTOLE32(value) (value)
+#define HTOLE64(value) (value)
+#define htole16(value) ((uint16_t) (value))
+#define htole32(value) ((uint32_t) (value))
+#define htole64(value) ((uint64_t) (value))
+
+//Little-endian byte order to host byte order
+#define LETOH16(value) (value)
+#define LETOH32(value) (value)
+#define LETOH64(value) (value)
+#define letoh16(value) ((uint16_t) (value))
+#define letoh32(value) ((uint32_t) (value))
+#define letoh64(value) ((uint64_t) (value))
+
+//Host byte order to big-endian byte order
+#define HTOBE16(value) SWAPINT16(value)
+#define HTOBE32(value) SWAPINT32(value)
+#define HTOBE64(value) SWAPINT64(value)
+#define htobe16(value) swapInt16((uint16_t) (value))
+#define htobe32(value) swapInt32((uint32_t) (value))
+#define htobe64(value) swapInt64((uint64_t) (value))
+
+//Big-endian byte order to host byte order
+#define BETOH16(value) SWAPINT16(value)
+#define BETOH32(value) SWAPINT32(value)
+#define BETOH64(value) SWAPINT64(value)
+#define betoh16(value) swapInt16((uint16_t) (value))
+#define betoh32(value) swapInt32((uint32_t) (value))
+#define betoh64(value) swapInt64((uint64_t) (value))
+
+#endif
+
+//Byte order conversion functions
+uint16_t swapInt16(uint16_t value);
+uint32_t swapInt32(uint32_t value);
+uint64_t swapInt64(uint64_t value);
+
+//Bit reversal functions
+uint8_t reverseInt4(uint8_t value);
+uint8_t reverseInt8(uint8_t value);
+uint16_t reverseInt16(uint16_t value);
+uint32_t reverseInt32(uint32_t value);
+uint64_t reverseInt64(uint64_t value);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/date_time.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,322 @@
+/**
+ * @file date_time.c
+ * @brief Date and time management
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Dependencies
+#include <stdio.h>
+#include <string.h>
+#include "date_time.h"
+
+#if defined(_WIN32)
+   #include <time.h>
+#endif
+
+//Days
+static const char days[8][10] =
+{
+   "",
+   "Monday",
+   "Tuesday",
+   "Wednesday",
+   "Thursday",
+   "Friday",
+   "Saturday",
+   "Sunday"
+};
+
+//Months
+static const char months[13][10] =
+{
+   "",
+   "January",
+   "February",
+   "March",
+   "April",
+   "May",
+   "June",
+   "July",
+   "August",
+   "September",
+   "October",
+   "November",
+   "December"
+};
+
+
+/**
+ * @brief Format system time
+ * @param[in] time System time
+ * @param[out] str NULL-terminated string representing the specified time
+ * @return Pointer to the formatted string
+ **/
+
+const char_t *formatSystemTime(systime_t time, char_t *str)
+{
+   uint16_t hours;
+   uint8_t minutes;
+   uint8_t seconds;
+   uint16_t milliseconds;
+   static char_t buffer[24];
+
+   //Retrieve milliseconds
+   milliseconds = time % 1000;
+   time /= 1000;
+   //Retrieve seconds
+   seconds = time % 60;
+   time /= 60;
+   //Retrieve minutes
+   minutes = time % 60;
+   time /= 60;
+   //Retrieve hours
+   hours = time;
+
+   //The str parameter is optional
+   if(!str) str = buffer;
+
+   //Format system time
+   if(hours > 0)
+   {
+      sprintf(str, "%" PRIu16 "h %02" PRIu8 "min %02" PRIu8 "s %03" PRIu16 "ms",
+         hours, minutes, seconds, milliseconds);
+   }
+   else if(minutes > 0)
+   {
+      sprintf(str, "%" PRIu8 "min %02" PRIu8 "s %03" PRIu16 "ms",
+         minutes, seconds, milliseconds);
+   }
+   else if(seconds > 0)
+   {
+      sprintf(str, "%" PRIu8 "s %03" PRIu16 "ms", seconds, milliseconds);
+   }
+   else
+   {
+      sprintf(str, "%" PRIu16 "ms", milliseconds);
+   }
+
+   //Return a pointer to the formatted string
+   return str;
+}
+
+
+/**
+ * @brief Format date
+ * @param[in] date Pointer to a structure representing the date
+ * @param[out] str NULL-terminated string representing the specified date
+ * @return Pointer to the formatted string
+ **/
+
+const char_t *formatDate(const DateTime *date, char_t *str)
+{
+   static char_t buffer[40];
+
+   //The str parameter is optional
+   if(!str) str = buffer;
+
+   //Format date
+   if(date->dayOfWeek)
+   {
+      sprintf(str, "%s, %s %" PRIu8 ", %" PRIu16 " %02" PRIu8 ":%02" PRIu8 ":%02" PRIu8,
+         days[MIN(date->dayOfWeek, 7)], months[MIN(date->month, 12)], date->day,
+         date->year, date->hours, date->minutes, date->seconds);
+   }
+   else
+   {
+      sprintf(str, "%s %" PRIu8 ", %" PRIu16 " %02" PRIu8 ":%02" PRIu8 ":%02" PRIu8,
+         months[MIN(date->month, 12)], date->day, date->year,
+         date->hours, date->minutes, date->seconds);
+   }
+
+   //Return a pointer to the formatted string
+   return str;
+}
+
+
+/**
+ * @brief Get current date and time
+ * @param[out] date Pointer to a structure representing the date and time
+ **/
+
+void getCurrentDate(DateTime *date)
+{
+   //Retrieve current time
+   time_t time = getCurrentUnixTime();
+
+   //Convert Unix timestamp to date
+   convertUnixTimeToDate(time, date);
+}
+
+
+/**
+ * @brief Get current time
+ * @return Unix timestamp
+ **/
+
+time_t getCurrentUnixTime(void)
+{
+#if defined(_WIN32)
+   //Retrieve current time
+   return time(NULL);
+#else
+   //Not implemented
+   return 0;
+#endif
+}
+
+
+/**
+ * @brief Convert Unix timestamp to date
+ * @param[in] t Unix timestamp
+ * @param[out] date Pointer to a structure representing the date and time
+ **/
+
+void convertUnixTimeToDate(time_t t, DateTime *date)
+{
+   uint32_t a;
+   uint32_t b;
+   uint32_t c;
+   uint32_t d;
+   uint32_t e;
+   uint32_t f;
+
+   //Negative Unix time values are not supported
+   if(t < 1) t = 0;
+
+   //Clear milliseconds
+   date->milliseconds = 0;
+
+   //Retrieve hours, minutes and seconds
+   date->seconds = t % 60;
+   t /= 60;
+   date->minutes = t % 60;
+   t /= 60;
+   date->hours = t % 24;
+   t /= 24;
+
+   //Convert Unix time to date
+   a = (uint32_t) ((4 * t + 102032) / 146097 + 15);
+   b = (uint32_t) (t + 2442113 + a - (a / 4));
+   c = (20 * b - 2442) / 7305;
+   d = b - 365 * c - (c / 4);
+   e = d * 1000 / 30601;
+   f = d - e * 30 - e * 601 / 1000;
+
+   //January and February are counted as months 13 and 14 of the previous year
+   if(e <= 13)
+   {
+      c -= 4716;
+      e -= 1;
+   }
+   else
+   {
+      c -= 4715;
+      e -= 13;
+   }
+
+   //Retrieve year, month and day
+   date->year = c;
+   date->month = e;
+   date->day = f;
+
+   //Calculate day of week
+   date->dayOfWeek = computeDayOfWeek(c, e, f);
+}
+
+
+/**
+ * @brief Convert date to Unix timestamp
+ * @param[in] date Pointer to a structure representing the date and time
+ * @return Unix timestamp
+ **/
+
+time_t convertDateToUnixTime(const DateTime *date)
+{
+   uint_t y;
+   uint_t m;
+   uint_t d;
+   uint32_t t;
+
+   //Year
+   y = date->year;
+   //Month of year
+   m = date->month;
+   //Day of month
+   d = date->day;
+
+   //January and February are counted as months 13 and 14 of the previous year
+   if(m <= 2)
+   {
+      m += 12;
+      y -= 1;
+   }
+
+   //Convert years to days
+   t = (365 * y) + (y / 4) - (y / 100) + (y / 400);
+   //Convert months to days
+   t += (30 * m) + (3 * (m + 1) / 5) + d;
+   //Unix time starts on January 1st, 1970
+   t -= 719561;
+   //Convert days to seconds
+   t *= 86400;
+   //Add hours, minutes and seconds
+   t += (3600 * date->hours) + (60 * date->minutes) + date->seconds;
+
+   //Return Unix time
+   return t;
+}
+
+
+/**
+ * @brief Calculate day of week
+ * @param[in] y Year
+ * @param[in] m Month of year (in range 1 to 12)
+ * @param[in] d Day of month (in range 1 to 31)
+ * @return Day of week (in range 1 to 7)
+ **/
+
+uint8_t computeDayOfWeek(uint16_t y, uint8_t m, uint8_t d)
+{
+   uint_t h;
+   uint_t j;
+   uint_t k;
+
+   //January and February are counted as months 13 and 14 of the previous year
+   if(m <= 2)
+   {
+      m += 12;
+      y -= 1;
+   }
+
+   //J is the century
+   j = y / 100;
+   //K the year of the century
+   k = y % 100;
+
+   //Compute H using Zeller's congruence
+   h = d + (26 * (m + 1) / 10) + k + (k / 4) + (5 * j) + (j / 4);
+
+   //Return the day of the week
+   return ((h + 5) % 7) + 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/date_time.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,65 @@
+/**
+ * @file date_time.h
+ * @brief Date and time management
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DATE_TIME_H
+#define _DATE_TIME_H
+
+//Dependencies
+#include <time.h>
+#include "os_port.h"
+
+
+/**
+ * @brief Date and time representation
+ **/
+
+typedef struct
+{
+   uint16_t year;
+   uint8_t month;
+   uint8_t day;
+   uint8_t dayOfWeek;
+   uint8_t hours;
+   uint8_t minutes;
+   uint8_t seconds;
+   uint16_t milliseconds;
+} DateTime;
+
+
+//Date and time management
+const char_t *formatSystemTime(systime_t time, char_t *str);
+const char_t *formatDate(const DateTime *date, char_t *str);
+
+void getCurrentDate(DateTime *date);
+time_t getCurrentUnixTime(void);
+
+void convertUnixTimeToDate(time_t t, DateTime *date);
+time_t convertDateToUnixTime(const DateTime *date);
+
+uint8_t computeDayOfWeek(uint16_t y, uint8_t m, uint8_t d);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/debug.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,56 @@
+/**
+ * @file debug.c
+ * @brief Debugging facilities
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Dependencies
+#include "debug.h"
+
+
+/**
+ * @brief Display the contents of an array
+ * @param[in] stream Pointer to a FILE object that identifies an output stream
+ * @param[in] prepend String to prepend to the left of each line
+ * @param[in] data Pointer to the data array
+ * @param[in] length Number of bytes to display
+ **/
+
+void debugDisplayArray(FILE *stream,
+   const char_t *prepend, const void *data, size_t length)
+{
+   uint_t i;
+
+   for(i = 0; i < length; i++)
+   {
+      //Beginning of a new line?
+      if((i % 16) == 0)
+         fprintf(stream, "%s", prepend);
+      //Display current data byte
+      fprintf(stream, "%02" PRIX8 " ", *((uint8_t *) data + i));
+      //End of current line?
+      if((i % 16) == 15 || i == (length - 1))
+         fprintf(stream, "\r\n");
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/debug.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,113 @@
+/**
+ * @file debug.h
+ * @brief Debugging facilities
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DEBUG_H
+#define _DEBUG_H
+
+//Dependencies
+#include <stdio.h>
+#include "os_port.h"
+
+//Trace level definitions
+#define TRACE_LEVEL_OFF      0
+#define TRACE_LEVEL_FATAL    1
+#define TRACE_LEVEL_ERROR    2
+#define TRACE_LEVEL_WARNING  3
+#define TRACE_LEVEL_INFO     4
+#define TRACE_LEVEL_DEBUG    5
+
+//Default trace level
+#ifndef TRACE_LEVEL
+   #define TRACE_LEVEL TRACE_LEVEL_DEBUG
+#endif
+
+//Trace output redirection
+#ifndef TRACE_PRINTF
+   #define TRACE_PRINTF(...) osSuspendAllTasks(), fprintf(stderr, __VA_ARGS__), osResumeAllTasks()
+#endif
+
+#ifndef TRACE_ARRAY
+   #define TRACE_ARRAY(p, a, n) osSuspendAllTasks(), debugDisplayArray(stderr, p, a, n), osResumeAllTasks()
+#endif
+
+#ifndef TRACE_MPI
+   #define TRACE_MPI(p, a) osSuspendAllTasks(), mpiDump(stderr, p, a), osResumeAllTasks()
+#endif
+
+//Debugging macros
+#if (TRACE_LEVEL >= TRACE_LEVEL_FATAL)
+   #define TRACE_FATAL(...) TRACE_PRINTF(__VA_ARGS__)
+#else
+   #define TRACE_FATAL(...)
+#endif
+
+#if (TRACE_LEVEL >= TRACE_LEVEL_ERROR)
+   #define TRACE_ERROR(...) TRACE_PRINTF(__VA_ARGS__)
+#else
+   #define TRACE_ERROR(...)
+#endif
+
+#if (TRACE_LEVEL >= TRACE_LEVEL_WARNING)
+   #define TRACE_WARNING(...) TRACE_PRINTF(__VA_ARGS__)
+#else
+   #define TRACE_WARNING(...)
+#endif
+
+#if (TRACE_LEVEL >= TRACE_LEVEL_INFO)
+   #define TRACE_INFO(...) TRACE_PRINTF(__VA_ARGS__)
+   #define TRACE_INFO_ARRAY(p, a, n) TRACE_ARRAY(p, a, n)
+   #define TRACE_INFO_NET_BUFFER(p, b, o, n)
+   #define TRACE_INFO_MPI(p, a) TRACE_MPI(p, a)
+#else
+   #define TRACE_INFO(...)
+   #define TRACE_INFO_ARRAY(p, a, n)
+   #define TRACE_INFO_NET_BUFFER(p, b, o, n)
+   #define TRACE_INFO_MPI(p, a)
+#endif
+
+#if (TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+   #define TRACE_DEBUG(...) TRACE_PRINTF(__VA_ARGS__)
+   #define TRACE_DEBUG_ARRAY(p, a, n) TRACE_ARRAY(p, a, n)
+   #define TRACE_DEBUG_NET_BUFFER(p, b, o, n)
+   #define TRACE_DEBUG_MPI(p, a) TRACE_MPI(p, a)
+#else
+   #define TRACE_DEBUG(...)
+   #define TRACE_DEBUG_ARRAY(p, a, n)
+   #define TRACE_DEBUG_NET_BUFFER(p, b, o, n)
+   #define TRACE_DEBUG_MPI(p, a)
+#endif
+
+//Debug related functions
+void debugInit(uint32_t baudrate);
+
+void debugDisplayArray(FILE *stream,
+   const char_t *prepend, const void *data, size_t length);
+
+//Deprecated definitions
+#define TRACE_LEVEL_NO_TRACE TRACE_LEVEL_OFF
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/error.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,243 @@
+/**
+ * @file error.h
+ * @brief Error codes description
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _ERROR_H
+#define _ERROR_H
+
+
+/**
+ * @brief Error codes
+ **/
+
+typedef enum
+{
+   NO_ERROR = 0,                    ///<Success
+   ERROR_FAILURE = 1,               ///<Generic error code
+
+   ERROR_INVALID_PARAMETER,         ///<Invalid parameter
+   ERROR_PARAMETER_OUT_OF_RANGE,    ///<Specified parameter is out of range
+
+   ERROR_BAD_CRC,
+   ERROR_BAD_BLOCK,
+   ERROR_INVALID_RECIPIENT,         ///<Invalid recipient
+   ERROR_INVALID_INTERFACE,         ///<Invalid interface
+   ERROR_INVALID_ENDPOINT,          ///<Invalid endpoint
+   ERROR_INVALID_ALT_SETTING,       ///<Alternate setting does not exist
+   ERROR_UNSUPPORTED_REQUEST,       ///<Unsupported request
+   ERROR_UNSUPPORTED_CONFIGURATION, ///<Unsupported configuration
+   ERROR_UNSUPPORTED_FEATURE,       ///<Unsupported feature
+   ERROR_ENDPOINT_BUSY,             ///<Endpoint already in use
+   ERROR_USB_RESET,
+   ERROR_ABORTED,
+
+   ERROR_OUT_OF_MEMORY = 100,
+   ERROR_OUT_OF_RESOURCES,
+   ERROR_INVALID_REQUEST,
+   ERROR_NOT_IMPLEMENTED,
+   ERROR_VERSION_NOT_SUPPORTED,
+   ERROR_INVALID_SYNTAX,
+   ERROR_AUTHENTICATION_FAILED,
+   ERROR_UNEXPECTED_RESPONSE,
+   ERROR_UNEXPECTED_VALUE,
+
+   ERROR_OPEN_FAILED = 200,
+   ERROR_CONNECTION_FAILED,
+   ERROR_CONNECTION_REFUSED,
+   ERROR_CONNECTION_CLOSING,
+   ERROR_CONNECTION_RESET,
+   ERROR_NOT_CONNECTED,
+   ERROR_ALREADY_CLOSED,
+   ERROR_ALREADY_CONNECTED,
+   ERROR_INVALID_SOCKET,
+   ERROR_PROTOCOL_UNREACHABLE,
+   ERROR_PORT_UNREACHABLE,
+   ERROR_INVALID_FRAME,
+   ERROR_INVALID_HEADER,
+   ERROR_WRONG_CHECKSUM,
+   ERROR_WRONG_IDENTIFIER,
+   ERROR_WRONG_CLIENT_ID,
+   ERROR_WRONG_SERVER_ID,
+   ERROR_NO_RESPONSE,
+   ERROR_RECEIVE_QUEUE_FULL,
+   ERROR_TIMEOUT,
+   ERROR_WOULD_BLOCK,
+   ERROR_INVALID_NAME,
+   ERROR_INVALID_OPTION,
+   ERROR_UNEXPECTED_STATE,
+   ERROR_INVALID_COMMAND,
+   ERROR_INVALID_PROTOCOL,
+   ERROR_INVALID_STATUS,
+   ERROR_INVALID_ADDRESS,
+   ERROR_INVALID_MESSAGE,
+   ERROR_INVALID_KEY,
+   ERROR_INVALID_KEY_LENGTH,
+   ERROR_INVALID_CHARACTER,
+   ERROR_INVALID_LENGTH,
+   ERROR_INVALID_PADDING,
+   ERROR_INVALID_MAC,
+   ERROR_INVALID_TAG,
+   ERROR_INVALID_TYPE,
+   ERROR_INVALID_VALUE,
+   ERROR_INVALID_CLASS,
+   ERROR_INVALID_VERSION,
+   ERROR_INVALID_PIN_CODE,
+   ERROR_WRONG_LENGTH,
+   ERROR_WRONG_TYPE,
+   ERROR_WRONG_ENCODING,
+   ERROR_WRONG_VALUE,
+   ERROR_UNSUPPORTED_TYPE,
+   ERROR_UNSUPPORTED_CIPHER_SUITE,
+   ERROR_UNSUPPORTED_CIPHER_MODE,
+   ERROR_UNSUPPORTED_CIPHER_ALGO,
+   ERROR_UNSUPPORTED_KEY_EXCH_METHOD,
+   ERROR_UNSUPPORTED_SIGNATURE_ALGO,
+   ERROR_INVALID_SIGNATURE_ALGO,
+   ERROR_CERTIFICATE_REQUIRED,
+   ERROR_MESSAGE_TOO_LONG,
+   ERROR_OUT_OF_RANGE,
+   ERROR_MESSAGE_DISCARDED,
+
+   ERROR_INVALID_PACKET,
+   ERROR_BUFFER_EMPTY,
+   ERROR_BUFFER_OVERFLOW,
+
+   ERROR_INVALID_RESOURCE,
+   ERROR_INVALID_PATH,
+   ERROR_NOT_FOUND,
+   ERROR_ACCESS_DENIED,
+   ERROR_NOT_WRITABLE,
+   ERROR_AUTH_REQUIRED,
+
+   ERROR_TRANSMITTER_BUSY,
+   ERROR_NO_RUNNING,
+
+   ERROR_INVALID_FILE = 300,
+   ERROR_FILE_NOT_FOUND,
+   ERROR_FILE_OPENING_FAILED,
+   ERROR_FILE_READING_FAILED,
+   ERROR_END_OF_FILE,
+   ERROR_UNEXPECTED_END_OF_FILE,
+   ERROR_UNKNOWN_FILE_FORMAT,
+
+   ERROR_INVALID_DIRECTORY,
+   ERROR_DIRECTORY_NOT_FOUND,
+
+   ERROR_FILE_SYSTEM_NOT_SUPPORTED = 400,
+   ERROR_UNKNOWN_FILE_SYSTEM,
+   ERROR_INVALID_FILE_SYSTEM,
+   ERROR_INVALID_BOOT_SECTOR_SIGNATURE,
+   ERROR_INVALID_SECTOR_SIZE,
+   ERROR_INVALID_CLUSTER_SIZE,
+   ERROR_INVALID_FILE_RECORD_SIZE,
+   ERROR_INVALID_INDEX_BUFFER_SIZE,
+   ERROR_INVALID_VOLUME_DESCRIPTOR_SIGNATURE,
+   ERROR_INVALID_VOLUME_DESCRIPTOR,
+   ERROR_INVALID_FILE_RECORD,
+   ERROR_INVALID_INDEX_BUFFER,
+   ERROR_INVALID_DATA_RUNS,
+   ERROR_WRONG_TAG_IDENTIFIER,
+   ERROR_WRONG_TAG_CHECKSUM,
+   ERROR_WRONG_MAGIC_NUMBER,
+   ERROR_WRONG_SEQUENCE_NUMBER,
+   ERROR_DESCRIPTOR_NOT_FOUND,
+   ERROR_ATTRIBUTE_NOT_FOUND,
+   ERROR_RESIDENT_ATTRIBUTE,
+   ERROR_NOT_RESIDENT_ATTRIBUTE,
+   ERROR_INVALID_SUPER_BLOCK,
+   ERROR_INVALID_SUPER_BLOCK_SIGNATURE,
+   ERROR_INVALID_BLOCK_SIZE,
+   ERROR_UNSUPPORTED_REVISION_LEVEL,
+   ERROR_INVALID_INODE_SIZE,
+   ERROR_INODE_NOT_FOUND,
+
+   ERROR_UNEXPECTED_MESSAGE = 500,
+
+   ERROR_URL_TOO_LONG,
+   ERROR_QUERY_STRING_TOO_LONG,
+
+   ERROR_NO_ADDRESS,
+   ERROR_NO_BINDING,
+   ERROR_NOT_ON_LINK,
+   ERROR_USE_MULTICAST,
+   ERROR_NAK_RECEIVED,
+
+   ERROR_NO_CARRIER,
+
+   ERROR_INVALID_LEVEL,
+   ERROR_WRONG_STATE,
+   ERROR_END_OF_STREAM,
+   ERROR_LINK_DOWN,
+   ERROR_INVALID_OPTION_LENGTH,
+   ERROR_IN_PROGRESS,
+
+   ERROR_NO_ACK,
+   ERROR_INVALID_METADATA,
+   ERROR_NOT_CONFIGURED,
+   ERROR_NAME_RESOLUTION_FAILED,
+   ERROR_NO_ROUTE,
+
+   ERROR_WRITE_FAILED,
+   ERROR_READ_FAILED,
+   ERROR_UPLOAD_FAILED,
+
+   ERROR_INVALID_SIGNATURE,
+
+   ERROR_BAD_RECORD_MAC,
+   ERROR_RECORD_OVERFLOW,
+   ERROR_HANDSHAKE_FAILED,
+   ERROR_NO_CERTIFICATE,
+   ERROR_BAD_CERTIFICATE,
+   ERROR_UNSUPPORTED_CERTIFICATE,
+   ERROR_CERTIFICATE_EXPIRED,
+   ERROR_UNKNOWN_CA,
+   ERROR_DECODING_FAILED,
+   ERROR_DECRYPTION_FAILED,
+   ERROR_ILLEGAL_PARAMETER,
+   ERROR_UNSUPPORTED_EXTENSION,
+
+   ERROR_MORE_DATA_REQUIRED,
+   ERROR_TLS_NOT_SUPPORTED,
+   ERROR_PRNG_NOT_READY,
+   ERROR_SERVICE_CLOSING,
+   ERROR_INVALID_TIMESTAMP,
+   ERROR_NO_DNS_SERVER,
+
+   ERROR_OBJECT_NOT_FOUND,
+   ERROR_INSTANCE_NOT_FOUND,
+   ERROR_ADDRESS_NOT_FOUND,
+
+   ERROR_UNKNOWN_IDENTITY,
+   ERROR_UNKNOWN_ENGINE_ID,
+   ERROR_UNKNOWN_USER_NAME,
+   ERROR_UNSUPPORTED_SECURITY_LEVEL,
+   ERROR_NOT_IN_TIME_WINDOW,
+
+   ERROR_NO_MATCH,
+   ERROR_PARTIAL_MATCH
+} error_t;
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/fs_port.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,148 @@
+/**
+ * @file fs_port.h
+ * @brief File system abstraction layer
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _FS_PORT_H
+#define _FS_PORT_H
+
+//Dependencies
+#include "fs_port_config.h"
+#include "os_port.h"
+#include "date_time.h"
+#include "error.h"
+
+//Number of files that can be opened simultaneously
+#ifndef FS_MAX_FILES
+   #define FS_MAX_FILES 8
+#elif (FS_MAX_FILES < 1)
+   #error FS_MAX_FILES parameter is not valid
+#endif
+
+//Number of directories that can be opened simultaneously
+#ifndef FS_MAX_DIRS
+   #define FS_MAX_DIRS 8
+#elif (FS_MAX_DIRS < 1)
+   #error FS_MAX_DIRS parameter is not valid
+#endif
+
+//Maximum filename length
+#ifndef FS_MAX_NAME_LEN
+   #define FS_MAX_NAME_LEN 127
+#elif (FS_MAX_NAME_LEN < 11)
+   #error FS_MAX_NAME_LEN parameter is not valid
+#endif
+
+
+/**
+ * @brief File attributes
+ **/
+
+typedef enum
+{
+   FS_FILE_ATTR_READ_ONLY   = 0x01,
+   FS_FILE_ATTR_HIDDEN      = 0x02,
+   FS_FILE_ATTR_SYSTEM      = 0x04,
+   FS_FILE_ATTR_VOLUME_NAME = 0x08,
+   FS_FILE_ATTR_DIRECTORY   = 0x10,
+   FS_FILE_ATTR_ARCHIVE     = 0x20
+} FsFileAttributes;
+
+
+/**
+ * @brief File access mode
+ **/
+
+typedef enum
+{
+   FS_FILE_MODE_READ   = 1,
+   FS_FILE_MODE_WRITE  = 2,
+   FS_FILE_MODE_CREATE = 4,
+   FS_FILE_MODE_TRUNC  = 8
+} FsFileMode;
+
+
+/**
+ * @brief File seek origin
+ **/
+
+typedef enum
+{
+   FS_SEEK_SET = 0,
+   FS_SEEK_CUR = 1,
+   FS_SEEK_END = 2
+} FsSeekOrigin;
+
+
+/**
+ * @brief Directory entry
+ **/
+
+typedef struct
+{
+   uint32_t attributes;
+   uint32_t size;
+   DateTime modified;
+   char_t name[FS_MAX_NAME_LEN + 1];
+} FsDirEntry;
+
+
+/**
+ * @brief File handle
+ **/
+
+typedef void FsFile;
+
+
+/**
+ * @brief Directory handle
+ **/
+
+typedef void FsDir;
+
+
+//File system abstraction layer
+error_t fsInit(void);
+
+bool_t fsFileExists(const char_t *path);
+error_t fsGetFileSize(const char_t *path, uint32_t *size);
+error_t fsRenameFile(const char_t *oldPath, const char_t *newPath);
+error_t fsDeleteFile(const char_t *path);
+
+FsFile *fsOpenFile(const char_t *path, uint_t mode);
+error_t fsSeekFile(FsFile *file, int_t offset, uint_t origin);
+error_t fsWriteFile(FsFile *file, void *data, size_t length);
+error_t fsReadFile(FsFile *file, void *data, size_t size, size_t *length);
+void fsCloseFile(FsFile *file);
+
+bool_t fsDirExists(const char_t *path);
+error_t fsCreateDir(const char_t *path);
+error_t fsRemoveDir(const char_t *path);
+
+FsDir *fsOpenDir(const char_t *path);
+error_t fsReadDir(FsDir *dir, FsDirEntry *dirEntry);
+void fsCloseDir(FsDir *dir);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/fs_port_fatfs.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,764 @@
+/**
+ * @file fs_port_fatfs.c
+ * @brief File system abstraction layer (FatFs)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Dependencies
+#include <string.h>
+#include "fs_port.h"
+#include "error.h"
+#include "debug.h"
+
+//FatFs specific headers
+#include "ff.h"
+
+//File system objects
+static FATFS fs;
+static FIL fileTable[FS_MAX_FILES];
+static DIR dirTable[FS_MAX_DIRS];
+
+//Mutex that protects critical sections
+static OsMutex fsMutex;
+
+
+/**
+ * @brief File system initialization
+ * @return Error code
+ **/
+
+error_t fsInit(void)
+{
+   FRESULT res;
+
+   //Clear file system objects
+   memset(fileTable, 0, sizeof(fileTable));
+   memset(dirTable, 0, sizeof(dirTable));
+
+   //Create a mutex to protect critical sections
+   if(!osCreateMutex(&fsMutex))
+   {
+      //Failed to create mutex
+      return ERROR_OUT_OF_RESOURCES;
+   }
+
+//Revision 0.09b or lower?
+#if (_FATFS == 124 || _FATFS == 126 || _FATFS == 8085 || _FATFS == 8255 || \
+   _FATFS == 8237 || _FATFS == 6502 || _FATFS == 4004 || _FATFS == 82786)
+   //Mount file system
+   res = f_mount(0, &fs);
+//Revision 0.10 or higher?
+#else
+   //Mount file system
+   res = f_mount(&fs, "", 1);
+#endif
+
+   //Failed to mount file system?
+   if(res != FR_OK)
+   {
+      //Clean up side effects
+      osDeleteMutex(&fsMutex);
+      //Report an error
+      return ERROR_FAILURE;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Check whether a file exists
+ * @param[in] path NULL-terminated string specifying the filename
+ * @return The function returns TRUE if the file exists. Otherwise FALSE is returned
+ **/
+
+bool_t fsFileExists(const char_t *path)
+{
+   FRESULT res;
+   FILINFO fno;
+
+//Long filename support?
+#if (_USE_LFN != 0)
+   fno.lfname = NULL;
+   fno.lfsize = 0;
+#endif
+
+   //Make sure the pathname is valid
+   if(path == NULL)
+      return FALSE;
+
+#if (_FS_REENTRANT == 0)
+   //Enter critical section
+   osAcquireMutex(&fsMutex);
+#endif
+
+   //Check whether the file exists
+   res = f_stat(path, &fno);
+
+#if (_FS_REENTRANT == 0)
+   //Leave critical section
+   osReleaseMutex(&fsMutex);
+#endif
+
+   //Any error to report?
+   if(res != FR_OK)
+      return FALSE;
+
+   //Valid file?
+   if(fno.fattrib & AM_DIR)
+      return FALSE;
+   else
+      return TRUE;
+}
+
+
+/**
+ * @brief Retrieve the size of the specified file
+ * @param[in] path NULL-terminated string specifying the filename
+ * @param[out] size Size of the file in bytes
+ * @return Error code
+ **/
+
+error_t fsGetFileSize(const char_t *path, uint32_t *size)
+{
+   FRESULT res;
+   FILINFO fno;
+
+//Long filename support?
+#if (_USE_LFN != 0)
+   fno.lfname = NULL;
+   fno.lfsize = 0;
+#endif
+
+   //Check parameters
+   if(path == NULL || size == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (_FS_REENTRANT == 0)
+   //Enter critical section
+   osAcquireMutex(&fsMutex);
+#endif
+
+   //Retrieve information about the specified file
+   res = f_stat(path, &fno);
+
+#if (_FS_REENTRANT == 0)
+   //Leave critical section
+   osReleaseMutex(&fsMutex);
+#endif
+
+   //Any error to report?
+   if(res != FR_OK)
+      return ERROR_FAILURE;
+   //Valid file?
+   if(fno.fattrib & AM_DIR)
+      return ERROR_FAILURE;
+
+   //Return the size of the file
+   *size = fno.fsize;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Rename the specified file
+ * @param[in] oldPath NULL-terminated string specifying the pathname of the file to be renamed
+ * @param[in] newPath NULL-terminated string specifying the new filename
+ * @return Error code
+ **/
+
+error_t fsRenameFile(const char_t *oldPath, const char_t *newPath)
+{
+   FRESULT res;
+
+   //Check parameters
+   if(oldPath == NULL || newPath == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (_FS_REENTRANT == 0)
+   //Enter critical section
+   osAcquireMutex(&fsMutex);
+#endif
+
+   //Rename the specified file
+   res = f_rename(oldPath, newPath);
+
+#if (_FS_REENTRANT == 0)
+   //Leave critical section
+   osReleaseMutex(&fsMutex);
+#endif
+
+   //Any error to report?
+   if(res != FR_OK)
+      return ERROR_FAILURE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Delete a file
+ * @param[in] path NULL-terminated string specifying the filename
+ * @return Error code
+ **/
+
+error_t fsDeleteFile(const char_t *path)
+{
+   FRESULT res;
+
+   //Make sure the pathname is valid
+   if(path == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (_FS_REENTRANT == 0)
+   //Enter critical section
+   osAcquireMutex(&fsMutex);
+#endif
+
+   //Delete the specified file
+   res = f_unlink(path);
+
+#if (_FS_REENTRANT == 0)
+   //Leave critical section
+   osReleaseMutex(&fsMutex);
+#endif
+
+   //Any error to report?
+   if(res != FR_OK)
+      return ERROR_FAILURE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Open the specified file for reading or writing
+ * @param[in] path NULL-terminated string specifying the filename
+ * @param[in] mode Type of access permitted (FS_FILE_MODE_READ,
+ *   FS_FILE_MODE_WRITE or FS_FILE_MODE_CREATE)
+ * @return File handle
+ **/
+
+FsFile *fsOpenFile(const char_t *path, uint_t mode)
+{
+   uint_t i;
+   uint_t flags;
+   FRESULT res;
+
+   //File pointer
+   FsFile *file = NULL;
+
+   //Make sure the pathname is valid
+   if(path == NULL)
+      return NULL;
+
+   //Enter critical section
+   osAcquireMutex(&fsMutex);
+
+   //Loop through the file objects
+   for(i = 0; i < FS_MAX_FILES; i++)
+   {
+      //Unused file object found?
+      if(fileTable[i].fs == NULL)
+      {
+         //Default access mode
+         flags = 0;
+
+         //Check access mode
+         if(mode & FS_FILE_MODE_READ)
+            flags |= FA_READ;
+         if(mode & FS_FILE_MODE_WRITE)
+            flags |= FA_WRITE;
+         if(mode & FS_FILE_MODE_CREATE)
+            flags |= FA_OPEN_ALWAYS;
+         if(mode & FS_FILE_MODE_TRUNC)
+            flags |= FA_CREATE_ALWAYS;
+
+         //Open the specified file
+         res = f_open(&fileTable[i], path, flags);
+
+         //Check status code
+         if(res == FR_OK)
+            file = &fileTable[i];
+
+         //Stop immediately
+         break;
+      }
+   }
+
+   //Leave critical section
+   osReleaseMutex(&fsMutex);
+   //Return a handle to the file
+   return file;
+}
+
+
+/**
+ * @brief Move to specified position in file
+ * @param[in] file Handle that identifies the file
+ * @param[in] offset Number of bytes to move from origin
+ * @param[in] origin Position used as reference for the offset (FS_SEEK_SET,
+ *   FS_SEEK_CUR or FS_SEEK_END)
+ * @return Error code
+ **/
+
+error_t fsSeekFile(FsFile *file, int_t offset, uint_t origin)
+{
+   FRESULT res;
+
+   //Make sure the file pointer is valid
+   if(file == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (_FS_REENTRANT == 0)
+   //Enter critical section
+   osAcquireMutex(&fsMutex);
+#endif
+
+   //Offset is relative to the current file pointer position?
+   if(origin == FS_SEEK_CUR)
+      offset += f_tell((FIL *) file);
+   //Offset is relative to the end of the file?
+   else if(origin == FS_SEEK_END)
+      offset += f_size((FIL *) file);
+
+   //Move read/write pointer
+   res = f_lseek((FIL *) file, offset);
+
+#if (_FS_REENTRANT == 0)
+   //Leave critical section
+   osReleaseMutex(&fsMutex);
+#endif
+
+   //Any error to report?
+   if(res != FR_OK)
+      return ERROR_FAILURE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write data to the specified file
+ * @param[in] file Handle that identifies the file to be written
+ * @param[in] data Pointer to a buffer containing the data to be written
+ * @param[in] length Number of data bytes to write
+ * @return Error code
+ **/
+
+error_t fsWriteFile(FsFile *file, void *data, size_t length)
+{
+   UINT n;
+   FRESULT res;
+
+   //Make sure the file pointer is valid
+   if(file == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (_FS_REENTRANT == 0)
+   //Enter critical section
+   osAcquireMutex(&fsMutex);
+#endif
+
+   //Write data
+   res = f_write((FIL *) file, data, length, &n);
+
+#if (_FS_REENTRANT == 0)
+   //Leave critical section
+   osReleaseMutex(&fsMutex);
+#endif
+
+   //Any error to report?
+   if(res != FR_OK)
+      return ERROR_FAILURE;
+
+   //Sanity check
+   if(n != length)
+      return ERROR_FAILURE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Read data from the specified file
+ * @param[in] file Handle that identifies the file to be read
+ * @param[in] data Pointer to the buffer where to copy the data
+ * @param[in] size Size of the buffer, in bytes
+ * @param[out] length Number of data bytes that have been read
+ * @return Error code
+ **/
+
+error_t fsReadFile(FsFile *file, void *data, size_t size, size_t *length)
+{
+   UINT n;
+   FRESULT res;
+
+   //No data has been read yet
+   *length = 0;
+
+   //Make sure the file pointer is valid
+   if(file == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (_FS_REENTRANT == 0)
+   //Enter critical section
+   osAcquireMutex(&fsMutex);
+#endif
+
+   //Read data
+   res = f_read((FIL *) file, data, size, &n);
+
+#if (_FS_REENTRANT == 0)
+   //Leave critical section
+   osReleaseMutex(&fsMutex);
+#endif
+
+   //Any error to report?
+   if(res != FR_OK)
+      return ERROR_FAILURE;
+
+   //End of file?
+   if(!n)
+      return ERROR_END_OF_FILE;
+
+   //Total number of data that have been read
+   *length = n;
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Close a file
+ * @param[in] file Handle that identifies the file to be closed
+ **/
+
+void fsCloseFile(FsFile *file)
+{
+   //Make sure the file pointer is valid
+   if(file != NULL)
+   {
+      //Enter critical section
+      osAcquireMutex(&fsMutex);
+
+      //Close the specified file
+      f_close((FIL *) file);
+      //Mark the corresponding entry as free
+      ((FIL *) file)->fs = NULL;
+
+      //Leave critical section
+      osReleaseMutex(&fsMutex);
+   }
+}
+
+
+/**
+ * @brief Check whether a directory exists
+ * @param[in] path NULL-terminated string specifying the directory path
+ * @return The function returns TRUE if the directory exists. Otherwise FALSE is returned
+ **/
+
+bool_t fsDirExists(const char_t *path)
+{
+   FRESULT res;
+   FILINFO fno;
+
+//Long filename support?
+#if (_USE_LFN != 0)
+   fno.lfname = NULL;
+   fno.lfsize = 0;
+#endif
+
+   //Make sure the pathname is valid
+   if(path == NULL)
+      return FALSE;
+
+   //Root directory?
+   if(!strcmp(path, "/"))
+      return TRUE;
+
+#if (_FS_REENTRANT == 0)
+   //Enter critical section
+   osAcquireMutex(&fsMutex);
+#endif
+
+   //Check whether the file exists
+   res = f_stat(path, &fno);
+
+#if (_FS_REENTRANT == 0)
+   //Leave critical section
+   osReleaseMutex(&fsMutex);
+#endif
+
+   //Any error to report?
+   if(res != FR_OK)
+      return FALSE;
+
+   //Valid directory?
+   if(fno.fattrib & AM_DIR)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Create a directory
+ * @param[in] path NULL-terminated string specifying the directory path
+ * @return Error code
+ **/
+
+error_t fsCreateDir(const char_t *path)
+{
+   FRESULT res;
+
+   //Make sure the pathname is valid
+   if(path == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (_FS_REENTRANT == 0)
+   //Enter critical section
+   osAcquireMutex(&fsMutex);
+#endif
+
+   //Create a new directory
+   res = f_mkdir(path);
+
+#if (_FS_REENTRANT == 0)
+   //Leave critical section
+   osReleaseMutex(&fsMutex);
+#endif
+
+   //Any error to report?
+   if(res != FR_OK)
+      return ERROR_FAILURE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Remove a directory
+ * @param[in] path NULL-terminated string specifying the directory path
+ * @return Error code
+ **/
+
+error_t fsRemoveDir(const char_t *path)
+{
+   FRESULT res;
+
+   //Make sure the pathname is valid
+   if(path == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (_FS_REENTRANT == 0)
+   //Enter critical section
+   osAcquireMutex(&fsMutex);
+#endif
+
+   //Remove the specified directory
+   res = f_unlink(path);
+
+#if (_FS_REENTRANT == 0)
+   //Leave critical section
+   osReleaseMutex(&fsMutex);
+#endif
+
+   //Any error to report?
+   if(res != FR_OK)
+      return ERROR_FAILURE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Open a directory stream
+ * @param[in] path NULL-terminated string specifying the directory path
+ * @return Directory handle
+ **/
+
+FsDir *fsOpenDir(const char_t *path)
+{
+   uint_t i;
+   FRESULT res;
+
+   //Directory pointer
+   FsDir *dir = NULL;
+
+   //Make sure the pathname is valid
+   if(path == NULL)
+      return NULL;
+
+   //Enter critical section
+   osAcquireMutex(&fsMutex);
+
+   //Loop through the directory objects
+   for(i = 0; i < FS_MAX_DIRS; i++)
+   {
+      //Unused directory object found?
+      if(dirTable[i].fs == NULL)
+      {
+         //Open the specified directory
+         res = f_opendir(&dirTable[i], path);
+
+         //Check status code
+         if(res == FR_OK)
+            dir = &dirTable[i];
+
+         //Stop immediately
+         break;
+      }
+   }
+
+   //Leave critical section
+   osReleaseMutex(&fsMutex);
+   //Return a handle to the directory
+   return dir;
+}
+
+
+/**
+ * @brief Read an entry from the specified directory stream
+ * @param[in] dir Handle that identifies the directory
+ * @param[out] dirEntry Pointer to a directory entry
+ * @return Error code
+ **/
+
+error_t fsReadDir(FsDir *dir, FsDirEntry *dirEntry)
+{
+   FRESULT res;
+   FILINFO fno;
+   char_t *fn;
+   size_t n;
+
+//Long filename support?
+#if (_USE_LFN != 0)
+   char_t lfn[_MAX_LFN + 1];
+   fno.lfname = lfn;
+   fno.lfsize = sizeof(lfn);
+#endif
+
+   //Make sure the directory pointer is valid
+   if(dir == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (_FS_REENTRANT == 0)
+   //Enter critical section
+   osAcquireMutex(&fsMutex);
+#endif
+
+   //Read the specified directory
+   res = f_readdir((DIR *) dir, &fno);
+
+#if (_FS_REENTRANT == 0)
+   //Leave critical section
+   osReleaseMutex(&fsMutex);
+#endif
+
+   //Any error to report?
+   if(res != FR_OK)
+      return ERROR_FAILURE;
+
+   //End of the directory stream?
+   if(fno.fname[0] == '\0')
+      return ERROR_END_OF_STREAM;
+
+//Long filename support?
+#if (_USE_LFN != 0)
+   fn = (*fno.lfname != '\0') ? fno.lfname : fno.fname;
+#else
+   fn = fno.fname;
+#endif
+
+   //File attributes
+   dirEntry->attributes = fno.fattrib;
+   //File size
+   dirEntry->size = fno.fsize;
+   //Last modified date
+   dirEntry->modified.year = 1980 + ((fno.fdate >> 9) & 0x7F);
+   dirEntry->modified.month = (fno.fdate >> 5) & 0x0F;
+   dirEntry->modified.day = fno.fdate & 0x1F;
+   dirEntry->modified.dayOfWeek = 0;
+   //Last modified time
+   dirEntry->modified.hours = (fno.ftime >> 11) & 0x1F;
+   dirEntry->modified.minutes = (fno.ftime >> 5) & 0x3F;
+   dirEntry->modified.seconds = (fno.ftime & 0x1F) * 2;
+   dirEntry->modified.milliseconds = 0;
+
+   //Make sure the date is valid
+   dirEntry->modified.month = MAX(dirEntry->modified.month, 1);
+   dirEntry->modified.month = MIN(dirEntry->modified.month, 12);
+   dirEntry->modified.day = MAX(dirEntry->modified.day, 1);
+   dirEntry->modified.day = MIN(dirEntry->modified.day, 31);
+
+   //Retrieve the length of the file name
+   n = strlen(fn);
+   //Limit the number of characters to be copied
+   n = MIN(n, FS_MAX_NAME_LEN);
+
+   //Copy file name
+   strncpy(dirEntry->name, fn, n);
+   //Properly terminate the string with a NULL character
+   dirEntry->name[n] = '\0';
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Close a directory stream
+ * @param[in] dir Handle that identifies the directory to be closed
+ **/
+
+void fsCloseDir(FsDir *dir)
+{
+   //Make sure the directory pointer is valid
+   if(dir != NULL)
+   {
+      //Enter critical section
+      osAcquireMutex(&fsMutex);
+
+#if (_FATFS == 80960)
+      //Close the specified directory
+      f_closedir((DIR *) dir);
+#endif
+      //Mark the corresponding entry as free
+      ((DIR *) dir)->fs = NULL;
+
+      //Leave critical section
+      osReleaseMutex(&fsMutex);
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,127 @@
+/**
+ * @file os_port.h
+ * @brief RTOS abstraction layer
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _OS_PORT_H
+#define _OS_PORT_H
+
+//Dependencies
+#include "os_port_config.h"
+#include "compiler_port.h"
+
+//Compilation flags used to enable/disable features
+#define ENABLED  1
+#define DISABLED 0
+
+#define PTR_OFFSET(addr, offset) ((void *) ((uint8_t *) (addr) + (offset)))
+
+#define timeCompare(t1, t2) ((int32_t) ((t1) - (t2)))
+
+//Miscellaneous macros
+#ifndef FALSE
+   #define FALSE 0
+#endif
+
+#ifndef TRUE
+   #define TRUE 1
+#endif
+
+#ifndef LSB
+   #define LSB(x) ((x) & 0xFF)
+#endif
+
+#ifndef MSB
+   #define MSB(x) (((x) >> 8) & 0xFF)
+#endif
+
+#ifndef MIN
+   #define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef MAX
+   #define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef arraysize
+   #define arraysize(a) (sizeof(a) / sizeof(a[0]))
+#endif
+
+//Infinite delay
+#define INFINITE_DELAY ((uint_t) -1)
+//Maximum delay
+#define MAX_DELAY (INFINITE_DELAY / 2)
+
+//Invalid handle value
+#define OS_INVALID_HANDLE NULL
+
+//No RTOS?
+#if defined(USE_NO_RTOS)
+   #include "os_port_none.h"
+//ChibiOS/RT port?
+#elif defined(USE_CHIBIOS)
+   #include "os_port_chibios.h"
+//CMSIS-RTOS port?
+#elif defined(USE_CMSIS_RTOS)
+   #include "os_port_cmsis_rtos.h"
+//CMSIS-RTOS2 port?
+#elif defined(USE_CMSIS_RTOS2)
+   #include "os_port_cmsis_rtos2.h"
+//FreeRTOS port?
+#elif defined(USE_FREERTOS)
+   #include "os_port_freertos.h"
+//Keil RTX port?
+#elif defined(USE_RTX)
+   #include "os_port_rtx.h"
+//Micrium uC/OS-II port?
+#elif defined(USE_UCOS2)
+   #include "os_port_ucos2.h"
+//Micrium uC/OS-III port?
+#elif defined(USE_UCOS3)
+   #include "os_port_ucos3.h"
+//Nut/OS port?
+#elif defined(USE_NUTOS)
+   #include "os_port_nutos.h"
+//Segger embOS port?
+#elif defined(USE_EMBOS)
+   #include "os_port_embos.h"
+//TI SYS/BIOS port?
+#elif defined(USE_SYS_BIOS)
+   #include "os_port_sys_bios.h"
+//Windows port?
+#elif defined(_WIN32)
+   #include "os_port_windows.h"
+#endif
+
+//Delay routines
+#ifndef usleep
+   #define usleep(delay) {volatile uint32_t n = delay * 4; while(n > 0) n--;}
+#endif
+
+#ifndef sleep
+   #define sleep(delay) {volatile uint32_t n = delay * 4000; while(n > 0) n--;}
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_chibios.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,568 @@
+/**
+ * @file os_port_chibios.c
+ * @brief RTOS abstraction layer (ChibiOS/RT)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TRACE_LEVEL_OFF
+
+//Dependencies
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "os_port.h"
+#include "os_port_chibios.h"
+#include "debug.h"
+
+//Variables
+static OsTask taskTable[OS_PORT_MAX_TASKS];
+static uint_t *waTable[OS_PORT_MAX_TASKS];
+
+
+/**
+ * @brief Kernel initialization
+ **/
+
+void osInitKernel(void)
+{
+   //Initialize tables
+   memset(taskTable, 0, sizeof(taskTable));
+   memset(waTable, 0, sizeof(waTable));
+
+   //Kernel initialization
+   chSysInit();
+}
+
+
+/**
+ * @brief Start kernel
+ **/
+
+void osStartKernel(void)
+{
+   //Terminate the main thread
+   chThdExit(MSG_OK);
+}
+
+
+/**
+ * @brief Create a static task
+ * @param[out] task Pointer to the task structure
+ * @param[in] name A name identifying the task
+ * @param[in] taskCode Pointer to the task entry function
+ * @param[in] params A pointer to a variable to be passed to the task
+ * @param[in] stack Pointer to the stack
+ * @param[in] stackSize The initial size of the stack, in words
+ * @param[in] priority The priority at which the task should run
+ * @return The function returns TRUE if the task was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateStaticTask(OsTask *task, const char_t *name, OsTaskCode taskCode,
+   void *params, void *stack, size_t stackSize, int_t priority)
+{
+   //Compute the size of the working area in bytes
+   stackSize *= sizeof(uint_t);
+
+   //Create a new task
+   task->tp = chThdCreateStatic(stack, stackSize,
+      priority, (tfunc_t) taskCode, params);
+
+   //Check whether the task was successfully created
+   if(task->tp != NULL)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Create a new task
+ * @param[in] name A name identifying the task
+ * @param[in] taskCode Pointer to the task entry function
+ * @param[in] params A pointer to a variable to be passed to the task
+ * @param[in] stackSize The initial size of the stack, in words
+ * @param[in] priority The priority at which the task should run
+ * @return If the function succeeds, the return value is a pointer to the
+ *   new task. If the function fails, the return value is NULL
+ **/
+
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority)
+{
+   uint_t i;
+   void *wa;
+   OsTask *task = NULL;
+
+   //Compute the size of the stack in bytes
+   stackSize *= sizeof(uint_t);
+
+   //Allocate a memory block to hold the working area
+   wa = osAllocMem(THD_WORKING_AREA_SIZE(stackSize));
+
+   //Successful memory allocation?
+   if(wa != NULL)
+   {
+      //Enter critical section
+      chSysLock();
+
+      //Loop through task table
+      for(i = 0; i < OS_PORT_MAX_TASKS; i++)
+      {
+         //Check whether the current entry is free
+         if(taskTable[i].tp == NULL)
+            break;
+      }
+
+      //Any entry available in the table?
+      if(i < OS_PORT_MAX_TASKS)
+      {
+         //Create a new task
+         taskTable[i].tp = chThdCreateI(wa, THD_WORKING_AREA_SIZE(stackSize),
+            priority, (tfunc_t) taskCode, params);
+
+         //Check whether the task was successfully created
+         if(taskTable[i].tp != NULL)
+         {
+            //Insert the newly created task in the ready list
+            chSchWakeupS(taskTable[i].tp, MSG_OK);
+
+            //Save task pointer
+            task = &taskTable[i];
+            //Save working area base address
+            waTable[i] = wa;
+
+            //Leave critical section
+            chSysUnlock();
+         }
+         else
+         {
+            //Leave critical section
+            chSysUnlock();
+            //Clean up side effects
+            osFreeMem(wa);
+         }
+      }
+      else
+      {
+         //Leave critical section
+         chSysUnlock();
+         //No entry available in the table
+         osFreeMem(wa);
+      }
+   }
+
+   //Return a pointer to the newly created task
+   return task;
+}
+
+
+/**
+ * @brief Delete a task
+ * @param[in] task Pointer to the task to be deleted
+ **/
+
+void osDeleteTask(OsTask *task)
+{
+   //Delete the specified task
+   if(task == NULL)
+      chThdExit(MSG_OK);
+   else
+      chThdTerminate(task->tp);
+}
+
+
+/**
+ * @brief Delay routine
+ * @param[in] delay Amount of time for which the calling task should block
+ **/
+
+void osDelayTask(systime_t delay)
+{
+   //Delay the task for the specified duration
+   chThdSleep(OS_MS_TO_SYSTICKS(delay));
+}
+
+
+/**
+ * @brief Yield control to the next task
+ **/
+
+void osSwitchTask(void)
+{
+   //Force a context switch
+   chThdYield();
+}
+
+
+/**
+ * @brief Suspend scheduler activity
+ **/
+
+void osSuspendAllTasks(void)
+{
+   //Suspend scheduler activity
+   chSysLock();
+}
+
+
+/**
+ * @brief Resume scheduler activity
+ **/
+
+void osResumeAllTasks(void)
+{
+   //Resume scheduler activity
+   chSysUnlock();
+}
+
+
+/**
+ * @brief Create an event object
+ * @param[in] event Pointer to the event object
+ * @return The function returns TRUE if the event object was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateEvent(OsEvent *event)
+{
+   //Initialize the binary semaphore object
+   chBSemObjectInit(event, TRUE);
+
+   //Event successfully created
+   return TRUE;
+}
+
+
+/**
+ * @brief Delete an event object
+ * @param[in] event Pointer to the event object
+ **/
+
+void osDeleteEvent(OsEvent *event)
+{
+   //No resource to release
+}
+
+
+/**
+ * @brief Set the specified event object to the signaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osSetEvent(OsEvent *event)
+{
+   //Set the specified event to the signaled state
+   chBSemSignal(event);
+}
+
+
+/**
+ * @brief Set the specified event object to the nonsignaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osResetEvent(OsEvent *event)
+{
+   //Force the specified event to the nonsignaled state
+   chBSemReset(event, TRUE);
+}
+
+
+/**
+ * @brief Wait until the specified event is in the signaled state
+ * @param[in] event Pointer to the event object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the state of the specified object is
+ *   signaled. FALSE is returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout)
+{
+   msg_t msg;
+
+   //Wait until the specified event is in the signaled
+   //state or the timeout interval elapses
+   if(timeout == 0)
+   {
+      //Non-blocking call
+      msg = chBSemWaitTimeout(event, TIME_IMMEDIATE);
+   }
+   else if(timeout == INFINITE_DELAY)
+   {
+      //Infinite timeout period
+      msg = chBSemWaitTimeout(event, TIME_INFINITE);
+   }
+   else
+   {
+      //Wait until the specified event becomes set
+      msg = chBSemWaitTimeout(event, OS_MS_TO_SYSTICKS(timeout));
+   }
+
+   //Check whether the specified event is set
+   if(msg == MSG_OK)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Set an event object to the signaled state from an interrupt service routine
+ * @param[in] event Pointer to the event object
+ * @return TRUE if setting the event to signaled state caused a task to unblock
+ *   and the unblocked task has a priority higher than the currently running task
+ **/
+
+bool_t osSetEventFromIsr(OsEvent *event)
+{
+   //Set the specified event to the signaled state
+   chBSemSignalI(event);
+
+   //The return value is not relevant
+   return FALSE;
+}
+
+
+/**
+ * @brief Create a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] count The maximum count for the semaphore object. This value
+ *   must be greater than zero
+ * @return The function returns TRUE if the semaphore was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count)
+{
+   //Initialize the semaphore object
+   chSemObjectInit(semaphore, count);
+
+   //Semaphore successfully created
+   return TRUE;
+}
+
+
+/**
+ * @brief Delete a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osDeleteSemaphore(OsSemaphore *semaphore)
+{
+   //No resource to release
+}
+
+
+/**
+ * @brief Wait for the specified semaphore to be available
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the semaphore is available. FALSE is
+ *   returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout)
+{
+   msg_t msg;
+
+   //Wait until the semaphore is available or the timeout interval elapses
+   if(timeout == 0)
+   {
+      //Non-blocking call
+      msg = chSemWaitTimeout(semaphore, TIME_IMMEDIATE);
+   }
+   else if(timeout == INFINITE_DELAY)
+   {
+      //Infinite timeout period
+      msg = chSemWaitTimeout(semaphore, TIME_INFINITE);
+   }
+   else
+   {
+      //Wait until the specified semaphore becomes available
+      msg = chSemWaitTimeout(semaphore, OS_MS_TO_SYSTICKS(timeout));
+   }
+
+   //Check whether the specified semaphore is available
+   if(msg == MSG_OK)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Release the specified semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osReleaseSemaphore(OsSemaphore *semaphore)
+{
+   //Release the semaphore
+   chSemSignal(semaphore);
+}
+
+
+/**
+ * @brief Create a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ * @return The function returns TRUE if the mutex was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateMutex(OsMutex *mutex)
+{
+   //Initialize the mutex object
+   chMtxObjectInit(mutex);
+
+   //Mutex successfully created
+   return TRUE;
+}
+
+
+/**
+ * @brief Delete a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osDeleteMutex(OsMutex *mutex)
+{
+   //No resource to release
+}
+
+
+/**
+ * @brief Acquire ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osAcquireMutex(OsMutex *mutex)
+{
+   //Obtain ownership of the mutex object
+   chMtxLock(mutex);
+}
+
+
+/**
+ * @brief Release ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osReleaseMutex(OsMutex *mutex)
+{
+   //Release ownership of the mutex object
+#if (CH_KERNEL_MAJOR < 3)
+   chMtxUnlock();
+#else
+   chMtxUnlock(mutex);
+#endif
+}
+
+
+/**
+ * @brief Retrieve system time
+ * @return Number of milliseconds elapsed since the system was last started
+ **/
+
+systime_t osGetSystemTime(void)
+{
+   systime_t time;
+
+   //Get current tick count
+   time = chVTGetSystemTime();
+
+   //Convert system ticks to milliseconds
+   return OS_SYSTICKS_TO_MS(time);
+}
+
+
+/**
+ * @brief Allocate a memory block
+ * @param[in] size Bytes to allocate
+ * @return A pointer to the allocated memory block or NULL if
+ *   there is insufficient memory available
+ **/
+
+void *osAllocMem(size_t size)
+{
+   void *p;
+
+   //Allocate a memory block
+   p = chHeapAlloc(NULL, size);
+
+   //Debug message
+   TRACE_DEBUG("Allocating %" PRIuSIZE " bytes at 0x%08" PRIXPTR "\r\n", size, (uintptr_t) p);
+
+   //Return a pointer to the newly allocated memory block
+   return p;
+}
+
+
+/**
+ * @brief Release a previously allocated memory block
+ * @param[in] p Previously allocated memory block to be freed
+ **/
+
+void osFreeMem(void *p)
+{
+   //Make sure the pointer is valid
+   if(p != NULL)
+   {
+      //Debug message
+      TRACE_DEBUG("Freeing memory at 0x%08" PRIXPTR "\r\n", (uintptr_t) p);
+
+      //Free memory block
+      chHeapFree(p);
+   }
+}
+
+
+/**
+ * @brief Idle loop hook
+ **/
+
+void osIdleLoopHook(void)
+{
+   uint_t i;
+
+   //Loop through task table
+   for(i = 0; i < OS_PORT_MAX_TASKS; i++)
+   {
+      //Check whether current entry is used
+      if(taskTable[i].tp != NULL)
+      {
+         //Wait for task termination
+         if(chThdTerminatedX(taskTable[i].tp))
+         {
+            //Free working area
+            osFreeMem(waTable[i]);
+
+            //Mark the entry as free
+            waTable[i] = NULL;
+            taskTable[i].tp = NULL;
+         }
+      }
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_chibios.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,167 @@
+/**
+ * @file os_port_chibios.h
+ * @brief RTOS abstraction layer (ChibiOS/RT)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _OS_PORT_CHIBIOS_H
+#define _OS_PORT_CHIBIOS_H
+
+//Dependencies
+#include "ch.h"
+
+//Maximum number of tasks that can be dynamically created
+#ifndef OS_PORT_MAX_TASKS
+   #define OS_PORT_MAX_TASKS 16
+#elif (OS_PORT_MAX_TASKS < 1)
+   #error OS_PORT_MAX_TASKS parameter is not valid
+#endif
+
+//Task priority (normal)
+#ifndef OS_TASK_PRIORITY_NORMAL
+   #define OS_TASK_PRIORITY_NORMAL NORMALPRIO
+#endif
+
+//Task priority (high)
+#ifndef OS_TASK_PRIORITY_HIGH
+   #define OS_TASK_PRIORITY_HIGH HIGHPRIO
+#endif
+
+//Milliseconds to system ticks
+#ifndef OS_MS_TO_SYSTICKS
+   #define OS_MS_TO_SYSTICKS(n) (n)
+#endif
+
+//System ticks to milliseconds
+#ifndef OS_SYSTICKS_TO_MS
+   #define OS_SYSTICKS_TO_MS(n) (n)
+#endif
+
+//Enter interrupt service routine
+#define osEnterIsr() CH_IRQ_PROLOGUE(); chSysLockFromISR()
+
+//Leave interrupt service routine
+#define osExitIsr(flag) chSysUnlockFromISR(); CH_IRQ_EPILOGUE()
+
+//Check kernel version
+#if (CH_KERNEL_MAJOR < 3)
+   #define thread_t Thread
+   #define semaphore_t Semaphore
+   #define binary_semaphore_t BinarySemaphore
+   #define mutex_t Mutex
+   #define chThdTerminatedX chThdTerminated
+   #define chSemObjectInit chSemInit
+   #define chBSemObjectInit chBSemInit
+   #define chMtxObjectInit chMtxInit
+   #define chVTGetSystemTime chTimeNow
+   #define chSysLockFromISR chSysLockFromIsr
+   #define chSysUnlockFromISR chSysUnlockFromIsr
+   #define THD_WORKING_AREA_SIZE THD_WA_SIZE
+   #define MSG_OK RDY_OK
+#endif
+
+
+/**
+ * @brief Task object
+ **/
+
+typedef struct
+{
+   thread_t *tp;
+} OsTask;
+
+
+/**
+ * @brief Event object
+ **/
+
+typedef binary_semaphore_t OsEvent;
+
+
+/**
+ * @brief Semaphore object
+ **/
+
+typedef semaphore_t OsSemaphore;
+
+
+/**
+ * @brief Mutex object
+ **/
+
+typedef mutex_t OsMutex;
+
+
+/**
+ * @brief Task routine
+ **/
+
+typedef void (*OsTaskCode)(void *params);
+
+
+//Kernel management
+void osInitKernel(void);
+void osStartKernel(void);
+
+//Task management
+bool_t osCreateStaticTask(OsTask *task, const char_t *name, OsTaskCode taskCode,
+   void *params, void *stack, size_t stackSize, int_t priority);
+
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority);
+
+void osDeleteTask(OsTask *task);
+void osDelayTask(systime_t delay);
+void osSwitchTask(void);
+void osSuspendAllTasks(void);
+void osResumeAllTasks(void);
+
+//Event management
+bool_t osCreateEvent(OsEvent *event);
+void osDeleteEvent(OsEvent *event);
+void osSetEvent(OsEvent *event);
+void osResetEvent(OsEvent *event);
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout);
+bool_t osSetEventFromIsr(OsEvent *event);
+
+//Semaphore management
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count);
+void osDeleteSemaphore(OsSemaphore *semaphore);
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout);
+void osReleaseSemaphore(OsSemaphore *semaphore);
+
+//Mutex management
+bool_t osCreateMutex(OsMutex *mutex);
+void osDeleteMutex(OsMutex *mutex);
+void osAcquireMutex(OsMutex *mutex);
+void osReleaseMutex(OsMutex *mutex);
+
+//System time
+systime_t osGetSystemTime(void);
+
+//Memory management
+void *osAllocMem(size_t size);
+void osFreeMem(void *p);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_cmsis_rtos.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,595 @@
+/**
+ * @file os_port_cmsis_rtos.c
+ * @brief RTOS abstraction layer (CMSIS-RTOS)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TRACE_LEVEL_OFF
+
+//Dependencies
+#include <stdio.h>
+#include <stdlib.h>
+#include "os_port.h"
+#include "os_port_cmsis_rtos.h"
+#include "debug.h"
+
+
+/**
+ * @brief Kernel initialization
+ **/
+
+void osInitKernel(void)
+{
+//Check CMSIS-RTOS API version
+#if (osCMSIS >= 0x10001)
+   //Initialize the kernel
+   osKernelInitialize();
+#endif
+}
+
+
+/**
+ * @brief Start kernel
+ **/
+
+void osStartKernel(void)
+{
+//Check CMSIS-RTOS API version
+#if (osCMSIS >= 0x10001)
+   //Start the kernel
+   osKernelStart();
+#else
+   //Start the kernel
+   osKernelStart(NULL, NULL);
+#endif
+}
+
+
+/**
+ * @brief Create a new task
+ * @param[in] name A name identifying the task
+ * @param[in] taskCode Pointer to the task entry function
+ * @param[in] params A pointer to a variable to be passed to the task
+ * @param[in] stackSize The initial size of the stack, in words
+ * @param[in] priority The priority at which the task should run
+ * @return If the function succeeds, the return value is a pointer to the
+ *   new task. If the function fails, the return value is NULL
+ **/
+
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority)
+{
+   osThreadId threadId;
+   osThreadDef_t threadDef;
+
+#if defined(osCMSIS_RTX)
+   threadDef.pthread = (os_pthread) taskCode;
+   threadDef.tpriority = (osPriority) priority;
+   threadDef.instances = 1;
+   threadDef.stacksize = stackSize * sizeof(uint_t);
+#else
+   threadDef.name = (char_t *) name;
+   threadDef.pthread = (os_pthread) taskCode;
+   threadDef.tpriority = (osPriority) priority;
+   threadDef.instances = 1;
+   threadDef.stacksize = stackSize;
+#endif
+
+   //Create a new thread
+   threadId = osThreadCreate(&threadDef, params);
+   //Return a handle to the newly created thread
+   return (OsTask *) threadId;
+}
+
+
+/**
+ * @brief Delete a task
+ * @param[in] task Pointer to the task to be deleted
+ **/
+
+void osDeleteTask(OsTask *task)
+{
+   //Delete the specified thread
+   osThreadTerminate((osThreadId) task);
+}
+
+
+/**
+ * @brief Delay routine
+ * @param[in] delay Amount of time for which the calling task should block
+ **/
+
+void osDelayTask(systime_t delay)
+{
+   //Delay the thread for the specified duration
+   osDelay(delay);
+}
+
+
+/**
+ * @brief Yield control to the next task
+ **/
+
+void osSwitchTask(void)
+{
+   //Force a context switch
+   osThreadYield();
+}
+
+
+/**
+ * @brief Suspend scheduler activity
+ **/
+
+void osSuspendAllTasks(void)
+{
+#if !defined(osCMSIS_RTX)
+   //Make sure the operating system is running
+   if(osKernelRunning())
+   {
+      //Suspend all threads
+      osThreadSuspendAll();
+   }
+#endif
+}
+
+
+/**
+ * @brief Resume scheduler activity
+ **/
+
+void osResumeAllTasks(void)
+{
+#if !defined(osCMSIS_RTX)
+   //Make sure the operating system is running
+   if(osKernelRunning())
+   {
+      //Resume all threads
+      osThreadResumeAll();
+   }
+#endif
+}
+
+
+/**
+ * @brief Create an event object
+ * @param[in] event Pointer to the event object
+ * @return The function returns TRUE if the event object was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateEvent(OsEvent *event)
+{
+   osSemaphoreDef_t semaphoreDef;
+
+#if defined(osCMSIS_RTX)
+   semaphoreDef.semaphore = event->cb;
+#else
+   semaphoreDef.dummy = 0;
+#endif
+
+   //Create a binary semaphore object
+   event->id = osSemaphoreCreate(&semaphoreDef, 1);
+
+   //Check whether the returned semaphore ID is valid
+   if(event->id != NULL)
+   {
+      //Force the specified event to the nonsignaled state
+      osSemaphoreWait(event->id, 0);
+      //Event successfully created
+      return TRUE;
+   }
+   else
+   {
+      //Failed to create event object
+      return FALSE;
+   }
+}
+
+
+/**
+ * @brief Delete an event object
+ * @param[in] event Pointer to the event object
+ **/
+
+void osDeleteEvent(OsEvent *event)
+{
+   //Make sure the semaphore ID is valid
+   if(event->id != NULL)
+   {
+      //Properly dispose the event object
+      osSemaphoreDelete(event->id);
+   }
+}
+
+
+/**
+ * @brief Set the specified event object to the signaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osSetEvent(OsEvent *event)
+{
+   //Set the specified event to the signaled state
+   osSemaphoreRelease(event->id);
+}
+
+
+/**
+ * @brief Set the specified event object to the nonsignaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osResetEvent(OsEvent *event)
+{
+#if defined(osCMSIS_RTX)
+   //Force the specified event to the nonsignaled state
+   while(osSemaphoreWait(event->id, 0) > 0);
+#else
+   //Force the specified event to the nonsignaled state
+   osSemaphoreWait(event->id, 0);
+#endif
+}
+
+
+/**
+ * @brief Wait until the specified event is in the signaled state
+ * @param[in] event Pointer to the event object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the state of the specified object is
+ *   signaled. FALSE is returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout)
+{
+   int32_t ret;
+
+   //Wait until the specified event is in the signaled
+   //state or the timeout interval elapses
+   if(timeout == INFINITE_DELAY)
+   {
+      //Infinite timeout period
+      ret = osSemaphoreWait(event->id, osWaitForever);
+   }
+   else
+   {
+#if defined(osCMSIS_RTX)
+      systime_t n;
+
+      //Loop until the assigned time period has elapsed
+      do
+      {
+         //Limit the timeout value
+         n = MIN(timeout, 10000);
+         //Wait for the specified time interval
+         ret = osSemaphoreWait(event->id, n);
+         //Decrement timeout value
+         timeout -= n;
+
+         //Check timeout value
+      } while(ret == 0 && timeout > 0);
+#else
+      //Wait for the specified time interval
+      ret = osSemaphoreWait(event->id, timeout);
+#endif
+   }
+
+#if defined(osCMSIS_RTX)
+   //Check return value
+   if(ret > 0)
+   {
+      //Force the event back to the nonsignaled state
+      while(osSemaphoreWait(event->id, 0) > 0);
+
+      //The specified event is in the signaled state
+      return TRUE;
+   }
+   else
+   {
+      //The timeout interval elapsed
+      return FALSE;
+   }
+#else
+   //Check return value
+   if(ret == osOK)
+      return TRUE;
+   else
+      return FALSE;
+#endif
+}
+
+
+/**
+ * @brief Set an event object to the signaled state from an interrupt service routine
+ * @param[in] event Pointer to the event object
+ * @return TRUE if setting the event to signaled state caused a task to unblock
+ *   and the unblocked task has a priority higher than the currently running task
+ **/
+
+bool_t osSetEventFromIsr(OsEvent *event)
+{
+   //Set the specified event to the signaled state
+   osSemaphoreRelease(event->id);
+
+   //The return value is not relevant
+   return FALSE;
+}
+
+
+/**
+ * @brief Create a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] count The maximum count for the semaphore object. This value
+ *   must be greater than zero
+ * @return The function returns TRUE if the semaphore was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count)
+{
+   osSemaphoreDef_t semaphoreDef;
+
+#if defined(osCMSIS_RTX)
+   semaphoreDef.semaphore = semaphore->cb;
+#else
+   semaphoreDef.dummy = 0;
+#endif
+
+   //Create a semaphore object
+   semaphore->id = osSemaphoreCreate(&semaphoreDef, count);
+
+   //Check whether the returned semaphore ID is valid
+   if(semaphore->id != NULL)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Delete a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osDeleteSemaphore(OsSemaphore *semaphore)
+{
+   //Make sure the semaphore ID is valid
+   if(semaphore->id != NULL)
+   {
+      //Properly dispose the specified semaphore
+      osSemaphoreDelete(semaphore->id);
+   }
+}
+
+
+/**
+ * @brief Wait for the specified semaphore to be available
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the semaphore is available. FALSE is
+ *   returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout)
+{
+   int32_t ret;
+
+   //Wait until the semaphore is available or the timeout interval elapses
+   if(timeout == INFINITE_DELAY)
+   {
+      //Infinite timeout period
+      ret = osSemaphoreWait(semaphore->id, osWaitForever);
+   }
+   else
+   {
+#if defined(osCMSIS_RTX)
+      systime_t n;
+
+      //Loop until the assigned time period has elapsed
+      do
+      {
+         //Limit the timeout value
+         n = MIN(timeout, 10000);
+         //Wait for the specified time interval
+         ret = osSemaphoreWait(semaphore->id, n);
+         //Decrement timeout value
+         timeout -= n;
+
+         //Check timeout value
+      } while(ret == 0 && timeout > 0);
+#else
+      //Wait for the specified time interval
+      ret = osSemaphoreWait(semaphore->id, timeout);
+#endif
+   }
+
+#if defined(osCMSIS_RTX)
+   //Check return value
+   if(ret > 0)
+      return TRUE;
+   else
+      return FALSE;
+#else
+   //Check return value
+   if(ret == osOK)
+      return TRUE;
+   else
+      return FALSE;
+#endif
+}
+
+
+/**
+ * @brief Release the specified semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osReleaseSemaphore(OsSemaphore *semaphore)
+{
+   //Release the semaphore
+   osSemaphoreRelease(semaphore->id);
+}
+
+
+/**
+ * @brief Create a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ * @return The function returns TRUE if the mutex was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateMutex(OsMutex *mutex)
+{
+   osMutexDef_t mutexDef;
+
+#if defined(osCMSIS_RTX)
+   mutexDef.mutex = mutex->cb;
+#else
+   mutexDef.dummy = 0;
+#endif
+
+   //Create a mutex object
+   mutex->id = osMutexCreate(&mutexDef);
+
+   //Check whether the returned mutex ID is valid
+   if(mutex->id != NULL)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Delete a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osDeleteMutex(OsMutex *mutex)
+{
+   //Make sure the mutex ID is valid
+   if(mutex->id != NULL)
+   {
+      //Properly dispose the specified mutex
+      osMutexDelete(mutex->id);
+   }
+}
+
+
+/**
+ * @brief Acquire ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osAcquireMutex(OsMutex *mutex)
+{
+   //Obtain ownership of the mutex object
+   osMutexWait(mutex->id, osWaitForever);
+}
+
+
+/**
+ * @brief Release ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osReleaseMutex(OsMutex *mutex)
+{
+   //Release ownership of the mutex object
+   osMutexRelease(mutex->id);
+}
+
+
+/**
+ * @brief Retrieve system time
+ * @return Number of milliseconds elapsed since the system was last started
+ **/
+
+systime_t osGetSystemTime(void)
+{
+   systime_t time;
+
+#if defined(osCMSIS_RTX)
+   //Forward function declaration
+   extern uint32_t rt_time_get(void);
+
+   //Get current tick count
+   time = rt_time_get();
+#else
+   //Get current tick count
+   time = osKernelSysTick();
+#endif
+
+   //Convert system ticks to milliseconds
+   return OS_SYSTICKS_TO_MS(time);
+}
+
+
+/**
+ * @brief Allocate a memory block
+ * @param[in] size Bytes to allocate
+ * @return A pointer to the allocated memory block or NULL if
+ *   there is insufficient memory available
+ **/
+
+void *osAllocMem(size_t size)
+{
+   void *p;
+
+   //Enter critical section
+   osSuspendAllTasks();
+   //Allocate a memory block
+   p = malloc(size);
+   //Leave critical section
+   osResumeAllTasks();
+
+   //Debug message
+   TRACE_DEBUG("Allocating %u bytes at 0x%08X\r\n", size, (uint_t) p);
+
+   //Return a pointer to the newly allocated memory block
+   return p;
+}
+
+
+/**
+ * @brief Release a previously allocated memory block
+ * @param[in] p Previously allocated memory block to be freed
+ **/
+
+void osFreeMem(void *p)
+{
+   //Make sure the pointer is valid
+   if(p != NULL)
+   {
+      //Debug message
+      TRACE_DEBUG("Freeing memory at 0x%08X\r\n", (uint_t) p);
+
+      //Enter critical section
+      osSuspendAllTasks();
+      //Free memory block
+      free(p);
+      //Leave critical section
+      osResumeAllTasks();
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_cmsis_rtos.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,155 @@
+/**
+ * @file os_port_cmsis_rtos.h
+ * @brief RTOS abstraction layer (CMSIS-RTOS)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _OS_PORT_CMSIS_RTOS_H
+#define _OS_PORT_CMSIS_RTOS_H
+
+//Dependencies
+#include "cmsis_os.h"
+
+//Task priority (normal)
+#ifndef OS_TASK_PRIORITY_NORMAL
+   #define OS_TASK_PRIORITY_NORMAL osPriorityNormal
+#endif
+
+//Task priority (high)
+#ifndef OS_TASK_PRIORITY_HIGH
+   #define OS_TASK_PRIORITY_HIGH osPriorityAboveNormal
+#endif
+
+//Milliseconds to system ticks
+#ifndef OS_MS_TO_SYSTICKS
+   #define OS_MS_TO_SYSTICKS(n) (n)
+#endif
+
+//System ticks to milliseconds
+#ifndef OS_SYSTICKS_TO_MS
+   #define OS_SYSTICKS_TO_MS(n) (n)
+#endif
+
+//Enter interrupt service routine
+#define osEnterIsr()
+
+//Leave interrupt service routine
+#define osExitIsr(flag)
+
+
+/**
+ * @brief Task object
+ **/
+
+typedef void OsTask;
+
+
+/**
+ * @brief Event object
+ **/
+
+typedef struct
+{
+   osSemaphoreId id;
+#if defined(osCMSIS_RTX)
+   uint32_t cb[2];
+#endif
+} OsEvent;
+
+
+/**
+ * @brief Semaphore object
+ **/
+
+typedef struct
+{
+   osSemaphoreId id;
+#if defined(osCMSIS_RTX)
+   uint32_t cb[2];
+#endif
+} OsSemaphore;
+
+
+/**
+ * @brief Mutex object
+ **/
+
+typedef struct
+{
+   osMutexId id;
+#if defined(osCMSIS_RTX)
+   uint32_t cb[4];
+#endif
+} OsMutex;
+
+
+/**
+ * @brief Task routine
+ **/
+
+typedef void (*OsTaskCode)(void *params);
+
+
+//Kernel management
+void osInitKernel(void);
+void osStartKernel(void);
+
+//Task management
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority);
+
+void osDeleteTask(OsTask *task);
+void osDelayTask(systime_t delay);
+void osSwitchTask(void);
+void osSuspendAllTasks(void);
+void osResumeAllTasks(void);
+
+//Event management
+bool_t osCreateEvent(OsEvent *event);
+void osDeleteEvent(OsEvent *event);
+void osSetEvent(OsEvent *event);
+void osResetEvent(OsEvent *event);
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout);
+bool_t osSetEventFromIsr(OsEvent *event);
+
+//Semaphore management
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count);
+void osDeleteSemaphore(OsSemaphore *semaphore);
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout);
+void osReleaseSemaphore(OsSemaphore *semaphore);
+
+//Mutex management
+bool_t osCreateMutex(OsMutex *mutex);
+void osDeleteMutex(OsMutex *mutex);
+void osAcquireMutex(OsMutex *mutex);
+void osReleaseMutex(OsMutex *mutex);
+
+//System time
+systime_t osGetSystemTime(void);
+
+//Memory management
+void *osAllocMem(size_t size);
+void osFreeMem(void *p);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_cmsis_rtos2.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,521 @@
+/**
+ * @file os_port_cmsis_rtos2.c
+ * @brief RTOS abstraction layer (CMSIS-RTOS 2 / RTX v5)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TRACE_LEVEL_OFF
+
+//Dependencies
+#include <stdio.h>
+#include <stdlib.h>
+#include "os_port.h"
+#include "os_port_cmsis_rtos2.h"
+#include "debug.h"
+
+
+/**
+ * @brief Kernel initialization
+ **/
+
+void osInitKernel(void)
+{
+   //Initialize the kernel
+   osKernelInitialize();
+}
+
+
+/**
+ * @brief Start kernel
+ **/
+
+void osStartKernel(void)
+{
+   //Start the kernel
+   osKernelStart();
+}
+
+
+/**
+ * @brief Create a new task
+ * @param[in] name A name identifying the task
+ * @param[in] taskCode Pointer to the task entry function
+ * @param[in] params A pointer to a variable to be passed to the task
+ * @param[in] stackSize The initial size of the stack, in words
+ * @param[in] priority The priority at which the task should run
+ * @return If the function succeeds, the return value is a pointer to the
+ *   new task. If the function fails, the return value is NULL
+ **/
+
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority)
+{
+   osThreadId_t threadId;
+   osThreadAttr_t threadAttr;
+
+   //Set thread attributes
+   threadAttr.name = name;
+   threadAttr.attr_bits = 0;
+   threadAttr.cb_mem = NULL;
+   threadAttr.cb_size = 0;
+   threadAttr.stack_mem = NULL;
+   threadAttr.stack_size = stackSize * sizeof(uint_t);
+   threadAttr.priority = (osPriority_t) priority;
+   threadAttr.tz_module = 0;
+   threadAttr.reserved = 0;
+
+   //Create a new thread
+   threadId = osThreadNew((os_thread_func_t ) taskCode, params, &threadAttr);
+   //Return a handle to the newly created thread
+   return (OsTask *) threadId;
+}
+
+
+/**
+ * @brief Delete a task
+ * @param[in] task Pointer to the task to be deleted
+ **/
+
+void osDeleteTask(OsTask *task)
+{
+   //Delete the specified thread
+   if(task == NULL)
+      osThreadExit();
+   else
+      osThreadTerminate((osThreadId_t) task);
+}
+
+
+/**
+ * @brief Delay routine
+ * @param[in] delay Amount of time for which the calling task should block
+ **/
+
+void osDelayTask(systime_t delay)
+{
+   //Delay the thread for the specified duration
+   osDelay(OS_MS_TO_SYSTICKS(delay));
+}
+
+
+/**
+ * @brief Yield control to the next task
+ **/
+
+void osSwitchTask(void)
+{
+   //Force a context switch
+   osThreadYield();
+}
+
+
+/**
+ * @brief Suspend scheduler activity
+ **/
+
+void osSuspendAllTasks(void)
+{
+   //Make sure the operating system is running
+   if(osKernelGetState() != osKernelInactive)
+   {
+      //Suspend all task switches
+      osKernelLock();
+   }
+}
+
+
+/**
+ * @brief Resume scheduler activity
+ **/
+
+void osResumeAllTasks(void)
+{
+   //Make sure the operating system is running
+   if(osKernelGetState() != osKernelInactive)
+   {
+      //Resume lock all task switches
+      osKernelUnlock();
+   }
+}
+
+
+/**
+ * @brief Create an event object
+ * @param[in] event Pointer to the event object
+ * @return The function returns TRUE if the event object was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateEvent(OsEvent *event)
+{
+   osSemaphoreAttr_t semaphoreAttr;
+
+   //Set semaphore attributes
+   semaphoreAttr.name = NULL;
+   semaphoreAttr.attr_bits = 0;
+
+#if defined(os_CMSIS_RTX)
+   semaphoreAttr.cb_mem = &event->cb;
+   semaphoreAttr.cb_size = sizeof(os_semaphore_t);
+#else
+   semaphoreAttr.cb_mem = NULL;
+   semaphoreAttr.cb_size = 0;
+#endif
+
+   //Create a binary semaphore object
+   event->id = osSemaphoreNew(1, 0, &semaphoreAttr);
+
+   //Check whether the returned semaphore ID is valid
+   if(event->id != NULL)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Delete an event object
+ * @param[in] event Pointer to the event object
+ **/
+
+void osDeleteEvent(OsEvent *event)
+{
+   //Make sure the semaphore ID is valid
+   if(event->id != NULL)
+   {
+      //Properly dispose the event object
+      osSemaphoreDelete(event->id);
+   }
+}
+
+
+/**
+ * @brief Set the specified event object to the signaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osSetEvent(OsEvent *event)
+{
+   //Set the specified event to the signaled state
+   osSemaphoreRelease(event->id);
+}
+
+
+/**
+ * @brief Set the specified event object to the nonsignaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osResetEvent(OsEvent *event)
+{
+   //Force the specified event to the nonsignaled state
+   osSemaphoreAcquire(event->id, 0);
+}
+
+
+/**
+ * @brief Wait until the specified event is in the signaled state
+ * @param[in] event Pointer to the event object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the state of the specified object is
+ *   signaled. FALSE is returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout)
+{
+   osStatus_t status;
+
+   //Wait until the specified event is in the signaled
+   //state or the timeout interval elapses
+   if(timeout == INFINITE_DELAY)
+   {
+      //Infinite timeout period
+      status = osSemaphoreAcquire(event->id, osWaitForever);
+   }
+   else
+   {
+      //Wait for the specified time interval
+      status = osSemaphoreAcquire(event->id, OS_MS_TO_SYSTICKS(timeout));
+   }
+
+   //Check return value
+   if(status == osOK)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Set an event object to the signaled state from an interrupt service routine
+ * @param[in] event Pointer to the event object
+ * @return TRUE if setting the event to signaled state caused a task to unblock
+ *   and the unblocked task has a priority higher than the currently running task
+ **/
+
+bool_t osSetEventFromIsr(OsEvent *event)
+{
+   //Set the specified event to the signaled state
+   osSemaphoreRelease(event->id);
+
+   //The return value is not relevant
+   return FALSE;
+}
+
+
+/**
+ * @brief Create a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] count The maximum count for the semaphore object. This value
+ *   must be greater than zero
+ * @return The function returns TRUE if the semaphore was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count)
+{
+   osSemaphoreAttr_t semaphoreAttr;
+
+   //Set semaphore attributes
+   semaphoreAttr.name = NULL;
+   semaphoreAttr.attr_bits = 0;
+
+#if defined(os_CMSIS_RTX)
+   semaphoreAttr.cb_mem = &semaphore->cb;
+   semaphoreAttr.cb_size = sizeof(os_semaphore_t);
+#else
+   semaphoreAttr.cb_mem = NULL;
+   semaphoreAttr.cb_size = 0;
+#endif
+
+   //Create a semaphore object
+   semaphore->id = osSemaphoreNew(count, count, &semaphoreAttr);
+
+   //Check whether the returned semaphore ID is valid
+   if(semaphore->id != NULL)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Delete a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osDeleteSemaphore(OsSemaphore *semaphore)
+{
+   //Make sure the semaphore ID is valid
+   if(semaphore->id != NULL)
+   {
+      //Properly dispose the specified semaphore
+      osSemaphoreDelete(semaphore->id);
+   }
+}
+
+
+/**
+ * @brief Wait for the specified semaphore to be available
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the semaphore is available. FALSE is
+ *   returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout)
+{
+   osStatus_t status;
+
+   //Wait until the semaphore is available or the timeout interval elapses
+   if(timeout == INFINITE_DELAY)
+   {
+      //Infinite timeout period
+      status = osSemaphoreAcquire(semaphore->id, osWaitForever);
+   }
+   else
+   {
+      //Wait for the specified time interval
+      status = osSemaphoreAcquire(semaphore->id, OS_MS_TO_SYSTICKS(timeout));
+   }
+
+   //Check return value
+   if(status == osOK)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Release the specified semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osReleaseSemaphore(OsSemaphore *semaphore)
+{
+   //Release the semaphore
+   osSemaphoreRelease(semaphore->id);
+}
+
+
+/**
+ * @brief Create a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ * @return The function returns TRUE if the mutex was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateMutex(OsMutex *mutex)
+{
+   osMutexAttr_t mutexAttr;
+
+   //Set mutex attributes
+   mutexAttr.name = NULL;
+   mutexAttr.attr_bits = 0;
+
+#if defined(os_CMSIS_RTX)
+   mutexAttr.cb_mem = &mutex->cb;
+   mutexAttr.cb_size = sizeof(os_mutex_t);
+#else
+   mutexAttr.cb_mem = NULL;
+   mutexAttr.cb_size = 0;
+#endif
+
+   //Create a mutex object
+   mutex->id = osMutexNew(&mutexAttr);
+
+   //Check whether the returned mutex ID is valid
+   if(mutex->id != NULL)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Delete a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osDeleteMutex(OsMutex *mutex)
+{
+   //Make sure the mutex ID is valid
+   if(mutex->id != NULL)
+   {
+      //Properly dispose the specified mutex
+      osMutexDelete(mutex->id);
+   }
+}
+
+
+/**
+ * @brief Acquire ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osAcquireMutex(OsMutex *mutex)
+{
+   //Obtain ownership of the mutex object
+   osMutexAcquire(mutex->id, osWaitForever);
+}
+
+
+/**
+ * @brief Release ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osReleaseMutex(OsMutex *mutex)
+{
+   //Release ownership of the mutex object
+   osMutexRelease(mutex->id);
+}
+
+
+/**
+ * @brief Retrieve system time
+ * @return Number of milliseconds elapsed since the system was last started
+ **/
+
+systime_t osGetSystemTime(void)
+{
+   systime_t time;
+
+   //Get current tick count
+   time = osKernelGetTickCount();
+
+   //Convert system ticks to milliseconds
+   return OS_SYSTICKS_TO_MS(time);
+}
+
+
+/**
+ * @brief Allocate a memory block
+ * @param[in] size Bytes to allocate
+ * @return A pointer to the allocated memory block or NULL if
+ *   there is insufficient memory available
+ **/
+
+void *osAllocMem(size_t size)
+{
+   void *p;
+
+   //Enter critical section
+   osSuspendAllTasks();
+   //Allocate a memory block
+   p = malloc(size);
+   //Leave critical section
+   osResumeAllTasks();
+
+   //Debug message
+   TRACE_DEBUG("Allocating %u bytes at 0x%08X\r\n", size, (uint_t) p);
+
+   //Return a pointer to the newly allocated memory block
+   return p;
+}
+
+
+/**
+ * @brief Release a previously allocated memory block
+ * @param[in] p Previously allocated memory block to be freed
+ **/
+
+void osFreeMem(void *p)
+{
+   //Make sure the pointer is valid
+   if(p != NULL)
+   {
+      //Debug message
+      TRACE_DEBUG("Freeing memory at 0x%08X\r\n", (uint_t) p);
+
+      //Enter critical section
+      osSuspendAllTasks();
+      //Free memory block
+      free(p);
+      //Leave critical section
+      osResumeAllTasks();
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_cmsis_rtos2.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,156 @@
+/**
+ * @file os_port_cmsis_rtos2.h
+ * @brief RTOS abstraction layer (CMSIS-RTOS 2 / RTX v5)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _OS_PORT_CMSIS_RTOS2_H
+#define _OS_PORT_CMSIS_RTOS2_H
+
+//Dependencies
+#include "cmsis_os2.h"
+#include "rtx_os.h"
+
+//Task priority (normal)
+#ifndef OS_TASK_PRIORITY_NORMAL
+   #define OS_TASK_PRIORITY_NORMAL osPriorityNormal
+#endif
+
+//Task priority (high)
+#ifndef OS_TASK_PRIORITY_HIGH
+   #define OS_TASK_PRIORITY_HIGH osPriorityAboveNormal
+#endif
+
+//Milliseconds to system ticks
+#ifndef OS_MS_TO_SYSTICKS
+   #define OS_MS_TO_SYSTICKS(n) (n)
+#endif
+
+//System ticks to milliseconds
+#ifndef OS_SYSTICKS_TO_MS
+   #define OS_SYSTICKS_TO_MS(n) (n)
+#endif
+
+//Enter interrupt service routine
+#define osEnterIsr()
+
+//Leave interrupt service routine
+#define osExitIsr(flag)
+
+
+/**
+ * @brief Task object
+ **/
+
+typedef void OsTask;
+
+
+/**
+ * @brief Event object
+ **/
+
+typedef struct
+{
+   osSemaphoreId_t id;
+#if defined(os_CMSIS_RTX)
+   os_semaphore_t cb;
+#endif
+} OsEvent;
+
+
+/**
+ * @brief Semaphore object
+ **/
+
+typedef struct
+{
+   osSemaphoreId_t id;
+#if defined(os_CMSIS_RTX)
+   os_semaphore_t cb;
+#endif
+} OsSemaphore;
+
+
+/**
+ * @brief Mutex object
+ **/
+
+typedef struct
+{
+   osMutexId_t id;
+#if defined(os_CMSIS_RTX)
+   os_mutex_t cb;
+#endif
+} OsMutex;
+
+
+/**
+ * @brief Task routine
+ **/
+
+typedef void (*OsTaskCode)(void *params);
+
+
+//Kernel management
+void osInitKernel(void);
+void osStartKernel(void);
+
+//Task management
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority);
+
+void osDeleteTask(OsTask *task);
+void osDelayTask(systime_t delay);
+void osSwitchTask(void);
+void osSuspendAllTasks(void);
+void osResumeAllTasks(void);
+
+//Event management
+bool_t osCreateEvent(OsEvent *event);
+void osDeleteEvent(OsEvent *event);
+void osSetEvent(OsEvent *event);
+void osResetEvent(OsEvent *event);
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout);
+bool_t osSetEventFromIsr(OsEvent *event);
+
+//Semaphore management
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count);
+void osDeleteSemaphore(OsSemaphore *semaphore);
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout);
+void osReleaseSemaphore(OsSemaphore *semaphore);
+
+//Mutex management
+bool_t osCreateMutex(OsMutex *mutex);
+void osDeleteMutex(OsMutex *mutex);
+void osAcquireMutex(OsMutex *mutex);
+void osReleaseMutex(OsMutex *mutex);
+
+//System time
+systime_t osGetSystemTime(void);
+
+//Memory management
+void *osAllocMem(size_t size);
+void osFreeMem(void *p);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_embos.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,545 @@
+/**
+ * @file os_port_embos.c
+ * @brief RTOS abstraction layer (Segger embOS)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TRACE_LEVEL_OFF
+
+//Dependencies
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "os_port.h"
+#include "os_port_embos.h"
+#include "debug.h"
+
+//Forward declaration of functions
+void osIdleTaskHook(void);
+
+//Variables
+static OS_TASK *tcbTable[OS_PORT_MAX_TASKS];
+static void *stkTable[OS_PORT_MAX_TASKS];
+
+
+/**
+ * @brief Kernel initialization
+ **/
+
+void osInitKernel(void)
+{
+   //Initialize tables
+   memset(tcbTable, 0, sizeof(tcbTable));
+   memset(stkTable, 0, sizeof(stkTable));
+
+   //Disable interrupts
+   OS_IncDI();
+   //Kernel initialization
+   OS_InitKern();
+   //Hardware initialization
+   OS_InitHW();
+}
+
+
+/**
+ * @brief Start kernel
+ **/
+
+void osStartKernel(void)
+{
+   //Start the scheduler
+   OS_Start();
+}
+
+
+/**
+ * @brief Create a static task
+ * @param[out] task Pointer to the task structure
+ * @param[in] name A name identifying the task
+ * @param[in] taskCode Pointer to the task entry function
+ * @param[in] params A pointer to a variable to be passed to the task
+ * @param[in] stack Pointer to the stack
+ * @param[in] stackSize The initial size of the stack, in words
+ * @param[in] priority The priority at which the task should run
+ * @return The function returns TRUE if the task was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateStaticTask(OsTask *task, const char_t *name, OsTaskCode taskCode,
+   void *params, void *stack, size_t stackSize, int_t priority)
+{
+   //Create a new task
+   OS_CreateTaskEx(task, name, priority, taskCode,
+      stack, stackSize * sizeof(uint_t), 1, params);
+
+   //The task was successfully created
+   return TRUE;
+}
+
+
+/**
+ * @brief Create a new task
+ * @param[in] name A name identifying the task
+ * @param[in] taskCode Pointer to the task entry function
+ * @param[in] params A pointer to a variable to be passed to the task
+ * @param[in] stackSize The initial size of the stack, in words
+ * @param[in] priority The priority at which the task should run
+ * @return If the function succeeds, the return value is a pointer to the
+ *   new task. If the function fails, the return value is NULL
+ **/
+
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority)
+{
+   uint_t i;
+   OS_TASK *task;
+   void *stack;
+
+   //Enter critical section
+   osSuspendAllTasks();
+
+   //Loop through TCB table
+   for(i = 0; i < OS_PORT_MAX_TASKS; i++)
+   {
+      //Check whether the current entry is free
+      if(tcbTable[i] == NULL)
+         break;
+   }
+
+   //Any entry available in the table?
+   if(i < OS_PORT_MAX_TASKS)
+   {
+      //Allocate a memory block to hold the task's control block
+      task = osAllocMem(sizeof(OS_TASK));
+
+      //Successful memory allocation?
+      if(task != NULL)
+      {
+         //Allocate a memory block to hold the task's stack
+         stack = osAllocMem(stackSize * sizeof(uint_t));
+
+         //Successful memory allocation?
+         if(stack != NULL)
+         {
+            //Create a new task
+            OS_CreateTaskEx(task, name, priority, taskCode,
+               stack, stackSize * sizeof(uint_t), 1, params);
+
+            //Save TCB base address
+            tcbTable[i] = task;
+            //Save stack base address
+            stkTable[i] = stack;
+         }
+         else
+         {
+            osFreeMem(task);
+            //Memory allocation failed
+            task = NULL;
+         }
+      }
+   }
+   else
+   {
+      //Memory allocation failed
+      task = NULL;
+   }
+
+   //Leave critical section
+   osResumeAllTasks();
+
+   //Return task pointer
+   return task;
+}
+
+
+/**
+ * @brief Delete a task
+ * @param[in] task Pointer to the task to be deleted
+ **/
+
+void osDeleteTask(OsTask *task)
+{
+   //Delete the specified task
+   OS_TerminateTask(task);
+}
+
+
+/**
+ * @brief Delay routine
+ * @param[in] delay Amount of time for which the calling task should block
+ **/
+
+void osDelayTask(systime_t delay)
+{
+   //Delay the task for the specified duration
+   OS_Delay(OS_MS_TO_SYSTICKS(delay));
+}
+
+
+/**
+ * @brief Yield control to the next task
+ **/
+
+void osSwitchTask(void)
+{
+   //Not implemented
+}
+
+
+/**
+ * @brief Suspend scheduler activity
+ **/
+
+void osSuspendAllTasks(void)
+{
+   //Make sure the operating system is running
+   if(OS_IsRunning())
+   {
+      //Suspend scheduler activity
+      OS_SuspendAllTasks();
+   }
+}
+
+
+/**
+ * @brief Resume scheduler activity
+ **/
+
+void osResumeAllTasks(void)
+{
+   //Make sure the operating system is running
+   if(OS_IsRunning())
+   {
+      //Resume scheduler activity
+      OS_ResumeAllSuspendedTasks();
+   }
+}
+
+
+/**
+ * @brief Create an event object
+ * @param[in] event Pointer to the event object
+ * @return The function returns TRUE if the event object was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateEvent(OsEvent *event)
+{
+   //Create an event object
+   OS_EVENT_Create(event);
+
+   //The event object was successfully created
+   return TRUE;
+}
+
+
+/**
+ * @brief Delete an event object
+ * @param[in] event Pointer to the event object
+ **/
+
+void osDeleteEvent(OsEvent *event)
+{
+   //Make sure the operating system is running
+   if(OS_IsRunning())
+   {
+      //Properly dispose the event object
+      OS_EVENT_Delete(event);
+   }
+}
+
+
+/**
+ * @brief Set the specified event object to the signaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osSetEvent(OsEvent *event)
+{
+   //Set the specified event to the signaled state
+   OS_EVENT_Set(event);
+}
+
+
+/**
+ * @brief Set the specified event object to the nonsignaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osResetEvent(OsEvent *event)
+{
+   //Force the specified event to the nonsignaled state
+   OS_EVENT_Reset(event);
+}
+
+
+/**
+ * @brief Wait until the specified event is in the signaled state
+ * @param[in] event Pointer to the event object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the state of the specified object is
+ *   signaled. FALSE is returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout)
+{
+   bool_t ret;
+
+   //Wait until the specified event is in the signaled
+   //state or the timeout interval elapses
+   if(timeout == 0)
+   {
+      //Non-blocking call
+      ret = OS_EVENT_Get(event);
+   }
+   else if(timeout == INFINITE_DELAY)
+   {
+      //Infinite timeout period
+      OS_EVENT_Wait(event);
+      ret = TRUE;
+   }
+   else
+   {
+      //Wait until the specified event becomes set
+      ret = !OS_EVENT_WaitTimed(event, OS_MS_TO_SYSTICKS(timeout));
+   }
+
+   //The return value specifies whether the event is set
+   return ret;
+}
+
+
+/**
+ * @brief Set an event object to the signaled state from an interrupt service routine
+ * @param[in] event Pointer to the event object
+ * @return TRUE if setting the event to signaled state caused a task to unblock
+ *   and the unblocked task has a priority higher than the currently running task
+ **/
+
+bool_t osSetEventFromIsr(OsEvent *event)
+{
+   //Set the specified event to the signaled state
+   OS_EVENT_Set(event);
+
+   //The return value is not relevant
+   return FALSE;
+}
+
+
+/**
+ * @brief Create a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] count The maximum count for the semaphore object. This value
+ *   must be greater than zero
+ * @return The function returns TRUE if the semaphore was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count)
+{
+   //Create a semaphore
+   OS_CreateCSema(semaphore, count);
+
+   //The event object was successfully created
+   return TRUE;
+}
+
+
+/**
+ * @brief Delete a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osDeleteSemaphore(OsSemaphore *semaphore)
+{
+   //Make sure the operating system is running
+   if(OS_IsRunning())
+   {
+      //Properly dispose the specified semaphore
+      OS_DeleteCSema(semaphore);
+   }
+}
+
+
+/**
+ * @brief Wait for the specified semaphore to be available
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the semaphore is available. FALSE is
+ *   returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout)
+{
+   bool_t ret;
+
+   //Wait until the semaphore is available or the timeout interval elapses
+   if(timeout == 0)
+   {
+      //Non-blocking call
+      ret = OS_CSemaRequest(semaphore);
+   }
+   else if(timeout == INFINITE_DELAY)
+   {
+      //Infinite timeout period
+      OS_WaitCSema(semaphore);
+      ret = TRUE;
+   }
+   else
+   {
+      //Wait until the specified semaphore becomes available
+      ret = OS_WaitCSemaTimed(semaphore, OS_MS_TO_SYSTICKS(timeout));
+   }
+
+   //The return value specifies whether the semaphore is available
+   return ret;
+}
+
+
+/**
+ * @brief Release the specified semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osReleaseSemaphore(OsSemaphore *semaphore)
+{
+   //Release the semaphore
+   OS_SignalCSema(semaphore);
+}
+
+
+/**
+ * @brief Create a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ * @return The function returns TRUE if the mutex was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateMutex(OsMutex *mutex)
+{
+   //Create a mutex
+   OS_CreateRSema(mutex);
+
+   //The mutex was successfully created
+   return TRUE;
+}
+
+
+/**
+ * @brief Delete a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osDeleteMutex(OsMutex *mutex)
+{
+   //Make sure the operating system is running
+   if(OS_IsRunning())
+   {
+      //Properly dispose the specified mutex
+      OS_DeleteRSema(mutex);
+   }
+}
+
+
+/**
+ * @brief Acquire ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osAcquireMutex(OsMutex *mutex)
+{
+   //Obtain ownership of the mutex object
+   OS_Use(mutex);
+}
+
+
+/**
+ * @brief Release ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osReleaseMutex(OsMutex *mutex)
+{
+   //Release ownership of the mutex object
+   OS_Unuse(mutex);
+}
+
+
+/**
+ * @brief Retrieve system time
+ * @return Number of milliseconds elapsed since the system was last started
+ **/
+
+systime_t osGetSystemTime(void)
+{
+   systime_t time;
+
+   //Get current tick count
+   time = OS_GetTime32();
+
+   //Convert system ticks to milliseconds
+   return OS_SYSTICKS_TO_MS(time);
+}
+
+
+/**
+ * @brief Allocate a memory block
+ * @param[in] size Bytes to allocate
+ * @return A pointer to the allocated memory block or NULL if
+ *   there is insufficient memory available
+ **/
+
+void *osAllocMem(size_t size)
+{
+   void *p;
+
+   //Allocate a memory block
+   p = OS_malloc(size);
+
+   //Debug message
+   TRACE_DEBUG("Allocating %" PRIuSIZE " bytes at 0x%08" PRIXPTR "\r\n", size, (uintptr_t) p);
+
+   //Return a pointer to the newly allocated memory block
+   return p;
+}
+
+
+/**
+ * @brief Release a previously allocated memory block
+ * @param[in] p Previously allocated memory block to be freed
+ **/
+
+void osFreeMem(void *p)
+{
+   //Make sure the pointer is valid
+   if(p != NULL)
+   {
+      //Debug message
+      TRACE_DEBUG("Freeing memory at 0x%08" PRIXPTR "\r\n", (uintptr_t) p);
+
+      //Free memory block
+      OS_free(p);
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_embos.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,147 @@
+/**
+ * @file os_port_embos.h
+ * @brief RTOS abstraction layer (Segger embOS)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _OS_PORT_EMBOS_H
+#define _OS_PORT_EMBOS_H
+
+//Dependencies
+#include "rtos.h"
+
+//Maximum number of tasks that can be dynamically created
+#ifndef OS_PORT_MAX_TASKS
+   #define OS_PORT_MAX_TASKS 16
+#elif (OS_PORT_MAX_TASKS < 1)
+   #error OS_PORT_MAX_TASKS parameter is not valid
+#endif
+
+//Task priority (normal)
+#ifndef OS_TASK_PRIORITY_NORMAL
+   #define OS_TASK_PRIORITY_NORMAL 1
+#endif
+
+//Task priority (high)
+#ifndef OS_TASK_PRIORITY_HIGH
+   #define OS_TASK_PRIORITY_HIGH 2
+#endif
+
+//Milliseconds to system ticks
+#ifndef OS_MS_TO_SYSTICKS
+   #define OS_MS_TO_SYSTICKS(n) (n)
+#endif
+
+//System ticks to milliseconds
+#ifndef OS_SYSTICKS_TO_MS
+   #define OS_SYSTICKS_TO_MS(n) (n)
+#endif
+
+//Enter interrupt service routine
+#define osEnterIsr() OS_EnterNestableInterrupt()
+
+//Leave interrupt service routine
+#define osExitIsr(flag) OS_LeaveNestableInterrupt()
+
+
+/**
+ * @brief Task object
+ **/
+
+typedef OS_TASK OsTask;
+
+
+/**
+ * @brief Event object
+ **/
+
+typedef OS_EVENT OsEvent;
+
+
+/**
+ * @brief Semaphore object
+ **/
+
+typedef OS_CSEMA OsSemaphore;
+
+
+/**
+ * @brief Mutex object
+ **/
+
+typedef OS_RSEMA OsMutex;
+
+
+/**
+ * @brief Task routine
+ **/
+
+typedef void (*OsTaskCode)(void *params);
+
+
+//Kernel management
+void osInitKernel(void);
+void osStartKernel(void);
+
+//Task management
+bool_t osCreateStaticTask(OsTask *task, const char_t *name, OsTaskCode taskCode,
+   void *params, void *stack, size_t stackSize, int_t priority);
+
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority);
+
+void osDeleteTask(OsTask *task);
+void osDelayTask(systime_t delay);
+void osSwitchTask(void);
+void osSuspendAllTasks(void);
+void osResumeAllTasks(void);
+
+//Event management
+bool_t osCreateEvent(OsEvent *event);
+void osDeleteEvent(OsEvent *event);
+void osSetEvent(OsEvent *event);
+void osResetEvent(OsEvent *event);
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout);
+bool_t osSetEventFromIsr(OsEvent *event);
+
+//Semaphore management
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count);
+void osDeleteSemaphore(OsSemaphore *semaphore);
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout);
+void osReleaseSemaphore(OsSemaphore *semaphore);
+
+//Mutex management
+bool_t osCreateMutex(OsMutex *mutex);
+void osDeleteMutex(OsMutex *mutex);
+void osAcquireMutex(OsMutex *mutex);
+void osReleaseMutex(OsMutex *mutex);
+
+//System time
+systime_t osGetSystemTime(void);
+
+//Memory management
+void *osAllocMem(size_t size);
+void osFreeMem(void *p);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_freertos.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,500 @@
+/**
+ * @file os_port_freertos.c
+ * @brief RTOS abstraction layer (FreeRTOS)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TRACE_LEVEL_OFF
+
+//Dependencies
+#include <stdio.h>
+#include <stdlib.h>
+#include "os_port.h"
+#include "os_port_freertos.h"
+#include "debug.h"
+
+
+/**
+ * @brief Kernel initialization
+ **/
+
+void osInitKernel(void)
+{
+}
+
+
+/**
+ * @brief Start kernel
+ **/
+
+void osStartKernel(void)
+{
+   //Start the scheduler
+   vTaskStartScheduler();
+}
+
+
+/**
+ * @brief Create a new task
+ * @param[in] name A name identifying the task
+ * @param[in] taskCode Pointer to the task entry function
+ * @param[in] params A pointer to a variable to be passed to the task
+ * @param[in] stackSize The initial size of the stack, in words
+ * @param[in] priority The priority at which the task should run
+ * @return If the function succeeds, the return value is a pointer to the
+ *   new task. If the function fails, the return value is NULL
+ **/
+
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority)
+{
+   portBASE_TYPE status;
+   xTaskHandle task = NULL;
+
+   //Create a new task
+   status = xTaskCreate((pdTASK_CODE) taskCode,
+      name, stackSize, params, priority, &task);
+
+   //Check whether the task was successfully created
+   if(status == pdPASS)
+      return task;
+   else
+      return NULL;
+}
+
+
+/**
+ * @brief Delete a task
+ * @param[in] task Pointer to the task to be deleted
+ **/
+
+void osDeleteTask(OsTask *task)
+{
+   //Delete the specified task
+   vTaskDelete((xTaskHandle) task);
+}
+
+
+/**
+ * @brief Delay routine
+ * @param[in] delay Amount of time for which the calling task should block
+ **/
+
+void osDelayTask(systime_t delay)
+{
+   //Delay the task for the specified duration
+   vTaskDelay(OS_MS_TO_SYSTICKS(delay));
+}
+
+
+/**
+ * @brief Yield control to the next task
+ **/
+
+void osSwitchTask(void)
+{
+   //Force a context switch
+   taskYIELD();
+}
+
+
+/**
+ * @brief Suspend scheduler activity
+ **/
+
+void osSuspendAllTasks(void)
+{
+   //Make sure the operating system is running
+   if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
+   {
+      //Suspend all tasks
+      vTaskSuspendAll();
+   }
+}
+
+
+/**
+ * @brief Resume scheduler activity
+ **/
+
+void osResumeAllTasks(void)
+{
+   //Make sure the operating system is running
+   if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
+   {
+      //Resume all tasks
+      xTaskResumeAll();
+   }
+}
+
+
+/**
+ * @brief Create an event object
+ * @param[in] event Pointer to the event object
+ * @return The function returns TRUE if the event object was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateEvent(OsEvent *event)
+{
+   //Create a binary semaphore
+   vSemaphoreCreateBinary(event->handle);
+
+   //Check whether the returned handle is valid
+   if(event->handle != NULL)
+   {
+      //Force the event to the nonsignaled state
+      xSemaphoreTake(event->handle, 0);
+      //Event successfully created
+      return TRUE;
+   }
+   else
+   {
+      //Failed to create event object
+      return FALSE;
+   }
+}
+
+
+/**
+ * @brief Delete an event object
+ * @param[in] event Pointer to the event object
+ **/
+
+void osDeleteEvent(OsEvent *event)
+{
+   //Make sure the handle is valid
+   if(event->handle != NULL)
+   {
+      //Properly dispose the event object
+      vSemaphoreDelete(event->handle);
+   }
+}
+
+
+/**
+ * @brief Set the specified event object to the signaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osSetEvent(OsEvent *event)
+{
+   //Set the specified event to the signaled state
+   xSemaphoreGive(event->handle);
+}
+
+
+/**
+ * @brief Set the specified event object to the nonsignaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osResetEvent(OsEvent *event)
+{
+   //Force the specified event to the nonsignaled state
+   xSemaphoreTake(event->handle, 0);
+}
+
+
+/**
+ * @brief Wait until the specified event is in the signaled state
+ * @param[in] event Pointer to the event object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the state of the specified object is
+ *   signaled. FALSE is returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout)
+{
+   portBASE_TYPE ret;
+
+   //Wait until the specified event is in the signaled state
+   if(timeout == INFINITE_DELAY)
+   {
+      //Infinite timeout period
+      ret = xSemaphoreTake(event->handle, portMAX_DELAY);
+   }
+   else
+   {
+      //Wait for the specified time interval
+      ret = xSemaphoreTake(event->handle, OS_MS_TO_SYSTICKS(timeout));
+   }
+
+   //The return value tells whether the event is set
+   return ret;
+}
+
+
+/**
+ * @brief Set an event object to the signaled state from an interrupt service routine
+ * @param[in] event Pointer to the event object
+ * @return TRUE if setting the event to signaled state caused a task to unblock
+ *   and the unblocked task has a priority higher than the currently running task
+ **/
+
+bool_t osSetEventFromIsr(OsEvent *event)
+{
+   portBASE_TYPE flag = FALSE;
+
+   //Set the specified event to the signaled state
+   xSemaphoreGiveFromISR(event->handle, &flag);
+
+   //A higher priority task has been woken?
+   return flag;
+}
+
+
+/**
+ * @brief Create a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] count The maximum count for the semaphore object. This value
+ *   must be greater than zero
+ * @return The function returns TRUE if the semaphore was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count)
+{
+   //Create a semaphore
+   semaphore->handle = xSemaphoreCreateCounting(count, count);
+
+   //Check whether the returned handle is valid
+   if(semaphore->handle != NULL)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Delete a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osDeleteSemaphore(OsSemaphore *semaphore)
+{
+   //Make sure the handle is valid
+   if(semaphore->handle != NULL)
+   {
+      //Properly dispose the specified semaphore
+      vSemaphoreDelete(semaphore->handle);
+   }
+}
+
+
+/**
+ * @brief Wait for the specified semaphore to be available
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the semaphore is available. FALSE is
+ *   returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout)
+{
+   portBASE_TYPE ret;
+
+   //Wait until the specified semaphore becomes available
+   if(timeout == INFINITE_DELAY)
+   {
+      //Infinite timeout period
+      ret = xSemaphoreTake(semaphore->handle, portMAX_DELAY);
+   }
+   else
+   {
+      //Wait for the specified time interval
+      ret = xSemaphoreTake(semaphore->handle, OS_MS_TO_SYSTICKS(timeout));
+   }
+
+   //The return value tells whether the semaphore is available
+   return ret;
+}
+
+
+/**
+ * @brief Release the specified semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osReleaseSemaphore(OsSemaphore *semaphore)
+{
+   //Release the semaphore
+   xSemaphoreGive(semaphore->handle);
+}
+
+
+/**
+ * @brief Create a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ * @return The function returns TRUE if the mutex was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateMutex(OsMutex *mutex)
+{
+   //Create a mutex object
+   mutex->handle = xSemaphoreCreateMutex();
+
+   //Check whether the returned handle is valid
+   if(mutex->handle != NULL)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Delete a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osDeleteMutex(OsMutex *mutex)
+{
+   //Make sure the handle is valid
+   if(mutex->handle != NULL)
+   {
+      //Properly dispose the specified mutex
+      vSemaphoreDelete(mutex->handle);
+   }
+}
+
+
+/**
+ * @brief Acquire ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osAcquireMutex(OsMutex *mutex)
+{
+   //Obtain ownership of the mutex object
+   xSemaphoreTake(mutex->handle, portMAX_DELAY);
+}
+
+
+/**
+ * @brief Release ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osReleaseMutex(OsMutex *mutex)
+{
+   //Release ownership of the mutex object
+   xSemaphoreGive(mutex->handle);
+}
+
+
+/**
+ * @brief Retrieve system time
+ * @return Number of milliseconds elapsed since the system was last started
+ **/
+
+systime_t osGetSystemTime(void)
+{
+   systime_t time;
+
+   //Get current tick count
+   time = xTaskGetTickCount();
+
+   //Convert system ticks to milliseconds
+   return OS_SYSTICKS_TO_MS(time);
+}
+
+
+/**
+ * @brief Allocate a memory block
+ * @param[in] size Bytes to allocate
+ * @return A pointer to the allocated memory block or NULL if
+ *   there is insufficient memory available
+ **/
+
+void *osAllocMem(size_t size)
+{
+   void *p;
+
+   //Allocate a memory block
+   p = pvPortMalloc(size);
+
+   //Debug message
+   TRACE_DEBUG("Allocating %" PRIuSIZE " bytes at 0x%08" PRIXPTR "\r\n", size, (uintptr_t) p);
+
+   //Return a pointer to the newly allocated memory block
+   return p;
+}
+
+
+/**
+ * @brief Release a previously allocated memory block
+ * @param[in] p Previously allocated memory block to be freed
+ **/
+
+void osFreeMem(void *p)
+{
+   //Make sure the pointer is valid
+   if(p != NULL)
+   {
+      //Debug message
+      TRACE_DEBUG("Freeing memory at 0x%08" PRIXPTR "\r\n", (uintptr_t) p);
+
+      //Free memory block
+      vPortFree(p);
+   }
+}
+
+
+/**
+ * @brief FreeRTOS stack overflow hook
+ **/
+
+void vApplicationStackOverflowHook(xTaskHandle pxTask, char *pcTaskName)
+{
+   (void) pcTaskName;
+   (void) pxTask;
+
+   taskDISABLE_INTERRUPTS();
+   while(1);
+}
+
+
+/**
+ * @brief Trap FreeRTOS errors
+ **/
+
+void vAssertCalled(const char *pcFile, unsigned long ulLine)
+{
+   volatile unsigned long ul = 0;
+
+   (void) pcFile;
+   (void) ulLine;
+
+   taskENTER_CRITICAL();
+
+   //Set ul to a non-zero value using the debugger to step out of this function
+   while(ul == 0)
+   {
+      portNOP();
+   }
+
+   taskEXIT_CRITICAL();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_freertos.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,161 @@
+/**
+ * @file os_port_freertos.h
+ * @brief RTOS abstraction layer (FreeRTOS)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _OS_PORT_FREERTOS_H
+#define _OS_PORT_FREERTOS_H
+
+//Dependencies
+#include "FreeRTOS.h"
+#include "task.h"
+#include "semphr.h"
+
+//Task priority (normal)
+#ifndef OS_TASK_PRIORITY_NORMAL
+   #define OS_TASK_PRIORITY_NORMAL (tskIDLE_PRIORITY + 1)
+#endif
+
+//Task priority (high)
+#ifndef OS_TASK_PRIORITY_HIGH
+   #define OS_TASK_PRIORITY_HIGH (tskIDLE_PRIORITY + 2)
+#endif
+
+//Milliseconds to system ticks
+#ifndef OS_MS_TO_SYSTICKS
+   #define OS_MS_TO_SYSTICKS(n) (n)
+#endif
+
+//System ticks to milliseconds
+#ifndef OS_SYSTICKS_TO_MS
+   #define OS_SYSTICKS_TO_MS(n) (n)
+#endif
+
+//Enter interrupt service routine
+#if defined(portENTER_SWITCHING_ISR)
+   #define osEnterIsr() portENTER_SWITCHING_ISR()
+#else
+   #define osEnterIsr()
+#endif
+
+//Leave interrupt service routine
+#if defined(portEXIT_SWITCHING_ISR)
+   #define osExitIsr(flag) portEXIT_SWITCHING_ISR()
+#elif defined(portEND_SWITCHING_ISR)
+   #define osExitIsr(flag) portEND_SWITCHING_ISR(flag)
+#elif defined(portYIELD_FROM_ISR)
+   #define osExitIsr(flag) portYIELD_FROM_ISR(flag)
+#else
+   #define osExitIsr(flag)
+#endif
+
+
+/**
+ * @brief Task object
+ **/
+
+//typedef void OsTask;
+#define OsTask void
+
+
+/**
+ * @brief Event object
+ **/
+
+typedef struct
+{
+   xSemaphoreHandle handle;
+} OsEvent;
+
+
+/**
+ * @brief Semaphore object
+ **/
+
+typedef struct
+{
+   xSemaphoreHandle handle;
+} OsSemaphore;
+
+
+/**
+ * @brief Mutex object
+ **/
+
+typedef struct
+{
+   xSemaphoreHandle handle;
+} OsMutex;
+
+
+/**
+ * @brief Task routine
+ **/
+
+typedef void (*OsTaskCode)(void *params);
+
+
+//Kernel management
+void osInitKernel(void);
+void osStartKernel(void);
+
+//Task management
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority);
+
+void osDeleteTask(OsTask *task);
+void osDelayTask(systime_t delay);
+void osSwitchTask(void);
+void osSuspendAllTasks(void);
+void osResumeAllTasks(void);
+
+//Event management
+bool_t osCreateEvent(OsEvent *event);
+void osDeleteEvent(OsEvent *event);
+void osSetEvent(OsEvent *event);
+void osResetEvent(OsEvent *event);
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout);
+bool_t osSetEventFromIsr(OsEvent *event);
+
+//Semaphore management
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count);
+void osDeleteSemaphore(OsSemaphore *semaphore);
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout);
+void osReleaseSemaphore(OsSemaphore *semaphore);
+
+//Mutex management
+bool_t osCreateMutex(OsMutex *mutex);
+void osDeleteMutex(OsMutex *mutex);
+void osAcquireMutex(OsMutex *mutex);
+void osReleaseMutex(OsMutex *mutex);
+
+//System time
+systime_t osGetSystemTime(void);
+
+//Memory management
+void *osAllocMem(size_t size);
+void osFreeMem(void *p);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_none.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,403 @@
+/**
+ * @file os_port_none.c
+ * @brief RTOS-less environment
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TRACE_LEVEL_OFF
+
+//Dependencies
+#include <stdio.h>
+#include <stdlib.h>
+#include "os_port.h"
+#include "os_port_none.h"
+#include "debug.h"
+
+//Platform-specific dependencies
+#if defined(__linux__) || defined(__FreeBSD__)
+   #include <sys/time.h>
+#elif defined(_WIN32)
+   #include <windows.h>
+#endif
+
+//Tick count
+systime_t systemTicks = 0;
+
+
+/**
+ * @brief Kernel initialization
+ **/
+
+void osInitKernel(void)
+{
+   //Initialize tick count
+   systemTicks = 0;
+}
+
+
+/**
+ * @brief Start kernel
+ **/
+
+void osStartKernel(void)
+{
+}
+
+
+/**
+ * @brief Create a new task
+ * @param[in] name A name identifying the task
+ * @param[in] taskCode Pointer to the task entry function
+ * @param[in] params A pointer to a variable to be passed to the task
+ * @param[in] stackSize The initial size of the stack, in words
+ * @param[in] priority The priority at which the task should run
+ * @return If the function succeeds, the return value is a pointer to the
+ *   new task. If the function fails, the return value is NULL
+ **/
+
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority)
+{
+   //Return a non-NULL pointer
+   return (OsTask *) (-1);
+}
+
+
+/**
+ * @brief Delete a task
+ * @param[in] task Pointer to the task to be deleted
+ **/
+
+void osDeleteTask(OsTask *task)
+{
+}
+
+
+/**
+ * @brief Delay routine
+ * @param[in] delay Amount of time for which the calling task should block
+ **/
+
+void osDelayTask(systime_t delay)
+{
+}
+
+
+/**
+ * @brief Yield control to the next task
+ **/
+
+void osSwitchTask(void)
+{
+}
+
+
+/**
+ * @brief Suspend scheduler activity
+ **/
+
+void osSuspendAllTasks(void)
+{
+}
+
+
+/**
+ * @brief Resume scheduler activity
+ **/
+
+void osResumeAllTasks(void)
+{
+}
+
+
+/**
+ * @brief Create an event object
+ * @param[in] event Pointer to the event object
+ * @return The function returns TRUE if the event object was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateEvent(OsEvent *event)
+{
+   //Force the event to the nonsignaled state
+   *event = FALSE;
+   //Event successfully created
+   return TRUE;
+}
+
+
+/**
+ * @brief Delete an event object
+ * @param[in] event Pointer to the event object
+ **/
+
+void osDeleteEvent(OsEvent *event)
+{
+}
+
+
+/**
+ * @brief Set the specified event object to the signaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osSetEvent(OsEvent *event)
+{
+   //Set the specified event to the signaled state
+   *event = TRUE;
+}
+
+
+/**
+ * @brief Set the specified event object to the nonsignaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osResetEvent(OsEvent *event)
+{
+   //Force the specified event to the nonsignaled state
+   *event = FALSE;
+}
+
+
+/**
+ * @brief Wait until the specified event is in the signaled state
+ * @param[in] event Pointer to the event object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the state of the specified object is
+ *   signaled. FALSE is returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout)
+{
+   //Check whether the specified event is set
+   if(*event)
+   {
+      //Clear event
+      *event = FALSE;
+      //The event is in the signaled state
+      return TRUE;
+   }
+   else
+   {
+      //The event is not in the signaled state
+      return FALSE;
+   }
+}
+
+
+/**
+ * @brief Set an event object to the signaled state from an interrupt service routine
+ * @param[in] event Pointer to the event object
+ * @return TRUE if setting the event to signaled state caused a task to unblock
+ *   and the unblocked task has a priority higher than the currently running task
+ **/
+
+bool_t osSetEventFromIsr(OsEvent *event)
+{
+   //Set the specified event to the signaled state
+   *event = TRUE;
+   //A higher priority task has been woken?
+   return FALSE;
+}
+
+
+/**
+ * @brief Create a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] count The maximum count for the semaphore object. This value
+ *   must be greater than zero
+ * @return The function returns TRUE if the semaphore was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count)
+{
+   //Create a semaphore
+   *semaphore = count;
+   //The semaphore was successfully created
+   return TRUE;
+}
+
+
+/**
+ * @brief Delete a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osDeleteSemaphore(OsSemaphore *semaphore)
+{
+}
+
+
+/**
+ * @brief Wait for the specified semaphore to be available
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the semaphore is available. FALSE is
+ *   returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout)
+{
+   //Check whether the specified semaphore is available
+   if(*semaphore > 0)
+   {
+      //Decrement semaphore value
+      *semaphore -= 1;
+      //The semaphore is available
+      return TRUE;
+   }
+   else
+   {
+      //The semaphore is not available
+      return FALSE;
+   }
+}
+
+
+/**
+ * @brief Release the specified semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osReleaseSemaphore(OsSemaphore *semaphore)
+{
+   //Release the semaphore
+   *semaphore += 1;
+}
+
+
+/**
+ * @brief Create a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ * @return The function returns TRUE if the mutex was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateMutex(OsMutex *mutex)
+{
+   //The mutex was successfully created
+   return TRUE;
+}
+
+
+/**
+ * @brief Delete a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osDeleteMutex(OsMutex *mutex)
+{
+}
+
+
+/**
+ * @brief Acquire ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osAcquireMutex(OsMutex *mutex)
+{
+}
+
+
+/**
+ * @brief Release ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osReleaseMutex(OsMutex *mutex)
+{
+}
+
+
+/**
+ * @brief Retrieve system time
+ * @return Number of milliseconds elapsed since the system was last started
+ **/
+
+systime_t osGetSystemTime(void)
+{
+   systime_t time;
+
+#if defined(__linux__) || defined(__FreeBSD__)
+   struct timeval tv;
+   //Get current time
+   gettimeofday(&tv, NULL);
+   //Convert resulting value to milliseconds
+   time = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+#elif defined(_WIN32)
+   //Get current tick count
+   time = GetTickCount();
+#else
+   //Get current tick count
+   time = systemTicks;
+#endif
+
+   //Convert system ticks to milliseconds
+   return OS_SYSTICKS_TO_MS(time);
+}
+
+
+/**
+ * @brief Allocate a memory block
+ * @param[in] size Bytes to allocate
+ * @return A pointer to the allocated memory block or NULL if
+ *   there is insufficient memory available
+ **/
+
+void *osAllocMem(size_t size)
+{
+   void *p;
+
+   //Allocate a memory block
+   p = malloc(size);
+
+   //Debug message
+   TRACE_DEBUG("Allocating %" PRIuSIZE " bytes at 0x%08" PRIXPTR "\r\n", size, (uintptr_t) p);
+
+   //Return a pointer to the newly allocated memory block
+   return p;
+}
+
+
+/**
+ * @brief Release a previously allocated memory block
+ * @param[in] p Previously allocated memory block to be freed
+ **/
+
+void osFreeMem(void *p)
+{
+   //Make sure the pointer is valid
+   if(p != NULL)
+   {
+      //Debug message
+      TRACE_DEBUG("Freeing memory at 0x%08" PRIXPTR "\r\n", (uintptr_t) p);
+
+      //Free memory block
+      free(p);
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_none.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,140 @@
+/**
+ * @file os_port_none.h
+ * @brief RTOS-less environment
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _OS_PORT_NONE_H
+#define _OS_PORT_NONE_H
+
+//Task priority (normal)
+#ifndef OS_TASK_PRIORITY_NORMAL
+   #define OS_TASK_PRIORITY_NORMAL 0
+#endif
+
+//Task priority (high)
+#ifndef OS_TASK_PRIORITY_HIGH
+   #define OS_TASK_PRIORITY_HIGH 0
+#endif
+
+//Milliseconds to system ticks
+#ifndef OS_MS_TO_SYSTICKS
+   #define OS_MS_TO_SYSTICKS(n) (n)
+#endif
+
+//System ticks to milliseconds
+#ifndef OS_SYSTICKS_TO_MS
+   #define OS_SYSTICKS_TO_MS(n) (n)
+#endif
+
+//Enter interrupt service routine
+#ifndef osEnterIsr
+   #define osEnterIsr()
+#endif
+
+//Leave interrupt service routine
+#ifndef osExitIsr
+   #define osExitIsr(flag) (void) flag
+#endif
+
+
+/**
+ * @brief Task object
+ **/
+
+typedef void OsTask;
+
+
+/**
+ * @brief Event object
+ **/
+
+typedef uint_t OsEvent;
+
+
+/**
+ * @brief Semaphore object
+ **/
+
+typedef uint_t OsSemaphore;
+
+
+/**
+ * @brief Mutex object
+ **/
+
+typedef uint_t OsMutex;
+
+
+/**
+ * @brief Task routine
+ **/
+
+typedef void (*OsTaskCode)(void *params);
+
+//Tick count
+extern systime_t systemTicks;
+
+//Kernel management
+void osInitKernel(void);
+void osStartKernel(void);
+
+//Task management
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority);
+
+void osDeleteTask(OsTask *task);
+void osDelayTask(systime_t delay);
+void osSwitchTask(void);
+void osSuspendAllTasks(void);
+void osResumeAllTasks(void);
+
+//Event management
+bool_t osCreateEvent(OsEvent *event);
+void osDeleteEvent(OsEvent *event);
+void osSetEvent(OsEvent *event);
+void osResetEvent(OsEvent *event);
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout);
+bool_t osSetEventFromIsr(OsEvent *event);
+
+//Semaphore management
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count);
+void osDeleteSemaphore(OsSemaphore *semaphore);
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout);
+void osReleaseSemaphore(OsSemaphore *semaphore);
+
+//Mutex management
+bool_t osCreateMutex(OsMutex *mutex);
+void osDeleteMutex(OsMutex *mutex);
+void osAcquireMutex(OsMutex *mutex);
+void osReleaseMutex(OsMutex *mutex);
+
+//System time
+systime_t osGetSystemTime(void);
+
+//Memory management
+void *osAllocMem(size_t size);
+void osFreeMem(void *p);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_rtx.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,601 @@
+/**
+ * @file os_port_rtx.c
+ * @brief RTOS abstraction layer (Keil RTX)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TRACE_LEVEL_OFF
+
+//Dependencies
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "os_port.h"
+#include "os_port_rtx.h"
+#include "debug.h"
+
+//Variables
+static bool_t running = FALSE;
+static OsTask taskTable[OS_PORT_MAX_TASKS];
+
+
+/**
+ * @brief Kernel initialization
+ **/
+
+void osInitKernel(void)
+{
+   //The scheduler is not running
+   running = FALSE;
+   //Initialize task table
+   memset(taskTable, 0, sizeof(taskTable));
+}
+
+
+/**
+ * @brief Start kernel
+ * @param[in] task Pointer to the task function to start after the kernel is initialized
+ **/
+
+void osStartKernel(OsInitTaskCode task)
+{
+   //The scheduler is now running
+   running = TRUE;
+   //Start the scheduler
+   os_sys_init(task);
+}
+
+
+/**
+ * @brief Create a static task
+ * @param[out] task Pointer to the task structure
+ * @param[in] name A name identifying the task
+ * @param[in] taskCode Pointer to the task entry function
+ * @param[in] params A pointer to a variable to be passed to the task
+ * @param[in] stack Pointer to the stack
+ * @param[in] stackSize The initial size of the stack, in words
+ * @param[in] priority The priority at which the task should run
+ * @return The function returns TRUE if the task was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateStaticTask(OsTask *task, const char_t *name, OsTaskCode taskCode,
+   void *params, void *stack, size_t stackSize, int_t priority)
+{
+   //Create a new task
+   task->tid = os_tsk_create_user_ex(taskCode, priority,
+      stack, stackSize * sizeof(uint_t), params);
+
+   //Check task identifier
+   if(task->tid != 0)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Create a new task
+ * @param[in] name A name identifying the task
+ * @param[in] taskCode Pointer to the task entry function
+ * @param[in] params A pointer to a variable to be passed to the task
+ * @param[in] stackSize The initial size of the stack, in words
+ * @param[in] priority The priority at which the task should run
+ * @return If the function succeeds, the return value is a pointer to the
+ *   new task. If the function fails, the return value is NULL
+ **/
+
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority)
+{
+   uint_t i;
+   OsTask *task = NULL;
+
+   //Enter critical section
+   osSuspendAllTasks();
+
+   //Loop through table
+   for(i = 0; i < OS_PORT_MAX_TASKS; i++)
+   {
+      //Check whether the current entry is free
+      if(!taskTable[i].tid)
+         break;
+   }
+
+   //Any entry available in the table?
+   if(i < OS_PORT_MAX_TASKS)
+   {
+      //Create a new task
+      taskTable[i].tid = os_tsk_create_ex(taskCode, priority, params);
+
+      //Check whether the task was successfully created
+      if(taskTable[i].tid != 0)
+         task = &taskTable[i];
+   }
+
+   //Leave critical section
+   osResumeAllTasks();
+
+   //Return a pointer to the newly created task
+   return task;
+}
+
+
+/**
+ * @brief Delete a task
+ * @param[in] task Pointer to the task to be deleted
+ **/
+
+void osDeleteTask(OsTask *task)
+{
+   uint_t i;
+   OS_TID tid;
+
+   //Retrieve task ID
+   if(task == NULL)
+      tid = os_tsk_self();
+   else
+      tid = task->tid;
+
+   //Enter critical section
+   osSuspendAllTasks();
+
+   //Loop through table
+   for(i = 0; i < OS_PORT_MAX_TASKS; i++)
+   {
+      //Check current entry
+      if(taskTable[i].tid == tid)
+      {
+         //Release current entry
+         taskTable[i].tid = 0;
+      }
+   }
+
+   //Leave critical section
+   osResumeAllTasks();
+
+   //Delete the currently running task?
+   if(task == NULL)
+   {
+      //Kill ourselves
+      os_tsk_delete_self();
+   }
+   else
+   {
+      //Delete the specified task
+      os_tsk_delete(tid);
+   }
+}
+
+
+/**
+ * @brief Delay routine
+ * @param[in] delay Amount of time for which the calling task should block
+ **/
+
+void osDelayTask(systime_t delay)
+{
+   uint16_t n;
+
+   //Convert milliseconds to system ticks
+   delay = OS_MS_TO_SYSTICKS(delay);
+
+   //Delay the task for the specified duration
+   while(delay > 0)
+   {
+      //The delay value cannot be higher than 0xFFFE...
+      n = MIN(delay, 0xFFFE);
+      //Wait for the specified amount of time
+      os_dly_wait(n);
+      //Decrement delay value
+      delay -= n;
+   }
+}
+
+
+/**
+ * @brief Yield control to the next task
+ **/
+
+void osSwitchTask(void)
+{
+   //Pass control to the next task
+   os_tsk_pass();
+}
+
+
+/**
+ * @brief Suspend scheduler activity
+ **/
+
+void osSuspendAllTasks(void)
+{
+   //Make sure the operating system is running
+   if(running)
+   {
+      //Suspend all tasks
+      tsk_lock();
+   }
+}
+
+
+/**
+ * @brief Resume scheduler activity
+ **/
+
+void osResumeAllTasks(void)
+{
+   //Make sure the operating system is running
+   if(running)
+   {
+      //Resume all tasks
+      tsk_unlock();
+   }
+}
+
+
+/**
+ * @brief Create an event object
+ * @param[in] event Pointer to the event object
+ * @return The function returns TRUE if the event object was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateEvent(OsEvent *event)
+{
+   //Initialize the event object
+   os_sem_init(event, 0);
+
+   //Event successfully created
+   return TRUE;
+}
+
+
+/**
+ * @brief Delete an event object
+ * @param[in] event Pointer to the event object
+ **/
+
+void osDeleteEvent(OsEvent *event)
+{
+   //No resource to release
+}
+
+
+/**
+ * @brief Set the specified event object to the signaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osSetEvent(OsEvent *event)
+{
+   //Set the specified event to the signaled state
+   os_sem_send(event);
+}
+
+
+/**
+ * @brief Set the specified event object to the nonsignaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osResetEvent(OsEvent *event)
+{
+   OS_RESULT res;
+
+   //Force the specified event to the nonsignaled state
+   do
+   {
+      //Decrement the semaphore's count by one
+      res = os_sem_wait(event, 0);
+
+      //Check status
+   } while(res == OS_R_OK);
+}
+
+
+/**
+ * @brief Wait until the specified event is in the signaled state
+ * @param[in] event Pointer to the event object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the state of the specified object is
+ *   signaled. FALSE is returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout)
+{
+   uint16_t n;
+   OS_RESULT res;
+
+   //Wait until the specified event is in the signaled
+   //state or the timeout interval elapses
+   if(timeout == INFINITE_DELAY)
+   {
+      //Infinite timeout period
+      res = os_sem_wait(event, 0xFFFF);
+   }
+   else
+   {
+      //Convert milliseconds to system ticks
+      timeout = OS_MS_TO_SYSTICKS(timeout);
+
+      //Loop until the assigned time period has elapsed
+      do
+      {
+         //The timeout value cannot be higher than 0xFFFE...
+         n = MIN(timeout, 0xFFFE);
+          //Wait for the specified time interval
+         res = os_sem_wait(event, n);
+         //Decrement timeout value
+         timeout -= n;
+
+         //Check timeout value
+      } while(res == OS_R_TMO && timeout > 0);
+   }
+
+   //Check whether the specified event is set
+   if(res == OS_R_OK || res == OS_R_SEM)
+   {
+      //Force the event back to the nonsignaled state
+      do
+      {
+         //Decrement the semaphore's count by one
+         res = os_sem_wait(event, 0);
+
+         //Check status
+      } while(res == OS_R_OK);
+
+      //The specified event is in the signaled state
+      return TRUE;
+   }
+   else
+   {
+      //The timeout interval elapsed
+      return FALSE;
+   }
+}
+
+
+/**
+ * @brief Set an event object to the signaled state from an interrupt service routine
+ * @param[in] event Pointer to the event object
+ * @return TRUE if setting the event to signaled state caused a task to unblock
+ *   and the unblocked task has a priority higher than the currently running task
+ **/
+
+bool_t osSetEventFromIsr(OsEvent *event)
+{
+   //Set the specified event to the signaled state
+   isr_sem_send(event);
+
+   //The return value is not relevant
+   return FALSE;
+}
+
+
+/**
+ * @brief Create a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] count The maximum count for the semaphore object. This value
+ *   must be greater than zero
+ * @return The function returns TRUE if the semaphore was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count)
+{
+   //Initialize the semaphore object
+   os_sem_init(semaphore, count);
+
+   //Semaphore successfully created
+   return TRUE;
+}
+
+
+/**
+ * @brief Delete a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osDeleteSemaphore(OsSemaphore *semaphore)
+{
+   //No resource to release
+}
+
+
+/**
+ * @brief Wait for the specified semaphore to be available
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the semaphore is available. FALSE is
+ *   returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout)
+{
+   uint16_t n;
+   OS_RESULT res;
+
+   //Wait until the semaphore is available or the timeout interval elapses
+   if(timeout == INFINITE_DELAY)
+   {
+      //Infinite timeout period
+      res = os_sem_wait(semaphore, 0xFFFF);
+   }
+   else
+   {
+      //Convert milliseconds to system ticks
+      timeout = OS_MS_TO_SYSTICKS(timeout);
+
+      //Loop until the assigned time period has elapsed
+      do
+      {
+         //The timeout value cannot be higher than 0xFFFE...
+         n = MIN(timeout, 0xFFFE);
+         //Wait for the specified time interval
+         res = os_sem_wait(semaphore, n);
+         //Decrement timeout value
+         timeout -= n;
+
+         //Check timeout value
+      } while(res == OS_R_TMO && timeout > 0);
+   }
+
+   //Check whether the specified semaphore is available
+   if(res == OS_R_OK || res == OS_R_SEM)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Release the specified semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osReleaseSemaphore(OsSemaphore *semaphore)
+{
+   //Release the semaphore
+   os_sem_send(semaphore);
+}
+
+
+/**
+ * @brief Create a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ * @return The function returns TRUE if the mutex was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateMutex(OsMutex *mutex)
+{
+   //Initialize the mutex object
+   os_mut_init(mutex);
+
+   //Mutex successfully created
+   return TRUE;
+}
+
+
+/**
+ * @brief Delete a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osDeleteMutex(OsMutex *mutex)
+{
+   //No resource to release
+}
+
+
+/**
+ * @brief Acquire ownership of the specified mutex object
+ * @param[in] mutex A handle to the mutex object
+ **/
+
+void osAcquireMutex(OsMutex *mutex)
+{
+   //Obtain ownership of the mutex object
+   os_mut_wait(mutex, 0xFFFF);
+}
+
+
+/**
+ * @brief Release ownership of the specified mutex object
+ * @param[in] mutex A handle to the mutex object
+ **/
+
+void osReleaseMutex(OsMutex *mutex)
+{
+   //Release ownership of the mutex object
+   os_mut_release(mutex);
+}
+
+
+/**
+ * @brief Retrieve system time
+ * @return Number of milliseconds elapsed since the system was last started
+ **/
+
+systime_t osGetSystemTime(void)
+{
+   systime_t time;
+
+   //Get current tick count
+   time = os_time_get();
+
+   //Convert system ticks to milliseconds
+   return OS_SYSTICKS_TO_MS(time);
+}
+
+
+/**
+ * @brief Allocate a memory block
+ * @param[in] size Bytes to allocate
+ * @return A pointer to the allocated memory block or NULL if
+ *   there is insufficient memory available
+ **/
+
+void *osAllocMem(size_t size)
+{
+   void *p;
+
+   //Enter critical section
+   osSuspendAllTasks();
+   //Allocate a memory block
+   p = malloc(size);
+   //Leave critical section
+   osResumeAllTasks();
+
+   //Debug message
+   TRACE_DEBUG("Allocating %u bytes at 0x%08X\r\n", size, (uint_t) p);
+
+   //Return a pointer to the newly allocated memory block
+   return p;
+}
+
+
+/**
+ * @brief Release a previously allocated memory block
+ * @param[in] p Previously allocated memory block to be freed
+ **/
+
+void osFreeMem(void *p)
+{
+   //Make sure the pointer is valid
+   if(p != NULL)
+   {
+      //Debug message
+      TRACE_DEBUG("Freeing memory at 0x%08X\r\n", (uint_t) p);
+
+      //Enter critical section
+      osSuspendAllTasks();
+      //Free memory block
+      free(p);
+      //Leave critical section
+      osResumeAllTasks();
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_rtx.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,172 @@
+/**
+ * @file os_port_rtx.h
+ * @brief RTOS abstraction layer (Keil RTX)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _OS_PORT_RTX_H
+#define _OS_PORT_RTX_H
+
+//Dependencies
+#ifdef RTX_CUSTOM_HEADER
+   #include RTX_CUSTOM_HEADER
+#else
+   #include "rtl.h"
+#endif
+
+//Maximum number of tasks that can be dynamically created
+#ifndef OS_PORT_MAX_TASKS
+   #define OS_PORT_MAX_TASKS 16
+#elif (OS_PORT_MAX_TASKS < 1)
+   #error OS_PORT_MAX_TASKS parameter is not valid
+#endif
+
+//Task priority (normal)
+#ifndef OS_TASK_PRIORITY_NORMAL
+   #define OS_TASK_PRIORITY_NORMAL 1
+#endif
+
+//Task priority (high)
+#ifndef OS_TASK_PRIORITY_HIGH
+   #define OS_TASK_PRIORITY_HIGH 2
+#endif
+
+//Milliseconds to system ticks
+#ifndef OS_MS_TO_SYSTICKS
+   #define OS_MS_TO_SYSTICKS(n) (n)
+#endif
+
+//System ticks to milliseconds
+#ifndef OS_SYSTICKS_TO_MS
+   #define OS_SYSTICKS_TO_MS(n) (n)
+#endif
+
+//Enter interrupt service routine
+#define osEnterIsr()
+
+//Leave interrupt service routine
+#define osExitIsr(flag)
+
+
+/**
+ * @brief Task object
+ **/
+
+typedef struct
+{
+   OS_TID tid;
+} OsTask;
+
+
+/**
+ * @brief Event object
+ **/
+
+typedef OS_SEM OsEvent;
+
+
+/**
+ * @brief Semaphore object
+ **/
+
+typedef OS_SEM OsSemaphore;
+
+
+/**
+ * @brief Mutex object
+ **/
+
+typedef OS_MUT OsMutex;
+
+
+/**
+ * @brief Task routine
+ **/
+
+typedef void (*OsTaskCode)(void *params);
+
+
+/**
+ * @brief Initialization task
+ **/
+
+typedef void (*OsInitTaskCode)(void);
+
+
+//Kernel management
+void osInitKernel(void);
+void osStartKernel(OsInitTaskCode task);
+
+//Task management
+bool_t osCreateStaticTask(OsTask *task, const char_t *name, OsTaskCode taskCode,
+   void *params, void *stack, size_t stackSize, int_t priority);
+
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority);
+
+void osDeleteTask(OsTask *task);
+void osDelayTask(systime_t delay);
+void osSwitchTask(void);
+void osSuspendAllTasks(void);
+void osResumeAllTasks(void);
+
+//Event management
+bool_t osCreateEvent(OsEvent *event);
+void osDeleteEvent(OsEvent *event);
+void osSetEvent(OsEvent *event);
+void osResetEvent(OsEvent *event);
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout);
+bool_t osSetEventFromIsr(OsEvent *event);
+
+//Semaphore management
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count);
+void osDeleteSemaphore(OsSemaphore *semaphore);
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout);
+void osReleaseSemaphore(OsSemaphore *semaphore);
+
+//Mutex management
+bool_t osCreateMutex(OsMutex *mutex);
+void osDeleteMutex(OsMutex *mutex);
+void osAcquireMutex(OsMutex *mutex);
+void osReleaseMutex(OsMutex *mutex);
+
+//System time
+systime_t osGetSystemTime(void);
+
+//Memory management
+void *osAllocMem(size_t size);
+void osFreeMem(void *p);
+
+//Undefine conflicting definitions
+#undef htons
+#undef htonl
+#undef ntohs
+#undef ntohl
+#undef TCP_STATE_CLOSED
+#undef TCP_STATE_LISTEN
+#undef TCP_STATE_SYN_SENT
+#undef TCP_STATE_CLOSING
+#undef TCP_STATE_LAST_ACK
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_sys_bios.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,499 @@
+/**
+ * @file os_port_sys_bios.c
+ * @brief RTOS abstraction layer (SYS/BIOS)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TRACE_LEVEL_OFF
+
+//Dependencies
+#include <stdio.h>
+#include <stdlib.h>
+#include "os_port.h"
+#include "os_port_sys_bios.h"
+#include "debug.h"
+
+//Variables
+static bool_t running = FALSE;
+
+
+/**
+ * @brief Kernel initialization
+ **/
+
+void osInitKernel(void)
+{
+   //The scheduler is not running
+   running = FALSE;
+}
+
+
+/**
+ * @brief Start kernel
+ **/
+
+void osStartKernel(void)
+{
+   //The scheduler is now running
+   running = TRUE;
+   //Start the scheduler
+   BIOS_start();
+}
+
+
+/**
+ * @brief Create a new task
+ * @param[in] name A name identifying the task
+ * @param[in] taskCode Pointer to the task entry function
+ * @param[in] params A pointer to a variable to be passed to the task
+ * @param[in] stackSize The initial size of the stack, in words
+ * @param[in] priority The priority at which the task should run
+ * @return If the function succeeds, the return value is a pointer to the
+ *   new task. If the function fails, the return value is NULL
+ **/
+
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority)
+{
+   Error_Block eb;
+   Task_Params taskParams;
+   Task_Handle task;
+
+   //Initialize error block
+   Error_init(&eb);
+
+   //Set parameters
+   Task_Params_init(&taskParams);
+   taskParams.arg0 = (UArg) params;
+   taskParams.stackSize = stackSize * sizeof(uint_t);
+   taskParams.priority = priority;
+
+   //Create a new task
+   task = Task_create((Task_FuncPtr) taskCode, &taskParams, &eb);
+
+   //Return a pointer to the newly created task
+   return task;
+}
+
+
+/**
+ * @brief Delete a task
+ * @param[in] task Pointer to the task to be deleted
+ **/
+
+void osDeleteTask(OsTask *task)
+{
+   //Delete the specified task
+   Task_delete(&task);
+}
+
+
+/**
+ * @brief Delay routine
+ * @param[in] delay Amount of time for which the calling task should block
+ **/
+
+void osDelayTask(systime_t delay)
+{
+   //Delay the task for the specified duration
+   Task_sleep(OS_MS_TO_SYSTICKS(delay));
+}
+
+
+/**
+ * @brief Yield control to the next task
+ **/
+
+void osSwitchTask(void)
+{
+   //Force a context switch
+   Task_yield();
+}
+
+
+/**
+ * @brief Suspend scheduler activity
+ **/
+
+void osSuspendAllTasks(void)
+{
+   //Make sure the operating system is running
+   if(running)
+   {
+      //Disable the task scheduler
+      Task_disable();
+   }
+}
+
+
+/**
+ * @brief Resume scheduler activity
+ **/
+
+void osResumeAllTasks(void)
+{
+   //Make sure the operating system is running
+   if(running)
+   {
+      //Enable the task scheduler
+      Task_enable();
+   }
+}
+
+
+/**
+ * @brief Create an event object
+ * @param[in] event Pointer to the event object
+ * @return The function returns TRUE if the event object was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateEvent(OsEvent *event)
+{
+   //Create an event object
+   event->handle = Event_create(NULL, NULL);
+
+   //Check whether the returned handle is valid
+   if(event->handle != NULL)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Delete an event object
+ * @param[in] event Pointer to the event object
+ **/
+
+void osDeleteEvent(OsEvent *event)
+{
+   //Make sure the handle is valid
+   if(event->handle != NULL)
+   {
+      //Properly dispose the event object
+      Event_delete(&event->handle);
+   }
+}
+
+
+/**
+ * @brief Set the specified event object to the signaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osSetEvent(OsEvent *event)
+{
+   //Set the specified event to the signaled state
+   Event_post(event->handle, Event_Id_00);
+}
+
+
+/**
+ * @brief Set the specified event object to the nonsignaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osResetEvent(OsEvent *event)
+{
+   //Force the specified event to the nonsignaled state
+   Event_pend(event->handle, Event_Id_00, Event_Id_NONE, BIOS_NO_WAIT);
+}
+
+
+/**
+ * @brief Wait until the specified event is in the signaled state
+ * @param[in] event Pointer to the event object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the state of the specified object is
+ *   signaled. FALSE is returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout)
+{
+   Bool ret;
+
+   //Wait until the specified event is in the signaled state
+   if(timeout == 0)
+   {
+      //Non-blocking call
+      ret = Event_pend(event->handle, Event_Id_00,
+         Event_Id_NONE, BIOS_NO_WAIT);
+   }
+   else if(timeout == INFINITE_DELAY)
+   {
+      //Infinite timeout period
+      ret = Event_pend(event->handle, Event_Id_00,
+         Event_Id_NONE, BIOS_WAIT_FOREVER);
+   }
+   else
+   {
+      //Wait for the specified time interval
+      ret = Event_pend(event->handle, Event_Id_00,
+         Event_Id_NONE,  OS_MS_TO_SYSTICKS(timeout));
+   }
+
+   //The return value tells whether the event is set
+   return ret;
+}
+
+
+/**
+ * @brief Set an event object to the signaled state from an interrupt service routine
+ * @param[in] event Pointer to the event object
+ * @return TRUE if setting the event to signaled state caused a task to unblock
+ *   and the unblocked task has a priority higher than the currently running task
+ **/
+
+bool_t osSetEventFromIsr(OsEvent *event)
+{
+   //Set the specified event to the signaled state
+   Event_post(event->handle, Event_Id_00);
+
+   //The return value is not relevant
+   return FALSE;
+}
+
+
+/**
+ * @brief Create a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] count The maximum count for the semaphore object. This value
+ *   must be greater than zero
+ * @return The function returns TRUE if the semaphore was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count)
+{
+   Semaphore_Params semaphoreParams;
+
+   //Set parameters
+   Semaphore_Params_init(&semaphoreParams);
+   semaphoreParams.mode = Semaphore_Mode_COUNTING;
+
+   //Create a semaphore
+   semaphore->handle = Semaphore_create(count, &semaphoreParams, NULL);
+
+   //Check whether the returned handle is valid
+   if(semaphore->handle != NULL)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Delete a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osDeleteSemaphore(OsSemaphore *semaphore)
+{
+   //Make sure the handle is valid
+   if(semaphore->handle != NULL)
+   {
+      //Properly dispose the specified semaphore
+      Semaphore_delete(&semaphore->handle);
+   }
+}
+
+
+/**
+ * @brief Wait for the specified semaphore to be available
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the semaphore is available. FALSE is
+ *   returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout)
+{
+   Bool ret;
+
+   //Wait until the specified semaphore becomes available
+   if(timeout == 0)
+   {
+      //Non-blocking call
+      ret = Semaphore_pend(semaphore->handle, BIOS_NO_WAIT);
+   }
+   else if(timeout == INFINITE_DELAY)
+   {
+      //Infinite timeout period
+      ret = Semaphore_pend(semaphore->handle, BIOS_WAIT_FOREVER);
+   }
+   else
+   {
+      //Wait for the specified time interval
+      ret = Semaphore_pend(semaphore->handle, OS_MS_TO_SYSTICKS(timeout));
+   }
+
+   //The return value tells whether the semaphore is available
+   return ret;
+}
+
+
+/**
+ * @brief Release the specified semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osReleaseSemaphore(OsSemaphore *semaphore)
+{
+   //Release the semaphore
+   Semaphore_post(semaphore->handle);
+}
+
+
+/**
+ * @brief Create a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ * @return The function returns TRUE if the mutex was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateMutex(OsMutex *mutex)
+{
+   Semaphore_Params semaphoreParams;
+
+   //Set parameters
+   Semaphore_Params_init(&semaphoreParams);
+   semaphoreParams.mode = Semaphore_Mode_BINARY_PRIORITY;
+
+   //Create a mutex
+   mutex->handle = Semaphore_create(1, &semaphoreParams, NULL);
+
+   //Check whether the returned handle is valid
+   if(mutex->handle != NULL)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Delete a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osDeleteMutex(OsMutex *mutex)
+{
+   //Make sure the handle is valid
+   if(mutex->handle != NULL)
+   {
+      //Properly dispose the specified mutex
+      Semaphore_delete(&mutex->handle);
+   }
+}
+
+
+/**
+ * @brief Acquire ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osAcquireMutex(OsMutex *mutex)
+{
+   //Obtain ownership of the mutex object
+   Semaphore_pend(mutex->handle, BIOS_WAIT_FOREVER);
+}
+
+
+/**
+ * @brief Release ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osReleaseMutex(OsMutex *mutex)
+{
+   //Release ownership of the mutex object
+   Semaphore_post(mutex->handle);
+}
+
+
+/**
+ * @brief Retrieve system time
+ * @return Number of milliseconds elapsed since the system was last started
+ **/
+
+systime_t osGetSystemTime(void)
+{
+   systime_t time;
+
+   //Get current tick count
+   time = Clock_getTicks();
+
+   //Convert system ticks to milliseconds
+   return OS_SYSTICKS_TO_MS(time);
+}
+
+
+/**
+ * @brief Allocate a memory block
+ * @param[in] size Bytes to allocate
+ * @return A pointer to the allocated memory block or NULL if
+ *   there is insufficient memory available
+ **/
+
+void *osAllocMem(size_t size)
+{
+   void *p;
+
+   //Enter critical section
+   osSuspendAllTasks();
+   //Allocate a memory block
+   p = malloc(size);
+   //Leave critical section
+   osResumeAllTasks();
+
+   //Debug message
+   TRACE_DEBUG("Allocating %" PRIuSIZE " bytes at 0x%08" PRIXPTR "\r\n", size, (uintptr_t) p);
+
+   //Return a pointer to the newly allocated memory block
+   return p;
+}
+
+
+/**
+ * @brief Release a previously allocated memory block
+ * @param[in] p Previously allocated memory block to be freed
+ **/
+
+void osFreeMem(void *p)
+{
+   //Make sure the pointer is valid
+   if(p != NULL)
+   {
+      //Debug message
+      TRACE_DEBUG("Freeing memory at 0x%08" PRIXPTR "\r\n", (uintptr_t) p);
+
+      //Enter critical section
+      osSuspendAllTasks();
+      //Free memory block
+      free(p);
+      //Leave critical section
+      osResumeAllTasks();
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_sys_bios.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,155 @@
+/**
+ * @file os_port_sys_bios.h
+ * @brief RTOS abstraction layer (SYS/BIOS)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _OS_PORT_SYS_BIOS_H
+#define _OS_PORT_SYS_BIOS_H
+
+//Dependencies
+#include <xdc/std.h>
+#include <xdc/runtime/error.h>
+#include <xdc/runtime/system.h>
+#include <xdc/runtime/memory.h>
+#include <ti/sysbios/bios.h>
+#include <ti/sysbios/knl/task.h>
+#include <ti/sysbios/knl/event.h>
+#include <ti/sysbios/knl/semaphore.h>
+#include <ti/sysbios/knl/clock.h>
+#include <ti/sysbios/hal/hwi.h>
+
+//Task priority (normal)
+#ifndef OS_TASK_PRIORITY_NORMAL
+   #define OS_TASK_PRIORITY_NORMAL 1
+#endif
+
+//Task priority (high)
+#ifndef OS_TASK_PRIORITY_HIGH
+   #define OS_TASK_PRIORITY_HIGH 2
+#endif
+
+//Milliseconds to system ticks
+#ifndef OS_MS_TO_SYSTICKS
+   #define OS_MS_TO_SYSTICKS(n) (n)
+#endif
+
+//System ticks to milliseconds
+#ifndef OS_SYSTICKS_TO_MS
+   #define OS_SYSTICKS_TO_MS(n) (n)
+#endif
+
+//Enter interrupt service routine
+#define osEnterIsr()
+
+//Leave interrupt service routine
+#define osExitIsr(flag)
+
+
+/**
+ * @brief Task object
+ **/
+
+typedef Task_Object OsTask;
+
+
+/**
+ * @brief Event object
+ **/
+
+typedef struct
+{
+   Event_Handle handle;
+} OsEvent;
+
+
+/**
+ * @brief Semaphore object
+ **/
+
+typedef struct
+{
+   Semaphore_Handle handle;
+} OsSemaphore;
+
+
+/**
+ * @brief Mutex object
+ **/
+
+typedef struct
+{
+   Semaphore_Handle handle;
+} OsMutex;
+
+
+/**
+ * @brief Task routine
+ **/
+
+typedef void (*OsTaskCode)(void *params);
+
+
+//Kernel management
+void osInitKernel(void);
+void osStartKernel(void);
+
+//Task management
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority);
+
+void osDeleteTask(OsTask *task);
+void osDelayTask(systime_t delay);
+void osSwitchTask(void);
+void osSuspendAllTasks(void);
+void osResumeAllTasks(void);
+
+//Event management
+bool_t osCreateEvent(OsEvent *event);
+void osDeleteEvent(OsEvent *event);
+void osSetEvent(OsEvent *event);
+void osResetEvent(OsEvent *event);
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout);
+bool_t osSetEventFromIsr(OsEvent *event);
+
+//Semaphore management
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count);
+void osDeleteSemaphore(OsSemaphore *semaphore);
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout);
+void osReleaseSemaphore(OsSemaphore *semaphore);
+
+//Mutex management
+bool_t osCreateMutex(OsMutex *mutex);
+void osDeleteMutex(OsMutex *mutex);
+void osAcquireMutex(OsMutex *mutex);
+void osReleaseMutex(OsMutex *mutex);
+
+//System time
+systime_t osGetSystemTime(void);
+
+//Memory management
+void *osAllocMem(size_t size);
+void osFreeMem(void *p);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_ucos2.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,651 @@
+/**
+ * @file os_port_ucos2.c
+ * @brief RTOS abstraction layer (Micrium uC/OS-II)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TRACE_LEVEL_OFF
+
+//Dependencies
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "os_port.h"
+#include "os_port_ucos2.h"
+#include "debug.h"
+
+//Variables
+static OsTask tcbTable[OS_LOWEST_PRIO];
+
+
+/**
+ * @brief Kernel initialization
+ **/
+
+void osInitKernel(void)
+{
+   //Initialize table
+   memset(tcbTable, 0, sizeof(tcbTable));
+
+   //Scheduler initialization
+   OSInit();
+}
+
+
+/**
+ * @brief Start kernel
+ **/
+
+void osStartKernel(void)
+{
+   //Start the scheduler
+   OSStart();
+}
+
+
+/**
+ * @brief Create a static task
+ * @param[out] task Pointer to the task structure
+ * @param[in] name A name identifying the task
+ * @param[in] taskCode Pointer to the task entry function
+ * @param[in] params A pointer to a variable to be passed to the task
+ * @param[in] stack Pointer to the stack
+ * @param[in] stackSize The initial size of the stack, in words
+ * @param[in] priority The priority at which the task should run
+ * @return The function returns TRUE if the task was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateStaticTask(OsTask *task, const char_t *name, OsTaskCode taskCode,
+   void *params, void *stack, size_t stackSize, int_t priority)
+{
+   INT8U err;
+   OS_STK *stackTop;
+
+   //Check stack size
+   if(stackSize == 0)
+      return FALSE;
+
+   //Top of the stack
+   stackTop = (OS_STK *) stack + (stackSize - 1);
+
+   //Search for a free TCB
+   while(priority < (OS_LOWEST_PRIO - 3) && OSTCBPrioTbl[priority] != 0)
+      priority++;
+
+   //No more TCB available?
+   if(priority >= (OS_LOWEST_PRIO - 3))
+      return FALSE;
+
+   //Create a new task
+   err = OSTaskCreateExt(taskCode, params, stackTop, priority, priority,
+      stack, stackSize, NULL, OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);
+
+   //Check whether the task was successfully created
+   if(err == OS_ERR_NONE)
+   {
+      //Save task priority
+      task->prio = priority;
+      //The task was successfully created
+      return TRUE;
+   }
+   else
+   {
+      //Report an error
+      return FALSE;
+   }
+}
+
+
+/**
+ * @brief Create a new task
+ * @param[in] name A name identifying the task
+ * @param[in] taskCode Pointer to the task entry function
+ * @param[in] params A pointer to a variable to be passed to the task
+ * @param[in] stackSize The initial size of the stack, in words
+ * @param[in] priority The priority at which the task should run
+ * @return If the function succeeds, the return value is a pointer to the
+ *   new task. If the function fails, the return value is NULL
+ **/
+
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority)
+{
+   //INT8U i;
+   OS_STK *stack;
+
+   //Allocate a memory block to hold the task's stack
+   stack = osAllocMem(stackSize * sizeof(OS_STK));
+
+   //Successful memory allocation?
+   if(stack != NULL)
+   {
+      //Create task
+      if(osCreateStaticTask(&tcbTable[priority], name,
+         taskCode, params, stack, stackSize, priority))
+      {
+         //Return a valid handle
+         return &tcbTable[priority];
+      }
+      else
+      {
+         //Clean up side effects
+         osFreeMem(stack);
+         //Report an error
+         return NULL;
+      }
+   }
+   else
+   {
+      //Memory allocation failed
+      return NULL;
+   }
+}
+
+
+/**
+ * @brief Delete a task
+ * @param[in] task Pointer to the task to be deleted
+ **/
+
+void osDeleteTask(OsTask *task)
+{
+   //Delete the specified task
+   OSTaskDel(task->prio);
+}
+
+
+/**
+ * @brief Delay routine
+ * @param[in] delay Amount of time for which the calling task should block
+ **/
+
+void osDelayTask(systime_t delay)
+{
+   INT16U n;
+
+   //Convert milliseconds to system ticks
+   delay = OS_MS_TO_SYSTICKS(delay);
+
+   //Delay the task for the specified duration
+   while(delay > 0)
+   {
+      //The maximum delay is 65535 clock ticks
+      n = MIN(delay, 65535);
+      //Wait for the specified amount of time
+      OSTimeDly(n);
+      //Decrement delay value
+      delay -= n;
+   }
+}
+
+
+/**
+ * @brief Yield control to the next task
+ **/
+
+void osSwitchTask(void)
+{
+   //Not implemented
+}
+
+
+/**
+ * @brief Suspend scheduler activity
+ **/
+
+void osSuspendAllTasks(void)
+{
+   //Make sure the operating system is running
+   if(OSRunning == OS_TRUE)
+   {
+      //Suspend scheduler activity
+      OSSchedLock();
+   }
+}
+
+
+/**
+ * @brief Resume scheduler activity
+ **/
+
+void osResumeAllTasks(void)
+{
+   //Make sure the operating system is running
+   if(OSRunning == OS_TRUE)
+   {
+      //Resume scheduler activity
+      OSSchedUnlock();
+   }
+}
+
+
+/**
+ * @brief Create an event object
+ * @param[in] event Pointer to the event object
+ * @return The function returns TRUE if the event object was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateEvent(OsEvent *event)
+{
+   INT8U err;
+
+   //Create an event flag group
+   event->p = OSFlagCreate(0, &err);
+
+   //Check whether the event flag group was successfully created
+   if(event->p != NULL && err == OS_ERR_NONE)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Delete an event object
+ * @param[in] event Pointer to the event object
+ **/
+
+void osDeleteEvent(OsEvent *event)
+{
+   INT8U err;
+
+   //Make sure the operating system is running
+   if(OSRunning == OS_TRUE)
+   {
+      //Properly dispose the event object
+      OSFlagDel(event->p, OS_DEL_ALWAYS, &err);
+   }
+}
+
+
+/**
+ * @brief Set the specified event object to the signaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osSetEvent(OsEvent *event)
+{
+   INT8U err;
+
+   //Set the specified event to the signaled state
+   OSFlagPost(event->p, 1, OS_FLAG_SET, &err);
+}
+
+
+/**
+ * @brief Set the specified event object to the nonsignaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osResetEvent(OsEvent *event)
+{
+   INT8U err;
+
+   //Force the specified event to the nonsignaled state
+   OSFlagPost(event->p, 1, OS_FLAG_CLR, &err);
+}
+
+
+/**
+ * @brief Wait until the specified event is in the signaled state
+ * @param[in] event Pointer to the event object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the state of the specified object is
+ *   signaled. FALSE is returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout)
+{
+   INT8U err;
+   INT16U n;
+
+   //Wait until the specified event is in the signaled
+   //state or the timeout interval elapses
+   if(timeout == 0)
+   {
+      //Non-blocking call
+      OSFlagAccept(event->p, 1, OS_FLAG_WAIT_SET_ANY | OS_FLAG_CONSUME, &err);
+   }
+   else if(timeout == INFINITE_DELAY)
+   {
+      //Infinite timeout period
+      OSFlagPend(event->p, 1, OS_FLAG_WAIT_SET_ANY | OS_FLAG_CONSUME, 0, &err);
+   }
+   else
+   {
+      //Convert milliseconds to system ticks
+      timeout = OS_MS_TO_SYSTICKS(timeout);
+
+      //Loop until the assigned time period has elapsed
+      do
+      {
+         //The maximum timeout is 65535 clock ticks
+         n = MIN(timeout, 65535);
+         //Wait for the specified time interval
+         OSFlagPend(event->p, 1, OS_FLAG_WAIT_SET_ANY | OS_FLAG_CONSUME, n, &err);
+         //Decrement timeout value
+         timeout -= n;
+
+         //Check timeout value
+      } while(err == OS_ERR_TIMEOUT && timeout > 0);
+   }
+
+   //Check whether the specified event is set
+   if(err == OS_ERR_NONE)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Set an event object to the signaled state from an interrupt service routine
+ * @param[in] event Pointer to the event object
+ * @return TRUE if setting the event to signaled state caused a task to unblock
+ *   and the unblocked task has a priority higher than the currently running task
+ **/
+
+bool_t osSetEventFromIsr(OsEvent *event)
+{
+   INT8U err;
+
+   //Set the specified event to the signaled state
+   OSFlagPost(event->p, 1, OS_FLAG_SET, &err);
+
+   //The return value is not relevant
+   return FALSE;
+}
+
+
+/**
+ * @brief Create a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] count The maximum count for the semaphore object. This value
+ *   must be greater than zero
+ * @return The function returns TRUE if the semaphore was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count)
+{
+   //Create a semaphore
+   semaphore->p = OSSemCreate(count);
+
+   //Check whether the semaphore was successfully created
+   if(semaphore->p != NULL)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Delete a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osDeleteSemaphore(OsSemaphore *semaphore)
+{
+   INT8U err;
+
+   //Make sure the operating system is running
+   if(OSRunning == OS_TRUE)
+   {
+      //Properly dispose the specified semaphore
+      OSSemDel(semaphore->p, OS_DEL_ALWAYS, &err);
+   }
+}
+
+
+/**
+ * @brief Wait for the specified semaphore to be available
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the semaphore is available. FALSE is
+ *   returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout)
+{
+   INT8U err;
+   INT16U n;
+
+   //Wait until the semaphore is available or the timeout interval elapses
+   if(timeout == 0)
+   {
+      //Non-blocking call
+      if(OSSemAccept(semaphore->p) > 0)
+         err = OS_ERR_NONE;
+      else
+         err = OS_ERR_TIMEOUT;
+   }
+   else if(timeout == INFINITE_DELAY)
+   {
+      //Infinite timeout period
+      OSSemPend(semaphore->p, 0, &err);
+   }
+   else
+   {
+      //Convert milliseconds to system ticks
+      timeout = OS_MS_TO_SYSTICKS(timeout);
+
+      //Loop until the assigned time period has elapsed
+      do
+      {
+         //The maximum timeout is 65535 clock ticks
+         n = MIN(timeout, 65535);
+         //Wait for the specified time interval
+         OSSemPend(semaphore->p, n, &err);
+         //Decrement timeout value
+         timeout -= n;
+
+         //Check timeout value
+      } while(err == OS_ERR_TIMEOUT && timeout > 0);
+   }
+
+   //Check whether the specified semaphore is available
+   if(err == OS_ERR_NONE)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Release the specified semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osReleaseSemaphore(OsSemaphore *semaphore)
+{
+   //Release the semaphore
+   OSSemPost(semaphore->p);
+}
+
+
+/**
+ * @brief Create a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ * @return The function returns TRUE if the mutex was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateMutex(OsMutex *mutex)
+{
+#if 1
+   bool_t status;
+
+   //Create an event object
+   status = osCreateEvent((OsEvent *) mutex);
+
+   //Check whether the event object was successfully created
+   if(status)
+   {
+      //Set event
+      osSetEvent((OsEvent *) mutex);
+   }
+
+   //Return status
+   return status;
+#else
+   INT8U err;
+
+   //Create a mutex
+   mutex->p = OSMutexCreate(10, &err);
+
+   //Check whether the mutex was successfully created
+   if(mutex->p != NULL && err == OS_ERR_NONE)
+      return TRUE;
+   else
+      return FALSE;
+#endif
+}
+
+
+/**
+ * @brief Delete a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osDeleteMutex(OsMutex *mutex)
+{
+#if 1
+   //Delete event object
+   osDeleteEvent((OsEvent *) mutex);
+#else
+   INT8U err;
+
+   //Make sure the operating system is running
+   if(OSRunning == OS_TRUE)
+   {
+      //Properly dispose the specified mutex
+      OSMutexDel(mutex->p, OS_DEL_ALWAYS, &err);
+   }
+#endif
+}
+
+
+/**
+ * @brief Acquire ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osAcquireMutex(OsMutex *mutex)
+{
+#if 1
+   //Wait for event
+   osWaitForEvent((OsEvent *) mutex, INFINITE_DELAY);
+#else
+   INT8U err;
+
+   //Obtain ownership of the mutex object
+   OSMutexPend(mutex->p, 0, &err);
+#endif
+}
+
+
+/**
+ * @brief Release ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osReleaseMutex(OsMutex *mutex)
+{
+#if 1
+   //Set event
+   osSetEvent((OsEvent *) mutex);
+#else
+   //Release ownership of the mutex object
+   OSMutexPost(mutex->p);
+#endif
+}
+
+
+/**
+ * @brief Retrieve system time
+ * @return Number of milliseconds elapsed since the system was last started
+ **/
+
+systime_t osGetSystemTime(void)
+{
+   systime_t time;
+
+   //Get current tick count
+   time = OSTimeGet();
+
+   //Convert system ticks to milliseconds
+   return OS_SYSTICKS_TO_MS(time);
+}
+
+
+/**
+ * @brief Allocate a memory block
+ * @param[in] size Bytes to allocate
+ * @return A pointer to the allocated memory block or NULL if
+ *   there is insufficient memory available
+ **/
+
+void *osAllocMem(size_t size)
+{
+   void *p;
+
+   //Enter critical section
+   osSuspendAllTasks();
+   //Allocate a memory block
+   p = malloc(size);
+   //Leave critical section
+   osResumeAllTasks();
+
+   //Debug message
+   TRACE_DEBUG("Allocating %" PRIuSIZE " bytes at 0x%08" PRIXPTR "\r\n", size, (uintptr_t) p);
+
+   //Return a pointer to the newly allocated memory block
+   return p;
+}
+
+
+/**
+ * @brief Release a previously allocated memory block
+ * @param[in] p Previously allocated memory block to be freed
+ **/
+
+void osFreeMem(void *p)
+{
+   //Make sure the pointer is valid
+   if(p != NULL)
+   {
+      //Debug message
+      TRACE_DEBUG("Freeing memory at 0x%08" PRIXPTR "\r\n", (uintptr_t) p);
+
+      //Enter critical section
+      osSuspendAllTasks();
+      //Free memory block
+      free(p);
+      //Leave critical section
+      osResumeAllTasks();
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_ucos2.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,163 @@
+/**
+ * @file os_port_ucos2.h
+ * @brief RTOS abstraction layer (Micrium uC/OS-II)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _OS_PORT_UCOS2_H
+#define _OS_PORT_UCOS2_H
+
+//Dependencies
+#include "ucos_ii.h"
+
+//Maximum number of tasks that can be dynamically created
+#ifndef OS_PORT_MAX_TASKS
+   #define OS_PORT_MAX_TASKS 16
+#elif (OS_PORT_MAX_TASKS < 1)
+   #error OS_PORT_MAX_TASKS parameter is not valid
+#endif
+
+//Task priority (normal)
+#ifndef OS_TASK_PRIORITY_NORMAL
+   #define OS_TASK_PRIORITY_NORMAL 0
+#endif
+
+//Task priority (high)
+#ifndef OS_TASK_PRIORITY_HIGH
+   #define OS_TASK_PRIORITY_HIGH 0
+#endif
+
+//Milliseconds to system ticks
+#ifndef OS_MS_TO_SYSTICKS
+   #define OS_MS_TO_SYSTICKS(n) (n)
+#endif
+
+//System ticks to milliseconds
+#ifndef OS_SYSTICKS_TO_MS
+   #define OS_SYSTICKS_TO_MS(n) (n)
+#endif
+
+//Enter interrupt service routine
+#define osEnterIsr() OSIntEnter()
+
+//Leave interrupt service routine
+#define osExitIsr(flag) OSIntExit()
+
+
+/**
+ * @brief Task object
+ **/
+
+typedef struct
+{
+   INT8U prio;
+} OsTask;
+
+
+/**
+ * @brief Event object
+ **/
+
+typedef struct
+{
+   OS_FLAG_GRP *p;
+} OsEvent;
+
+
+/**
+ * @brief Semaphore object
+ **/
+
+typedef struct
+{
+   OS_EVENT *p;
+} OsSemaphore;
+
+
+/**
+ * @brief Mutex object
+ **/
+
+typedef struct
+{
+   OS_EVENT *p;
+} OsMutex;
+
+
+/**
+ * @brief Task routine
+ **/
+
+typedef void (*OsTaskCode)(void *params);
+
+
+//Kernel management
+void osInitKernel(void);
+void osStartKernel(void);
+
+//Task management
+bool_t osCreateStaticTask(OsTask *task, const char_t *name, OsTaskCode taskCode,
+   void *params, void *stack, size_t stackSize, int_t priority);
+
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority);
+
+void osDeleteTask(OsTask *task);
+void osDelayTask(systime_t delay);
+void osSwitchTask(void);
+void osSuspendAllTasks(void);
+void osResumeAllTasks(void);
+
+//Event management
+bool_t osCreateEvent(OsEvent *event);
+void osDeleteEvent(OsEvent *event);
+void osSetEvent(OsEvent *event);
+void osResetEvent(OsEvent *event);
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout);
+bool_t osSetEventFromIsr(OsEvent *event);
+
+//Semaphore management
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count);
+void osDeleteSemaphore(OsSemaphore *semaphore);
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout);
+void osReleaseSemaphore(OsSemaphore *semaphore);
+
+//Mutex management
+bool_t osCreateMutex(OsMutex *mutex);
+void osDeleteMutex(OsMutex *mutex);
+void osAcquireMutex(OsMutex *mutex);
+void osReleaseMutex(OsMutex *mutex);
+
+//System time
+systime_t osGetSystemTime(void);
+
+//Memory management
+void *osAllocMem(size_t size);
+void osFreeMem(void *p);
+
+//Undefine conflicting definitions
+#undef TRACE_LEVEL_OFF
+#undef TRACE_LEVEL_INFO
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_ucos3.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,672 @@
+/**
+ * @file os_port_ucos3.c
+ * @brief RTOS abstraction layer (Micrium uC/OS-III)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TRACE_LEVEL_OFF
+
+//Dependencies
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "os_port.h"
+#include "os_port_ucos3.h"
+#include "debug.h"
+
+//Forward declaration of functions
+void osIdleTaskHook(void);
+
+//Variables
+static OS_TCB *tcbTable[OS_PORT_MAX_TASKS];
+static CPU_STK *stkTable[OS_PORT_MAX_TASKS];
+
+
+/**
+ * @brief Kernel initialization
+ **/
+
+void osInitKernel(void)
+{
+   OS_ERR err;
+
+   //Initialize tables
+   memset(tcbTable, 0, sizeof(tcbTable));
+   memset(stkTable, 0, sizeof(stkTable));
+
+   //Scheduler initialization
+   OSInit(&err);
+
+   //Set idle task hook
+   OS_AppIdleTaskHookPtr = osIdleTaskHook;
+}
+
+
+/**
+ * @brief Start kernel
+ **/
+
+void osStartKernel(void)
+{
+   OS_ERR err;
+
+   //Start the scheduler
+   OSStart(&err);
+}
+
+
+/**
+ * @brief Create a static task
+ * @param[out] task Pointer to the task structure
+ * @param[in] name A name identifying the task
+ * @param[in] taskCode Pointer to the task entry function
+ * @param[in] params A pointer to a variable to be passed to the task
+ * @param[in] stack Pointer to the stack
+ * @param[in] stackSize The initial size of the stack, in words
+ * @param[in] priority The priority at which the task should run
+ * @return The function returns TRUE if the task was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateStaticTask(OsTask *task, const char_t *name, OsTaskCode taskCode,
+   void *params, void *stack, size_t stackSize, int_t priority)
+{
+   OS_ERR err;
+   CPU_STK stackLimit;
+
+   //The watermark limit is used to monitor and ensure that the stack does not overflow
+   stackLimit = stackSize / 10;
+
+   //Create a new task
+   OSTaskCreate(task, (CPU_CHAR *) name, taskCode, params,
+      priority, stack, stackLimit, stackSize, 0, 1, NULL,
+      OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR, &err);
+
+   //Check whether the task was successfully created
+   if(err == OS_ERR_NONE)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Create a new task
+ * @param[in] name A name identifying the task
+ * @param[in] taskCode Pointer to the task entry function
+ * @param[in] params A pointer to a variable to be passed to the task
+ * @param[in] stackSize The initial size of the stack, in words
+ * @param[in] priority The priority at which the task should run
+ * @return If the function succeeds, the return value is a pointer to the
+ *   new task. If the function fails, the return value is NULL
+ **/
+
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority)
+{
+   OS_ERR err;
+   CPU_INT32U i;
+   CPU_STK stackLimit;
+   OS_TCB *task;
+   CPU_STK *stack;
+
+   //The watermark limit is used to monitor and ensure that the stack does not overflow
+   stackLimit = stackSize / 10;
+
+   //Enter critical section
+   osSuspendAllTasks();
+
+   //Loop through TCB table
+   for(i = 0; i < OS_PORT_MAX_TASKS; i++)
+   {
+      //Check whether the current entry is free
+      if(tcbTable[i] == NULL)
+         break;
+   }
+
+   //Any entry available in the table?
+   if(i < OS_PORT_MAX_TASKS)
+   {
+      //Allocate a memory block to hold the task's control block
+      task = osAllocMem(sizeof(OS_TCB));
+
+      //Successful memory allocation?
+      if(task != NULL)
+      {
+         //Allocate a memory block to hold the task's stack
+         stack = osAllocMem(stackSize * sizeof(CPU_STK));
+
+         //Successful memory allocation?
+         if(stack != NULL)
+         {
+            //Create a new task
+            OSTaskCreate(task, (CPU_CHAR *) name, taskCode, params,
+               priority, stack, stackLimit, stackSize, 0, 1, NULL,
+               OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR, &err);
+
+            //Check the return value
+            if(err == OS_ERR_NONE)
+            {
+               //Save TCB base address
+               tcbTable[i] = task;
+               //Save stack base address
+               stkTable[i] = stack;
+            }
+            else
+            {
+               //Clean up side effects
+               osFreeMem(task);
+               osFreeMem(stack);
+            }
+         }
+         else
+         {
+            //Memory allocation failed
+            err = OS_ERR_MEM_FULL;
+            //Clean up side effects
+            osFreeMem(task);
+         }
+      }
+      else
+      {
+         //Memory allocation failed
+         err = OS_ERR_MEM_FULL;
+      }
+   }
+   else
+   {
+      //No entry available in the table
+      err = OS_ERR_MEM_FULL;
+   }
+
+   //Leave critical section
+   osResumeAllTasks();
+
+   //Check whether the task was successfully created
+   if(err == OS_ERR_NONE)
+      return task;
+   else
+      return NULL;
+}
+
+
+/**
+ * @brief Delete a task
+ * @param[in] task Pointer to the task to be deleted
+ **/
+
+void osDeleteTask(OsTask *task)
+{
+   OS_ERR err;
+
+   //Delete the specified task
+   OSTaskDel(task, &err);
+}
+
+
+/**
+ * @brief Delay routine
+ * @param[in] delay Amount of time for which the calling task should block
+ **/
+
+void osDelayTask(systime_t delay)
+{
+   OS_ERR err;
+
+   //Delay the task for the specified duration
+   OSTimeDly(OS_MS_TO_SYSTICKS(delay), OS_OPT_TIME_DLY, &err);
+}
+
+
+/**
+ * @brief Yield control to the next task
+ **/
+
+void osSwitchTask(void)
+{
+   //Force a context switch
+   OSSched();
+}
+
+
+/**
+ * @brief Suspend scheduler activity
+ **/
+
+void osSuspendAllTasks(void)
+{
+   OS_ERR err;
+
+   //Make sure the operating system is running
+   if(OSRunning == OS_STATE_OS_RUNNING)
+   {
+      //Suspend scheduler activity
+      OSSchedLock(&err);
+   }
+}
+
+
+/**
+ * @brief Resume scheduler activity
+ **/
+
+void osResumeAllTasks(void)
+{
+   OS_ERR err;
+
+   //Make sure the operating system is running
+   if(OSRunning == OS_STATE_OS_RUNNING)
+   {
+      //Resume scheduler activity
+      OSSchedUnlock(&err);
+   }
+}
+
+
+/**
+ * @brief Create an event object
+ * @param[in] event Pointer to the event object
+ * @return The function returns TRUE if the event object was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateEvent(OsEvent *event)
+{
+   OS_ERR err;
+
+   //Create an event flag group
+   OSFlagCreate(event, "EVENT", 0, &err);
+
+   //Check whether the event flag group was successfully created
+   if(err == OS_ERR_NONE)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Delete an event object
+ * @param[in] event Pointer to the event object
+ **/
+
+void osDeleteEvent(OsEvent *event)
+{
+   OS_ERR err;
+
+   //Make sure the operating system is running
+   if(OSRunning == OS_STATE_OS_RUNNING)
+   {
+      //Properly dispose the event object
+      OSFlagDel(event, OS_OPT_DEL_ALWAYS, &err);
+   }
+}
+
+
+/**
+ * @brief Set the specified event object to the signaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osSetEvent(OsEvent *event)
+{
+   OS_ERR err;
+
+   //Set the specified event to the signaled state
+   OSFlagPost(event, 1, OS_OPT_POST_FLAG_SET, &err);
+}
+
+
+/**
+ * @brief Set the specified event object to the nonsignaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osResetEvent(OsEvent *event)
+{
+   OS_ERR err;
+
+   //Force the specified event to the nonsignaled state
+   OSFlagPost(event, 1, OS_OPT_POST_FLAG_CLR, &err);
+}
+
+
+/**
+ * @brief Wait until the specified event is in the signaled state
+ * @param[in] event Pointer to the event object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the state of the specified object is
+ *   signaled. FALSE is returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout)
+{
+   OS_ERR err;
+
+   //Wait until the specified event is in the signaled
+   //state or the timeout interval elapses
+   if(timeout == 0)
+   {
+      //Non-blocking call
+      OSFlagPend(event, 1, 0, OS_OPT_PEND_FLAG_SET_ANY |
+         OS_OPT_PEND_FLAG_CONSUME | OS_OPT_PEND_NON_BLOCKING, NULL, &err);
+   }
+   else if(timeout == INFINITE_DELAY)
+   {
+      //Infinite timeout period
+      OSFlagPend(event, 1, 0, OS_OPT_PEND_FLAG_SET_ANY |
+         OS_OPT_PEND_FLAG_CONSUME | OS_OPT_PEND_BLOCKING, NULL, &err);
+   }
+   else
+   {
+      //Wait until the specified event becomes set
+      OSFlagPend(event, 1, OS_MS_TO_SYSTICKS(timeout), OS_OPT_PEND_FLAG_SET_ANY |
+         OS_OPT_PEND_FLAG_CONSUME | OS_OPT_PEND_BLOCKING, NULL, &err);
+   }
+
+   //Check whether the specified event is set
+   if(err == OS_ERR_NONE)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Set an event object to the signaled state from an interrupt service routine
+ * @param[in] event Pointer to the event object
+ * @return TRUE if setting the event to signaled state caused a task to unblock
+ *   and the unblocked task has a priority higher than the currently running task
+ **/
+
+bool_t osSetEventFromIsr(OsEvent *event)
+{
+   OS_ERR err;
+
+   //Set the specified event to the signaled state
+   OSFlagPost(event, 1, OS_OPT_POST_FLAG_SET, &err);
+
+   //The return value is not relevant
+   return FALSE;
+}
+
+
+/**
+ * @brief Create a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] count The maximum count for the semaphore object. This value
+ *   must be greater than zero
+ * @return The function returns TRUE if the semaphore was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count)
+{
+   OS_ERR err;
+
+   //Create a semaphore
+   OSSemCreate(semaphore, "SEMAPHORE", count, &err);
+
+   //Check whether the semaphore was successfully created
+   if(err == OS_ERR_NONE)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Delete a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osDeleteSemaphore(OsSemaphore *semaphore)
+{
+   OS_ERR err;
+
+   //Make sure the operating system is running
+   if(OSRunning == OS_STATE_OS_RUNNING)
+   {
+      //Properly dispose the specified semaphore
+      OSSemDel(semaphore, OS_OPT_DEL_ALWAYS, &err);
+   }
+}
+
+
+/**
+ * @brief Wait for the specified semaphore to be available
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the semaphore is available. FALSE is
+ *   returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout)
+{
+   OS_ERR err;
+
+   //Wait until the semaphore is available or the timeout interval elapses
+   if(timeout == 0)
+   {
+      //Non-blocking call
+      OSSemPend(semaphore, 0, OS_OPT_PEND_NON_BLOCKING, NULL, &err);
+   }
+   else if(timeout == INFINITE_DELAY)
+   {
+      //Infinite timeout period
+      OSSemPend(semaphore, 0, OS_OPT_PEND_BLOCKING, NULL, &err);
+   }
+   else
+   {
+      //Wait until the specified semaphore becomes available
+      OSSemPend(semaphore, OS_MS_TO_SYSTICKS(timeout),
+         OS_OPT_PEND_BLOCKING, NULL, &err);
+   }
+
+   //Check whether the specified semaphore is available
+   if(err == OS_ERR_NONE)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Release the specified semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osReleaseSemaphore(OsSemaphore *semaphore)
+{
+   OS_ERR err;
+
+   //Release the semaphore
+   OSSemPost(semaphore, OS_OPT_POST_1, &err);
+}
+
+
+/**
+ * @brief Create a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ * @return The function returns TRUE if the mutex was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateMutex(OsMutex *mutex)
+{
+   OS_ERR err;
+
+   //Create a mutex
+   OSMutexCreate(mutex, "MUTEX", &err);
+
+   //Check whether the mutex was successfully created
+   if(err == OS_ERR_NONE)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Delete a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osDeleteMutex(OsMutex *mutex)
+{
+   OS_ERR err;
+
+   //Make sure the operating system is running
+   if(OSRunning == OS_STATE_OS_RUNNING)
+   {
+      //Properly dispose the specified mutex
+      OSMutexDel(mutex, OS_OPT_DEL_ALWAYS, &err);
+   }
+}
+
+
+/**
+ * @brief Acquire ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osAcquireMutex(OsMutex *mutex)
+{
+   OS_ERR err;
+
+   //Obtain ownership of the mutex object
+   OSMutexPend(mutex, 0, OS_OPT_PEND_BLOCKING, NULL, &err);
+}
+
+
+/**
+ * @brief Release ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osReleaseMutex(OsMutex *mutex)
+{
+   OS_ERR err;
+
+   //Release ownership of the mutex object
+   OSMutexPost(mutex, OS_OPT_POST_NONE, &err);
+}
+
+
+/**
+ * @brief Retrieve system time
+ * @return Number of milliseconds elapsed since the system was last started
+ **/
+
+systime_t osGetSystemTime(void)
+{
+   OS_ERR err;
+   systime_t time;
+
+   //Get current tick count
+   time = OSTimeGet(&err);
+
+   //Convert system ticks to milliseconds
+   return OS_SYSTICKS_TO_MS(time);
+}
+
+
+/**
+ * @brief Allocate a memory block
+ * @param[in] size Bytes to allocate
+ * @return A pointer to the allocated memory block or NULL if
+ *   there is insufficient memory available
+ **/
+
+void *osAllocMem(size_t size)
+{
+   void *p;
+
+   //Enter critical section
+   osSuspendAllTasks();
+   //Allocate a memory block
+   p = malloc(size);
+   //Leave critical section
+   osResumeAllTasks();
+
+   //Debug message
+   TRACE_DEBUG("Allocating %" PRIuSIZE " bytes at 0x%08" PRIXPTR "\r\n", size, (uintptr_t) p);
+
+   //Return a pointer to the newly allocated memory block
+   return p;
+}
+
+
+/**
+ * @brief Release a previously allocated memory block
+ * @param[in] p Previously allocated memory block to be freed
+ **/
+
+void osFreeMem(void *p)
+{
+   //Make sure the pointer is valid
+   if(p != NULL)
+   {
+      //Debug message
+      TRACE_DEBUG("Freeing memory at 0x%08" PRIXPTR "\r\n", (uintptr_t) p);
+
+      //Enter critical section
+      osSuspendAllTasks();
+      //Free memory block
+      free(p);
+      //Leave critical section
+      osResumeAllTasks();
+   }
+}
+
+
+/**
+ * @brief Idle task hook
+ **/
+
+void osIdleTaskHook(void)
+{
+   uint_t i;
+
+   //Loop through TCB table
+   for(i = 0; i < OS_PORT_MAX_TASKS; i++)
+   {
+      //Check whether current entry is used
+      if(tcbTable[i] != NULL)
+      {
+         //Wait for task termination
+         if(tcbTable[i]->TaskState == OS_TASK_STATE_DEL)
+         {
+            //Free previously allocated resources
+            osFreeMem(stkTable[i]);
+            osFreeMem(tcbTable[i]);
+
+            //Mark the entry as free
+            stkTable[i] = NULL;
+            tcbTable[i] = NULL;
+         }
+      }
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_ucos3.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,151 @@
+/**
+ * @file os_port_ucos3.h
+ * @brief RTOS abstraction layer (Micrium uC/OS-III)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _OS_PORT_UCOS3_H
+#define _OS_PORT_UCOS3_H
+
+//Dependencies
+#include "os.h"
+
+//Task priority (normal)
+#ifndef OS_TASK_PRIORITY_NORMAL
+   #define OS_TASK_PRIORITY_NORMAL (OS_CFG_PRIO_MAX - 2)
+#endif
+
+//Task priority (high)
+#ifndef OS_TASK_PRIORITY_HIGH
+   #define OS_TASK_PRIORITY_HIGH (OS_CFG_PRIO_MAX - 3)
+#endif
+
+//Maximum number of tasks that can be dynamically created
+#ifndef OS_PORT_MAX_TASKS
+   #define OS_PORT_MAX_TASKS 16
+#elif (OS_PORT_MAX_TASKS < 1)
+   #error OS_PORT_MAX_TASKS parameter is not valid
+#endif
+
+//Milliseconds to system ticks
+#ifndef OS_MS_TO_SYSTICKS
+   #define OS_MS_TO_SYSTICKS(n) (n)
+#endif
+
+//System ticks to milliseconds
+#ifndef OS_SYSTICKS_TO_MS
+   #define OS_SYSTICKS_TO_MS(n) (n)
+#endif
+
+//Enter interrupt service routine
+#define osEnterIsr() OSIntEnter()
+
+//Leave interrupt service routine
+#define osExitIsr(flag) OSIntExit()
+
+
+/**
+ * @brief Task object
+ **/
+
+typedef OS_TCB OsTask;
+
+
+/**
+ * @brief Event object
+ **/
+
+typedef OS_FLAG_GRP OsEvent;
+
+
+/**
+ * @brief Semaphore object
+ **/
+
+typedef OS_SEM OsSemaphore;
+
+
+/**
+ * @brief Mutex object
+ **/
+
+typedef OS_MUTEX OsMutex;
+
+
+/**
+ * @brief Task routine
+ **/
+
+typedef void (*OsTaskCode)(void *params);
+
+
+//Kernel management
+void osInitKernel(void);
+void osStartKernel(void);
+
+//Task management
+bool_t osCreateStaticTask(OsTask *task, const char_t *name, OsTaskCode taskCode,
+   void *params, void *stack, size_t stackSize, int_t priority);
+
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority);
+
+void osDeleteTask(OsTask *task);
+void osDelayTask(systime_t delay);
+void osSwitchTask(void);
+void osSuspendAllTasks(void);
+void osResumeAllTasks(void);
+
+//Event management
+bool_t osCreateEvent(OsEvent *event);
+void osDeleteEvent(OsEvent *event);
+void osSetEvent(OsEvent *event);
+void osResetEvent(OsEvent *event);
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout);
+bool_t osSetEventFromIsr(OsEvent *event);
+
+//Semaphore management
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count);
+void osDeleteSemaphore(OsSemaphore *semaphore);
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout);
+void osReleaseSemaphore(OsSemaphore *semaphore);
+
+//Mutex management
+bool_t osCreateMutex(OsMutex *mutex);
+void osDeleteMutex(OsMutex *mutex);
+void osAcquireMutex(OsMutex *mutex);
+void osReleaseMutex(OsMutex *mutex);
+
+//System time
+systime_t osGetSystemTime(void);
+
+//Memory management
+void *osAllocMem(size_t size);
+void osFreeMem(void *p);
+
+//Undefine conflicting definitions
+#undef TRACE_LEVEL_OFF
+#undef TRACE_LEVEL_INFO
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_windows.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,410 @@
+/**
+ * @file os_port_windows.c
+ * @brief RTOS abstraction layer (Windows)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TRACE_LEVEL_OFF
+
+//Memory leaks detection
+#if (defined(_WIN32) && defined(_DEBUG))
+   #define _CRTDBG_MAP_ALLOC
+   #include <stdlib.h>
+   #include <crtdbg.h>
+#endif
+
+//Dependencies
+#include <stdio.h>
+#include <stdlib.h>
+#include <windows.h>
+#include "os_port.h"
+#include "os_port_windows.h"
+#include "debug.h"
+
+
+/**
+ * @brief Kernel initialization
+ **/
+
+void osInitKernel(void)
+{
+   //Not implemented
+}
+
+
+/**
+ * @brief Start kernel
+ **/
+
+void osStartKernel(void)
+{
+   //Not implemented
+}
+
+
+/**
+ * @brief Create a new task
+ * @param[in] name A name identifying the task
+ * @param[in] taskCode Pointer to the task entry function
+ * @param[in] params A pointer to a variable to be passed to the task
+ * @param[in] stackSize The initial size of the stack, in words
+ * @param[in] priority The priority at which the task should run
+ * @return If the function succeeds, the return value is a pointer to the
+ *   new task. If the function fails, the return value is NULL
+ **/
+
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority)
+{
+   void *handle;
+
+   //Create a new thread
+   handle = CreateThread(NULL, 0,
+      (LPTHREAD_START_ROUTINE) taskCode, params, 0, NULL);
+
+   //Return a pointer to the newly created thread
+   return handle;
+}
+
+
+/**
+ * @brief Delete a task
+ * @param[in] task Pointer to the task to be deleted
+ **/
+
+void osDeleteTask(OsTask *task)
+{
+   //Delete the calling thread?
+   if(task == NULL)
+   {
+      //Kill ourselves
+      ExitThread(0);
+   }
+   else
+   {
+      //Delete the specified thread
+      TerminateThread(task, 0);
+   }
+}
+
+
+/**
+ * @brief Delay routine
+ * @param[in] delay Amount of time for which the calling task should block
+ **/
+
+void osDelayTask(systime_t delay)
+{
+   //Delay the task for the specified duration
+   Sleep(delay);
+}
+
+
+/**
+ * @brief Yield control to the next task
+ **/
+
+void osSwitchTask(void)
+{
+   //Not implemented
+}
+
+
+/**
+ * @brief Suspend scheduler activity
+ **/
+
+void osSuspendAllTasks(void)
+{
+   //Not implemented
+}
+
+
+/**
+ * @brief Resume scheduler activity
+ **/
+
+void osResumeAllTasks(void)
+{
+   //Not implemented
+}
+
+
+/**
+ * @brief Create an event object
+ * @param[in] event Pointer to the event object
+ * @return The function returns TRUE if the event object was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateEvent(OsEvent *event)
+{
+   //Create an event object
+   event->handle = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+   //Check whether the returned handle is valid
+   if(event->handle != NULL)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Delete an event object
+ * @param[in] event Pointer to the event object
+ **/
+
+void osDeleteEvent(OsEvent *event)
+{
+   //Make sure the handle is valid
+   if(event->handle != NULL)
+   {
+      //Properly dispose the event object
+      CloseHandle(event->handle);
+   }
+}
+
+
+/**
+ * @brief Set the specified event object to the signaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osSetEvent(OsEvent *event)
+{
+   //Set the specified event to the signaled state
+   SetEvent(event->handle);
+}
+
+
+/**
+ * @brief Set the specified event object to the nonsignaled state
+ * @param[in] event Pointer to the event object
+ **/
+
+void osResetEvent(OsEvent *event)
+{
+   //Force the specified event to the nonsignaled state
+   ResetEvent(event->handle);
+}
+
+
+/**
+ * @brief Wait until the specified event is in the signaled state
+ * @param[in] event Pointer to the event object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the state of the specified object is
+ *   signaled. FALSE is returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout)
+{
+   //Wait until the specified event is in the signaled state
+   if(WaitForSingleObject(event->handle, timeout) == WAIT_OBJECT_0)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Set an event object to the signaled state from an interrupt service routine
+ * @param[in] event Pointer to the event object
+ * @return TRUE if setting the event to signaled state caused a task to unblock
+ *   and the unblocked task has a priority higher than the currently running task
+ **/
+
+bool_t osSetEventFromIsr(OsEvent *event)
+{
+   //Not implemented
+   return FALSE;
+}
+
+
+/**
+ * @brief Create a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] count The maximum count for the semaphore object. This value
+ *   must be greater than zero
+ * @return The function returns TRUE if the semaphore was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count)
+{
+   //Create a semaphore object
+   semaphore->handle = CreateSemaphore(NULL, count, count, NULL);
+
+   //Check whether the returned handle is valid
+   if(semaphore->handle != NULL)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Delete a semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osDeleteSemaphore(OsSemaphore *semaphore)
+{
+   //Make sure the handle is valid
+   if(semaphore->handle != NULL)
+   {
+      //Properly dispose the semaphore object
+      CloseHandle(semaphore->handle);
+   }
+}
+
+
+/**
+ * @brief Wait for the specified semaphore to be available
+ * @param[in] semaphore Pointer to the semaphore object
+ * @param[in] timeout Timeout interval
+ * @return The function returns TRUE if the semaphore is available. FALSE is
+ *   returned if the timeout interval elapsed
+ **/
+
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout)
+{
+   //Wait until the specified semaphore becomes available
+   if(WaitForSingleObject(semaphore->handle, timeout) == WAIT_OBJECT_0)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Release the specified semaphore object
+ * @param[in] semaphore Pointer to the semaphore object
+ **/
+
+void osReleaseSemaphore(OsSemaphore *semaphore)
+{
+   //Release the semaphore
+   ReleaseSemaphore(semaphore->handle, 1, NULL);
+}
+
+
+/**
+ * @brief Create a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ * @return The function returns TRUE if the mutex was successfully
+ *   created. Otherwise, FALSE is returned
+ **/
+
+bool_t osCreateMutex(OsMutex *mutex)
+{
+   //Create a mutex object
+   mutex->handle = CreateMutex(NULL, FALSE, NULL);
+
+   //Check whether the returned handle is valid
+   if(mutex->handle != NULL)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Delete a mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osDeleteMutex(OsMutex *mutex)
+{
+   //Make sure the handle is valid
+   if(mutex->handle != NULL)
+   {
+      //Properly dispose the mutex object
+      CloseHandle(mutex->handle);
+   }
+}
+
+
+/**
+ * @brief Acquire ownership of the specified mutex object
+ * @param[in] mutex A handle to the mutex object
+ **/
+
+void osAcquireMutex(OsMutex *mutex)
+{
+   //Obtain ownership of the mutex object
+   WaitForSingleObject(mutex->handle, INFINITE);
+}
+
+
+/**
+ * @brief Release ownership of the specified mutex object
+ * @param[in] mutex Pointer to the mutex object
+ **/
+
+void osReleaseMutex(OsMutex *mutex)
+{
+   //Release ownership of the mutex object
+   ReleaseMutex(mutex->handle);
+}
+
+
+/**
+ * @brief Retrieve system time
+ * @return Number of milliseconds elapsed since the system was last started
+ **/
+
+systime_t osGetSystemTime(void)
+{
+   //Get current tick count
+   return GetTickCount();
+}
+
+
+/**
+ * @brief Allocate a memory block
+ * @param[in] size Bytes to allocate
+ * @return A pointer to the allocated memory block or NULL if
+ *   there is insufficient memory available
+ **/
+
+void *osAllocMem(size_t size)
+{
+   //Allocate a memory block
+   return malloc(size);
+}
+
+
+/**
+ * @brief Release a previously allocated memory block
+ * @param[in] p Previously allocated memory block to be freed
+ **/
+
+void osFreeMem(void *p)
+{
+   //Free memory block
+   free(p);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/os_port_windows.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,149 @@
+/**
+ * @file os_port_windows.h
+ * @brief RTOS abstraction layer (Windows)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _OS_PORT_WINDOWS_H
+#define _OS_PORT_WINDOWS_H
+
+//Task priority (normal)
+#ifndef OS_TASK_PRIORITY_NORMAL
+   #define OS_TASK_PRIORITY_NORMAL 0
+#endif
+
+//Task priority (high)
+#ifndef OS_TASK_PRIORITY_HIGH
+   #define OS_TASK_PRIORITY_HIGH 0
+#endif
+
+//Milliseconds to system ticks
+#ifndef OS_MS_TO_SYSTICKS
+   #define OS_MS_TO_SYSTICKS(n) (n)
+#endif
+
+//System ticks to milliseconds
+#ifndef OS_SYSTICKS_TO_MS
+   #define OS_SYSTICKS_TO_MS(n) (n)
+#endif
+
+//Enter interrupt service routine
+#define osEnterIsr()
+
+//Leave interrupt service routine
+#define osExitIsr(flag)
+
+
+/**
+ * @brief Task object
+ **/
+
+typedef void OsTask;
+
+
+/**
+ * @brief Event object
+ **/
+
+typedef struct
+{
+   void *handle;
+} OsEvent;
+
+
+/**
+ * @brief Semaphore object
+ **/
+
+typedef struct
+{
+   void *handle;
+} OsSemaphore;
+
+
+/**
+ * @brief Mutex object
+ **/
+
+typedef struct
+{
+   void *handle;
+} OsMutex;
+
+
+/**
+ * @brief Task routine
+ **/
+
+typedef void (*OsTaskCode)(void *params);
+
+
+//Kernel management
+void osInitKernel(void);
+void osStartKernel(void);
+
+//Task management
+OsTask *osCreateTask(const char_t *name, OsTaskCode taskCode,
+   void *params, size_t stackSize, int_t priority);
+
+void osDeleteTask(OsTask *task);
+void osDelayTask(systime_t delay);
+void osSwitchTask(void);
+void osSuspendAllTasks(void);
+void osResumeAllTasks(void);
+
+//Event management
+bool_t osCreateEvent(OsEvent *event);
+void osDeleteEvent(OsEvent *event);
+void osSetEvent(OsEvent *event);
+void osResetEvent(OsEvent *event);
+bool_t osWaitForEvent(OsEvent *event, systime_t timeout);
+bool_t osSetEventFromIsr(OsEvent *event);
+
+//Semaphore management
+bool_t osCreateSemaphore(OsSemaphore *semaphore, uint_t count);
+void osDeleteSemaphore(OsSemaphore *semaphore);
+bool_t osWaitForSemaphore(OsSemaphore *semaphore, systime_t timeout);
+void osReleaseSemaphore(OsSemaphore *semaphore);
+
+//Mutex management
+bool_t osCreateMutex(OsMutex *mutex);
+void osDeleteMutex(OsMutex *mutex);
+void osAcquireMutex(OsMutex *mutex);
+void osReleaseMutex(OsMutex *mutex);
+
+//System time
+systime_t osGetSystemTime(void);
+
+//Memory management
+void *osAllocMem(size_t size);
+void osFreeMem(void *p);
+
+//Miscellaneous definitions
+#define strlwr _strlwr
+#define strcasecmp _stricmp
+#define strncasecmp _strnicmp
+#define strtok_r(str, delim, p) strtok(str, delim)
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/path.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,432 @@
+/**
+ * @file path.c
+ * @brief Path manipulation helper functions
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Dependencies
+#include <string.h>
+#include <ctype.h>
+#include "path.h"
+
+
+/**
+ * @brief Test if the path is absolute
+ * @param[in] path NULL-terminated string that contains the path
+ * @return TRUE is the path is absolute, else FALSE
+ **/
+
+bool_t pathIsAbsolute(const char_t *path)
+{
+   //Determine if the path is absolute or relative
+   if(path[0] == '/' || path[0] == '\\')
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Test if the path is relative
+ * @param[in] path NULL-terminated string that contains the path
+ * @return TRUE is the path is relative, else FALSE
+ **/
+
+bool_t pathIsRelative(const char_t *path)
+{
+   //Determine if the path is absolute or relative
+   if(path[0] == '/' || path[0] == '\\')
+      return FALSE;
+   else
+      return TRUE;
+}
+
+
+/**
+ * @brief Search a path for a file name
+ * @param[in] path NULL-terminated string that contains the path to search
+ * @return Pointer to the file name
+ **/
+
+const char_t *pathFindFileName(const char_t *path)
+{
+   size_t n;
+
+   //Retrieve the length of the path
+   n = strlen(path);
+
+   //Skip trailing slash or backslash characters
+   while(n > 0)
+   {
+      //Forward slash or backslash character found?
+      if(path[n - 1] != '/' && path[n - 1] != '\\')
+         break;
+
+      //Previous character
+      n--;
+   }
+
+   //Search the string for the last separator
+   while(n > 0)
+   {
+      //Forward slash or backslash character found?
+      if(path[n - 1] == '/' || path[n - 1] == '\\')
+         break;
+
+      //Previous character
+      n--;
+   }
+
+   //Return a pointer to the file name
+   return path + n;
+}
+
+
+/**
+ * @brief Simplify a path
+ * @param[in] path NULL-terminated string containing the path to be canonicalized
+ **/
+
+void pathCanonicalize(char_t *path)
+{
+   size_t i;
+   size_t j;
+   size_t k;
+
+   //Move to the beginning of the string
+   i = 0;
+   k = 0;
+
+   //Replace backslashes with forward slashes
+   while(path[i] != '\0')
+   {
+      //Forward slash or backslash separator found?
+      if(path[i] == '/' || path[i] == '\\')
+      {
+         path[k++] = '/';
+         while(path[i] == '/' || path[i] == '\\') i++;
+      }
+      else
+      {
+         path[k++] = path[i++];
+      }
+   }
+
+   //Properly terminate the string with a NULL character
+   path[k] = '\0';
+
+   //Move back to the beginning of the string
+   i = 0;
+   j = 0;
+   k = 0;
+
+   //Parse the entire string
+   do
+   {
+      //Forward slash separator found?
+      if(path[i] == '/' || path[i] == '\0')
+      {
+         //"." element found?
+         if((i - j) == 1 && !strncmp(path + j, ".", 1))
+         {
+            //Check whether the pathname is empty?
+            if(k == 0)
+            {
+               if(path[i] == '\0')
+               {
+                  path[k++] = '.';
+               }
+               else if(path[i] == '/' && path[i + 1] == '\0')
+               {
+                  path[k++] = '.';
+                  path[k++] = '/';
+               }
+            }
+            else if(k > 1)
+            {
+               //Remove the final slash if necessary
+               if(path[i] == '\0')
+                  k--;
+            }
+         }
+         //".." element found?
+         else if((i - j) == 2 && !strncmp(path + j, "..", 2))
+         {
+            //Check whether the pathname is empty?
+            if(k == 0)
+            {
+               path[k++] = '.';
+               path[k++] = '.';
+
+               //Append a slash if necessary
+               if(path[i] == '/')
+                  path[k++] = '/';
+            }
+            else if(k > 1)
+            {
+               //Search the path for the previous slash
+               for(j = 1; j < k; j++)
+               {
+                  if(path[k - j - 1] == '/')
+                     break;
+               }
+
+               //Slash separator found?
+               if(j < k)
+               {
+                  if(!strncmp(path + k - j, "..", 2))
+                  {
+                     path[k++] = '.';
+                     path[k++] = '.';
+                  }
+                  else
+                  {
+                     k = k - j - 1;
+                  }
+
+                  //Append a slash if necessary
+                  if(k == 0 && path[0] == '/')
+                     path[k++] = '/';
+                  else if(path[i] == '/')
+                     path[k++] = '/';
+               }
+               //No slash separator found?
+               else
+               {
+                  if(k == 3 && !strncmp(path, "..", 2))
+                  {
+                     path[k++] = '.';
+                     path[k++] = '.';
+
+                     //Append a slash if necessary
+                     if(path[i] == '/')
+                        path[k++] = '/';
+                  }
+                  else if(path[i] == '\0')
+                  {
+                     k = 0;
+                     path[k++] = '.';
+                  }
+                  else if(path[i] == '/' && path[i + 1] == '\0')
+                  {
+                     k = 0;
+                     path[k++] = '.';
+                     path[k++] = '/';
+                  }
+                  else
+                  {
+                     k = 0;
+                  }
+               }
+            }
+         }
+         else
+         {
+            //Copy directory name
+            memmove(path + k, path + j, i - j);
+            //Advance write pointer
+            k += i - j;
+
+            //Append a slash if necessary
+            if(path[i] == '/')
+               path[k++] = '/';
+         }
+
+         //Move to the next token
+         while(path[i] == '/') i++;
+         j = i;
+      }
+   } while(path[i++] != '\0');
+
+   //Properly terminate the string with a NULL character
+   path[k] = '\0';
+}
+
+
+/**
+ * @brief Add a slash to the end of a string
+ * @param[in,out] path NULL-terminated string that represents the path
+ * @param[in] maxLen Maximum pathname length
+ **/
+
+void pathAddSlash(char_t *path, size_t maxLen)
+{
+   size_t n;
+
+   //Retrieve the length of the string
+   n = strlen(path);
+
+   //Add a slash character only if necessary
+   if(!n)
+   {
+      //Check the length of the resulting string
+      if(maxLen >= 1)
+         strcpy(path, "/");
+   }
+   else if(path[n - 1] != '/' && path[n - 1] != '\\')
+   {
+      //Check the length of the resulting string
+      if(maxLen >= (n + 1))
+         strcat(path, "/");
+   }
+}
+
+
+/**
+ * @brief Remove the trailing slash from a given path
+ * @param[in,out] path NULL-terminated string that contains the path
+ **/
+
+void pathRemoveSlash(char_t *path)
+{
+   char_t *end;
+
+   //Skip the leading slash character
+   if(pathIsAbsolute(path))
+      path++;
+
+   //Search for the first slash character to be removed
+   for(end = NULL; *path != '\0'; path++)
+   {
+      if(*path != '/' && *path != '\\')
+         end = NULL;
+      else if(!end)
+         end = path;
+   }
+
+   //Remove the trailing slash characters
+   if(end) *end = '\0';
+}
+
+
+/**
+ * @brief Concatenate two paths
+ * @param[in,out] path NULL-terminated string containing the first path
+ * @param[in] more NULL-terminated string containing the second path
+ * @param[in] maxLen Maximum pathname length
+ **/
+
+void pathCombine(char_t *path, const char_t *more, size_t maxLen)
+{
+   size_t n1;
+   size_t n2;
+
+   //Append a slash character to the first path
+   if(*path != '\0')
+      pathAddSlash(path, maxLen);
+
+   //Skip any slash character at the beginning of the second path
+   while(*more == '/' || *more == '\\') more++;
+
+   //Retrieve the length of the first path
+   n1 = strlen(path);
+   //Retrieve the length of second path
+   n2 = strlen(more);
+
+   //Check the length of the resulting string
+   if(n1 < maxLen)
+   {
+      //Limit the number of characters to be copied
+      n2 = MIN(n2, maxLen - n1);
+      //Concatenate the resulting string
+      strncpy(path + n1, more, n2);
+      //Properly terminate the string with a NULL character
+      path[n1 + n2] = '\0';
+   }
+}
+
+
+/**
+ * @brief Check whether a file name matches the specified pattern
+ * @param[in] path NULL-terminated string that contains the path to be matched
+ * @param[in] pattern NULL-terminated string that contains the pattern for
+ *   which to search. The pattern may contain wildcard characters
+ * @return TRUE if the path matches the specified pattern, else FALSE
+ **/
+
+bool_t pathMatch(const char_t *path, const char_t* pattern)
+{
+   size_t i = 0;
+   size_t j = 0;
+
+   //Parse the pattern string
+   while(pattern[j] != '\0')
+   {
+      //Any wildcard character found?
+      if(pattern[j] == '?')
+      {
+         //The question mark matches a single character
+         if(path[i] == '\0')
+         {
+            return FALSE;
+         }
+         else
+         {
+            //Advance position in pathname
+            i++;
+            //Advance position in pattern string
+            j++;
+         }
+      }
+      else if(pattern[j] == '*')
+      {
+         //The asterisk sign matches zero or more characters
+         if(path[i] == '\0')
+         {
+            //Advance position in pattern string
+            j++;
+         }
+         else if(pathMatch(path + i, pattern + j + 1))
+         {
+            return TRUE;
+         }
+         else
+         {
+            //Advance position in pathname
+            i++;
+         }
+      }
+      else
+      {
+         //Case insensitive comparison
+         if(tolower((uint8_t) path[i]) != tolower((uint8_t) pattern[j]))
+         {
+            return FALSE;
+         }
+         else
+         {
+            //Advance position in pathname
+            i++;
+            //Advance position in pattern string
+            j++;
+         }
+      }
+   }
+
+   //Check whether the file name matches the specified pattern
+   if(path[i] == '\0' && pattern[j] == '\0')
+      return TRUE;
+   else
+      return FALSE;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/path.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,49 @@
+/**
+ * @file path.h
+ * @brief Path manipulation helper functions
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _PATH_H
+#define _PATH_H
+
+//Dependencies
+#include "os_port.h"
+
+//Path manipulation helper functions
+bool_t pathIsAbsolute(const char_t *path);
+bool_t pathIsRelative(const char_t *path);
+
+const char_t *pathFindFileName(const char_t *path);
+
+void pathCanonicalize(char_t *path);
+
+void pathAddSlash(char_t *path, size_t maxLen);
+void pathRemoveSlash(char_t *path);
+
+void pathCombine(char_t *path, const char_t *more, size_t maxLen);
+
+bool_t pathMatch(const char_t *path, const char_t* pattern);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/resource_manager.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,309 @@
+/**
+ * @file resource_manager.c
+ * @brief Embedded resource management
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Dependencies
+#include <string.h>
+#include "os_port.h"
+#include "resource_manager.h"
+#include "debug.h"
+
+//Resource data
+extern uint8_t res[];
+
+
+error_t resGetData(const char_t *path, uint8_t **data, size_t *length)
+{
+   bool_t found;
+   bool_t match;
+   uint_t n;
+   uint_t dirLength;
+   ResEntry *resEntry;
+
+   //Point to the resource header
+   ResHeader *resHeader = (ResHeader *) res;
+
+   //Make sure the resource data is valid
+   if(resHeader->totalSize < sizeof(ResHeader))
+      return ERROR_INVALID_RESOURCE;
+
+   //Retrieve the length of the root directory
+   dirLength = resHeader->rootEntry.dataLength;
+   //Point to the contents of the root directory
+   resEntry = (ResEntry *) (res + resHeader->rootEntry.dataStart);
+
+   //Parse the entire path
+   for(found = FALSE; !found && path[0] != '\0'; path += n + 1)
+   {
+      //Search for the separator that terminates the current token
+      for(n = 0; path[n] != '\\' && path[n] != '/' && path[n] != '\0'; n++);
+
+      if(n == 0 && path[n] != '\0')
+      {
+         path++;
+         for(n = 0; path[n] != '\\' && path[n] != '/' && path[n] != '\0'; n++);
+      }
+
+      //Loop through the directory
+      for(match = FALSE; !match && dirLength > 0; )
+      {
+         //Check the number of remaining bytes
+         if(dirLength < sizeof(ResEntry))
+            return ERROR_INVALID_RESOURCE;
+         //Make sure the entry is valid
+         if(dirLength < (sizeof(ResEntry) + resEntry->nameLength))
+            return ERROR_INVALID_RESOURCE;
+
+         //Compare current entry name against the expected one
+         if(resEntry->nameLength == n && !strncasecmp(resEntry->name, path, n))
+         {
+            //Check the type of the entry
+            if(resEntry->type == RES_TYPE_DIR)
+            {
+               //Save the length of the directory
+               dirLength = resEntry->dataLength;
+               //Point to the contents of the directory
+               resEntry = (ResEntry *) (res + resEntry->dataStart);
+            }
+            else
+            {
+               //A file may only appear at the end of the path
+               if(path[n] != '\0')
+                  return ERROR_NOT_FOUND;
+
+               //The search process is complete
+               found = TRUE;
+            }
+            //The current entry matches the specified path
+            match = TRUE;
+         }
+         else
+         {
+            //Remaining bytes to process
+            dirLength -= sizeof(ResEntry) + resEntry->nameLength;
+            //Point to the next entry
+            resEntry = (ResEntry *) ((uint8_t *) resEntry + sizeof(ResEntry) + resEntry->nameLength);
+         }
+      }
+
+      //Unable to find the specified file?
+      if(!match)
+         return ERROR_NOT_FOUND;
+   }
+
+   //Unable to find the specified file?
+   if(!found)
+      return ERROR_NOT_FOUND;
+   //Enforce the entry type
+   if(resEntry->type != RES_TYPE_FILE)
+      return ERROR_NOT_FOUND;
+
+   //Return the location of the specified resource
+   *data = res + resEntry->dataStart;
+   //Return the length of the resource
+   *length = resEntry->dataLength;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+error_t resSearchFile(const char_t *path, DirEntry *dirEntry)
+{
+   bool_t found;
+   bool_t match;
+   uint_t n;
+   uint_t length;
+   ResEntry *resEntry;
+
+   //Point to the resource header
+   ResHeader *resHeader = (ResHeader *) res;
+
+   //Make sure the resource data is valid
+   if(resHeader->totalSize < sizeof(ResHeader))
+      return ERROR_INVALID_RESOURCE;
+
+   //Retrieve the length of the root directory
+   length = resHeader->rootEntry.dataLength;
+   //Point to the contents of the root directory
+   resEntry = (ResEntry *) (res + resHeader->rootEntry.dataStart);
+
+   //Parse the entire path
+   for(found = FALSE; !found && path[0] != '\0'; path += n + 1)
+   {
+      //Search for the separator that terminates the current token
+      for(n = 0; path[n] != '\\' && path[n] != '/' && path[n] != '\0'; n++);
+
+      if(n == 0 && path[n] != '\0')
+      {
+         path++;
+         for(n = 0; path[n] != '\\' && path[n] != '/' && path[n] != '\0'; n++);
+      }
+
+      //Loop through the directory
+      for(match = FALSE; !match && length > 0; )
+      {
+         //Check the number of remaining bytes
+         if(length < sizeof(ResEntry))
+            return ERROR_INVALID_RESOURCE;
+         //Make sure the entry is valid
+         if(length < (sizeof(ResEntry) + resEntry->nameLength))
+            return ERROR_INVALID_RESOURCE;
+
+         //Compare current entry name against the expected one
+         if(resEntry->nameLength == n && !strncasecmp(resEntry->name, path, n))
+         {
+            //Check the type of the entry
+            if(resEntry->type == RES_TYPE_DIR)
+            {
+               //Save the length of the directory
+               length = resEntry->dataLength;
+               //Point to the contents of the directory
+               resEntry = (ResEntry *) (res + resEntry->dataStart);
+            }
+            else
+            {
+               //A file may only appear at the end of the path
+               if(path[n] != '\0')
+                  return ERROR_INVALID_PATH;
+
+               //The search process is complete
+               found = TRUE;
+            }
+            //The current entry matches the specified path
+            match = TRUE;
+         }
+         else
+         {
+            //Remaining bytes to process
+            length -= sizeof(ResEntry) + resEntry->nameLength;
+            //Point to the next entry
+            resEntry = (ResEntry *) ((uint8_t *) resEntry + sizeof(ResEntry) + resEntry->nameLength);
+         }
+      }
+
+      //Unable to find the specified file?
+      if(!match)
+         return ERROR_NOT_FOUND;
+   }
+
+   //Unable to find the specified file?
+   if(!found)
+      return ERROR_NOT_FOUND;
+
+   //Return information about the file
+   dirEntry->type = resEntry->type;
+   dirEntry->volume = 0;
+   dirEntry->dataStart = resEntry->dataStart;
+   dirEntry->dataLength = resEntry->dataLength;
+   dirEntry->nameLength = 0; //resEntry->nameLength;
+   //Copy the filename
+   //strncpy(dirEntry->name, resEntry->name, dirEntry->nameLength);
+   //Properly terminate the filename
+   //dirEntry->name[dirEntry->nameLength] = '\0';
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+#if 0
+
+error_t resOpenFile(FsFile *file, const DirEntry *dirEntry, uint_t mode)
+{
+   file->mode = mode;
+   file->offset = 0;
+   file->start = dirEntry->dataStart;
+   file->size = dirEntry->dataLength;
+
+   return NO_ERROR;
+}
+
+
+error_t resSeekFile(FsFile *file, uint32_t *position)
+{
+   return ERROR_NOT_IMPLEMENTED;
+}
+
+
+uint_t resReadFile(FsFile *file, void *data, size_t length)
+{
+   length = MIN(length, file->size - file->offset);
+   memcpy(data, res + file->start + file->offset, length);
+   file->offset += length;
+   return length;
+}
+
+FILE *fopen(const char_t *filename, const char_t *mode)
+{
+   error_t error;
+   DirEntry dirEntry;
+   FsFile *file;
+
+   error = resSearchFile(filename, &dirEntry);
+   if(error)
+      return NULL;
+
+   file = osAllocMem(sizeof(FsFile));
+   if(!file)
+      return NULL;
+
+   error = resOpenFile(file, &dirEntry, MODE_BINARY);
+   if(error)
+   {
+      osFreeMem(file);
+      return NULL;
+   }
+
+   return (FILE *) file;
+}
+
+
+size_t fread(void *ptr, size_t size, size_t count, FILE *stream)
+{
+   uint_t n;
+
+   n = resReadFile((FsFile *) stream, ptr, size * count);
+
+   return n / size;
+}
+
+
+int_t fclose(FILE * stream)
+{
+   osFreeMem(stream);
+   //The stream is successfully closed
+   return 0;
+}
+
+
+uint_t fileGetSize(FILE *stream)
+{
+   uint_t n;
+   n = ((FsFile *) stream)->size;
+   return n;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/resource_manager.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,130 @@
+/**
+ * @file resource_manager.h
+ * @brief Embedded resource management
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _RESOURCE_MANAGER_H
+#define _RESOURCE_MANAGER_H
+
+//Dependencies
+#include "compiler_port.h"
+#include "error.h"
+
+
+/**
+ * @brief Resource type
+ **/
+
+typedef enum
+{
+   RES_TYPE_DIR  = 1,
+   RES_TYPE_FILE = 2
+} ResType;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief Resource entry
+ **/
+
+typedef __start_packed struct
+{
+   char_t type;
+   uint32_t dataStart;
+   uint32_t dataLength;
+   uint8_t nameLength;
+   char_t name[];
+} __end_packed ResEntry;
+
+
+/**
+ * @brief Root entry
+ **/
+
+typedef __start_packed struct
+{
+   char_t type;
+   uint32_t dataStart;
+   uint32_t dataLength;
+   uint8_t nameLength;
+} __end_packed ResRootEntry;
+
+
+/**
+ * @brief Resource header
+ **/
+
+typedef __start_packed struct
+{
+   uint32_t totalSize;
+   ResRootEntry rootEntry;
+} __end_packed ResHeader;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+typedef struct
+{
+   uint_t type;
+   uint_t volume;
+   uint32_t dataStart;
+   uint32_t dataLength;
+   uint8_t nameLength;
+   char_t name[];
+} DirEntry;
+
+
+//Resource management
+error_t resGetData(const char_t *path, uint8_t **data, size_t *length);
+
+error_t resSearchFile(const char_t *path, DirEntry *dirEntry);
+
+//error_t resOpenDirectory(Directory *directory, const DirEntry *entry);
+//error_t resReadDirectory(Directory *directory, DirEntry *entry);
+
+#if 0
+typedef struct
+{
+   uint_t mode;
+   uint32_t start;
+   uint32_t size;
+   uint32_t offset;
+} FsFile;
+
+error_t resOpenFile(FsFile *file, const DirEntry *dirEntry, uint_t mode);
+error_t resSeekFile(FsFile *file, uint32_t *position);
+uint_t resReadFile(FsFile *file, void *data, size_t length);
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/str.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,169 @@
+/**
+ * @file str.c
+ * @brief String manipulation helper functions
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Dependencies
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "str.h"
+
+
+/**
+ * @brief Duplicate a string
+ * @param[in] s Pointer to a constant NULL-terminated character string
+ * @return Address of the string that was copied, or NULL if the string cannot be copied
+ **/
+
+char_t *strDuplicate(const char_t *s)
+{
+   uint_t n;
+   char_t *p;
+
+   //Calculate the length occupied by the input string
+   n = strlen(s) + 1;
+
+   //Allocate memory to hold the new string
+   p = osAllocMem(n);
+   //Failed to allocate memory?
+   if(!p)
+      return NULL;
+
+   //Make a copy of the input string
+   memcpy(p, s, n);
+
+   //Return a pointer to the newly created string
+   return p;
+}
+
+
+/**
+ * @brief Removes all leading and trailing whitespace from a string
+ * @param[in] s The string that will be trimmed
+ * @return String with whitespace stripped from the beginning and end
+ **/
+
+char_t *strTrimWhitespace(char_t *s)
+{
+   char_t *end;
+   char_t *result;
+
+   //Trim whitespace from the beginning
+   while(isspace((uint8_t) *s)) s++;
+   //Save the current position
+   result = s;
+
+   //Search for the first whitespace to remove
+   //at the end of the string
+   for(end = NULL; *s != '\0'; s++)
+   {
+      if(!isspace((uint8_t) *s))
+         end = NULL;
+      else if(!end)
+         end = s;
+   }
+
+   //Trim whitespace from the end
+   if(end) *end = '\0';
+
+   //Return the string with leading and
+   //trailing whitespace omitted
+   return result;
+}
+
+
+/**
+ * @brief Removes all trailing whitespace from a string
+ * @param[in,out] s Pointer to a NULL-terminated character string
+ **/
+
+void strRemoveTrailingSpace(char_t *s)
+{
+   char_t *end;
+
+   //Search for the first whitespace to remove
+   //at the end of the string
+   for(end = NULL; *s != '\0'; s++)
+   {
+      if(!isspace((uint8_t) *s))
+         end = NULL;
+      else if(!end)
+         end = s;
+   }
+
+   //Trim whitespace from the end
+   if(end) *end = '\0';
+}
+
+
+/**
+ * @brief Replace all occurrences of the specified character
+ * @param[in,out] s Pointer to a NULL-terminated character string
+ * @param[in] oldChar The character to be replaced
+ * @param[in] newChar The character that will replace all occurrences of oldChar
+ **/
+
+void strReplaceChar(char_t *s, char_t oldChar, char_t newChar)
+{
+   //Parse the specified string
+   while(*s != '\0')
+   {
+      //Remplace all occurrences of the specified character
+      if(*s == oldChar)
+         *s = newChar;
+   }
+}
+
+
+/**
+ * @brief Copy string
+ * @param[out] dest Pointer to the destination string
+ * @param[in] src Pointer to the source string
+ * @param[in] destSize Size of the buffer allocated for the destination string
+ * @return Error code
+ **/
+
+error_t strSafeCopy(char_t *dest, const char_t *src, size_t destSize)
+{
+   size_t n;
+
+   //Check parameters
+   if(dest == NULL || src == NULL || destSize < 1)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get the length of the source name
+   n = strlen(src);
+   //Limit the number of characters to be copied
+   n = MIN(n, destSize - 1);
+
+   //Copy the string
+   strncpy(dest, src, n);
+   //Properly terminate the string with a NULL character
+   dest[n] = '\0';
+
+   //Successful processing
+   return NO_ERROR;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/str.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,43 @@
+/**
+ * @file str.h
+ * @brief String manipulation helper functions
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _STR_H
+#define _STR_H
+
+//Dependencies
+#include "os_port.h"
+#include "error.h"
+
+//String manipulation helper functions
+char_t *strDuplicate(const char_t *s);
+char_t *strTrimWhitespace(char_t *s);
+void strRemoveTrailingSpace(char_t *s);
+void strReplaceChar(char_t *s, char_t oldChar, char_t newChar);
+
+error_t strSafeCopy(char_t *dest, const char_t *src, size_t destSize);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/aes.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,507 @@
+/**
+ * @file aes.c
+ * @brief AES (Advanced Encryption Standard)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * AES is an encryption standard based on Rijndael algorithm, a symmetric block
+ * cipher that can process data blocks of 128 bits, using cipher keys with
+ * lengths of 128, 192, and 256 bits. Refer to FIPS 197 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "aes.h"
+
+//Check crypto library configuration
+#if (AES_SUPPORT == ENABLED)
+
+//Substitution table used by encryption algorithm (S-box)
+static const uint8_t sbox[256] =
+{
+   0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
+   0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
+   0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
+   0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
+   0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
+   0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
+   0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
+   0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
+   0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
+   0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
+   0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
+   0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
+   0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
+   0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
+   0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
+   0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
+};
+
+//Substitution table used by decryption algorithm (inverse S-box)
+static const uint8_t isbox[256] =
+{
+   0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
+   0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
+   0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
+   0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
+   0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
+   0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
+   0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
+   0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
+   0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
+   0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
+   0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
+   0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
+   0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
+   0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
+   0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
+   0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
+};
+
+//Precalculated table (encryption)
+static const uint32_t te[256] =
+{
+   0xA56363C6, 0x847C7CF8, 0x997777EE, 0x8D7B7BF6, 0x0DF2F2FF, 0xBD6B6BD6, 0xB16F6FDE, 0x54C5C591,
+   0x50303060, 0x03010102, 0xA96767CE, 0x7D2B2B56, 0x19FEFEE7, 0x62D7D7B5, 0xE6ABAB4D, 0x9A7676EC,
+   0x45CACA8F, 0x9D82821F, 0x40C9C989, 0x877D7DFA, 0x15FAFAEF, 0xEB5959B2, 0xC947478E, 0x0BF0F0FB,
+   0xECADAD41, 0x67D4D4B3, 0xFDA2A25F, 0xEAAFAF45, 0xBF9C9C23, 0xF7A4A453, 0x967272E4, 0x5BC0C09B,
+   0xC2B7B775, 0x1CFDFDE1, 0xAE93933D, 0x6A26264C, 0x5A36366C, 0x413F3F7E, 0x02F7F7F5, 0x4FCCCC83,
+   0x5C343468, 0xF4A5A551, 0x34E5E5D1, 0x08F1F1F9, 0x937171E2, 0x73D8D8AB, 0x53313162, 0x3F15152A,
+   0x0C040408, 0x52C7C795, 0x65232346, 0x5EC3C39D, 0x28181830, 0xA1969637, 0x0F05050A, 0xB59A9A2F,
+   0x0907070E, 0x36121224, 0x9B80801B, 0x3DE2E2DF, 0x26EBEBCD, 0x6927274E, 0xCDB2B27F, 0x9F7575EA,
+   0x1B090912, 0x9E83831D, 0x742C2C58, 0x2E1A1A34, 0x2D1B1B36, 0xB26E6EDC, 0xEE5A5AB4, 0xFBA0A05B,
+   0xF65252A4, 0x4D3B3B76, 0x61D6D6B7, 0xCEB3B37D, 0x7B292952, 0x3EE3E3DD, 0x712F2F5E, 0x97848413,
+   0xF55353A6, 0x68D1D1B9, 0x00000000, 0x2CEDEDC1, 0x60202040, 0x1FFCFCE3, 0xC8B1B179, 0xED5B5BB6,
+   0xBE6A6AD4, 0x46CBCB8D, 0xD9BEBE67, 0x4B393972, 0xDE4A4A94, 0xD44C4C98, 0xE85858B0, 0x4ACFCF85,
+   0x6BD0D0BB, 0x2AEFEFC5, 0xE5AAAA4F, 0x16FBFBED, 0xC5434386, 0xD74D4D9A, 0x55333366, 0x94858511,
+   0xCF45458A, 0x10F9F9E9, 0x06020204, 0x817F7FFE, 0xF05050A0, 0x443C3C78, 0xBA9F9F25, 0xE3A8A84B,
+   0xF35151A2, 0xFEA3A35D, 0xC0404080, 0x8A8F8F05, 0xAD92923F, 0xBC9D9D21, 0x48383870, 0x04F5F5F1,
+   0xDFBCBC63, 0xC1B6B677, 0x75DADAAF, 0x63212142, 0x30101020, 0x1AFFFFE5, 0x0EF3F3FD, 0x6DD2D2BF,
+   0x4CCDCD81, 0x140C0C18, 0x35131326, 0x2FECECC3, 0xE15F5FBE, 0xA2979735, 0xCC444488, 0x3917172E,
+   0x57C4C493, 0xF2A7A755, 0x827E7EFC, 0x473D3D7A, 0xAC6464C8, 0xE75D5DBA, 0x2B191932, 0x957373E6,
+   0xA06060C0, 0x98818119, 0xD14F4F9E, 0x7FDCDCA3, 0x66222244, 0x7E2A2A54, 0xAB90903B, 0x8388880B,
+   0xCA46468C, 0x29EEEEC7, 0xD3B8B86B, 0x3C141428, 0x79DEDEA7, 0xE25E5EBC, 0x1D0B0B16, 0x76DBDBAD,
+   0x3BE0E0DB, 0x56323264, 0x4E3A3A74, 0x1E0A0A14, 0xDB494992, 0x0A06060C, 0x6C242448, 0xE45C5CB8,
+   0x5DC2C29F, 0x6ED3D3BD, 0xEFACAC43, 0xA66262C4, 0xA8919139, 0xA4959531, 0x37E4E4D3, 0x8B7979F2,
+   0x32E7E7D5, 0x43C8C88B, 0x5937376E, 0xB76D6DDA, 0x8C8D8D01, 0x64D5D5B1, 0xD24E4E9C, 0xE0A9A949,
+   0xB46C6CD8, 0xFA5656AC, 0x07F4F4F3, 0x25EAEACF, 0xAF6565CA, 0x8E7A7AF4, 0xE9AEAE47, 0x18080810,
+   0xD5BABA6F, 0x887878F0, 0x6F25254A, 0x722E2E5C, 0x241C1C38, 0xF1A6A657, 0xC7B4B473, 0x51C6C697,
+   0x23E8E8CB, 0x7CDDDDA1, 0x9C7474E8, 0x211F1F3E, 0xDD4B4B96, 0xDCBDBD61, 0x868B8B0D, 0x858A8A0F,
+   0x907070E0, 0x423E3E7C, 0xC4B5B571, 0xAA6666CC, 0xD8484890, 0x05030306, 0x01F6F6F7, 0x120E0E1C,
+   0xA36161C2, 0x5F35356A, 0xF95757AE, 0xD0B9B969, 0x91868617, 0x58C1C199, 0x271D1D3A, 0xB99E9E27,
+   0x38E1E1D9, 0x13F8F8EB, 0xB398982B, 0x33111122, 0xBB6969D2, 0x70D9D9A9, 0x898E8E07, 0xA7949433,
+   0xB69B9B2D, 0x221E1E3C, 0x92878715, 0x20E9E9C9, 0x49CECE87, 0xFF5555AA, 0x78282850, 0x7ADFDFA5,
+   0x8F8C8C03, 0xF8A1A159, 0x80898909, 0x170D0D1A, 0xDABFBF65, 0x31E6E6D7, 0xC6424284, 0xB86868D0,
+   0xC3414182, 0xB0999929, 0x772D2D5A, 0x110F0F1E, 0xCBB0B07B, 0xFC5454A8, 0xD6BBBB6D, 0x3A16162C
+};
+
+//Precalculated table (decryption)
+static const uint32_t td[256] =
+{
+   0x50A7F451, 0x5365417E, 0xC3A4171A, 0x965E273A, 0xCB6BAB3B, 0xF1459D1F, 0xAB58FAAC, 0x9303E34B,
+   0x55FA3020, 0xF66D76AD, 0x9176CC88, 0x254C02F5, 0xFCD7E54F, 0xD7CB2AC5, 0x80443526, 0x8FA362B5,
+   0x495AB1DE, 0x671BBA25, 0x980EEA45, 0xE1C0FE5D, 0x02752FC3, 0x12F04C81, 0xA397468D, 0xC6F9D36B,
+   0xE75F8F03, 0x959C9215, 0xEB7A6DBF, 0xDA595295, 0x2D83BED4, 0xD3217458, 0x2969E049, 0x44C8C98E,
+   0x6A89C275, 0x78798EF4, 0x6B3E5899, 0xDD71B927, 0xB64FE1BE, 0x17AD88F0, 0x66AC20C9, 0xB43ACE7D,
+   0x184ADF63, 0x82311AE5, 0x60335197, 0x457F5362, 0xE07764B1, 0x84AE6BBB, 0x1CA081FE, 0x942B08F9,
+   0x58684870, 0x19FD458F, 0x876CDE94, 0xB7F87B52, 0x23D373AB, 0xE2024B72, 0x578F1FE3, 0x2AAB5566,
+   0x0728EBB2, 0x03C2B52F, 0x9A7BC586, 0xA50837D3, 0xF2872830, 0xB2A5BF23, 0xBA6A0302, 0x5C8216ED,
+   0x2B1CCF8A, 0x92B479A7, 0xF0F207F3, 0xA1E2694E, 0xCDF4DA65, 0xD5BE0506, 0x1F6234D1, 0x8AFEA6C4,
+   0x9D532E34, 0xA055F3A2, 0x32E18A05, 0x75EBF6A4, 0x39EC830B, 0xAAEF6040, 0x069F715E, 0x51106EBD,
+   0xF98A213E, 0x3D06DD96, 0xAE053EDD, 0x46BDE64D, 0xB58D5491, 0x055DC471, 0x6FD40604, 0xFF155060,
+   0x24FB9819, 0x97E9BDD6, 0xCC434089, 0x779ED967, 0xBD42E8B0, 0x888B8907, 0x385B19E7, 0xDBEEC879,
+   0x470A7CA1, 0xE90F427C, 0xC91E84F8, 0x00000000, 0x83868009, 0x48ED2B32, 0xAC70111E, 0x4E725A6C,
+   0xFBFF0EFD, 0x5638850F, 0x1ED5AE3D, 0x27392D36, 0x64D90F0A, 0x21A65C68, 0xD1545B9B, 0x3A2E3624,
+   0xB1670A0C, 0x0FE75793, 0xD296EEB4, 0x9E919B1B, 0x4FC5C080, 0xA220DC61, 0x694B775A, 0x161A121C,
+   0x0ABA93E2, 0xE52AA0C0, 0x43E0223C, 0x1D171B12, 0x0B0D090E, 0xADC78BF2, 0xB9A8B62D, 0xC8A91E14,
+   0x8519F157, 0x4C0775AF, 0xBBDD99EE, 0xFD607FA3, 0x9F2601F7, 0xBCF5725C, 0xC53B6644, 0x347EFB5B,
+   0x7629438B, 0xDCC623CB, 0x68FCEDB6, 0x63F1E4B8, 0xCADC31D7, 0x10856342, 0x40229713, 0x2011C684,
+   0x7D244A85, 0xF83DBBD2, 0x1132F9AE, 0x6DA129C7, 0x4B2F9E1D, 0xF330B2DC, 0xEC52860D, 0xD0E3C177,
+   0x6C16B32B, 0x99B970A9, 0xFA489411, 0x2264E947, 0xC48CFCA8, 0x1A3FF0A0, 0xD82C7D56, 0xEF903322,
+   0xC74E4987, 0xC1D138D9, 0xFEA2CA8C, 0x360BD498, 0xCF81F5A6, 0x28DE7AA5, 0x268EB7DA, 0xA4BFAD3F,
+   0xE49D3A2C, 0x0D927850, 0x9BCC5F6A, 0x62467E54, 0xC2138DF6, 0xE8B8D890, 0x5EF7392E, 0xF5AFC382,
+   0xBE805D9F, 0x7C93D069, 0xA92DD56F, 0xB31225CF, 0x3B99ACC8, 0xA77D1810, 0x6E639CE8, 0x7BBB3BDB,
+   0x097826CD, 0xF418596E, 0x01B79AEC, 0xA89A4F83, 0x656E95E6, 0x7EE6FFAA, 0x08CFBC21, 0xE6E815EF,
+   0xD99BE7BA, 0xCE366F4A, 0xD4099FEA, 0xD67CB029, 0xAFB2A431, 0x31233F2A, 0x3094A5C6, 0xC066A235,
+   0x37BC4E74, 0xA6CA82FC, 0xB0D090E0, 0x15D8A733, 0x4A9804F1, 0xF7DAEC41, 0x0E50CD7F, 0x2FF69117,
+   0x8DD64D76, 0x4DB0EF43, 0x544DAACC, 0xDF0496E4, 0xE3B5D19E, 0x1B886A4C, 0xB81F2CC1, 0x7F516546,
+   0x04EA5E9D, 0x5D358C01, 0x737487FA, 0x2E410BFB, 0x5A1D67B3, 0x52D2DB92, 0x335610E9, 0x1347D66D,
+   0x8C61D79A, 0x7A0CA137, 0x8E14F859, 0x893C13EB, 0xEE27A9CE, 0x35C961B7, 0xEDE51CE1, 0x3CB1477A,
+   0x59DFD29C, 0x3F73F255, 0x79CE1418, 0xBF37C773, 0xEACDF753, 0x5BAAFD5F, 0x146F3DDF, 0x86DB4478,
+   0x81F3AFCA, 0x3EC468B9, 0x2C342438, 0x5F40A3C2, 0x72C31D16, 0x0C25E2BC, 0x8B493C28, 0x41950DFF,
+   0x7101A839, 0xDEB30C08, 0x9CE4B4D8, 0x90C15664, 0x6184CB7B, 0x70B632D5, 0x745C6C48, 0x4257B8D0
+};
+
+//Round constant word array
+static const uint32_t rcon[11] =
+{
+   0x00000000,
+   0x00000001,
+   0x00000002,
+   0x00000004,
+   0x00000008,
+   0x00000010,
+   0x00000020,
+   0x00000040,
+   0x00000080,
+   0x0000001B,
+   0x00000036
+};
+
+//Common interface for encryption algorithms
+const CipherAlgo aesCipherAlgo =
+{
+   "AES",
+   sizeof(AesContext),
+   CIPHER_ALGO_TYPE_BLOCK,
+   AES_BLOCK_SIZE,
+   (CipherAlgoInit) aesInit,
+   NULL,
+   NULL,
+   (CipherAlgoEncryptBlock) aesEncryptBlock,
+   (CipherAlgoDecryptBlock) aesDecryptBlock
+};
+
+
+/**
+ * @brief Key expansion
+ * @param[in] context Pointer to the AES context to initialize
+ * @param[in] key Pointer to the key
+ * @param[in] keyLength Length of the key
+ * @return Error code
+ **/
+
+error_t aesInit(AesContext *context, const uint8_t *key, size_t keyLength)
+{
+   uint_t i;
+   uint32_t temp;
+   size_t keyScheduleSize;
+
+   //10 rounds are required for 128-bit key
+   if(keyLength == 16)
+      context->nr = 10;
+   //12 rounds are required for 192-bit key
+   else if(keyLength == 24)
+      context->nr = 12;
+   //14 rounds are required for 256-bit key
+   else if(keyLength == 32)
+      context->nr = 14;
+   //Key length is not supported...
+   else
+      return ERROR_INVALID_KEY_LENGTH;
+
+   //Determine the number of 32-bit words in the key
+   keyLength /= 4;
+
+   //Copy the original key
+   for(i = 0; i < keyLength; i++)
+      context->ek[i] = LOAD32LE(key + (i * 4));
+
+   //The size of the key schedule depends on the number of rounds
+   keyScheduleSize = 4 * (context->nr + 1);
+
+   //Generate the key schedule (encryption)
+   for(i = keyLength; i < keyScheduleSize; i++)
+   {
+      //Save previous word
+      temp = context->ek[i - 1];
+
+      //Apply transformation
+      if((i % keyLength) == 0)
+      {
+         context->ek[i] = sbox[(temp >> 8) & 0xFF];
+         context->ek[i] |= (sbox[(temp >> 16) & 0xFF] << 8);
+         context->ek[i] |= (sbox[(temp >> 24) & 0xFF] << 16);
+         context->ek[i] |= (sbox[temp & 0xFF] << 24);
+         context->ek[i] ^= rcon[i / keyLength];
+      }
+      else if(keyLength > 6 && (i % keyLength) == 4)
+      {
+         context->ek[i] = sbox[temp & 0xFF];
+         context->ek[i] |= (sbox[(temp >> 8) & 0xFF] << 8);
+         context->ek[i] |= (sbox[(temp >> 16) & 0xFF] << 16);
+         context->ek[i] |= (sbox[(temp >> 24) & 0xFF] << 24);
+      }
+      else
+      {
+         context->ek[i] = temp;
+      }
+
+      //Update the key schedule
+      context->ek[i] ^= context->ek[i - keyLength];
+   }
+
+   //Generate the key schedule (decryption)
+   for(i = 0; i < keyScheduleSize; i++)
+   {
+      //Apply the InvMixColumns transformation to all round keys
+      //but the first and the last
+      if(i < 4 || i >= (keyScheduleSize - 4))
+      {
+         context->dk[i] = context->ek[i];
+      }
+      else
+      {
+         context->dk[i] = td[sbox[context->ek[i] & 0xFF]];
+         temp = td[sbox[(context->ek[i] >> 8) & 0xFF]];
+         context->dk[i] ^= ROL32(temp, 8);
+         temp = td[sbox[(context->ek[i] >> 16) & 0xFF]];
+         context->dk[i] ^= ROL32(temp, 16);
+         temp = td[sbox[(context->ek[i] >> 24) & 0xFF]];
+         context->dk[i] ^= ROL32(temp, 24);
+      }
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Encrypt a 16-byte block using AES algorithm
+ * @param[in] context Pointer to the AES context
+ * @param[in] input Plaintext block to encrypt
+ * @param[out] output Ciphertext block resulting from encryption
+ **/
+
+void aesEncryptBlock(AesContext *context, const uint8_t *input, uint8_t *output)
+{
+   uint_t i;
+   uint32_t s0;
+   uint32_t s1;
+   uint32_t s2;
+   uint32_t s3;
+   uint32_t t0;
+   uint32_t t1;
+   uint32_t t2;
+   uint32_t t3;
+   uint32_t temp;
+
+   //Copy the plaintext to the state array
+   s0 = LOAD32LE(input + 0);
+   s1 = LOAD32LE(input + 4);
+   s2 = LOAD32LE(input + 8);
+   s3 = LOAD32LE(input + 12);
+
+   //Initial round key addition
+   s0 ^= context->ek[0];
+   s1 ^= context->ek[1];
+   s2 ^= context->ek[2];
+   s3 ^= context->ek[3];
+
+   //The number of rounds depends on the key length
+   for(i = 1; i < context->nr; i++)
+   {
+      //Apply round function
+      t0 = te[s0 & 0xFF];
+      temp = te[(s1 >> 8) & 0xFF];
+      t0 ^= ROL32(temp, 8);
+      temp = te[(s2 >> 16) & 0xFF];
+      t0 ^= ROL32(temp, 16);
+      temp = te[(s3 >> 24) & 0xFF];
+      t0 ^= ROL32(temp, 24);
+
+      t1 = te[s1 & 0xFF];
+      temp = te[(s2 >> 8) & 0xFF];
+      t1 ^= ROL32(temp, 8);
+      temp = te[(s3 >> 16) & 0xFF];
+      t1 ^= ROL32(temp, 16);
+      temp = te[(s0 >> 24) & 0xFF];
+      t1 ^= ROL32(temp, 24);
+
+      t2 = te[s2 & 0xFF];
+      temp = te[(s3 >> 8) & 0xFF];
+      t2 ^= ROL32(temp, 8);
+      temp = te[(s0 >> 16) & 0xFF];
+      t2 ^= ROL32(temp, 16);
+      temp = te[(s1 >> 24) & 0xFF];
+      t2 ^= ROL32(temp, 24);
+
+      t3 = te[s3 & 0xFF];
+      temp = te[(s0 >> 8) & 0xFF];
+      t3 ^= ROL32(temp, 8);
+      temp = te[(s1 >> 16) & 0xFF];
+      t3 ^= ROL32(temp, 16);
+      temp = te[(s2 >> 24) & 0xFF];
+      t3 ^= ROL32(temp, 24);
+
+      //Round key addition
+      s0 = t0 ^ context->ek[i * 4];
+      s1 = t1 ^ context->ek[i * 4 + 1];
+      s2 = t2 ^ context->ek[i * 4 + 2];
+      s3 = t3 ^ context->ek[i * 4 + 3];
+   }
+
+   //The last round differs slightly from the first rounds
+   t0 = sbox[s0 & 0xFF];
+   t0 |= sbox[(s1 >> 8) & 0xFF] << 8;
+   t0 |= sbox[(s2 >> 16) & 0xFF] << 16;
+   t0 |= sbox[(s3 >> 24) & 0xFF] << 24;
+
+   t1 = sbox[s1 & 0xFF];
+   t1 |= sbox[(s2 >> 8) & 0xFF] << 8;
+   t1 |= sbox[(s3 >> 16) & 0xFF] << 16;
+   t1 |= sbox[(s0 >> 24) & 0xFF] << 24;
+
+   t2 = sbox[s2 & 0xFF];
+   t2 |= sbox[(s3 >> 8) & 0xFF] << 8;
+   t2 |= sbox[(s0 >> 16) & 0xFF] << 16;
+   t2 |= sbox[(s1 >> 24) & 0xFF] << 24;
+
+   t3 = sbox[s3 & 0xFF];
+   t3 |= sbox[(s0 >> 8) & 0xFF] << 8;
+   t3 |= sbox[(s1 >> 16) & 0xFF] << 16;
+   t3 |= sbox[(s2 >> 24) & 0xFF] << 24;
+
+   //Last round key addition
+   s0 = t0 ^ context->ek[context->nr * 4];
+   s1 = t1 ^ context->ek[context->nr * 4 + 1];
+   s2 = t2 ^ context->ek[context->nr * 4 + 2];
+   s3 = t3 ^ context->ek[context->nr * 4 + 3];
+
+   //The final state is then copied to the output
+   STORE32LE(s0, output + 0);
+   STORE32LE(s1, output + 4);
+   STORE32LE(s2, output + 8);
+   STORE32LE(s3, output + 12);
+}
+
+
+/**
+ * @brief Decrypt a 16-byte block using AES algorithm
+ * @param[in] context Pointer to the AES context
+ * @param[in] input Ciphertext block to decrypt
+ * @param[out] output Plaintext block resulting from decryption
+ **/
+
+void aesDecryptBlock(AesContext *context, const uint8_t *input, uint8_t *output)
+{
+   uint_t i;
+   uint32_t s0;
+   uint32_t s1;
+   uint32_t s2;
+   uint32_t s3;
+   uint32_t t0;
+   uint32_t t1;
+   uint32_t t2;
+   uint32_t t3;
+   uint32_t temp;
+
+   //Copy the ciphertext to the state array
+   s0 = LOAD32LE(input + 0);
+   s1 = LOAD32LE(input + 4);
+   s2 = LOAD32LE(input + 8);
+   s3 = LOAD32LE(input + 12);
+
+   //Initial round key addition
+   s0 ^= context->dk[context->nr * 4];
+   s1 ^= context->dk[context->nr * 4 + 1];
+   s2 ^= context->dk[context->nr * 4 + 2];
+   s3 ^= context->dk[context->nr * 4 + 3];
+
+   //The number of rounds depends on the key length
+   for(i = context->nr - 1; i >= 1; i--)
+   {
+      //Apply round function
+      t0 = td[s0 & 0xFF];
+      temp = td[(s3 >> 8) & 0xFF];
+      t0 ^= ROL32(temp, 8);
+      temp = td[(s2 >> 16) & 0xFF];
+      t0 ^= ROL32(temp, 16);
+      temp = td[(s1 >> 24) & 0xFF];
+      t0 ^= ROL32(temp, 24);
+
+      t1 = td[s1 & 0xFF];
+      temp = td[(s0 >> 8) & 0xFF];
+      t1 ^= ROL32(temp, 8);
+      temp = td[(s3 >> 16) & 0xFF];
+      t1 ^= ROL32(temp, 16);
+      temp = td[(s2 >> 24) & 0xFF];
+      t1 ^= ROL32(temp, 24);
+
+      t2 = td[s2 & 0xFF];
+      temp = td[(s1 >> 8) & 0xFF];
+      t2 ^= ROL32(temp, 8);
+      temp = td[(s0 >> 16) & 0xFF];
+      t2 ^= ROL32(temp, 16);
+      temp = td[(s3 >> 24) & 0xFF];
+      t2 ^= ROL32(temp, 24);
+
+      t3 = td[s3 & 0xFF];
+      temp = td[(s2 >> 8) & 0xFF];
+      t3 ^= ROL32(temp, 8);
+      temp = td[(s1 >> 16) & 0xFF];
+      t3 ^= ROL32(temp, 16);
+      temp = td[(s0 >> 24) & 0xFF];
+      t3 ^= ROL32(temp, 24);
+
+      //Round key addition
+      s0 = t0 ^ context->dk[i * 4];
+      s1 = t1 ^ context->dk[i * 4 + 1];
+      s2 = t2 ^ context->dk[i * 4 + 2];
+      s3 = t3 ^ context->dk[i * 4 + 3];
+   }
+
+   //The last round differs slightly from the first rounds
+   t0 = isbox[s0 & 0xFF];
+   t0 |= isbox[(s3 >> 8) & 0xFF] << 8;
+   t0 |= isbox[(s2 >> 16) & 0xFF] << 16;
+   t0 |= isbox[(s1 >> 24) & 0xFF] << 24;
+
+   t1 = isbox[s1 & 0xFF];
+   t1 |= isbox[(s0 >> 8) & 0xFF] << 8;
+   t1 |= isbox[(s3 >> 16) & 0xFF] << 16;
+   t1 |= isbox[(s2 >> 24) & 0xFF] << 24;
+
+   t2 = isbox[s2 & 0xFF];
+   t2 |= isbox[(s1 >> 8) & 0xFF] << 8;
+   t2 |= isbox[(s0 >> 16) & 0xFF] << 16;
+   t2 |= isbox[(s3 >> 24) & 0xFF] << 24;
+
+   t3 = isbox[s3 & 0xFF];
+   t3 |= isbox[(s2 >> 8) & 0xFF] << 8;
+   t3 |= isbox[(s1 >> 16) & 0xFF] << 16;
+   t3 |= isbox[(s0 >> 24) & 0xFF] << 24;
+
+   //Last round key addition
+   s0 = t0 ^ context->dk[0];
+   s1 = t1 ^ context->dk[1];
+   s2 = t2 ^ context->dk[2];
+   s3 = t3 ^ context->dk[3];
+
+   //The final state is then copied to the output
+   STORE32LE(s0, output + 0);
+   STORE32LE(s1, output + 4);
+   STORE32LE(s2, output + 8);
+   STORE32LE(s3, output + 12);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/aes.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,62 @@
+/**
+ * @file aes.h
+ * @brief AES (Advanced Encryption Standard)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _AES_H
+#define _AES_H
+
+//Dependencies
+#include "crypto.h"
+
+//AES block size
+#define AES_BLOCK_SIZE 16
+//Common interface for encryption algorithms
+#define AES_CIPHER_ALGO (&aesCipherAlgo)
+
+
+/**
+ * @brief AES algorithm context
+ **/
+
+typedef struct
+{
+   uint_t nr;
+   uint32_t ek[60];
+   uint32_t dk[60];
+} AesContext;
+
+
+//AES related constants
+extern const CipherAlgo aesCipherAlgo;
+
+//AES related functions
+error_t aesInit(AesContext *context, const uint8_t *key, size_t keyLength);
+void aesEncryptBlock(AesContext *context, const uint8_t *input, uint8_t *output);
+void aesDecryptBlock(AesContext *context, const uint8_t *input, uint8_t *output);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/aria.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,556 @@
+/**
+ * @file aria.c
+ * @brief ARIA encryption algorithm
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * ARIA is a 128-bit block cipher with 128-, 192-, and 256-bit keys. The
+ * algorithm consists of a key scheduling part and data randomizing part.
+ * Refer to RFC 5794 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "aria.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (ARIA_SUPPORT == ENABLED)
+
+//Move operation
+#define MOV128(b, a) \
+{ \
+   (b)[0] = (a)[0]; \
+   (b)[1] = (a)[1]; \
+   (b)[2] = (a)[2]; \
+   (b)[3] = (a)[3]; \
+}
+
+//XOR operation
+#define XOR128(b, a) \
+{ \
+   (b)[0] ^= (a)[0]; \
+   (b)[1] ^= (a)[1]; \
+   (b)[2] ^= (a)[2]; \
+   (b)[3] ^= (a)[3]; \
+}
+
+//Rotate left operation
+#define ROL128(b, a, n) \
+{ \
+   (b)[0] = ((a)[((n) / 32 + 0) % 4] << ((n) % 32)) | ((a)[((n) / 32 + 1) % 4] >> (32 - ((n) % 32))); \
+   (b)[1] = ((a)[((n) / 32 + 1) % 4] << ((n) % 32)) | ((a)[((n) / 32 + 2) % 4] >> (32 - ((n) % 32))); \
+   (b)[2] = ((a)[((n) / 32 + 2) % 4] << ((n) % 32)) | ((a)[((n) / 32 + 3) % 4] >> (32 - ((n) % 32))); \
+   (b)[3] = ((a)[((n) / 32 + 3) % 4] << ((n) % 32)) | ((a)[((n) / 32 + 0) % 4] >> (32 - ((n) % 32))); \
+}
+
+//Substitution layer SL1
+#define SL1(b, a) \
+{ \
+   uint8_t *x = (uint8_t *) (a); \
+   uint8_t *y = (uint8_t *) (b); \
+   y[0] = sb1[x[0]]; \
+   y[1] = sb2[x[1]]; \
+   y[2] = sb3[x[2]]; \
+   y[3] = sb4[x[3]]; \
+   y[4] = sb1[x[4]]; \
+   y[5] = sb2[x[5]]; \
+   y[6] = sb3[x[6]]; \
+   y[7] = sb4[x[7]]; \
+   y[8] = sb1[x[8]]; \
+   y[9] = sb2[x[9]]; \
+   y[10] = sb3[x[10]]; \
+   y[11] = sb4[x[11]]; \
+   y[12] = sb1[x[12]]; \
+   y[13] = sb2[x[13]]; \
+   y[14] = sb3[x[14]]; \
+   y[15] = sb4[x[15]]; \
+}
+
+//Substitution layer SL2
+#define SL2(b, a) \
+{ \
+   uint8_t *x = (uint8_t *) (a); \
+   uint8_t *y = (uint8_t *) (b); \
+   y[0] = sb3[x[0]]; \
+   y[1] = sb4[x[1]]; \
+   y[2] = sb1[x[2]]; \
+   y[3] = sb2[x[3]]; \
+   y[4] = sb3[x[4]]; \
+   y[5] = sb4[x[5]]; \
+   y[6] = sb1[x[6]]; \
+   y[7] = sb2[x[7]]; \
+   y[8] = sb3[x[8]]; \
+   y[9] = sb4[x[9]]; \
+   y[10] = sb1[x[10]]; \
+   y[11] = sb2[x[11]]; \
+   y[12] = sb3[x[12]]; \
+   y[13] = sb4[x[13]]; \
+   y[14] = sb1[x[14]]; \
+   y[15] = sb2[x[15]]; \
+}
+
+//Diffusion layer
+#define A(b, a) \
+{ \
+   uint8_t *x = (uint8_t *) (a); \
+   uint8_t *y = (uint8_t *) (b); \
+   y[0] = x[3] ^ x[4] ^ x[6] ^ x[8] ^ x[9] ^ x[13] ^ x[14]; \
+   y[1] = x[2] ^ x[5] ^ x[7] ^ x[8] ^ x[9] ^ x[12] ^ x[15]; \
+   y[2] = x[1] ^ x[4] ^ x[6] ^ x[10] ^ x[11] ^ x[12] ^ x[15]; \
+   y[3] = x[0] ^ x[5] ^ x[7] ^ x[10] ^ x[11] ^ x[13] ^ x[14]; \
+   y[4] = x[0] ^ x[2] ^ x[5] ^ x[8] ^ x[11] ^ x[14] ^ x[15]; \
+   y[5] = x[1] ^ x[3] ^ x[4] ^ x[9] ^ x[10] ^ x[14] ^ x[15]; \
+   y[6] = x[0] ^ x[2] ^ x[7] ^ x[9] ^ x[10] ^ x[12] ^ x[13]; \
+   y[7] = x[1] ^ x[3] ^ x[6] ^ x[8] ^ x[11] ^ x[12] ^ x[13]; \
+   y[8] = x[0] ^ x[1] ^ x[4] ^ x[7] ^ x[10] ^ x[13] ^ x[15]; \
+   y[9] = x[0] ^ x[1] ^ x[5] ^ x[6] ^ x[11] ^ x[12] ^ x[14]; \
+   y[10] = x[2] ^ x[3] ^ x[5] ^ x[6] ^ x[8] ^ x[13] ^ x[15]; \
+   y[11] = x[2] ^ x[3] ^ x[4] ^ x[7] ^ x[9] ^ x[12] ^ x[14]; \
+   y[12] = x[1] ^ x[2] ^ x[6] ^ x[7] ^ x[9] ^ x[11] ^ x[12]; \
+   y[13] = x[0] ^ x[3] ^ x[6] ^ x[7] ^ x[8] ^ x[10] ^ x[13]; \
+   y[14] = x[0] ^ x[3] ^ x[4] ^ x[5] ^ x[9] ^ x[11] ^ x[14]; \
+   y[15] = x[1] ^ x[2] ^ x[4] ^ x[5] ^ x[8] ^ x[10] ^ x[15]; \
+}
+
+//S-box 1
+static const uint8_t sb1[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
+};
+
+//S-box 2
+static const uint8_t sb2[256] =
+{
+   0xE2, 0x4E, 0x54, 0xFC, 0x94, 0xC2, 0x4A, 0xCC, 0x62, 0x0D, 0x6A, 0x46, 0x3C, 0x4D, 0x8B, 0xD1,
+   0x5E, 0xFA, 0x64, 0xCB, 0xB4, 0x97, 0xBE, 0x2B, 0xBC, 0x77, 0x2E, 0x03, 0xD3, 0x19, 0x59, 0xC1,
+   0x1D, 0x06, 0x41, 0x6B, 0x55, 0xF0, 0x99, 0x69, 0xEA, 0x9C, 0x18, 0xAE, 0x63, 0xDF, 0xE7, 0xBB,
+   0x00, 0x73, 0x66, 0xFB, 0x96, 0x4C, 0x85, 0xE4, 0x3A, 0x09, 0x45, 0xAA, 0x0F, 0xEE, 0x10, 0xEB,
+   0x2D, 0x7F, 0xF4, 0x29, 0xAC, 0xCF, 0xAD, 0x91, 0x8D, 0x78, 0xC8, 0x95, 0xF9, 0x2F, 0xCE, 0xCD,
+   0x08, 0x7A, 0x88, 0x38, 0x5C, 0x83, 0x2A, 0x28, 0x47, 0xDB, 0xB8, 0xC7, 0x93, 0xA4, 0x12, 0x53,
+   0xFF, 0x87, 0x0E, 0x31, 0x36, 0x21, 0x58, 0x48, 0x01, 0x8E, 0x37, 0x74, 0x32, 0xCA, 0xE9, 0xB1,
+   0xB7, 0xAB, 0x0C, 0xD7, 0xC4, 0x56, 0x42, 0x26, 0x07, 0x98, 0x60, 0xD9, 0xB6, 0xB9, 0x11, 0x40,
+   0xEC, 0x20, 0x8C, 0xBD, 0xA0, 0xC9, 0x84, 0x04, 0x49, 0x23, 0xF1, 0x4F, 0x50, 0x1F, 0x13, 0xDC,
+   0xD8, 0xC0, 0x9E, 0x57, 0xE3, 0xC3, 0x7B, 0x65, 0x3B, 0x02, 0x8F, 0x3E, 0xE8, 0x25, 0x92, 0xE5,
+   0x15, 0xDD, 0xFD, 0x17, 0xA9, 0xBF, 0xD4, 0x9A, 0x7E, 0xC5, 0x39, 0x67, 0xFE, 0x76, 0x9D, 0x43,
+   0xA7, 0xE1, 0xD0, 0xF5, 0x68, 0xF2, 0x1B, 0x34, 0x70, 0x05, 0xA3, 0x8A, 0xD5, 0x79, 0x86, 0xA8,
+   0x30, 0xC6, 0x51, 0x4B, 0x1E, 0xA6, 0x27, 0xF6, 0x35, 0xD2, 0x6E, 0x24, 0x16, 0x82, 0x5F, 0xDA,
+   0xE6, 0x75, 0xA2, 0xEF, 0x2C, 0xB2, 0x1C, 0x9F, 0x5D, 0x6F, 0x80, 0x0A, 0x72, 0x44, 0x9B, 0x6C,
+   0x90, 0x0B, 0x5B, 0x33, 0x7D, 0x5A, 0x52, 0xF3, 0x61, 0xA1, 0xF7, 0xB0, 0xD6, 0x3F, 0x7C, 0x6D,
+   0xED, 0x14, 0xE0, 0xA5, 0x3D, 0x22, 0xB3, 0xF8, 0x89, 0xDE, 0x71, 0x1A, 0xAF, 0xBA, 0xB5, 0x81
+};
+
+//S-box 3
+static const uint8_t sb3[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
+};
+
+//S-box 4
+static const uint8_t sb4[256] =
+{
+   0x30, 0x68, 0x99, 0x1B, 0x87, 0xB9, 0x21, 0x78, 0x50, 0x39, 0xDB, 0xE1, 0x72, 0x09, 0x62, 0x3C,
+   0x3E, 0x7E, 0x5E, 0x8E, 0xF1, 0xA0, 0xCC, 0xA3, 0x2A, 0x1D, 0xFB, 0xB6, 0xD6, 0x20, 0xC4, 0x8D,
+   0x81, 0x65, 0xF5, 0x89, 0xCB, 0x9D, 0x77, 0xC6, 0x57, 0x43, 0x56, 0x17, 0xD4, 0x40, 0x1A, 0x4D,
+   0xC0, 0x63, 0x6C, 0xE3, 0xB7, 0xC8, 0x64, 0x6A, 0x53, 0xAA, 0x38, 0x98, 0x0C, 0xF4, 0x9B, 0xED,
+   0x7F, 0x22, 0x76, 0xAF, 0xDD, 0x3A, 0x0B, 0x58, 0x67, 0x88, 0x06, 0xC3, 0x35, 0x0D, 0x01, 0x8B,
+   0x8C, 0xC2, 0xE6, 0x5F, 0x02, 0x24, 0x75, 0x93, 0x66, 0x1E, 0xE5, 0xE2, 0x54, 0xD8, 0x10, 0xCE,
+   0x7A, 0xE8, 0x08, 0x2C, 0x12, 0x97, 0x32, 0xAB, 0xB4, 0x27, 0x0A, 0x23, 0xDF, 0xEF, 0xCA, 0xD9,
+   0xB8, 0xFA, 0xDC, 0x31, 0x6B, 0xD1, 0xAD, 0x19, 0x49, 0xBD, 0x51, 0x96, 0xEE, 0xE4, 0xA8, 0x41,
+   0xDA, 0xFF, 0xCD, 0x55, 0x86, 0x36, 0xBE, 0x61, 0x52, 0xF8, 0xBB, 0x0E, 0x82, 0x48, 0x69, 0x9A,
+   0xE0, 0x47, 0x9E, 0x5C, 0x04, 0x4B, 0x34, 0x15, 0x79, 0x26, 0xA7, 0xDE, 0x29, 0xAE, 0x92, 0xD7,
+   0x84, 0xE9, 0xD2, 0xBA, 0x5D, 0xF3, 0xC5, 0xB0, 0xBF, 0xA4, 0x3B, 0x71, 0x44, 0x46, 0x2B, 0xFC,
+   0xEB, 0x6F, 0xD5, 0xF6, 0x14, 0xFE, 0x7C, 0x70, 0x5A, 0x7D, 0xFD, 0x2F, 0x18, 0x83, 0x16, 0xA5,
+   0x91, 0x1F, 0x05, 0x95, 0x74, 0xA9, 0xC1, 0x5B, 0x4A, 0x85, 0x6D, 0x13, 0x07, 0x4F, 0x4E, 0x45,
+   0xB2, 0x0F, 0xC9, 0x1C, 0xA6, 0xBC, 0xEC, 0x73, 0x90, 0x7B, 0xCF, 0x59, 0x8F, 0xA1, 0xF9, 0x2D,
+   0xF2, 0xB1, 0x00, 0x94, 0x37, 0x9F, 0xD0, 0x2E, 0x9C, 0x6E, 0x28, 0x3F, 0x80, 0xF0, 0x3D, 0xD3,
+   0x25, 0x8A, 0xB5, 0xE7, 0x42, 0xB3, 0xC7, 0xEA, 0xF7, 0x4C, 0x11, 0x33, 0x03, 0xA2, 0xAC, 0x60
+};
+
+//Key scheduling constants
+static const uint32_t c[12] =
+{
+   BETOH32(0x517CC1B7), BETOH32(0x27220A94), BETOH32(0xFE13ABE8), BETOH32(0xFA9A6EE0),
+   BETOH32(0x6DB14ACC), BETOH32(0x9E21C820), BETOH32(0xFF28B1D5), BETOH32(0xEF5DE2B0),
+   BETOH32(0xDB92371D), BETOH32(0x2126E970), BETOH32(0x03249775), BETOH32(0x04E8C90E)
+};
+
+//Common interface for encryption algorithms
+const CipherAlgo ariaCipherAlgo =
+{
+   "ARIA",
+   sizeof(AriaContext),
+   CIPHER_ALGO_TYPE_BLOCK,
+   ARIA_BLOCK_SIZE,
+   (CipherAlgoInit) ariaInit,
+   NULL,
+   NULL,
+   (CipherAlgoEncryptBlock) ariaEncryptBlock,
+   (CipherAlgoDecryptBlock) ariaDecryptBlock
+};
+
+
+/**
+ * @brief Odd round function
+ * @param[in,out] d 128-bit string
+ * @param[in] rk 128-bit string
+ **/
+
+static void OF(uint32_t *d, const uint32_t *rk)
+{
+   uint32_t t[4];
+
+   //XOR D with RK
+   XOR128(d, rk);
+   //Substitution layer SL1
+   SL1(t, d);
+   //Diffusion layer
+   A(d, t);
+}
+
+
+/**
+ * @brief Even round function
+ * @param[in,out] d 128-bit string
+ * @param[in] rk 128-bit string
+ **/
+
+static void EF(uint32_t *d, const uint32_t *rk)
+{
+   uint32_t t[4];
+
+   //XOR D with RK
+   XOR128(d, rk);
+   //Substitution layer SL2
+   SL2(t, d);
+   //Diffusion layer
+   A(d, t);
+}
+
+
+/**
+ * @brief Initialize a ARIA context using the supplied key
+ * @param[in] context Pointer to the ARIA context to initialize
+ * @param[in] key Pointer to the key
+ * @param[in] keyLength Length of the key
+ * @return Error code
+ **/
+
+error_t ariaInit(AriaContext *context, const uint8_t *key, size_t keyLength)
+{
+   uint_t i;
+   uint32_t *ek;
+   uint32_t *dk;
+   const uint32_t *ck1;
+   const uint32_t *ck2;
+   const uint32_t *ck3;
+   uint32_t w[16];
+
+   //128-bit master key?
+   if(keyLength == 16)
+   {
+      //Select the relevant constants
+      ck1 = c + 0;
+      ck2 = c + 4;
+      ck3 = c + 8;
+      //The number of rounds depends on the size of the master key
+      context->nr = 12;
+   }
+   //192-bit master key?
+   else if(keyLength == 24)
+   {
+      //Select the relevant constants
+      ck1 = c + 4;
+      ck2 = c + 8;
+      ck3 = c + 0;
+      //The number of rounds depends on the size of the master key
+      context->nr = 14;
+   }
+   //256-bit master key?
+   else if(keyLength == 32)
+   {
+      //Select the relevant constants
+      ck1 = c + 8;
+      ck2 = c + 0;
+      ck3 = c + 4;
+      //The number of rounds depends on the size of the master key
+      context->nr = 16;
+   }
+   else
+   {
+      //Report an error
+      return ERROR_INVALID_KEY_LENGTH;
+   }
+
+   //Compute 128-bit values KL and KR
+   memset(w, 0, sizeof(w));
+   memcpy(w, key, keyLength);
+
+   //Save KR...
+   MOV128(w + 8, w + 4);
+
+   //Compute intermediate values W0, W1, W2, and W3
+   MOV128(w + 4, w + 0);
+   OF(w + 4, ck1);
+   XOR128(w + 4, w + 8);
+
+   MOV128(w + 8, w + 4);
+   EF(w + 8, ck2);
+   XOR128(w + 8, w + 0);
+
+   MOV128(w + 12, w + 8);
+   OF(w + 12, ck3);
+   XOR128(w + 12, w + 4);
+
+   //Convert from big-endian byte order to host byte order
+   for(i = 0; i < 16; i++)
+      w[i] = betoh32(w[i]);
+
+   //Point to the encryption round keys
+   ek = context->ek;
+
+   //Compute ek1, ..., ek17 as follow
+   ROL128(ek + 0, w + 4, 109);
+   XOR128(ek + 0, w + 0);
+   ROL128(ek + 4, w + 8, 109);
+   XOR128(ek + 4, w + 4);
+   ROL128(ek + 8, w + 12, 109);
+   XOR128(ek + 8, w + 8);
+   ROL128(ek + 12, w + 0, 109);
+   XOR128(ek + 12, w + 12);
+   ROL128(ek + 16, w + 4, 97);
+   XOR128(ek + 16, w + 0);
+   ROL128(ek + 20, w + 8, 97);
+   XOR128(ek + 20, w + 4);
+   ROL128(ek + 24, w + 12, 97);
+   XOR128(ek + 24, w + 8);
+   ROL128(ek + 28, w + 0, 97);
+   XOR128(ek + 28, w + 12);
+   ROL128(ek + 32, w + 4, 61);
+   XOR128(ek + 32, w + 0);
+   ROL128(ek + 36, w + 8, 61);
+   XOR128(ek + 36, w + 4);
+   ROL128(ek + 40, w + 12, 61);
+   XOR128(ek + 40, w + 8);
+   ROL128(ek + 44, w + 0, 61);
+   XOR128(ek + 44, w + 12);
+   ROL128(ek + 48, w + 4, 31);
+   XOR128(ek + 48, w + 0);
+   ROL128(ek + 52, w + 8, 31);
+   XOR128(ek + 52, w + 4);
+   ROL128(ek + 56, w + 12, 31);
+   XOR128(ek + 56, w + 8);
+   ROL128(ek + 60, w + 0, 31);
+   XOR128(ek + 60, w + 12);
+   ROL128(ek + 64, w + 4, 19);
+   XOR128(ek + 64, w + 0);
+
+   //Convert from host byte order to big-endian byte order
+   for(i = 0; i < 68; i++)
+      ek[i] = htobe32(ek[i]);
+
+   //Decryption round keys are derived from the encryption round keys
+   dk = context->dk;
+   //Compute dk1
+   MOV128(dk + 0, ek + context->nr * 4);
+
+   //Compute dk2, ..., dk(n)
+   for(i = 1; i < context->nr; i++)
+      A(dk + i * 4, ek + (context->nr - i) * 4);
+
+   //Compute dk(n + 1)
+   MOV128(dk + i * 4, ek + 0);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Encrypt a 16-byte block using ARIA algorithm
+ * @param[in] context Pointer to the ARIA context
+ * @param[in] input Plaintext block to encrypt
+ * @param[out] output Ciphertext block resulting from encryption
+ **/
+
+void ariaEncryptBlock(AriaContext *context, const uint8_t *input, uint8_t *output)
+{
+   uint32_t *ek;
+   uint32_t p[4];
+   uint32_t q[4];
+
+   //Copy the plaintext to the buffer
+   memcpy(p, input, ARIA_BLOCK_SIZE);
+
+   //Point to the encryption round keys
+   ek = context->ek;
+
+   //Apply 11 rounds
+   OF(p, ek + 0);
+   EF(p, ek + 4);
+   OF(p, ek + 8);
+   EF(p, ek + 12);
+   OF(p, ek + 16);
+   EF(p, ek + 20);
+   OF(p, ek + 24);
+   EF(p, ek + 28);
+   OF(p, ek + 32);
+   EF(p, ek + 36);
+   OF(p, ek + 40);
+
+   //128-bit master keys require a total of 12 rounds
+   if(context->nr == 12)
+   {
+      XOR128(p, ek + 44);
+      SL2(q, p);
+      XOR128(q, ek + 48);
+   }
+   //192-bit master keys require a total of 14 rounds
+   else if(context->nr == 14)
+   {
+      EF(p, ek + 44);
+      OF(p, ek + 48);
+      XOR128(p, ek + 52);
+      SL2(q, p);
+      XOR128(q, ek + 56);
+   }
+   //256-bit master keys require a total of 16 rounds
+   else
+   {
+      EF(p, ek + 44);
+      OF(p, ek + 48);
+      EF(p, ek + 52);
+      OF(p, ek + 56);
+      XOR128(p, ek + 60);
+      SL2(q, p);
+      XOR128(q, ek + 64);
+   }
+
+   //Copy the resulting ciphertext from the buffer
+   memcpy(output, q, ARIA_BLOCK_SIZE);
+}
+
+
+/**
+ * @brief Decrypt a 16-byte block using ARIA algorithm
+ * @param[in] context Pointer to the ARIA context
+ * @param[in] input Ciphertext block to decrypt
+ * @param[out] output Plaintext block resulting from decryption
+ **/
+
+void ariaDecryptBlock(AriaContext *context, const uint8_t *input, uint8_t *output)
+{
+   uint32_t *dk;
+   uint32_t p[4];
+   uint32_t q[4];
+
+   //Copy the ciphertext to the buffer
+   memcpy(p, input, ARIA_BLOCK_SIZE);
+
+   //Point to the decryption round keys
+   dk = context->dk;
+
+   //Apply 11 rounds
+   OF(p, dk + 0);
+   EF(p, dk + 4);
+   OF(p, dk + 8);
+   EF(p, dk + 12);
+   OF(p, dk + 16);
+   EF(p, dk + 20);
+   OF(p, dk + 24);
+   EF(p, dk + 28);
+   OF(p, dk + 32);
+   EF(p, dk + 36);
+   OF(p, dk + 40);
+
+   //128-bit master keys require a total of 12 rounds
+   if(context->nr == 12)
+   {
+      XOR128(p, dk + 44);
+      SL2(q, p);
+      XOR128(q, dk + 48);
+   }
+   //192-bit master keys require a total of 14 rounds
+   else if(context->nr == 14)
+   {
+      EF(p, dk + 44);
+      OF(p, dk + 48);
+      XOR128(p, dk + 52);
+      SL2(q, p);
+      XOR128(q, dk + 56);
+   }
+   //256-bit master keys require a total of 16 rounds
+   else
+   {
+      EF(p, dk + 44);
+      OF(p, dk + 48);
+      EF(p, dk + 52);
+      OF(p, dk + 56);
+      XOR128(p, dk + 60);
+      SL2(q, p);
+      XOR128(q, dk + 64);
+   }
+
+   //The resulting value is the plaintext
+   memcpy(output, q, ARIA_BLOCK_SIZE);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/aria.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,63 @@
+/**
+ * @file aria.h
+ * @brief ARIA encryption algorithm
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _ARIA_H
+#define _ARIA_H
+
+//Dependencies
+#include "crypto.h"
+
+//ARIA block size
+#define ARIA_BLOCK_SIZE 16
+//Common interface for encryption algorithms
+#define ARIA_CIPHER_ALGO (&ariaCipherAlgo)
+
+
+/**
+ * @brief ARIA algorithm context
+ **/
+
+typedef struct
+{
+   uint_t nr;
+   uint32_t k[16];
+   uint32_t ek[68];
+   uint32_t dk[68];
+} AriaContext;
+
+
+//ARIA related constants
+extern const CipherAlgo ariaCipherAlgo;
+
+//ARIA related functions
+error_t ariaInit(AriaContext *context, const uint8_t *key, size_t keyLength);
+void ariaEncryptBlock(AriaContext *context, const uint8_t *input, uint8_t *output);
+void ariaDecryptBlock(AriaContext *context, const uint8_t *input, uint8_t *output);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/asn1.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,637 @@
+/**
+ * @file asn1.c
+ * @brief ASN.1 (Abstract Syntax Notation One)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "asn1.h"
+#include "oid.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (ASN1_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Read an ASN.1 tag from the input stream
+ * @param[in] data Input stream where to read the tag
+ * @param[in] length Number of bytes available in the input stream
+ * @param[out] tag Structure describing the ASN.1 tag
+ * @return Error code
+ **/
+
+error_t asn1ReadTag(const uint8_t *data, size_t length, Asn1Tag *tag)
+{
+   uint_t i;
+   uint_t n;
+
+   //Make sure the identifier octet is present
+   if(!length)
+      return ERROR_INVALID_TAG;
+
+   //Save the class of the ASN.1 tag
+   tag->objClass = data[0] & ASN1_CLASS_MASK;
+   //Primitive or constructed encoding?
+   tag->constructed = (data[0] & ASN1_ENCODING_CONSTRUCTED) ? TRUE : FALSE;
+
+   //Check the tag number
+   if((data[0] & ASN1_TAG_NUMBER_MASK) < 31)
+   {
+      //Tag number is in the range 0 to 30
+      tag->objType = data[0] & ASN1_TAG_NUMBER_MASK;
+      //Point to the tag length field
+      i = 1;
+   }
+   else
+   {
+      //If the tag number is greater than or equal to 31,
+      //the subsequent octets will encode the tag number
+      tag->objType = 0;
+
+      //Decode the tag number
+      for(i = 1; ; i++)
+      {
+         //The field cannot exceed 5 bytes
+         if(i > (sizeof(tag->objType) + 1))
+            return ERROR_INVALID_TAG;
+         //Insufficient number of bytes to decode the tag number?
+         if(!(length - i))
+            return ERROR_INVALID_TAG;
+
+         //Update the tag number with bits 7 to 1
+         tag->objType = (tag->objType << 7) | (data[i] & 0x7F);
+
+         //Bit 8 shall be set unless it is the last octet
+         if(!(data[i] & 0x80))
+            break;
+      }
+      //Point to the tag length field
+      i++;
+   }
+
+   //Insufficient number of bytes to decode the tag length?
+   if(!(length - i))
+      return ERROR_INVALID_TAG;
+
+   //Short form is used?
+   if(data[i] < 128)
+   {
+      //Bits 7 to 1 encode the number of bytes in the contents
+      tag->length = data[i];
+      //Point to the contents of the tag
+      i++;
+   }
+   //Long form is used?
+   else if(data[i] > 128 && data[i] < 255)
+   {
+      //Bits 7 to 1 encode the number of octets in the length field
+      n = data[i] & 0x7F;
+
+      //The field cannot exceed 4 bytes
+      if(n > sizeof(tag->length))
+         return ERROR_INVALID_TAG;
+      //Insufficient number of bytes to decode the tag length?
+      if((length - i) < n)
+         return ERROR_INVALID_TAG;
+
+      //Clear the tag length
+      tag->length = 0;
+      //Read the subsequent octets
+      for(i++; n > 0; n--)
+         tag->length = (tag->length << 8) | data[i++];
+   }
+   //Indefinite form is used?
+   else
+   {
+      //Indefinite form is not supported
+      return ERROR_INVALID_TAG;
+   }
+
+   //Save the pointer to the tag contents
+   tag->value = data + i;
+   //Check the length of tag
+   if((length - i) < tag->length)
+      return ERROR_INVALID_TAG;
+
+   //Total length occupied by the ASN.1 tag in the input stream
+   tag->totalLength = i + tag->length;
+   //ASN.1 tag successfully decoded
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Read an integer from the input stream
+ * @param[in] data Input stream where to read the tag
+ * @param[in] length Number of bytes available in the input stream
+ * @param[out] tag Structure describing the ASN.1 tag
+ * @param[out] value Integer value
+ * @return Error code
+ **/
+
+error_t asn1ReadInt32(const uint8_t *data, size_t length, Asn1Tag *tag, int32_t *value)
+{
+   error_t error;
+   size_t i;
+
+   //Read ASN.1 tag
+   error = asn1ReadTag(data, length, tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //The contents shall consist of one or more octets
+   if(tag->length < 1 || tag->length > 4)
+      return ERROR_INVALID_TAG;
+
+   //The contents octets shall be a two's complement binary
+   //number equal to the integer value
+   *value = (tag->value[0] & 0x80) ? -1 : 0;
+
+   //Process contents octets
+   for(i = 0; i < tag->length; i++)
+   {
+      //Rotate left operation
+      *value <<= 8;
+      //Reconstruct integer value
+      *value |= tag->value[i];
+   }
+
+   //ASN.1 tag successfully decoded
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write an ASN.1 tag
+ * @param[in] tag Structure describing the ASN.1 tag
+ * @param[in] reverse Use reverse encoding
+ * @param[out] data Output stream where to write the tag (optional parameter)
+ * @param[out] written Number of bytes written to the output stream (optional parameter)
+ * @return Error code
+ **/
+
+error_t asn1WriteTag(Asn1Tag *tag, bool_t reverse, uint8_t *data, size_t *written)
+{
+   size_t i;
+   size_t m;
+   size_t n;
+
+   //Compute the number of octets that are necessary to encode the tag number
+   if(tag->objType < 31)
+      m = 0;
+   else if(tag->objType < 128)
+      m = 1;
+   else if(tag->objType < 16384)
+      m = 2;
+   else if(tag->objType < 2097152)
+      m = 3;
+   else if(tag->objType < 268435456)
+      m = 4;
+   else
+      m = 5;
+
+   //Compute the number of octets that are necessary to encode the length field
+   if(tag->length < 128)
+      n = 0;
+   else if(tag->length < 256)
+      n = 1;
+   else if(tag->length < 65536)
+      n = 2;
+   else if(tag->length < 16777216)
+      n = 3;
+   else
+      n = 4;
+
+   //Valid output stream?
+   if(data != NULL)
+   {
+      //Use reverse encoding?
+      if(reverse)
+      {
+         //Any data to copy?
+         if(tag->value != NULL && tag->length > 0)
+         {
+            //Make room for the data
+            data -= tag->length;
+            //Copy data
+            memmove(data, tag->value, tag->length);
+         }
+
+         //Move backward
+         data -= m + n + 2;
+      }
+      else
+      {
+         //Any data to copy?
+         if(tag->value != NULL && tag->length > 0)
+         {
+            //Copy data
+            memmove(data + m + n + 2, tag->value, tag->length);
+         }
+      }
+
+      //Save the class of the ASN.1 tag
+      data[0] = tag->objClass;
+
+      //Primitive or constructed encoding?
+      if(tag->constructed)
+         data[0] |= ASN1_ENCODING_CONSTRUCTED;
+
+      //Encode the tag number
+      if(m == 0)
+      {
+         //Tag number is in the range 0 to 30
+         data[0] |= tag->objType;
+      }
+      else
+      {
+         //The tag number is greater than or equal to 31
+         data[0] |= ASN1_TAG_NUMBER_MASK;
+
+         //The subsequent octets will encode the tag number
+         for(i = 0; i < m; i++)
+         {
+            //Bits 7 to 1 encode the tag number
+            data[m - i] = (tag->objType >> (i * 7)) & 0x7F;
+
+            //Bit 8 of each octet shall be set to one unless it is the
+            //last octet of the identifier octets
+            if(i != 0)
+               data[m - i] |= 0x80;
+         }
+      }
+
+      //Encode the length field
+      if(n == 0)
+      {
+         //Use short form encoding
+         data[1 + m] = tag->length & 0x7F;
+      }
+      else
+      {
+         //Bits 7 to 1 encode the number of octets in the length field
+         data[1 + m] = 0x80 | (n & 0x7F);
+
+         //The subsequent octets will encode the length field
+         for(i = 0; i < n; i++)
+         {
+            data[1 + m + n - i] = (tag->length >> (i * 8)) & 0xFF;
+         }
+      }
+   }
+
+   //Total length occupied by the ASN.1 tag
+   tag->totalLength = tag->length + m + n + 2;
+
+   //The last parameter is optional
+   if(written != NULL)
+   {
+      //Number of bytes written to the output stream
+      *written = m + n + 2;
+
+      //Any data copied?
+      if(tag->value != NULL)
+         *written += tag->length;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write an integer to the output stream
+ * @param[in] value Integer value
+ * @param[in] reverse Use reverse encoding
+ * @param[out] data Output stream where to write the tag (optional parameter)
+ * @param[out] written Number of bytes written to the output stream
+ * @return Error code
+ **/
+
+error_t asn1WriteInt32(int32_t value, bool_t reverse, uint8_t *data, size_t *written)
+{
+   size_t i;
+   size_t n;
+   uint16_t msb;
+
+   //An integer value is always encoded in the smallest possible number of octets
+   for(n = 4; n > 1; n--)
+   {
+      //Retrieve the upper 9 bits
+      msb = (value >> (n * 8 - 9)) & 0x01FF;
+
+      //The upper 9 bits shall not have the same value (all 0 or all 1)
+      if(msb != 0x0000 && msb != 0x01FF)
+         break;
+   }
+
+   //Valid output stream?
+   if(data != NULL)
+   {
+      //Use reverse encoding?
+      if(reverse)
+         data -= n + 2;
+
+      //Write tag type
+      data[0] = ASN1_CLASS_UNIVERSAL | ASN1_TYPE_INTEGER;
+      //Write tag length
+      data[1] = n & 0xFF;
+
+      //Write contents octets
+      for(i = 0; i < n; i++)
+      {
+         data[1 + n - i] = (value >> (i * 8)) & 0xFF;
+      }
+   }
+
+   //Number of bytes written to the output stream
+   if(written != NULL)
+      *written = n + 2;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Enforce the type of a specified tag
+ * @param[in] tag Pointer to an ASN.1 tag
+ * @param[in] constructed Expected encoding (TRUE for constructed, FALSE for primitive)
+ * @param[in] objClass Expected tag class
+ * @param[in] objType Expected tag type
+ * @return Error code
+ **/
+
+error_t asn1CheckTag(const Asn1Tag *tag, bool_t constructed, uint_t objClass, uint_t objType)
+{
+   //Check encoding
+   if(tag->constructed != constructed)
+      return ERROR_WRONG_ENCODING;
+   //Enforce class
+   if(tag->objClass != objClass)
+      return ERROR_INVALID_CLASS;
+   //Enforce type
+   if(tag->objType != objType)
+      return ERROR_INVALID_TYPE;
+
+   //The tag matches all the criteria
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Check ASN.1 tag against a specified OID
+ * @param[in] tag Pointer to an ASN.1 tag
+ * @param[in] oid Expected object identifier (OID)
+ * @param[in] length Length of the OID
+ * @return Error code
+ **/
+
+error_t asn1CheckOid(const Asn1Tag *tag, const uint8_t *oid, size_t length)
+{
+   error_t error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OBJECT_IDENTIFIER);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Compare OID against the specified value
+   if(oidComp(tag->value, tag->length, oid, length))
+      return ERROR_WRONG_IDENTIFIER;
+
+   //The tag matches all the criteria
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Display an ASN.1 data object
+ * @param[in] data Pointer to the ASN.1 object to dump
+ * @param[in] length Length of the ASN.1 object
+ * @param[in] level Current level of recursion (this parameter shall be set to 0)
+ * @return Error code
+ **/
+
+error_t asn1DumpObject(const uint8_t *data, size_t length, uint_t level)
+{
+//Check debugging level
+#if (TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+   error_t error;
+   uint_t i;
+   Asn1Tag tag;
+
+   //ASN.1 universal types
+   static const char_t *label[32] =
+   {
+      "[0]",
+      "BOOLEAN",
+      "INTEGER",
+      "BIT STRING",
+      "OCTET STRING",
+      "NULL",
+      "OBJECT IDENTIFIER",
+      "OBJECT DESCRIPTOR",
+      "EXTERNAL",
+      "REAL",
+      "ENUMERATED",
+      "[11]",
+      "UTF8 STRING",
+      "[13]",
+      "[14]",
+      "[15]",
+      "SEQUENCE",
+      "SET",
+      "NUMERIC STRING",
+      "PRINTABLE STRING",
+      "TELETEX STRING",
+      "VIDEOTEX STRING",
+      "IA5 STRING",
+      "UTC TIME",
+      "GENERALIZED TIME",
+      "GRAPHIC STRING",
+      "VISIBLE STRING",
+      "GENERAL STRING",
+      "UNIVERSAL STRING",
+      "[29]",
+      "BMP STRING",
+      "[31]"
+   };
+
+   //Prefix used to format the structure
+   static const char_t *prefix[8] =
+   {
+      "",
+      "  ",
+      "    ",
+      "      ",
+      "        ",
+      "          ",
+      "            ",
+      "              "
+   };
+
+   //Parse ASN.1 object
+   while(length > 0)
+   {
+      //Decode current ASN.1 tag
+      error = asn1ReadTag(data, length, &tag);
+      //Decoding failed?
+      if(error)
+         return error;
+
+      //Point to the next field
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Dump tag number, tag class, and contents length fields
+      if(tag.objType < 32 && (tag.objClass & ASN1_CLASS_MASK) == ASN1_CLASS_UNIVERSAL)
+         TRACE_DEBUG("%s%s (%" PRIuSIZE " bytes)\r\n", prefix[level], label[tag.objType], tag.length);
+      else
+         TRACE_DEBUG("%s[%u] (%" PRIuSIZE " bytes)\r\n", prefix[level], tag.objType, tag.length);
+
+      //Constructed type?
+      if(tag.constructed)
+      {
+         //Check whether the maximum level of recursion is reached
+         if(level < 7)
+         {
+            //Recursive decoding of the ASN.1 tag
+            error = asn1DumpObject(tag.value, tag.length, level + 1);
+            //Decoding failed?
+            if(error)
+               return error;
+         }
+         else
+         {
+            //If the maximum level of recursion is reached, then dump contents
+            TRACE_DEBUG_ARRAY(prefix[level + 1], tag.value, tag.length);
+         }
+      }
+      //Primitive type?
+      else
+      {
+         //Check the type of the current tag
+         switch(tag.objType)
+         {
+         //OID?
+         case ASN1_TYPE_OBJECT_IDENTIFIER:
+            //Append prefix
+            TRACE_DEBUG(prefix[level + 1]);
+            //Print OID
+            TRACE_DEBUG("%s", oidToString(tag.value, tag.length, NULL, 0));
+            //Add a line feed
+            TRACE_DEBUG("\r\n");
+            break;
+         //String?
+         case ASN1_TYPE_UTF8_STRING:
+         case ASN1_TYPE_NUMERIC_STRING:
+         case ASN1_TYPE_PRINTABLE_STRING:
+         case ASN1_TYPE_TELETEX_STRING:
+         case ASN1_TYPE_VIDEOTEX_STRING:
+         case ASN1_TYPE_IA5_STRING:
+         case ASN1_TYPE_GRAPHIC_STRING:
+         case ASN1_TYPE_VISIBLE_STRING:
+         case ASN1_TYPE_GENERAL_STRING:
+         case ASN1_TYPE_UNIVERSAL_STRING:
+         case ASN1_TYPE_BMP_STRING:
+            //Append prefix
+            TRACE_DEBUG("%s", prefix[level + 1]);
+            //Dump the entire string
+            for(i = 0; i < tag.length; i++)
+               TRACE_DEBUG("%c", tag.value[i]);
+            //Add a line feed
+            TRACE_DEBUG("\r\n");
+            break;
+         //UTC time?
+         case ASN1_TYPE_UTC_TIME:
+            //Check length
+            if(tag.length < 13)
+               return ERROR_WRONG_ENCODING;
+            //The encoding shall terminate with a "Z"
+            if(tag.value[tag.length - 1] != 'Z')
+               return ERROR_WRONG_ENCODING;
+
+            //Append prefix
+            TRACE_DEBUG("%s", prefix[level + 1]);
+            //Display date
+            TRACE_DEBUG("%c%c/%c%c/%c%c ", tag.value[0], tag.value[1],
+               tag.value[2], tag.value[3], tag.value[4], tag.value[5]);
+            //Display time
+            TRACE_DEBUG("%c%c:%c%c:%c%c", tag.value[6], tag.value[7],
+               tag.value[8], tag.value[9], tag.value[10], tag.value[11]);
+            //Add a line feed
+            TRACE_DEBUG("\r\n");
+            break;
+         //Generalized time?
+         case ASN1_TYPE_GENERALIZED_TIME:
+            //Check length
+            if(tag.length < 13)
+               return ERROR_WRONG_ENCODING;
+            //The encoding shall terminate with a "Z"
+            if(tag.value[tag.length - 1] != 'Z')
+               return ERROR_WRONG_ENCODING;
+
+            //Append prefix
+            TRACE_DEBUG("%s", prefix[level + 1]);
+            //Display date
+            TRACE_DEBUG("%c%c%c%c/%c%c/%c%c ", tag.value[0], tag.value[1], tag.value[2],
+               tag.value[3], tag.value[4], tag.value[5], tag.value[6], tag.value[7]);
+            //Display time
+            TRACE_DEBUG("%c%c:%c%c:%c%c", tag.value[8], tag.value[9],
+               tag.value[10], tag.value[11], tag.value[12], tag.value[13]);
+            //Add a line feed
+            TRACE_DEBUG("\r\n");
+            break;
+         //Any other type?
+         default:
+            //Dump the contents of the tag
+            TRACE_DEBUG_ARRAY(prefix[level + 1], tag.value, tag.length);
+            break;
+         }
+      }
+   }
+#endif
+
+   //ASN.1 object successfully decoded
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/asn1.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,113 @@
+/**
+ * @file asn1.h
+ * @brief ASN.1 (Abstract Syntax Notation One)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _ASN1_H
+#define _ASN1_H
+
+//Dependencies
+#include "crypto.h"
+
+//Tag number mask
+#define ASN1_TAG_NUMBER_MASK        0x1F
+
+//ASN.1 encoding
+#define ASN1_ENCODING_MASK          0x20
+#define ASN1_ENCODING_PRIMITIVE     0x00
+#define ASN1_ENCODING_CONSTRUCTED   0x20
+
+//ASN.1 class
+#define ASN1_CLASS_MASK             0xC0
+#define ASN1_CLASS_UNIVERSAL        0x00
+#define ASN1_CLASS_APPLICATION      0x40
+#define ASN1_CLASS_CONTEXT_SPECIFIC 0x80
+#define ASN1_CLASS_PRIVATE          0xC0
+
+
+/**
+ * @brief ASN.1 data types
+ **/
+
+typedef enum
+{
+   ASN1_TYPE_BOOLEAN           = 1,
+   ASN1_TYPE_INTEGER           = 2,
+   ASN1_TYPE_BIT_STRING        = 3,
+   ASN1_TYPE_OCTET_STRING      = 4,
+   ASN1_TYPE_NULL              = 5,
+   ASN1_TYPE_OBJECT_IDENTIFIER = 6,
+   ASN1_TYPE_OBJECT_DESCRIPTOR = 7,
+   ASN1_TYPE_EXTERNAL          = 8,
+   ASN1_TYPE_REAL              = 9,
+   ASN1_TYPE_ENUMERATED        = 10,
+   ASN1_TYPE_UTF8_STRING       = 12,
+   ASN1_TYPE_SEQUENCE          = 16,
+   ASN1_TYPE_SET               = 17,
+   ASN1_TYPE_NUMERIC_STRING    = 18,
+   ASN1_TYPE_PRINTABLE_STRING  = 19,
+   ASN1_TYPE_TELETEX_STRING    = 20,
+   ASN1_TYPE_VIDEOTEX_STRING   = 21,
+   ASN1_TYPE_IA5_STRING        = 22,
+   ASN1_TYPE_UTC_TIME          = 23,
+   ASN1_TYPE_GENERALIZED_TIME  = 24,
+   ASN1_TYPE_GRAPHIC_STRING    = 25,
+   ASN1_TYPE_VISIBLE_STRING    = 26,
+   ASN1_TYPE_GENERAL_STRING    = 27,
+   ASN1_TYPE_UNIVERSAL_STRING  = 28,
+   ASN1_TYPE_BMP_STRING        = 30,
+} Asn1Type;
+
+
+/**
+ * @brief ASN.1 tag
+ **/
+
+typedef struct
+{
+   bool_t constructed;
+   uint_t objClass;
+   uint_t objType;
+   size_t length;
+   const uint8_t *value;
+   size_t totalLength;
+} Asn1Tag;
+
+
+//ASN.1 related functions
+error_t asn1ReadTag(const uint8_t *data, size_t length, Asn1Tag *tag);
+error_t asn1ReadInt32(const uint8_t *data, size_t length, Asn1Tag *tag, int32_t *value);
+
+error_t asn1WriteTag(Asn1Tag *tag, bool_t reverse, uint8_t *data, size_t *written);
+error_t asn1WriteInt32(int32_t value, bool_t reverse, uint8_t *data, size_t *written);
+
+error_t asn1CheckTag(const Asn1Tag *tag, bool_t constructed, uint_t objClass, uint_t objType);
+error_t asn1CheckOid(const Asn1Tag *tag, const uint8_t *oid, size_t length);
+
+error_t asn1DumpObject(const uint8_t *data, size_t length, uint_t level);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/base64.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,206 @@
+/**
+ * @file base64.c
+ * @brief Base64 encoding scheme
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * Base64 is a encoding scheme that represents binary data in an ASCII string
+ * format by translating it into a radix-64 representation. Refer to RFC 4648
+ * for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include "crypto.h"
+#include "base64.h"
+
+//Check crypto library configuration
+#if (BASE64_SUPPORT == ENABLED)
+
+//Base64 encoding table
+static const char_t base64EncTable[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', '+', '/'
+};
+
+//Base64 decoding table
+static const uint8_t base64DecTable[128] =
+{
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F,
+   0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 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, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+   0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+   0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+
+/**
+ * @brief Base64 encoding algorithm
+ * @param[in] input Input data to encode
+ * @param[in] inputLength Length of the data to encode
+ * @param[out] output NULL-terminated string encoded with Base64 algorithm
+ * @param[out] outputLength Length of the encoded string (optional parameter)
+ **/
+
+void base64Encode(const void *input, size_t inputLength, char_t *output, size_t *outputLength)
+{
+   size_t i;
+   const uint8_t *p;
+
+   //Point to the first byte of the input stream
+   p = input;
+   //Length of the encoded string
+   i = 0;
+
+   //Divide the input stream into blocks of 3 bytes
+   while(inputLength >= 3)
+   {
+      //Map each 3-byte block to 4 printable characters using the Base64 character set
+      output[i++] = base64EncTable[(p[0] & 0xFC) >> 2];
+      output[i++] = base64EncTable[((p[0] & 0x03) << 4) | ((p[1] & 0xF0) >> 4)];
+      output[i++] = base64EncTable[((p[1] & 0x0F) << 2) | ((p[2] & 0xC0) >> 6)];
+      output[i++] = base64EncTable[p[2] & 0x3F];
+      //Point to the next 3-byte block
+      p += 3;
+      //Remaining bytes to process
+      inputLength -= 3;
+   }
+
+   //The last block contains only 1 byte?
+   if(inputLength == 1)
+   {
+      output[i++] = base64EncTable[(p[0] & 0xFC) >> 2];
+      output[i++] = base64EncTable[(p[0] & 0x03) << 4];
+      output[i++] = '=';
+      output[i++] = '=';
+   }
+   //The last block contains only 2 bytes?
+   else if(inputLength == 2)
+   {
+      output[i++] = base64EncTable[(p[0] & 0xFC) >> 2];
+      output[i++] = base64EncTable[((p[0] & 0x03) << 4) | ((p[1] & 0xF0) >> 4)];
+      output[i++] = base64EncTable[(p[1] & 0x0F) << 2];
+      output[i++] = '=';
+   }
+
+   //Properly terminate the resulting string
+   output[i] = '\0';
+
+   //Return the length of the encoded string (excluding the terminating NULL)
+   if(outputLength != NULL)
+      *outputLength = i;
+}
+
+
+/**
+ * @brief Base64 decoding algorithm
+ * @param[in] input Base64 encoded string
+ * @param[in] inputLength Length of the encoded string
+ * @param[out] output Resulting decoded data
+ * @param[out] outputLength Length of the decoded data
+ * @return Error code
+ **/
+
+error_t base64Decode(const char_t *input, size_t inputLength, void *output, size_t *outputLength)
+{
+   size_t i;
+   size_t j;
+   uint32_t value;
+   uint8_t *p;
+
+   //Point to the first byte of the output stream
+   p = output;
+   //Length of the decoded stream
+   i = 0;
+
+   //The length of the string to decode must be a multiple of 4
+   if(inputLength % 4)
+      return ERROR_INVALID_LENGTH;
+
+   //Process the Base64 encoded string
+   while(inputLength >= 4)
+   {
+      //Divide the input stream into blocks of 4 characters
+      for(value = 0, j = 0; j < 4; j++)
+      {
+         //The "==" sequence indicates that the last block contains only 1 byte
+         if(inputLength == 2 && input[0] == '=' && input[1] == '=')
+         {
+            //Decode the last byte
+            p[i++] = (value >> 4) & 0xFF;
+            //Return the length of the decoded data
+            *outputLength = i;
+            //Decoding is now complete
+            return NO_ERROR;
+         }
+         //The "=" sequence indicates that the last block contains only 2 bytes
+         else if(inputLength == 1 && *input == '=')
+         {
+            //Decode the last two bytes
+            p[i++] = (value >> 10) & 0xFF;
+            p[i++] = (value >> 2) & 0xFF;
+            //Return the length of the decoded data
+            *outputLength = i;
+            //Decoding is now complete
+            return NO_ERROR;
+         }
+         //Ensure the current character belongs to the Base64 character set
+         else if(((uint8_t) *input) > 127 || base64DecTable[(uint8_t) *input] > 63)
+         {
+            //Decoding failed
+            return ERROR_INVALID_CHARACTER;
+         }
+
+         //Decode the current character
+         value = (value << 6) | base64DecTable[(uint8_t) *input];
+         //Point to the next character to decode
+         input++;
+         //Remaining bytes to process
+         inputLength--;
+      }
+
+      //Map each 4-character block to 3 bytes
+      p[i++] = (value >> 16) & 0xFF;
+      p[i++] = (value >> 8) & 0xFF;
+      p[i++] = value  & 0xFF;
+   }
+
+   //Return the length of the decoded data
+   *outputLength = i;
+   //Decoding is now complete
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/base64.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,40 @@
+/**
+ * @file base64.h
+ * @brief Base64 encoding scheme
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _BASE64_H
+#define _BASE64_H
+
+//Dependencies
+#include "crypto.h"
+
+//Base64 encoding related functions
+void base64Encode(const void *input, size_t inputLength, char_t *output, size_t *outputLength);
+error_t base64Decode(const char_t *input, size_t inputLength, void *output, size_t *outputLength);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/camellia.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,544 @@
+/**
+ * @file camellia.c
+ * @brief Camellia encryption algorithm
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * Camellia is an encryption algorithm designed to encipher and decipher
+ * blocks of 128 bits under control of a 128/192/256-bit secret key
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "camellia.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (CAMELLIA_SUPPORT == ENABLED)
+
+//Camellia round function
+#define CAMELLIA_ROUND(left1, left2, right1, right2, k1, k2) \
+{ \
+   temp1 = left1 ^ k1; \
+   temp2 = left2 ^ k2; \
+   CAMELLIA_S(temp1, temp2); \
+   CAMELLIA_P(temp1, temp2); \
+   temp1 ^= right2; \
+   temp2 ^= right1; \
+   right1 = left1; \
+   right2 = left2; \
+   left1 = temp2; \
+   left2 = temp1; \
+}
+
+//F-function
+#define CAMELLIA_F(xl, xr, kl, kr) \
+{ \
+   xl = xl ^ kl; \
+   xl = xr ^ kr; \
+   CAMELLIA_S(xl, xr); \
+   CAMELLIA_P(xl, xr); \
+}
+
+//FL-function
+#define CAMELLIA_FL(xl, xr, kl, kr) \
+{ \
+   temp1 = (xl & kl); \
+   xr ^= ROL32(temp1, 1); \
+   xl ^= (xr | kr); \
+}
+
+//Inverse FL-function
+#define CAMELLIA_INV_FL(yl, yr, kl, kr) \
+{ \
+   yl ^= (yr | kr); \
+   temp1 = (yl & kl); \
+   yr ^= ROL32(temp1, 1); \
+}
+
+//S-function
+#define CAMELLIA_S(zl, zr) \
+{ \
+   zl = (sbox1[(zl >> 24) & 0xFF] << 24) | (sbox2[(zl >> 16) & 0xFF] << 16) | \
+      (sbox3[(zl >> 8) & 0xFF] << 8) | sbox4[zl & 0xFF]; \
+   zr = (sbox2[(zr >> 24) & 0xFF] << 24) | (sbox3[(zr >> 16) & 0xFF] << 16) | \
+      (sbox4[(zr >> 8) & 0xFF] << 8) | sbox1[zr & 0xFF]; \
+}
+
+//P-function
+#define CAMELLIA_P(zl, zr) \
+{ \
+   zl ^= ROL32(zr, 8); \
+   zr ^= ROL32(zl, 16); \
+   zl ^= ROR32(zr, 8); \
+   zr ^= ROR32(zl, 8); \
+}
+
+//Key schedule related constants
+#define KL 0
+#define KR 4
+#define KA 8
+#define KB 12
+#define L  0
+#define R  64
+
+//Key schedule for 128-bit key
+static const CamelliaSubkey ks1[] =
+{
+   {0,  KL, 0,  L},  //kw1
+   {2,  KL, 0,  R},  //kw2
+   {4,  KA, 0,  L},  //k1
+   {6,  KA, 0,  R},  //k2
+   {8,  KL, 15, L},  //k3
+   {10, KL, 15, R},  //k4
+   {12, KA, 15, L},  //k5
+   {14, KA, 15, R},  //k6
+   {16, KA, 30, L},  //ke1
+   {18, KA, 30, R},  //ke2
+   {20, KL, 45, L},  //k7
+   {22, KL, 45, R},  //k8
+   {24, KA, 45, L},  //k9
+   {26, KL, 60, R},  //k10
+   {28, KA, 60, L},  //k11
+   {30, KA, 60, R},  //k12
+   {32, KL, 77, L},  //ke3
+   {34, KL, 77, R},  //ke4
+   {36, KL, 94, L},  //k13
+   {38, KL, 94, R},  //k14
+   {40, KA, 94, L},  //k15
+   {42, KA, 94, R},  //k16
+   {44, KL, 111, L}, //k17
+   {46, KL, 111, R}, //k18
+   {48, KA, 111, L}, //kw3
+   {50, KA, 111, R}, //kw4
+};
+
+//Key schedule for 192 and 256-bit keys
+static const CamelliaSubkey ks2[] =
+{
+   {0,  KL, 0,  L},  //kw1
+   {2,  KL, 0,  R},  //k2
+   {4,  KB, 0,  L},  //k1
+   {6,  KB, 0,  R},  //k2
+   {8,  KR, 15, L},  //k3
+   {10, KR, 15, R},  //k4
+   {12, KA, 15, L},  //k5
+   {14, KA, 15, R},  //k6
+   {16, KR, 30, L},  //ke1
+   {18, KR, 30, R},  //ke2
+   {20, KB, 30, L},  //k7
+   {22, KB, 30, R},  //k8
+   {24, KL, 45, L},  //k9
+   {26, KL, 45, R},  //k10
+   {28, KA, 45, L},  //k11
+   {30, KA, 45, R},  //k12
+   {32, KL, 60, L},  //ke3
+   {34, KL, 60, R},  //ke4
+   {36, KR, 60, L},  //k13
+   {38, KR, 60, R},  //k14
+   {40, KB, 60, L},  //k15
+   {42, KB, 60, R},  //k16
+   {44, KL, 77, L},  //k17
+   {46, KL, 77, R},  //k18
+   {48, KA, 77, L},  //ke5
+   {50, KA, 77, R},  //ke6
+   {52, KR, 94, L},  //k19
+   {54, KR, 94, R},  //k20
+   {56, KA, 94, L},  //k21
+   {58, KA, 94, R},  //k22
+   {60, KL, 111, L}, //k23
+   {62, KL, 111, R}, //k24
+   {64, KB, 111, L}, //kw3
+   {66, KB, 111, R}, //kw4
+};
+
+//Key schedule constants
+static const uint32_t sigma[12] =
+{
+   0xA09E667F, 0x3BCC908B,
+   0xB67AE858, 0x4CAA73B2,
+   0xC6EF372F, 0xE94F82BE,
+   0x54FF53A5, 0xF1D36F1C,
+   0x10E527FA, 0xDE682D1D,
+   0xB05688C2, 0xB3E6C1FD
+};
+
+//Substitution table 1
+static const uint8_t sbox1[256] =
+{
+   0x70, 0x82, 0x2C, 0xEC, 0xB3, 0x27, 0xC0, 0xE5, 0xE4, 0x85, 0x57, 0x35, 0xEA, 0x0C, 0xAE, 0x41,
+   0x23, 0xEF, 0x6B, 0x93, 0x45, 0x19, 0xA5, 0x21, 0xED, 0x0E, 0x4F, 0x4E, 0x1D, 0x65, 0x92, 0xBD,
+   0x86, 0xB8, 0xAF, 0x8F, 0x7C, 0xEB, 0x1F, 0xCE, 0x3E, 0x30, 0xDC, 0x5F, 0x5E, 0xC5, 0x0B, 0x1A,
+   0xA6, 0xE1, 0x39, 0xCA, 0xD5, 0x47, 0x5D, 0x3D, 0xD9, 0x01, 0x5A, 0xD6, 0x51, 0x56, 0x6C, 0x4D,
+   0x8B, 0x0D, 0x9A, 0x66, 0xFB, 0xCC, 0xB0, 0x2D, 0x74, 0x12, 0x2B, 0x20, 0xF0, 0xB1, 0x84, 0x99,
+   0xDF, 0x4C, 0xCB, 0xC2, 0x34, 0x7E, 0x76, 0x05, 0x6D, 0xB7, 0xA9, 0x31, 0xD1, 0x17, 0x04, 0xD7,
+   0x14, 0x58, 0x3A, 0x61, 0xDE, 0x1B, 0x11, 0x1C, 0x32, 0x0F, 0x9C, 0x16, 0x53, 0x18, 0xF2, 0x22,
+   0xFE, 0x44, 0xCF, 0xB2, 0xC3, 0xB5, 0x7A, 0x91, 0x24, 0x08, 0xE8, 0xA8, 0x60, 0xFC, 0x69, 0x50,
+   0xAA, 0xD0, 0xA0, 0x7D, 0xA1, 0x89, 0x62, 0x97, 0x54, 0x5B, 0x1E, 0x95, 0xE0, 0xFF, 0x64, 0xD2,
+   0x10, 0xC4, 0x00, 0x48, 0xA3, 0xF7, 0x75, 0xDB, 0x8A, 0x03, 0xE6, 0xDA, 0x09, 0x3F, 0xDD, 0x94,
+   0x87, 0x5C, 0x83, 0x02, 0xCD, 0x4A, 0x90, 0x33, 0x73, 0x67, 0xF6, 0xF3, 0x9D, 0x7F, 0xBF, 0xE2,
+   0x52, 0x9B, 0xD8, 0x26, 0xC8, 0x37, 0xC6, 0x3B, 0x81, 0x96, 0x6F, 0x4B, 0x13, 0xBE, 0x63, 0x2E,
+   0xE9, 0x79, 0xA7, 0x8C, 0x9F, 0x6E, 0xBC, 0x8E, 0x29, 0xF5, 0xF9, 0xB6, 0x2F, 0xFD, 0xB4, 0x59,
+   0x78, 0x98, 0x06, 0x6A, 0xE7, 0x46, 0x71, 0xBA, 0xD4, 0x25, 0xAB, 0x42, 0x88, 0xA2, 0x8D, 0xFA,
+   0x72, 0x07, 0xB9, 0x55, 0xF8, 0xEE, 0xAC, 0x0A, 0x36, 0x49, 0x2A, 0x68, 0x3C, 0x38, 0xF1, 0xA4,
+   0x40, 0x28, 0xD3, 0x7B, 0xBB, 0xC9, 0x43, 0xC1, 0x15, 0xE3, 0xAD, 0xF4, 0x77, 0xC7, 0x80, 0x9E
+};
+
+//Substitution table 2
+static const uint8_t sbox2[256] =
+{
+   0xE0, 0x05, 0x58, 0xD9, 0x67, 0x4E, 0x81, 0xCB, 0xC9, 0x0B, 0xAE, 0x6A, 0xD5, 0x18, 0x5D, 0x82,
+   0x46, 0xDF, 0xD6, 0x27, 0x8A, 0x32, 0x4B, 0x42, 0xDB, 0x1C, 0x9E, 0x9C, 0x3A, 0xCA, 0x25, 0x7B,
+   0x0D, 0x71, 0x5F, 0x1F, 0xF8, 0xD7, 0x3E, 0x9D, 0x7C, 0x60, 0xB9, 0xBE, 0xBC, 0x8B, 0x16, 0x34,
+   0x4D, 0xC3, 0x72, 0x95, 0xAB, 0x8E, 0xBA, 0x7A, 0xB3, 0x02, 0xB4, 0xAD, 0xA2, 0xAC, 0xD8, 0x9A,
+   0x17, 0x1A, 0x35, 0xCC, 0xF7, 0x99, 0x61, 0x5A, 0xE8, 0x24, 0x56, 0x40, 0xE1, 0x63, 0x09, 0x33,
+   0xBF, 0x98, 0x97, 0x85, 0x68, 0xFC, 0xEC, 0x0A, 0xDA, 0x6F, 0x53, 0x62, 0xA3, 0x2E, 0x08, 0xAF,
+   0x28, 0xB0, 0x74, 0xC2, 0xBD, 0x36, 0x22, 0x38, 0x64, 0x1E, 0x39, 0x2C, 0xA6, 0x30, 0xE5, 0x44,
+   0xFD, 0x88, 0x9F, 0x65, 0x87, 0x6B, 0xF4, 0x23, 0x48, 0x10, 0xD1, 0x51, 0xC0, 0xF9, 0xD2, 0xA0,
+   0x55, 0xA1, 0x41, 0xFA, 0x43, 0x13, 0xC4, 0x2F, 0xA8, 0xB6, 0x3C, 0x2B, 0xC1, 0xFF, 0xC8, 0xA5,
+   0x20, 0x89, 0x00, 0x90, 0x47, 0xEF, 0xEA, 0xB7, 0x15, 0x06, 0xCD, 0xB5, 0x12, 0x7E, 0xBB, 0x29,
+   0x0F, 0xB8, 0x07, 0x04, 0x9B, 0x94, 0x21, 0x66, 0xE6, 0xCE, 0xED, 0xE7, 0x3B, 0xFE, 0x7F, 0xC5,
+   0xA4, 0x37, 0xB1, 0x4C, 0x91, 0x6E, 0x8D, 0x76, 0x03, 0x2D, 0xDE, 0x96, 0x26, 0x7D, 0xC6, 0x5C,
+   0xD3, 0xF2, 0x4F, 0x19, 0x3F, 0xDC, 0x79, 0x1D, 0x52, 0xEB, 0xF3, 0x6D, 0x5E, 0xFB, 0x69, 0xB2,
+   0xF0, 0x31, 0x0C, 0xD4, 0xCF, 0x8C, 0xE2, 0x75, 0xA9, 0x4A, 0x57, 0x84, 0x11, 0x45, 0x1B, 0xF5,
+   0xE4, 0x0E, 0x73, 0xAA, 0xF1, 0xDD, 0x59, 0x14, 0x6C, 0x92, 0x54, 0xD0, 0x78, 0x70, 0xE3, 0x49,
+   0x80, 0x50, 0xA7, 0xF6, 0x77, 0x93, 0x86, 0x83, 0x2A, 0xC7, 0x5B, 0xE9, 0xEE, 0x8F, 0x01, 0x3D
+};
+
+//Substitution table 3
+static const uint8_t sbox3[256] =
+{
+   0x38, 0x41, 0x16, 0x76, 0xD9, 0x93, 0x60, 0xF2, 0x72, 0xC2, 0xAB, 0x9A, 0x75, 0x06, 0x57, 0xA0,
+   0x91, 0xF7, 0xB5, 0xC9, 0xA2, 0x8C, 0xD2, 0x90, 0xF6, 0x07, 0xA7, 0x27, 0x8E, 0xB2, 0x49, 0xDE,
+   0x43, 0x5C, 0xD7, 0xC7, 0x3E, 0xF5, 0x8F, 0x67, 0x1F, 0x18, 0x6E, 0xAF, 0x2F, 0xE2, 0x85, 0x0D,
+   0x53, 0xF0, 0x9C, 0x65, 0xEA, 0xA3, 0xAE, 0x9E, 0xEC, 0x80, 0x2D, 0x6B, 0xA8, 0x2B, 0x36, 0xA6,
+   0xC5, 0x86, 0x4D, 0x33, 0xFD, 0x66, 0x58, 0x96, 0x3A, 0x09, 0x95, 0x10, 0x78, 0xD8, 0x42, 0xCC,
+   0xEF, 0x26, 0xE5, 0x61, 0x1A, 0x3F, 0x3B, 0x82, 0xB6, 0xDB, 0xD4, 0x98, 0xE8, 0x8B, 0x02, 0xEB,
+   0x0A, 0x2C, 0x1D, 0xB0, 0x6F, 0x8D, 0x88, 0x0E, 0x19, 0x87, 0x4E, 0x0B, 0xA9, 0x0C, 0x79, 0x11,
+   0x7F, 0x22, 0xE7, 0x59, 0xE1, 0xDA, 0x3D, 0xC8, 0x12, 0x04, 0x74, 0x54, 0x30, 0x7E, 0xB4, 0x28,
+   0x55, 0x68, 0x50, 0xBE, 0xD0, 0xC4, 0x31, 0xCB, 0x2A, 0xAD, 0x0F, 0xCA, 0x70, 0xFF, 0x32, 0x69,
+   0x08, 0x62, 0x00, 0x24, 0xD1, 0xFB, 0xBA, 0xED, 0x45, 0x81, 0x73, 0x6D, 0x84, 0x9F, 0xEE, 0x4A,
+   0xC3, 0x2E, 0xC1, 0x01, 0xE6, 0x25, 0x48, 0x99, 0xB9, 0xB3, 0x7B, 0xF9, 0xCE, 0xBF, 0xDF, 0x71,
+   0x29, 0xCD, 0x6C, 0x13, 0x64, 0x9B, 0x63, 0x9D, 0xC0, 0x4B, 0xB7, 0xA5, 0x89, 0x5F, 0xB1, 0x17,
+   0xF4, 0xBC, 0xD3, 0x46, 0xCF, 0x37, 0x5E, 0x47, 0x94, 0xFA, 0xFC, 0x5B, 0x97, 0xFE, 0x5A, 0xAC,
+   0x3C, 0x4C, 0x03, 0x35, 0xF3, 0x23, 0xB8, 0x5D, 0x6A, 0x92, 0xD5, 0x21, 0x44, 0x51, 0xC6, 0x7D,
+   0x39, 0x83, 0xDC, 0xAA, 0x7C, 0x77, 0x56, 0x05, 0x1B, 0xA4, 0x15, 0x34, 0x1E, 0x1C, 0xF8, 0x52,
+   0x20, 0x14, 0xE9, 0xBD, 0xDD, 0xE4, 0xA1, 0xE0, 0x8A, 0xF1, 0xD6, 0x7A, 0xBB, 0xE3, 0x40, 0x4F
+};
+
+//Substitution table 4
+static const uint8_t sbox4[256] =
+{
+   0x70, 0x2C, 0xB3, 0xC0, 0xE4, 0x57, 0xEA, 0xAE, 0x23, 0x6B, 0x45, 0xA5, 0xED, 0x4F, 0x1D, 0x92,
+   0x86, 0xAF, 0x7C, 0x1F, 0x3E, 0xDC, 0x5E, 0x0B, 0xA6, 0x39, 0xD5, 0x5D, 0xD9, 0x5A, 0x51, 0x6C,
+   0x8B, 0x9A, 0xFB, 0xB0, 0x74, 0x2B, 0xF0, 0x84, 0xDF, 0xCB, 0x34, 0x76, 0x6D, 0xA9, 0xD1, 0x04,
+   0x14, 0x3A, 0xDE, 0x11, 0x32, 0x9C, 0x53, 0xF2, 0xFE, 0xCF, 0xC3, 0x7A, 0x24, 0xE8, 0x60, 0x69,
+   0xAA, 0xA0, 0xA1, 0x62, 0x54, 0x1E, 0xE0, 0x64, 0x10, 0x00, 0xA3, 0x75, 0x8A, 0xE6, 0x09, 0xDD,
+   0x87, 0x83, 0xCD, 0x90, 0x73, 0xF6, 0x9D, 0xBF, 0x52, 0xD8, 0xC8, 0xC6, 0x81, 0x6F, 0x13, 0x63,
+   0xE9, 0xA7, 0x9F, 0xBC, 0x29, 0xF9, 0x2F, 0xB4, 0x78, 0x06, 0xE7, 0x71, 0xD4, 0xAB, 0x88, 0x8D,
+   0x72, 0xB9, 0xF8, 0xAC, 0x36, 0x2A, 0x3C, 0xF1, 0x40, 0xD3, 0xBB, 0x43, 0x15, 0xAD, 0x77, 0x80,
+   0x82, 0xEC, 0x27, 0xE5, 0x85, 0x35, 0x0C, 0x41, 0xEF, 0x93, 0x19, 0x21, 0x0E, 0x4E, 0x65, 0xBD,
+   0xB8, 0x8F, 0xEB, 0xCE, 0x30, 0x5F, 0xC5, 0x1A, 0xE1, 0xCA, 0x47, 0x3D, 0x01, 0xD6, 0x56, 0x4D,
+   0x0D, 0x66, 0xCC, 0x2D, 0x12, 0x20, 0xB1, 0x99, 0x4C, 0xC2, 0x7E, 0x05, 0xB7, 0x31, 0x17, 0xD7,
+   0x58, 0x61, 0x1B, 0x1C, 0x0F, 0x16, 0x18, 0x22, 0x44, 0xB2, 0xB5, 0x91, 0x08, 0xA8, 0xFC, 0x50,
+   0xD0, 0x7D, 0x89, 0x97, 0x5B, 0x95, 0xFF, 0xD2, 0xC4, 0x48, 0xF7, 0xDB, 0x03, 0xDA, 0x3F, 0x94,
+   0x5C, 0x02, 0x4A, 0x33, 0x67, 0xF3, 0x7F, 0xE2, 0x9B, 0x26, 0x37, 0x3B, 0x96, 0x4B, 0xBE, 0x2E,
+   0x79, 0x8C, 0x6E, 0x8E, 0xF5, 0xB6, 0xFD, 0x59, 0x98, 0x6A, 0x46, 0xBA, 0x25, 0x42, 0xA2, 0xFA,
+   0x07, 0x55, 0xEE, 0x0A, 0x49, 0x68, 0x38, 0xA4, 0x28, 0x7B, 0xC9, 0xC1, 0xE3, 0xF4, 0xC7, 0x9E
+};
+
+//Common interface for encryption algorithms
+const CipherAlgo camelliaCipherAlgo =
+{
+   "CAMELLIA",
+   sizeof(CamelliaContext),
+   CIPHER_ALGO_TYPE_BLOCK,
+   CAMELLIA_BLOCK_SIZE,
+   (CipherAlgoInit) camelliaInit,
+   NULL,
+   NULL,
+   (CipherAlgoEncryptBlock) camelliaEncryptBlock,
+   (CipherAlgoDecryptBlock) camelliaDecryptBlock
+};
+
+
+/**
+ * @brief Initialize a Camellia context using the supplied key
+ * @param[in] context Pointer to the Camellia context to initialize
+ * @param[in] key Pointer to the key
+ * @param[in] keyLength Length of the key
+ * @return Error code
+ **/
+
+error_t camelliaInit(CamelliaContext *context, const uint8_t *key, size_t keyLength)
+{
+   uint_t i;
+   uint32_t temp1;
+   uint32_t temp2;
+   uint32_t *k;
+   const CamelliaSubkey *p;
+
+   //18 rounds are required for 128-bit key
+   if(keyLength == 16)
+      context->nr = 18;
+   //24 rounds are required for 192 and 256-bit keys
+   else if(keyLength == 24 || keyLength == 32)
+      context->nr = 24;
+   //Key length is not supported...
+   else
+      return ERROR_INVALID_KEY_LENGTH;
+
+   //Point to KA, KB, KL and KR
+   k = context->k;
+   //Clear key contents
+   memset(k, 0, 64);
+   //Save the supplied secret key
+   memcpy(k, key, keyLength);
+
+   //192-bit keys require special processing
+   if(keyLength == 24)
+   {
+      //Form a 256-bit key
+      k[KR + 2] = ~k[KR + 0];
+      k[KR + 3] = ~k[KR + 1];
+   }
+
+   //XOR KL and KR before applying the rounds
+   for(i = 0; i < 4; i++)
+   {
+      k[KL + i] = betoh32(k[KL + i]);
+      k[KR + i] = betoh32(k[KR + i]);
+      k[KB + i] = k[KL + i] ^ k[KR + i];
+   }
+
+   //Generate the 128-bit keys KA and KB
+   for(i = 0; i < 6; i++)
+   {
+      //Apply round function
+      CAMELLIA_ROUND(k[KB + 0], k[KB + 1], k[KB + 2], k[KB + 3], sigma[2 * i], sigma[2 * i + 1]);
+
+      //The 2nd round requires special processing
+      if(i == 1)
+      {
+         //The result is XORed with KL
+         k[KB + 0] ^= k[KL + 0];
+         k[KB + 1] ^= k[KL + 1];
+         k[KB + 2] ^= k[KL + 2];
+         k[KB + 3] ^= k[KL + 3];
+      }
+      //The 4th round requires special processing
+      else if(i == 3)
+      {
+         //Save KA after the 4th round
+         memcpy(k + KA, k + KB, 16);
+         //The result is XORed with KR
+         k[KB + 0] ^= k[KR + 0];
+         k[KB + 1] ^= k[KR + 1];
+         k[KB + 2] ^= k[KR + 2];
+         k[KB + 3] ^= k[KR + 3];
+      }
+   }
+
+   //The key schedule depends on the length of key
+   if(keyLength == 16)
+   {
+      //Key schedule for 128-bit key
+      i = arraysize(ks1);
+      p = ks1;
+   }
+   else
+   {
+      //Key schedule for 192 and 256-bit keys
+      i = arraysize(ks2);
+      p = ks2;
+   }
+
+   //Generate subkeys
+   while(i > 0)
+   {
+      //Calculate the shift count
+      uint_t n = (p->shift + p->position) / 32;
+      uint_t m = (p->shift + p->position) % 32;
+      //Point to KL, KR, KA or KB
+      k = context->k + p->key;
+
+      //Generate the current subkey
+      if(m == 0)
+      {
+         context->ks[p->index] = k[n % 4];
+         context->ks[p->index + 1] = k[(n + 1) % 4];
+      }
+      else
+      {
+         context->ks[p->index] = (k[n % 4] << m) | (k[(n + 1) % 4] >> (32 - m));
+         context->ks[p->index + 1] = (k[(n + 1) % 4] << m) | (k[(n + 2) % 4] >> (32 - m));
+      }
+
+      //Next subkey
+      p++;
+      i--;
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Encrypt a 16-byte block using Camellia algorithm
+ * @param[in] context Pointer to the Camellia context
+ * @param[in] input Plaintext block to encrypt
+ * @param[out] output Ciphertext block resulting from encryption
+ **/
+
+void camelliaEncryptBlock(CamelliaContext *context, const uint8_t *input, uint8_t *output)
+{
+   uint_t i;
+   uint32_t temp1;
+   uint32_t temp2;
+   uint32_t *ks;
+
+   //The plaintext is separated into two parts (L and R)
+   uint32_t left1 = LOAD32BE(input + 0);
+   uint32_t left2 = LOAD32BE(input + 4);
+   uint32_t right1 = LOAD32BE(input + 8);
+   uint32_t right2 = LOAD32BE(input + 12);
+
+   //The key schedule must be applied in ascending order
+   ks = context->ks;
+
+   //XOR plaintext with kw1 and kw2
+   left1 ^= ks[0];
+   left2 ^= ks[1];
+   right1 ^= ks[2];
+   right2 ^= ks[3];
+   //Advance current location in key schedule
+   ks += 4;
+
+   //Apply round function 18 or 24 times depending on the key length
+   for(i = context->nr; i > 0; i--)
+   {
+      //Apply round function
+      CAMELLIA_ROUND(left1, left2, right1, right2, ks[0], ks[1]);
+      //Advance current location in key schedule
+      ks += 2;
+
+      //6th, 12th and 18th rounds require special processing
+      if(i == 7 || i == 13 || i == 19)
+      {
+         //Apply FL-function
+         CAMELLIA_FL(left1, left2, ks[0], ks[1])
+         //Apply inverse FL-function
+         CAMELLIA_INV_FL(right1, right2, ks[2], ks[3])
+         //Advance current location in key schedule
+         ks += 4;
+      }
+   }
+
+   //XOR operation with kw3 and kw4
+   right1 ^= ks[0];
+   right2 ^= ks[1];
+   left1 ^= ks[2];
+   left2 ^= ks[3];
+
+   //The resulting value is the ciphertext
+   STORE32BE(right1, output + 0);
+   STORE32BE(right2, output + 4);
+   STORE32BE(left1, output + 8);
+   STORE32BE(left2, output + 12);
+}
+
+
+/**
+ * @brief Decrypt a 16-byte block using Camellia algorithm
+ * @param[in] context Pointer to the Camellia context
+ * @param[in] input Ciphertext block to decrypt
+ * @param[out] output Plaintext block resulting from decryption
+ **/
+
+void camelliaDecryptBlock(CamelliaContext *context, const uint8_t *input, uint8_t *output)
+{
+   uint_t i;
+   uint32_t temp1;
+   uint32_t temp2;
+   uint32_t *ks;
+
+   //The ciphertext is separated into two parts (L and R)
+   uint32_t right1 = LOAD32BE(input + 0);
+   uint32_t right2 = LOAD32BE(input + 4);
+   uint32_t left1 = LOAD32BE(input + 8);
+   uint32_t left2 = LOAD32BE(input + 12);
+
+   //The key schedule must be applied in reverse order
+   ks = (context->nr == 18) ? (context->ks + 48) : (context->ks + 64);
+
+   //XOR ciphertext with kw3 and kw4
+   right1 ^= ks[0];
+   right2 ^= ks[1];
+   left1 ^= ks[2];
+   left2 ^= ks[3];
+
+   //Apply round function 18 or 24 times depending on the key length
+   for(i = context->nr; i > 0; i--)
+   {
+      //Update current location in key schedule
+      ks -= 2;
+      //Apply round function
+      CAMELLIA_ROUND(right1, right2, left1, left2, ks[0], ks[1]);
+
+      //6th, 12th and 18th rounds require special processing
+      if(i == 7 || i == 13 || i == 19)
+      {
+         //Update current location in key schedule
+         ks -= 4;
+         //Apply FL-function
+         CAMELLIA_FL(right1, right2, ks[2], ks[3])
+         //Apply inverse FL-function
+         CAMELLIA_INV_FL(left1, left2, ks[0], ks[1])
+      }
+   }
+
+   //Update current location in key schedule
+   ks -= 4;
+   //XOR operation with kw1 and kw2
+   left1 ^= ks[0];
+   left2 ^= ks[1];
+   right1 ^= ks[2];
+   right2 ^= ks[3];
+
+   //The resulting value is the plaintext
+   STORE32BE(left1, output + 0);
+   STORE32BE(left2, output + 4);
+   STORE32BE(right1, output + 8);
+   STORE32BE(right2, output + 12);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/camellia.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,75 @@
+/**
+ * @file camellia.h
+ * @brief Camellia encryption algorithm
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _CAMELLIA_H
+#define _CAMELLIA_H
+
+//Dependencies
+#include "crypto.h"
+
+//Camellia block size
+#define CAMELLIA_BLOCK_SIZE 16
+//Common interface for encryption algorithms
+#define CAMELLIA_CIPHER_ALGO (&camelliaCipherAlgo)
+
+
+/**
+ * @brief Structure describing subkey generation
+ **/
+
+typedef struct
+{
+   uint8_t index;
+   uint8_t key;
+   uint8_t shift;
+   uint8_t position;
+} CamelliaSubkey;
+
+
+/**
+ * @brief Camellia algorithm context
+ **/
+
+typedef struct
+{
+   uint_t nr;
+   uint32_t k[16];
+   uint32_t ks[68];
+} CamelliaContext;
+
+
+//Camellia related constants
+extern const CipherAlgo camelliaCipherAlgo;
+
+//Camellia related functions
+error_t camelliaInit(CamelliaContext *context, const uint8_t *key, size_t keyLength);
+void camelliaEncryptBlock(CamelliaContext *context, const uint8_t *input, uint8_t *output);
+void camelliaDecryptBlock(CamelliaContext *context, const uint8_t *input, uint8_t *output);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/chacha.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,283 @@
+/**
+ * @file chacha.c
+ * @brief ChaCha encryption algorithm
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include "crypto.h"
+#include "chacha.h"
+
+//Check crypto library configuration
+#if (CHACHA_SUPPORT == ENABLED)
+
+//ChaCha quarter-round function
+#define CHACHA_QUARTER_ROUND(a, b, c, d) \
+{ \
+   a += b; d ^= a; d = ROL32(d, 16); \
+   c += d; b ^= c; b = ROL32(b, 12); \
+   a += b; d ^= a; d = ROL32(d, 8); \
+   c += d; b ^= c; b = ROL32(b, 7); \
+}
+
+
+/**
+ * @brief Initialize ChaCha context using the supplied key and nonce
+ * @param[in] context Pointer to the ChaCha context to initialize
+ * @param[in] nr Number of rounds to be applied (8, 12 or 20)
+ * @param[in] key Pointer to the key
+ * @param[in] keyLength Length of the key, in bytes (16 or 32)
+ * @param[in] nonce Pointer to the nonce
+ * @param[in] nonceLength Length of the nonce, in bytes (8 or 12)
+ * @return Error code
+ **/
+
+error_t chachaInit(ChachaContext *context, uint_t nr, const uint8_t *key,
+   size_t keyLength, const uint8_t *nonce, size_t nonceLength)
+{
+   uint32_t *w;
+
+   //The number of rounds must be 8, 12 or 20
+   if(nr != 8 && nr != 12 && nr != 20)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save the number of rounds to be applied
+   context->nr = nr;
+
+   //Point to the state
+   w = context->state;
+
+   //Check the length of the key
+   if(keyLength == 16)
+   {
+      //The first four input words are constants
+      w[0] = 0x61707865;
+      w[1] = 0x3120646E;
+      w[2] = 0x79622D36;
+      w[3] = 0x6B206574;
+
+      //Input words 4 through 7 are taken from the 128-bit key, by reading
+      //the bytes in little-endian order, in 4-byte chunks
+      w[4] = LOAD32LE(key);
+      w[5] = LOAD32LE(key + 4);
+      w[6] = LOAD32LE(key + 8);
+      w[7] = LOAD32LE(key + 12);
+
+      //Input words 8 through 11 are taken from the 128-bit key, again by
+      //reading the bytes in little-endian order, in 4-byte chunks
+      w[8] = LOAD32LE(key);
+      w[9] = LOAD32LE(key + 4);
+      w[10] = LOAD32LE(key + 8);
+      w[11] = LOAD32LE(key + 12);
+   }
+   else if(keyLength == 32)
+   {
+      //The first four input words are constants
+      w[0] = 0x61707865;
+      w[1] = 0x3320646E;
+      w[2] = 0x79622D32;
+      w[3] = 0x6B206574;
+
+      //Input words 4 through 11 are taken from the 256-bit key, by reading
+      //the bytes in little-endian order, in 4-byte chunks
+      w[4] = LOAD32LE(key);
+      w[5] = LOAD32LE(key + 4);
+      w[6] = LOAD32LE(key + 8);
+      w[7] = LOAD32LE(key + 12);
+      w[8] = LOAD32LE(key + 16);
+      w[9] = LOAD32LE(key + 20);
+      w[10] = LOAD32LE(key + 24);
+      w[11] = LOAD32LE(key + 28);
+   }
+   else
+   {
+      //Invalid key length
+      return ERROR_INVALID_PARAMETER;
+   }
+
+   //Check the length of the nonce
+   if(nonceLength == 8)
+   {
+      //Input words 12 and 13 are a block counter, with word 12
+      //overflowing into word 13
+      w[12] = 0;
+      w[13] = 0;
+
+      //Input words 14 and 15 are taken from an 64-bit nonce, by reading
+      //the bytes in little-endian order, in 4-byte chunks
+      w[14] = LOAD32LE(nonce);
+      w[15] = LOAD32LE(nonce + 4);
+   }
+   else if(nonceLength == 12)
+   {
+      //Input word 12 is a block counter
+      w[12] = 0;
+
+      //Input words 13 to 15 are taken from an 96-bit nonce, by reading
+      //the bytes in little-endian order, in 4-byte chunks
+      w[13] = LOAD32LE(nonce);
+      w[14] = LOAD32LE(nonce + 4);
+      w[15] = LOAD32LE(nonce + 8);
+   }
+   else
+   {
+      //Invalid nonce length
+      return ERROR_INVALID_PARAMETER;
+   }
+
+   //The keystream block is empty
+   context->pos = 0;
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Encrypt/decrypt data with the ChaCha algorithm
+ * @param[in] context Pointer to the ChaCha context
+ * @param[in] input Pointer to the data to encrypt/decrypt (optional)
+ * @param[in] output Pointer to the resulting data (optional)
+ * @param[in] length Number of bytes to be processed
+ **/
+
+void chachaCipher(ChachaContext *context, const uint8_t *input,
+   uint8_t *output, size_t length)
+{
+   uint_t i;
+   uint_t n;
+   uint8_t *k;
+
+   //Encryption loop
+   while(length > 0)
+   {
+      //Check whether a new keystream block must be generated
+      if(context->pos == 0 || context->pos >= 64)
+      {
+         //ChaCha successively calls the ChaCha block function, with the same key
+         //and nonce, and with successively increasing block counter parameters
+         chachaProcessBlock(context);
+
+         //Increment block counter
+         context->state[12]++;
+
+         //Propagate the carry if necessary
+         if(context->state[12] == 0)
+            context->state[13]++;
+
+         //Rewind to the beginning of the keystream block
+         context->pos = 0;
+      }
+
+      //Compute the number of bytes to encrypt/decrypt at a time
+      n = MIN(length, 64 - context->pos);
+
+      //Valid output pointer?
+      if(output != NULL)
+      {
+         //Point to the keystream
+         k = (uint8_t *) context->block + context->pos;
+
+         //Valid input pointer?
+         if(input != NULL)
+         {
+            //XOR the input data with the keystream
+            for(i = 0; i < n; i++)
+               output[i] = input[i] ^ k[i];
+
+            //Advance input pointer
+            input += n;
+         }
+         else
+         {
+            //Output the keystream
+            for(i = 0; i < n; i++)
+               output[i] = k[i];
+         }
+
+         //Advance output pointer
+         output += n;
+      }
+
+      //Current position in the keystream block
+      context->pos += n;
+      //Remaining bytes to process
+      length -= n;
+   }
+}
+
+
+/**
+ * @brief Generate a keystream block
+ * @param[in] context Pointer to the ChaCha context
+ **/
+
+void chachaProcessBlock(ChachaContext *context)
+{
+   uint_t i;
+   uint32_t *w;
+
+   //Point to the working state
+   w = (uint32_t *) context->block;
+
+   //Copy the state to the working state
+   for(i = 0; i < 16; i++)
+      w[i] = context->state[i];
+
+   //ChaCha runs 8, 12 or 20 rounds, alternating between column rounds
+   //and diagonal rounds
+   for(i = 0; i < context->nr; i += 2)
+   {
+      //The column rounds apply the quarter-round function to the four
+      //columns, from left to right
+      CHACHA_QUARTER_ROUND(w[0], w[4], w[8], w[12]);
+      CHACHA_QUARTER_ROUND(w[1], w[5], w[9], w[13]);
+      CHACHA_QUARTER_ROUND(w[2], w[6], w[10], w[14]);
+      CHACHA_QUARTER_ROUND(w[3], w[7], w[11], w[15]);
+
+      //The diagonal rounds apply the quarter-round function to the top-left,
+      //bottom-right diagonal, followed by the pattern shifted one place to
+      //the right, for three more quarter-rounds
+      CHACHA_QUARTER_ROUND(w[0], w[5], w[10], w[15]);
+      CHACHA_QUARTER_ROUND(w[1], w[6], w[11], w[12]);
+      CHACHA_QUARTER_ROUND(w[2], w[7], w[8], w[13]);
+      CHACHA_QUARTER_ROUND(w[3], w[4], w[9], w[14]);
+   }
+
+   //Add the original input words to the output words
+   for(i = 0; i < 16; i++)
+      w[i] += context->state[i];
+
+   //Serialize the result by sequencing the words one-by-one in
+   //little-endian order
+   for(i = 0; i < 16; i++)
+      w[i] = htole32(w[i]);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/chacha.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,59 @@
+/**
+ * @file chacha.h
+ * @brief ChaCha encryption algorithm
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _CHACHA_H
+#define _CHACHA_H
+
+//Dependencies
+#include "crypto.h"
+
+
+/**
+ * @brief ChaCha algorithm context
+ **/
+
+typedef struct
+{
+   uint_t nr;
+   uint32_t state[16];
+   uint32_t block[16];
+   size_t pos;
+} ChachaContext;
+
+
+//ChaCha related functions
+error_t chachaInit(ChachaContext *context, uint_t nr, const uint8_t *key,
+   size_t keyLength, const uint8_t *nonce, size_t nonceLength);
+
+void chachaCipher(ChachaContext *context, const uint8_t *input,
+   uint8_t *output, size_t length);
+
+void chachaProcessBlock(ChachaContext *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/chacha20_poly1305.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,256 @@
+/**
+ * @file chacha20_poly1305.c
+ * @brief ChaCha20Poly1305 AEAD
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "chacha.h"
+#include "poly1305.h"
+#include "chacha20_poly1305.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (CHACHA20_POLY1305_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Authenticated encryption using ChaCha20Poly1305
+ * @param[in] k key
+ * @param[in] kLen Length of the key
+ * @param[in] n Nonce
+ * @param[in] nLen Length of the nonce
+ * @param[in] a Additional authenticated data
+ * @param[in] aLen Length of the additional data
+ * @param[in] p Plaintext to be encrypted
+ * @param[out] c Ciphertext resulting from the encryption
+ * @param[in] length Total number of data bytes to be encrypted
+ * @param[out] t MAC resulting from the encryption process
+ * @param[in] tLen Length of the MAC
+ * @return Error code
+ **/
+
+error_t chacha20Poly1305Encrypt(const uint8_t *k, size_t kLen,
+   const uint8_t *n, size_t nLen, const uint8_t *a, size_t aLen,
+   const uint8_t *p, uint8_t *c, size_t length, uint8_t *t, size_t tLen)
+{
+   error_t error;
+   size_t paddingLen;
+   ChachaContext chachaContext;
+   Poly1305Context poly1305Context;
+   uint8_t temp[32];
+
+   //Check the length of the message-authentication code
+   if(tLen != 16)
+      return ERROR_INVALID_LENGTH;
+
+   //Initialize ChaCha20 context
+   error = chachaInit(&chachaContext, 20, k, kLen, n, nLen);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //First, a Poly1305 one-time key is generated from the 256-bit key
+   //and nonce
+   chachaCipher(&chachaContext, NULL, temp, 32);
+
+   //The other 256 bits of the Chacha20 block are discarded
+   chachaCipher(&chachaContext, NULL, NULL, 32);
+
+   //Next, the ChaCha20 encryption function is called to encrypt the
+   //plaintext, using the same key and nonce
+   chachaCipher(&chachaContext, p, c, length);
+
+   //Initialize the Poly1305 function with the key calculated above
+   poly1305Init(&poly1305Context, temp);
+
+   //Compute MAC over the AAD
+   poly1305Update(&poly1305Context, a, aLen);
+
+   //If the length of the AAD is not an integral multiple of 16 bytes,
+   //then padding is required
+   if(aLen % 16)
+   {
+      //Compute the number of padding bytes
+      paddingLen = 16 - (aLen % 16);
+
+      //The padding is up to 15 zero bytes, and it brings the total
+      //length so far to an integral multiple of 16
+      memset(temp, 0, paddingLen);
+
+      //Compute MAC over the padding
+      poly1305Update(&poly1305Context, temp, paddingLen);
+   }
+
+   //Compute MAC over the ciphertext
+   poly1305Update(&poly1305Context, c, length);
+
+   //If the length of the ciphertext is not an integral multiple of 16 bytes,
+   //then padding is required
+   if(length % 16)
+   {
+      //Compute the number of padding bytes
+      paddingLen = 16 - (length % 16);
+
+      //The padding is up to 15 zero bytes, and it brings the total
+      //length so far to an integral multiple of 16
+      memset(temp, 0, paddingLen);
+
+      //Compute MAC over the padding
+      poly1305Update(&poly1305Context, temp, paddingLen);
+   }
+
+   //Encode the length of the AAD as a 64-bit little-endian integer
+   STORE64LE(aLen, temp);
+   //Compute MAC over the length field
+   poly1305Update(&poly1305Context, temp, sizeof(uint64_t));
+
+   //Encode the length of the ciphertext as a 64-bit little-endian integer
+   STORE64LE(length, temp);
+   //Compute MAC over the length field
+   poly1305Update(&poly1305Context, temp, sizeof(uint64_t));
+
+   //Compute message-authentication code
+   poly1305Final(&poly1305Context, t);
+
+   //Successful encryption
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Authenticated decryption using ChaCha20Poly1305
+ * @param[in] k key
+ * @param[in] kLen Length of the key
+ * @param[in] n Nonce
+ * @param[in] nLen Length of the nonce
+ * @param[in] a Additional authenticated data
+ * @param[in] aLen Length of the additional data
+ * @param[in] c Ciphertext to be decrypted
+ * @param[out] p Plaintext resulting from the decryption
+ * @param[in] length Total number of data bytes to be decrypted
+ * @param[in] t MAC to be verified
+ * @param[in] tLen Length of the MAC
+ * @return Error code
+ **/
+
+error_t chacha20Poly1305Decrypt(const uint8_t *k, size_t kLen,
+   const uint8_t *n, size_t nLen, const uint8_t *a, size_t aLen,
+   const uint8_t *c, uint8_t *p, size_t length, const uint8_t *t, size_t tLen)
+{
+   error_t error;
+   size_t paddingLen;
+   ChachaContext chachaContext;
+   Poly1305Context poly1305Context;
+   uint8_t temp[32];
+
+   //Check the length of the message-authentication code
+   if(tLen != 16)
+      return ERROR_INVALID_LENGTH;
+
+   //Initialize ChaCha20 context
+   error = chachaInit(&chachaContext, 20, k, kLen, n, nLen);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //First, a Poly1305 one-time key is generated from the 256-bit key
+   //and nonce
+   chachaCipher(&chachaContext, NULL, temp, 32);
+
+   //The other 256 bits of the Chacha20 block are discarded
+   chachaCipher(&chachaContext, NULL, NULL, 32);
+
+   //Initialize the Poly1305 function with the key calculated above
+   poly1305Init(&poly1305Context, temp);
+
+   //Compute MAC over the AAD
+   poly1305Update(&poly1305Context, a, aLen);
+
+   //If the length of the AAD is not an integral multiple of 16 bytes,
+   //then padding is required
+   if(aLen % 16)
+   {
+      //Compute the number of padding bytes
+      paddingLen = 16 - (aLen % 16);
+
+      //The padding is up to 15 zero bytes, and it brings the total
+      //length so far to an integral multiple of 16
+      memset(temp, 0, paddingLen);
+
+      //Compute MAC over the padding
+      poly1305Update(&poly1305Context, temp, paddingLen);
+   }
+
+   //Compute MAC over the ciphertext
+   poly1305Update(&poly1305Context, c, length);
+
+   //If the length of the ciphertext is not an integral multiple of 16 bytes,
+   //then padding is required
+   if(length % 16)
+   {
+      //Compute the number of padding bytes
+      paddingLen = 16 - (length % 16);
+
+      //The padding is up to 15 zero bytes, and it brings the total
+      //length so far to an integral multiple of 16
+      memset(temp, 0, paddingLen);
+
+      //Compute MAC over the padding
+      poly1305Update(&poly1305Context, temp, paddingLen);
+   }
+
+   //Encode the length of the AAD as a 64-bit little-endian integer
+   STORE64LE(aLen, temp);
+   //Compute MAC over the length field
+   poly1305Update(&poly1305Context, temp, sizeof(uint64_t));
+
+   //Encode the length of the ciphertext as a 64-bit little-endian integer
+   STORE64LE(length, temp);
+   //Compute MAC over the length field
+   poly1305Update(&poly1305Context, temp, sizeof(uint64_t));
+
+   //Compute message-authentication code
+   poly1305Final(&poly1305Context, temp);
+
+   //Finally, we decrypt the ciphertext
+   chachaCipher(&chachaContext, c, p, length);
+
+   //The calculated tag is bitwise compared to the received tag. The
+   //message is authenticated if and only if the tags match
+   if(memcmp(temp, t, tLen))
+      return ERROR_FAILURE;
+
+   //Successful encryption
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/chacha20_poly1305.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,45 @@
+/**
+ * @file chacha20_poly1305.h
+ * @brief ChaCha20Poly1305 AEAD
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _CHACHA20_POLY1305_H
+#define _CHACHA20_POLY1305_H
+
+//Dependencies
+#include "crypto.h"
+
+//ChaCha20Poly1305 related functions
+error_t chacha20Poly1305Encrypt(const uint8_t *k, size_t kLen,
+   const uint8_t *n, size_t nLen, const uint8_t *a, size_t aLen,
+   const uint8_t *p, uint8_t *c, size_t length, uint8_t *t, size_t tLen);
+
+error_t chacha20Poly1305Decrypt(const uint8_t *k, size_t kLen,
+   const uint8_t *n, size_t nLen, const uint8_t *a, size_t aLen,
+   const uint8_t *c, uint8_t *p, size_t length, const uint8_t *t, size_t tLen);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/cipher_mode_cbc.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,142 @@
+/**
+ * @file cipher_mode_cbc.c
+ * @brief Cipher Block Chaining (CBC) mode
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The Cipher Block Chaining (CBC) mode is a confidentiality mode whose
+ * encryption process features the combining of the plaintext blocks with
+ * the previous ciphertext blocks. The CBC mode requires an IV to combine
+ * with the first plaintext block. Refer to SP 800-38A for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "cipher_mode_cbc.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (CBC_SUPPORT == ENABLED)
+
+
+/**
+ * @brief CBC encryption
+ * @param[in] cipher Cipher algorithm
+ * @param[in] context Cipher algorithm context
+ * @param[in,out] iv Initialization vector
+ * @param[in] p Plaintext to be encrypted
+ * @param[out] c Ciphertext resulting from the encryption
+ * @param[in] length Total number of data bytes to be encrypted
+ * @return Error code
+ **/
+
+error_t cbcEncrypt(const CipherAlgo *cipher, void *context,
+   uint8_t *iv, const uint8_t *p, uint8_t *c, size_t length)
+{
+   size_t i;
+
+   //CBC mode operates in a block-by-block fashion
+   while(length >= cipher->blockSize)
+   {
+      //XOR input block with IV contents
+      for(i = 0; i < cipher->blockSize; i++)
+         c[i] = p[i] ^ iv[i];
+
+      //Encrypt the current block based upon the output
+      //of the previous encryption
+      cipher->encryptBlock(context, c, c);
+
+      //Update IV with output block contents
+      memcpy(iv, c, cipher->blockSize);
+
+      //Next block
+      p += cipher->blockSize;
+      c += cipher->blockSize;
+      length -= cipher->blockSize;
+   }
+
+   //The plaintext must be a multiple of the block size
+   if(length != 0)
+      return ERROR_INVALID_LENGTH;
+
+   //Successful encryption
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief CBC decryption
+ * @param[in] cipher Cipher algorithm
+ * @param[in] context Cipher algorithm context
+ * @param[in,out] iv Initialization vector
+ * @param[in] c Ciphertext to be decrypted
+ * @param[out] p Plaintext resulting from the decryption
+ * @param[in] length Total number of data bytes to be decrypted
+ * @return Error code
+ **/
+
+error_t cbcDecrypt(const CipherAlgo *cipher, void *context,
+   uint8_t *iv, const uint8_t *c, uint8_t *p, size_t length)
+{
+   size_t i;
+   uint8_t t[16];
+
+   //CBC mode operates in a block-by-block fashion
+   while(length >= cipher->blockSize)
+   {
+      //Save input block
+      memcpy(t, c, cipher->blockSize);
+
+      //Decrypt the current block
+      cipher->decryptBlock(context, c, p);
+
+      //XOR output block with IV contents
+      for(i = 0; i < cipher->blockSize; i++)
+         p[i] ^= iv[i];
+
+      //Update IV with input block contents
+      memcpy(iv, t, cipher->blockSize);
+
+      //Next block
+      c += cipher->blockSize;
+      p += cipher->blockSize;
+      length -= cipher->blockSize;
+   }
+
+   //The ciphertext must be a multiple of the block size
+   if(length != 0)
+      return ERROR_INVALID_LENGTH;
+
+   //Successful encryption
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/cipher_mode_cbc.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,43 @@
+/**
+ * @file cipher_mode_cbc.h
+ * @brief Cipher Block Chaining (CBC) mode
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _CIPHER_MODE_CBC_H
+#define _CIPHER_MODE_CBC_H
+
+//Dependencies
+#include "crypto.h"
+
+//CBC encryption and decryption routines
+error_t cbcEncrypt(const CipherAlgo *cipher, void *context,
+   uint8_t *iv, const uint8_t *p, uint8_t *c, size_t length);
+
+error_t cbcDecrypt(const CipherAlgo *cipher, void *context,
+   uint8_t *iv, const uint8_t *c, uint8_t *p, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/cipher_mode_ccm.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,427 @@
+/**
+ * @file cipher_mode_ccm.c
+ * @brief Cipher Block Chaining-Message Authentication Code (CCM)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * CCM mode (Cipher Block Chaining-Message Authentication Code) is a mode of
+ * operation for cryptographic block ciphers. It is an authenticated encryption
+ * algorithm designed to provide both authentication and confidentiality. CCM
+ * mode is only defined for block ciphers with a block length of 128 bits.
+ * Refer to SP 800-38D for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "cipher_mode_ccm.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (CCM_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Authenticated encryption using CCM
+ * @param[in] cipher Cipher algorithm
+ * @param[in] context Cipher algorithm context
+ * @param[in] n Nonce
+ * @param[in] nLen Length of the nonce
+ * @param[in] a Additional authenticated data
+ * @param[in] aLen Length of the additional data
+ * @param[in] p Plaintext to be encrypted
+ * @param[out] c Ciphertext resulting from the encryption
+ * @param[in] length Total number of data bytes to be encrypted
+ * @param[out] t MAC resulting from the encryption process
+ * @param[in] tLen Length of the MAC
+ * @return Error code
+ **/
+
+error_t ccmEncrypt(const CipherAlgo *cipher, void *context, const uint8_t *n, size_t nLen,
+   const uint8_t *a, size_t aLen, const uint8_t *p, uint8_t *c, size_t length, uint8_t *t, size_t tLen)
+{
+   size_t m;
+   size_t q;
+   size_t qLen;
+   uint8_t b[16];
+   uint8_t y[16];
+   uint8_t s[16];
+
+   //Check parameters
+   if(cipher == NULL || context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //CCM supports only symmetric block ciphers whose block size is 128 bits
+   if(cipher->type != CIPHER_ALGO_TYPE_BLOCK || cipher->blockSize != 16)
+      return ERROR_INVALID_PARAMETER;
+
+   //Check the length of the nonce
+   if(nLen < 7 || nLen > 13)
+      return ERROR_INVALID_LENGTH;
+   //Check the length of the MAC
+   if(tLen < 4 || tLen > 16 || tLen % 2)
+      return ERROR_INVALID_LENGTH;
+
+   //Q is the bit string representation of the octet length of P
+   q = length;
+   //Compute the octet length of Q
+   qLen = 15 - nLen;
+
+   //Format the leading octet of the first block
+   b[0] = (aLen > 0) ? 0x40 : 0x00;
+   //Encode the octet length of T
+   b[0] |= ((tLen - 2) / 2) << 3;
+   //Encode the octet length of Q
+   b[0] |= qLen - 1;
+
+   //Copy the nonce
+   memcpy(b + 1, n, nLen);
+
+   //Encode the length field Q
+   for(m = 0; m < qLen; m++, q >>= 8)
+      b[15 - m] = q & 0xFF;
+
+   //Invalid length?
+   if(q != 0)
+      return ERROR_INVALID_LENGTH;
+
+   //Set Y(0) = CIPH(B(0))
+   cipher->encryptBlock(context, b, y);
+
+   //Any additional data?
+   if(aLen > 0)
+   {
+      //Format the associated data
+      memset(b, 0, 16);
+
+      //Check the length of the associated data string
+      if(aLen < 0xFF00)
+      {
+         //The length is encoded as 2 octets
+         STORE16BE(aLen, b);
+         //Number of bytes to copy
+         m = MIN(aLen, 16 - 2);
+         //Concatenate the associated data A
+         memcpy(b + 2, a, m);
+      }
+      else
+      {
+         //The length is encoded as 6 octets
+         b[0] = 0xFF;
+         b[1] = 0xFE;
+         //MSB is stored first
+         STORE32BE(aLen, b + 2);
+         //Number of bytes to copy
+         m = MIN(aLen, 16 - 6);
+         //Concatenate the associated data A
+         memcpy(b + 6, a, m);
+      }
+
+      //XOR B(1) with Y(0)
+      ccmXorBlock(y, b, y, 16);
+      //Compute Y(1) = CIPH(B(1) ^ Y(0))
+      cipher->encryptBlock(context, y, y);
+
+      //Number of remaining data bytes
+      aLen -= m;
+      a += m;
+
+      //Process the remaining data bytes
+      while(aLen > 0)
+      {
+         //Associated data are processed in a block-by-block fashion
+         m = MIN(aLen, 16);
+
+         //XOR B(i) with Y(i-1)
+         ccmXorBlock(y, a, y, m);
+         //Compute Y(i) = CIPH(B(i) ^ Y(i-1))
+         cipher->encryptBlock(context, y, y);
+
+         //Next block
+         aLen -= m;
+         a += m;
+      }
+   }
+
+   //Format CTR(0)
+   b[0] = (uint8_t) (qLen - 1);
+   //Copy the nonce
+   memcpy(b + 1, n, nLen);
+   //Initialize counter value
+   memset(b + 1 + nLen, 0, qLen);
+
+   //Compute S(0) = CIPH(CTR(0))
+   cipher->encryptBlock(context, b, s);
+   //Save MSB(S(0))
+   memcpy(t, s, tLen);
+
+   //Encrypt plaintext
+   while(length > 0)
+   {
+      //The encryption operates in a block-by-block fashion
+      m = MIN(length, 16);
+
+      //XOR B(i) with Y(i-1)
+      ccmXorBlock(y, p, y, m);
+      //Compute Y(i) = CIPH(B(i) ^ Y(i-1))
+      cipher->encryptBlock(context, y, y);
+
+      //Increment counter
+      ccmIncCounter(b, qLen);
+      //Compute S(i) = CIPH(CTR(i))
+      cipher->encryptBlock(context, b, s);
+      //Compute C(i) = B(i) XOR S(i)
+      ccmXorBlock(c, p, s, m);
+
+      //Next block
+      length -= m;
+      p += m;
+      c += m;
+   }
+
+   //Compute MAC
+   ccmXorBlock(t, t, y, tLen);
+
+   //Successful encryption
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Authenticated decryption using CCM
+ * @param[in] cipher Cipher algorithm
+ * @param[in] context Cipher algorithm context
+ * @param[in] n Nonce
+ * @param[in] nLen Length of the nonce
+ * @param[in] a Additional authenticated data
+ * @param[in] aLen Length of the additional data
+ * @param[in] c Ciphertext to be decrypted
+ * @param[out] p Plaintext resulting from the decryption
+ * @param[in] length Total number of data bytes to be decrypted
+ * @param[in] t MAC to be verified
+ * @param[in] tLen Length of the MAC
+ * @return Error code
+ **/
+
+error_t ccmDecrypt(const CipherAlgo *cipher, void *context, const uint8_t *n, size_t nLen,
+   const uint8_t *a, size_t aLen, const uint8_t *c, uint8_t *p, size_t length, const uint8_t *t, size_t tLen)
+{
+   size_t m;
+   size_t q;
+   size_t qLen;
+   uint8_t b[16];
+   uint8_t y[16];
+   uint8_t r[16];
+   uint8_t s[16];
+
+   //Check parameters
+   if(cipher == NULL || context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //CCM supports only symmetric block ciphers whose block size is 128 bits
+   if(cipher->type != CIPHER_ALGO_TYPE_BLOCK || cipher->blockSize != 16)
+      return ERROR_INVALID_PARAMETER;
+
+   //Check the length of the nonce
+   if(nLen < 7 || nLen > 13)
+      return ERROR_INVALID_LENGTH;
+   //Check the length of the MAC
+   if(tLen < 4 || tLen > 16 || tLen % 2)
+      return ERROR_INVALID_LENGTH;
+
+   //Q is the bit string representation of the octet length of C
+   q = length;
+   //Compute the octet length of Q
+   qLen = 15 - nLen;
+
+   //Format the leading octet of the first block
+   b[0] = (aLen > 0) ? 0x40 : 0x00;
+   //Encode the octet length of T
+   b[0] |= ((tLen - 2) / 2) << 3;
+   //Encode the octet length of Q
+   b[0] |= qLen - 1;
+
+   //Copy the nonce
+   memcpy(b + 1, n, nLen);
+
+   //Encode the length field Q
+   for(m = 0; m < qLen; m++, q >>= 8)
+      b[15 - m] = q & 0xFF;
+
+   //Invalid length?
+   if(q != 0)
+      return ERROR_INVALID_LENGTH;
+
+   //Set Y(0) = CIPH(B(0))
+   cipher->encryptBlock(context, b, y);
+
+   //Any additional data?
+   if(aLen > 0)
+   {
+      //Format the associated data
+      memset(b, 0, 16);
+
+      //Check the length of the associated data string
+      if(aLen < 0xFF00)
+      {
+         //The length is encoded as 2 octets
+         STORE16BE(aLen, b);
+         //Number of bytes to copy
+         m = MIN(aLen, 16 - 2);
+         //Concatenate the associated data A
+         memcpy(b + 2, a, m);
+      }
+      else
+      {
+         //The length is encoded as 6 octets
+         b[0] = 0xFF;
+         b[1] = 0xFE;
+         //MSB is stored first
+         STORE32BE(aLen, b + 2);
+         //Number of bytes to copy
+         m = MIN(aLen, 16 - 6);
+         //Concatenate the associated data A
+         memcpy(b + 6, a, m);
+      }
+
+      //XOR B(1) with Y(0)
+      ccmXorBlock(y, b, y, 16);
+      //Compute Y(1) = CIPH(B(1) ^ Y(0))
+      cipher->encryptBlock(context, y, y);
+
+      //Number of remaining data bytes
+      aLen -= m;
+      a += m;
+
+      //Process the remaining data bytes
+      while(aLen > 0)
+      {
+         //Associated data are processed in a block-by-block fashion
+         m = MIN(aLen, 16);
+
+         //XOR B(i) with Y(i-1)
+         ccmXorBlock(y, a, y, m);
+         //Compute Y(i) = CIPH(B(i) ^ Y(i-1))
+         cipher->encryptBlock(context, y, y);
+
+         //Next block
+         aLen -= m;
+         a += m;
+      }
+   }
+
+   //Format CTR(0)
+   b[0] = (uint8_t) (qLen - 1);
+   //Copy the nonce
+   memcpy(b + 1, n, nLen);
+   //Initialize counter value
+   memset(b + 1 + nLen, 0, qLen);
+
+   //Compute S(0) = CIPH(CTR(0))
+   cipher->encryptBlock(context, b, s);
+   //Save MSB(S(0))
+   memcpy(r, s, tLen);
+
+   //Decrypt ciphertext
+   while(length > 0)
+   {
+      //The decryption operates in a block-by-block fashion
+      m = MIN(length, 16);
+
+      //Increment counter
+      ccmIncCounter(b, qLen);
+      //Compute S(i) = CIPH(CTR(i))
+      cipher->encryptBlock(context, b, s);
+      //Compute B(i) = C(i) XOR S(i)
+      ccmXorBlock(p, c, s, m);
+
+      //XOR B(i) with Y(i-1)
+      ccmXorBlock(y, p, y, m);
+      //Compute Y(i) = CIPH(B(i) ^ Y(i-1))
+      cipher->encryptBlock(context, y, y);
+
+      //Next block
+      length -= m;
+      c += m;
+      p += m;
+   }
+
+   //Compute MAC
+   ccmXorBlock(r, r, y, tLen);
+
+   //The calculated tag is bitwise compared to the received tag. The
+   //message is authenticated if and only if the tags match
+   if(memcmp(r, t, tLen))
+      return ERROR_FAILURE;
+
+   //Successful decryption
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief XOR operation
+ * @param[out] x Block resulting from the XOR operation
+ * @param[in] a First block
+ * @param[in] b Second block
+ * @param[in] n Size of the block
+ **/
+
+void ccmXorBlock(uint8_t *x, const uint8_t *a, const uint8_t *b, size_t n)
+{
+   size_t i;
+
+   //Perform XOR operation
+   for(i = 0; i < n; i++)
+      x[i] = a[i] ^ b[i];
+}
+
+
+/**
+ * @brief Increment counter block
+ * @param[in,out] x Pointer to the counter block
+ * @param[in] n Size in bytes of the specific part of the block to be incremented
+ **/
+
+void ccmIncCounter(uint8_t *x, size_t n)
+{
+   size_t i;
+
+   //The function increments the right-most bytes of the block. The remaining
+   //left-most bytes remain unchanged
+   for(i = 0; i < n; i++)
+   {
+      //Increment the current byte and propagate the carry if necessary
+      if(++(x[15 - i]) != 0)
+         break;
+   }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/cipher_mode_ccm.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,46 @@
+/**
+ * @file cipher_mode_ccm.h
+ * @brief Cipher Block Chaining-Message Authentication Code (CCM)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _CIPHER_MODE_CCM_H
+#define _CIPHER_MODE_CCM_H
+
+//Dependencies
+#include "crypto.h"
+
+//CCM related functions
+error_t ccmEncrypt(const CipherAlgo *cipher, void *context, const uint8_t *n, size_t nLen,
+   const uint8_t *a, size_t aLen, const uint8_t *p, uint8_t *c, size_t length, uint8_t *t, size_t tLen);
+
+error_t ccmDecrypt(const CipherAlgo *cipher, void *context, const uint8_t *n, size_t nLen,
+   const uint8_t *a, size_t aLen, const uint8_t *c, uint8_t *p, size_t length, const uint8_t *t, size_t tLen);
+
+void ccmXorBlock(uint8_t *x, const uint8_t *a, const uint8_t *b, size_t n);
+void ccmIncCounter(uint8_t *x, size_t n);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/cipher_mode_cfb.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,167 @@
+/**
+ * @file cipher_mode_cfb.c
+ * @brief Cipher Feedback (CFB) mode
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The Cipher Feedback (CFB) mode is a confidentiality mode that features the
+ * feedback of successive ciphertext segments into the input blocks of the
+ * forward cipher to generate output blocks that are exclusive-ORed with the
+ * plaintext to produce the ciphertext, and vice versa. The CFB mode requires
+ * an IV as the initial input block. The IV need not be secret, but it must be
+ * unpredictable. Refer to SP 800-38A for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "cipher_mode_cfb.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (CFB_SUPPORT == ENABLED)
+
+
+/**
+ * @brief CFB encryption
+ * @param[in] cipher Cipher algorithm
+ * @param[in] context Cipher algorithm context
+ * @param[in] s Size of the plaintext and ciphertext segments
+ * @param[in,out] iv Initialization vector
+ * @param[in] p Plaintext to be encrypted
+ * @param[out] c Ciphertext resulting from the encryption
+ * @param[in] length Total number of data bytes to be encrypted
+ * @return Error code
+ **/
+
+error_t cfbEncrypt(const CipherAlgo *cipher, void *context, uint_t s,
+   uint8_t *iv, const uint8_t *p, uint8_t *c, size_t length)
+{
+   size_t i;
+   size_t n;
+   uint8_t o[16];
+
+   //The parameter must be a multiple of 8
+   if(s % 8)
+      return ERROR_INVALID_PARAMETER;
+
+   //Determine the size, in bytes, of the plaintext and ciphertext segments
+   s = s / 8;
+
+   //Check the resulting value
+   if(s < 1 || s > cipher->blockSize)
+      return ERROR_INVALID_PARAMETER;
+
+   //Process each plaintext segment
+   while(length > 0)
+   {
+      //Compute the number of bytes to process at a time
+      n = MIN(length, s);
+
+      //Compute O(j) = CIPH(I(j))
+      cipher->encryptBlock(context, iv, o);
+
+      //Compute C(j) = P(j) XOR MSB(O(j))
+      for(i = 0; i < n; i++)
+         c[i] = p[i] ^ o[i];
+
+      //Compute I(j+1) = LSB(I(j)) | C(j)
+      memmove(iv, iv + s, cipher->blockSize - s);
+      memcpy(iv + cipher->blockSize - s, c, s);
+
+      //Next block
+      p += n;
+      c += n;
+      length -= n;
+   }
+
+   //Successful encryption
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief CFB decryption
+ * @param[in] cipher Cipher algorithm
+ * @param[in] context Cipher algorithm context
+ * @param[in] s Size of the plaintext and ciphertext segments
+ * @param[in,out] iv Initialization vector
+ * @param[in] c Ciphertext to be decrypted
+ * @param[out] p Plaintext resulting from the decryption
+ * @param[in] length Total number of data bytes to be decrypted
+ * @return Error code
+ **/
+
+error_t cfbDecrypt(const CipherAlgo *cipher, void *context, uint_t s,
+   uint8_t *iv, const uint8_t *c, uint8_t *p, size_t length)
+{
+   size_t i;
+   size_t n;
+   uint8_t o[16];
+
+   //The parameter must be a multiple of 8
+   if(s % 8)
+      return ERROR_INVALID_PARAMETER;
+
+   //Determine the size, in bytes, of the plaintext and ciphertext segments
+   s = s / 8;
+
+   //Check the resulting value
+   if(s < 1 || s > cipher->blockSize)
+      return ERROR_INVALID_PARAMETER;
+
+   //Process each ciphertext segment
+   while(length > 0)
+   {
+      //Compute the number of bytes to process at a time
+      n = MIN(length, s);
+
+      //Compute O(j) = CIPH(I(j))
+      cipher->encryptBlock(context, iv, o);
+
+      //Compute I(j+1) = LSB(I(j)) | C(j)
+      memmove(iv, iv + s, cipher->blockSize - s);
+      memcpy(iv + cipher->blockSize - s, c, s);
+
+      //Compute P(j) = C(j) XOR MSB(O(j))
+      for(i = 0; i < n; i++)
+         p[i] = c[i] ^ o[i];
+
+      //Next block
+      c += n;
+      p += n;
+      length -= n;
+   }
+
+   //Successful encryption
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/cipher_mode_cfb.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,43 @@
+/**
+ * @file cipher_mode_cfb.h
+ * @brief Cipher Feedback (CFB) mode
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _CIPHER_MODE_CFB_H
+#define _CIPHER_MODE_CFB_H
+
+//Dependencies
+#include "crypto.h"
+
+//CFB encryption and decryption routines
+error_t cfbEncrypt(const CipherAlgo *cipher, void *context, uint_t s,
+   uint8_t *iv, const uint8_t *p, uint8_t *c, size_t length);
+
+error_t cfbDecrypt(const CipherAlgo *cipher, void *context, uint_t s,
+   uint8_t *iv, const uint8_t *c, uint8_t *p, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/cipher_mode_ctr.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,175 @@
+/**
+ * @file cipher_mode_ctr.c
+ * @brief Counter(CTR) mode
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The Counter (CTR) mode is a confidentiality mode that features the application
+ * of the forward cipher to a set of input blocks, called counters, to produce
+ * a sequence of output blocks that are exclusive-ORed with the plaintext to
+ * produce the ciphertext, and vice versa. Refer to SP 800-38A for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "cipher_mode_ctr.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (CTR_SUPPORT == ENABLED)
+
+
+/**
+ * @brief CTR encryption
+ * @param[in] cipher Cipher algorithm
+ * @param[in] context Cipher algorithm context
+ * @param[in] m Size in bits of the specific part of the block to be incremented
+ * @param[in,out] t Initial counter block
+ * @param[in] p Plaintext to be encrypted
+ * @param[out] c Ciphertext resulting from the encryption
+ * @param[in] length Total number of data bytes to be encrypted
+ * @return Error code
+ **/
+
+error_t ctrEncrypt(const CipherAlgo *cipher, void *context, uint_t m,
+   uint8_t *t, const uint8_t *p, uint8_t *c, size_t length)
+{
+   size_t i;
+   size_t n;
+   uint8_t o[16];
+
+   //The parameter must be a multiple of 8
+   if(m % 8)
+      return ERROR_INVALID_PARAMETER;
+
+   //Determine the size, in bytes, of the specific part of
+   //the block to be incremented
+   m = m / 8;
+
+   //Check the resulting value
+   if(m > cipher->blockSize)
+      return ERROR_INVALID_PARAMETER;
+
+   //Process plaintext
+   while(length > 0)
+   {
+      //CTR mode operates in a block-by-block fashion
+      n = MIN(length, cipher->blockSize);
+
+      //Compute O(j) = CIPH(T(j))
+      cipher->encryptBlock(context, t, o);
+
+      //Compute C(j) = P(j) XOR T(j)
+      for(i = 0; i < n; i++)
+         c[i] = p[i] ^ o[i];
+
+      //Standard incrementing function
+      for(i = 0; i < m; i++)
+      {
+         //Increment the current byte and propagate the carry if necessary
+         if(++(t[cipher->blockSize - 1 - i]) != 0)
+            break;
+      }
+
+      //Next block
+      p += n;
+      c += n;
+      length -= n;
+   }
+
+   //Successful encryption
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief CTR decryption
+ * @param[in] cipher Cipher algorithm
+ * @param[in] context Cipher algorithm context
+ * @param[in] m Size in bits of the specific part of the block to be incremented
+ * @param[in,out] t Initial counter block
+ * @param[in] c Ciphertext to be decrypted
+ * @param[out] p Plaintext resulting from the decryption
+ * @param[in] length Total number of data bytes to be decrypted
+ * @return Error code
+ **/
+
+error_t ctrDecrypt(const CipherAlgo *cipher, void *context, uint_t m,
+   uint8_t *t, const uint8_t *c, uint8_t *p, size_t length)
+{
+   size_t i;
+   size_t n;
+   uint8_t o[16];
+
+   //The parameter must be a multiple of 8
+   if(m % 8)
+      return ERROR_INVALID_PARAMETER;
+
+   //Determine the size, in bytes, of the specific part of
+   //the block to be incremented
+   m = m / 8;
+
+   //Check the resulting value
+   if(m > cipher->blockSize)
+      return ERROR_INVALID_PARAMETER;
+
+   //Process ciphertext
+   while(length > 0)
+   {
+      //CTR mode operates in a block-by-block fashion
+      n = MIN(length, cipher->blockSize);
+
+      //Compute O(j) = CIPH(T(j))
+      cipher->encryptBlock(context, t, o);
+
+      //Compute P(j) = C(j) XOR T(j)
+      for(i = 0; i < n; i++)
+         p[i] = c[i] ^ o[i];
+
+      //Standard incrementing function
+      for(i = 0; i < m; i++)
+      {
+         //Increment the current byte and propagate the carry if necessary
+         if(++(t[cipher->blockSize - 1 - i]) != 0)
+            break;
+      }
+
+      //Next block
+      c += n;
+      p += n;
+      length -= n;
+   }
+
+   //Successful encryption
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/cipher_mode_ctr.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,43 @@
+/**
+ * @file cipher_mode_ctr.h
+ * @brief Counter(CTR) mode
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _CIPHER_MODE_CTR_H
+#define _CIPHER_MODE_CTR_H
+
+//Dependencies
+#include "crypto.h"
+
+//CTR encryption and decryption routines
+error_t ctrEncrypt(const CipherAlgo *cipher, void *context, uint_t m,
+   uint8_t *t, const uint8_t *p, uint8_t *c, size_t length);
+
+error_t ctrDecrypt(const CipherAlgo *cipher, void *context, uint_t m,
+   uint8_t *t, const uint8_t *c, uint8_t *p, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/cipher_mode_ecb.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,117 @@
+/**
+ * @file cipher_mode_ecb.c
+ * @brief Electronic Codebook (ECB) mode
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The Electronic Codebook (ECB) mode is a confidentiality mode that features,
+ * for a given key, the assignment of a fixed ciphertext block to each
+ * plaintext block, analogous to the assignment of code words in a codebook.
+ * Refer to SP 800-38A for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "cipher_mode_ecb.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (ECB_SUPPORT == ENABLED)
+
+
+/**
+ * @brief ECB encryption
+ * @param[in] cipher Cipher algorithm
+ * @param[in] context Cipher algorithm context
+ * @param[in] p Plaintext to be encrypted
+ * @param[out] c Ciphertext resulting from the encryption
+ * @param[in] length Total number of data bytes to be encrypted
+ * @return Error code
+ **/
+
+error_t ecbEncrypt(const CipherAlgo *cipher, void *context,
+   const uint8_t *p, uint8_t *c, size_t length)
+{
+   //ECB mode operates in a block-by-block fashion
+   while(length >= cipher->blockSize)
+   {
+      //Encrypt current block
+      cipher->encryptBlock(context, p, c);
+
+      //Next block
+      p += cipher->blockSize;
+      c += cipher->blockSize;
+      length -= cipher->blockSize;
+   }
+
+   //The plaintext must be a multiple of the block size
+   if(length != 0)
+      return ERROR_INVALID_LENGTH;
+
+   //Successful encryption
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief ECB decryption
+ * @param[in] cipher Cipher algorithm
+ * @param[in] context Cipher algorithm context
+ * @param[in] c Ciphertext to be decrypted
+ * @param[out] p Plaintext resulting from the decryption
+ * @param[in] length Total number of data bytes to be decrypted
+ * @return Error code
+ **/
+
+error_t ecbDecrypt(const CipherAlgo *cipher, void *context,
+   const uint8_t *c, uint8_t *p, size_t length)
+{
+   //ECB mode operates in a block-by-block fashion
+   while(length >= cipher->blockSize)
+   {
+      //Decrypt current block
+      cipher->decryptBlock(context, c, p);
+
+      //Next block
+      c += cipher->blockSize;
+      p += cipher->blockSize;
+      length -= cipher->blockSize;
+   }
+
+   //The ciphertext must be a multiple of the block size
+   if(length != 0)
+      return ERROR_INVALID_LENGTH;
+
+   //Successful encryption
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/cipher_mode_ecb.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,43 @@
+/**
+ * @file cipher_mode_ecb.h
+ * @brief Electronic Codebook (ECB) mode
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _CIPHER_MODE_ECB_H
+#define _CIPHER_MODE_ECB_H
+
+//Dependencies
+#include "crypto.h"
+
+//ECB encryption and decryption routines
+error_t ecbEncrypt(const CipherAlgo *cipher, void *context,
+   const uint8_t *p, uint8_t *c, size_t length);
+
+error_t ecbDecrypt(const CipherAlgo *cipher, void *context,
+   const uint8_t *c, uint8_t *p, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/cipher_mode_gcm.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,569 @@
+/**
+ * @file cipher_mode_gcm.c
+ * @brief Galois/Counter Mode (GCM)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The Galois/Counter Mode (GCM) is an authenticated encryption algorithm
+ * designed to provide both data authenticity (integrity) and confidentiality.
+ * Refer to SP 800-38D for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "cipher_mode_gcm.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (GCM_SUPPORT == ENABLED)
+
+//Reduction table
+static const uint32_t r[16] =
+{
+   0x00000000,
+   0x1C200000,
+   0x38400000,
+   0x24600000,
+   0x70800000,
+   0x6CA00000,
+   0x48C00000,
+   0x54E00000,
+   0xE1000000,
+   0xFD200000,
+   0xD9400000,
+   0xC5600000,
+   0x91800000,
+   0x8DA00000,
+   0xA9C00000,
+   0xB5E00000
+};
+
+
+/**
+ * @brief Initialize GCM context
+ * @param[in] context Pointer to the GCM context
+ * @param[in] cipherAlgo Cipher algorithm
+ * @param[in] cipherContext Pointer to the cipher algorithm context
+ * @return Error code
+ **/
+
+error_t gcmInit(GcmContext *context, const CipherAlgo *cipherAlgo, void *cipherContext)
+{
+   uint_t i;
+   uint_t j;
+   uint8_t c;
+   uint32_t h[4];
+
+   //GCM supports only symmetric block ciphers whose block size is 128 bits
+   if(cipherAlgo->type != CIPHER_ALGO_TYPE_BLOCK || cipherAlgo->blockSize != 16)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save cipher algorithm context
+   context->cipherAlgo = cipherAlgo;
+   context->cipherContext = cipherContext;
+
+   //Let H = 0
+   h[0] = 0;
+   h[1] = 0;
+   h[2] = 0;
+   h[3] = 0;
+
+   //Generate the hash subkey H
+   context->cipherAlgo->encryptBlock(context->cipherContext,
+      (uint8_t *) h, (uint8_t *) h);
+
+   //Pre-compute M(0) = H * 0
+   j = reverseInt4(0);
+   context->m[j][0] = 0;
+   context->m[j][1] = 0;
+   context->m[j][2] = 0;
+   context->m[j][3] = 0;
+
+   //Pre-compute M(1) = H * 1
+   j = reverseInt4(1);
+   context->m[j][0] = betoh32(h[3]);
+   context->m[j][1] = betoh32(h[2]);
+   context->m[j][2] = betoh32(h[1]);
+   context->m[j][3] = betoh32(h[0]);
+
+   //Pre-compute all 4-bit multiples of H
+   for(i = 2; i < 16; i++)
+   {
+      //Odd value?
+      if(i & 1)
+      {
+         //Compute M(i) = M(i - 1) + H
+         j = reverseInt4(i - 1);
+         h[0] = context->m[j][0];
+         h[1] = context->m[j][1];
+         h[2] = context->m[j][2];
+         h[3] = context->m[j][3];
+
+         //An addition in GF(2^128) is identical to a bitwise
+         //exclusive-OR operation
+         j = reverseInt4(1);
+         h[0] ^= context->m[j][0];
+         h[1] ^= context->m[j][1];
+         h[2] ^= context->m[j][2];
+         h[3] ^= context->m[j][3];
+      }
+      //Even value?
+      else
+      {
+         //Compute M(i) = M(i / 2) * x
+         j = reverseInt4(i / 2);
+         h[0] = context->m[j][0];
+         h[1] = context->m[j][1];
+         h[2] = context->m[j][2];
+         h[3] = context->m[j][3];
+
+         //The multiplication of a polynomial by x in GF(2^128)
+         //corresponds to a shift of indices
+         c = h[0] & 0x01;
+         h[0] = (h[0] >> 1) | (h[1] << 31);
+         h[1] = (h[1] >> 1) | (h[2] << 31);
+         h[2] = (h[2] >> 1) | (h[3] << 31);
+         h[3] >>= 1;
+
+         //If the highest term of the result is equal to one,
+         //then perform reduction
+         if(c != 0)
+            h[3] ^= r[reverseInt4(1)];
+      }
+
+      //Save M(i)
+      j = reverseInt4(i);
+      context->m[j][0] = h[0];
+      context->m[j][1] = h[1];
+      context->m[j][2] = h[2];
+      context->m[j][3] = h[3];
+   }
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Authenticated encryption using GCM
+ * @param[in] context Pointer to the GCM context
+ * @param[in] iv Initialization vector
+ * @param[in] ivLen Length of the initialization vector
+ * @param[in] a Additional authenticated data
+ * @param[in] aLen Length of the additional data
+ * @param[in] p Plaintext to be encrypted
+ * @param[out] c Ciphertext resulting from the encryption
+ * @param[in] length Total number of data bytes to be encrypted
+ * @param[out] t Authentication tag
+ * @param[in] tLen Length of the authentication tag
+ * @return Error code
+ **/
+
+error_t gcmEncrypt(GcmContext *context, const uint8_t *iv, size_t ivLen, const uint8_t *a,
+   size_t aLen, const uint8_t *p, uint8_t *c, size_t length, uint8_t *t, size_t tLen)
+{
+   size_t k;
+   size_t n;
+   uint8_t b[16];
+   uint8_t j[16];
+   uint8_t s[16];
+
+   //Make sure the GCM context is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //The length of the IV shall meet SP 800-38D requirements
+   if(ivLen < 1)
+      return ERROR_INVALID_PARAMETER;
+   //Check the length of the authentication tag
+   if(tLen < 4 || tLen > 16)
+      return ERROR_INVALID_PARAMETER;
+
+   //Check whether the length of the IV is 96 bits
+   if(ivLen == 12)
+   {
+      //When the length of the IV is 96 bits, the padding string is
+      //appended to the IV to form the pre-counter block
+      memcpy(j, iv, 12);
+      STORE32BE(1, j + 12);
+   }
+   else
+   {
+      //Initialize GHASH calculation
+      memset(j, 0, 16);
+
+      //Length of the IV
+      n = ivLen;
+
+      //Process the initialization vector
+      while(n > 0)
+      {
+         //The IV processed in a block-by-block fashion
+         k = MIN(n, 16);
+
+         //Apply GHASH function
+         gcmXorBlock(j, j, iv, k);
+         gcmMul(context, j);
+
+         //Next block
+         iv += k;
+         n -= k;
+      }
+
+      //The string is appended with 64 additional 0 bits, followed by the
+      //64-bit representation of the length of the IV
+      memset(b, 0, 16);
+      STORE32BE(ivLen * 8, b + 12);
+
+      //The GHASH function is applied to the resulting string to form the
+      //pre-counter block
+      gcmXorBlock(j, j, b, 16);
+      gcmMul(context, j);
+   }
+
+   //Compute MSB(CIPH(J(0)))
+   context->cipherAlgo->encryptBlock(context->cipherContext, j, b);
+   memcpy(t, b, tLen);
+
+   //Initialize GHASH calculation
+   memset(s, 0, 16);
+   //Length of the AAD
+   n = aLen;
+
+   //Process AAD
+   while(n > 0)
+   {
+      //Additional data are processed in a block-by-block fashion
+      k = MIN(n, 16);
+
+      //Apply GHASH function
+      gcmXorBlock(s, s, a, k);
+      gcmMul(context, s);
+
+      //Next block
+      a += k;
+      n -= k;
+   }
+
+   //Length of the plaintext
+   n = length;
+
+   //Process plaintext
+   while(n > 0)
+   {
+      //The encryption operates in a block-by-block fashion
+      k = MIN(n, 16);
+
+      //Increment counter
+      gcmIncCounter(j);
+
+      //Encrypt plaintext
+      context->cipherAlgo->encryptBlock(context->cipherContext, j, b);
+      gcmXorBlock(c, p, b, k);
+
+      //Apply GHASH function
+      gcmXorBlock(s, s, c, k);
+      gcmMul(context, s);
+
+      //Next block
+      p += k;
+      c += k;
+      n -= k;
+   }
+
+   //Append the 64-bit representation of the length of the AAD and the ciphertext
+   memset(b, 0, 16);
+   STORE32BE(aLen * 8, b + 4);
+   STORE32BE(length * 8, b + 12);
+
+   //The GHASH function is applied to the result to produce a single output block S
+   gcmXorBlock(s, s, b, 16);
+   gcmMul(context, s);
+
+   //Let T = MSB(GCTR(J(0), S)
+   gcmXorBlock(t, t, s, tLen);
+
+   //Successful encryption
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Authenticated decryption using GCM
+ * @param[in] context Pointer to the GCM context
+ * @param[in] iv Initialization vector
+ * @param[in] ivLen Length of the initialization vector
+ * @param[in] a Additional authenticated data
+ * @param[in] aLen Length of the additional data
+ * @param[in] c Ciphertext to be decrypted
+ * @param[out] p Plaintext resulting from the decryption
+ * @param[in] length Total number of data bytes to be decrypted
+ * @param[in] t Authentication tag
+ * @param[in] tLen Length of the authentication tag
+ * @return Error code
+ **/
+
+error_t gcmDecrypt(GcmContext *context, const uint8_t *iv, size_t ivLen, const uint8_t *a,
+   size_t aLen, const uint8_t *c, uint8_t *p, size_t length, const uint8_t *t, size_t tLen)
+{
+   size_t k;
+   size_t n;
+   uint8_t b[16];
+   uint8_t j[16];
+   uint8_t r[16];
+   uint8_t s[16];
+
+   ///Make sure the GCM context is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //The length of the IV shall meet SP 800-38D requirements
+   if(ivLen < 1)
+      return ERROR_INVALID_PARAMETER;
+   //Check the length of the authentication tag
+   if(tLen < 4 || tLen > 16)
+      return ERROR_INVALID_PARAMETER;
+
+   //Check whether the length of the IV is 96 bits
+   if(ivLen == 12)
+   {
+      //When the length of the IV is 96 bits, the padding string is
+      //appended to the IV to form the pre-counter block
+      memcpy(j, iv, 12);
+      STORE32BE(1, j + 12);
+   }
+   else
+   {
+      //Initialize GHASH calculation
+      memset(j, 0, 16);
+
+      //Length of the IV
+      n = ivLen;
+
+      //Process the initialization vector
+      while(n > 0)
+      {
+         //The IV processed in a block-by-block fashion
+         k = MIN(n, 16);
+
+         //Apply GHASH function
+         gcmXorBlock(j, j, iv, k);
+         gcmMul(context, j);
+
+         //Next block
+         iv += k;
+         n -= k;
+      }
+
+      //The string is appended with 64 additional 0 bits, followed by the
+      //64-bit representation of the length of the IV
+      memset(b, 0, 16);
+      STORE32BE(ivLen * 8, b + 12);
+
+      //The GHASH function is applied to the resulting string to form the
+      //pre-counter block
+      gcmXorBlock(j, j, b, 16);
+      gcmMul(context, j);
+   }
+
+   //Compute MSB(CIPH(J(0)))
+   context->cipherAlgo->encryptBlock(context->cipherContext, j, b);
+   memcpy(r, b, tLen);
+
+   //Initialize GHASH calculation
+   memset(s, 0, 16);
+   //Length of the AAD
+   n = aLen;
+
+   //Process AAD
+   while(n > 0)
+   {
+      //Additional data are processed in a block-by-block fashion
+      k = MIN(n, 16);
+
+      //Apply GHASH function
+      gcmXorBlock(s, s, a, k);
+      gcmMul(context, s);
+
+      //Next block
+      a += k;
+      n -= k;
+   }
+
+   //Length of the ciphertext
+   n = length;
+
+   //Process ciphertext
+   while(n > 0)
+   {
+      //The decryption operates in a block-by-block fashion
+      k = MIN(n, 16);
+
+      //Apply GHASH function
+      gcmXorBlock(s, s, c, k);
+      gcmMul(context, s);
+
+      //Increment counter
+      gcmIncCounter(j);
+
+      //Decrypt ciphertext
+      context->cipherAlgo->encryptBlock(context->cipherContext, j, b);
+      gcmXorBlock(p, c, b, k);
+
+      //Next block
+      c += k;
+      p += k;
+      n -= k;
+   }
+
+   //Append the 64-bit representation of the length of the AAD and the ciphertext
+   memset(b, 0, 16);
+   STORE32BE(aLen * 8, b + 4);
+   STORE32BE(length * 8, b + 12);
+
+   //The GHASH function is applied to the result to produce a single output block S
+   gcmXorBlock(s, s, b, 16);
+   gcmMul(context, s);
+
+   //Let R = MSB(GCTR(J(0), S)
+   gcmXorBlock(r, r, s, tLen);
+
+   //The calculated tag is bitwise compared to the received tag. The
+   //message is authenticated if and only if the tags match
+   if(memcmp(r, t, tLen))
+      return ERROR_FAILURE;
+
+   //Successful decryption
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Multiplication operation
+ * @param[in] context Pointer to the GCM context
+ * @param[in, out] x 16-byte block to be multiplied by H
+ **/
+
+void gcmMul(GcmContext *context, uint8_t *x)
+{
+   int_t i;
+   uint8_t b;
+   uint8_t c;
+   uint32_t z[4];
+
+   //Let Z = 0
+   z[0] = 0;
+   z[1] = 0;
+   z[2] = 0;
+   z[3] = 0;
+
+   //Fast table-driven implementation
+   for(i = 15; i >= 0; i--)
+   {
+      //Process the lower nibble
+      b = x[i] & 0x0F;
+
+      c = z[0] & 0x0F;
+      z[0] = (z[0] >> 4) | (z[1] << 28);
+      z[1] = (z[1] >> 4) | (z[2] << 28);
+      z[2] = (z[2] >> 4) | (z[3] << 28);
+      z[3] >>= 4;
+
+      z[3] ^= r[c];
+
+      z[0] ^= context->m[b][0];
+      z[1] ^= context->m[b][1];
+      z[2] ^= context->m[b][2];
+      z[3] ^= context->m[b][3];
+
+      //Process the upper nibble
+      b = (x[i] >> 4) & 0x0F;
+
+      c = z[0] & 0x0F;
+      z[0] = (z[0] >> 4) | (z[1] << 28);
+      z[1] = (z[1] >> 4) | (z[2] << 28);
+      z[2] = (z[2] >> 4) | (z[3] << 28);
+      z[3] >>= 4;
+
+      z[3] ^= r[c];
+
+      z[0] ^= context->m[b][0];
+      z[1] ^= context->m[b][1];
+      z[2] ^= context->m[b][2];
+      z[3] ^= context->m[b][3];
+   }
+
+   //Save the result
+   STORE32BE(z[3], x);
+   STORE32BE(z[2], x + 4);
+   STORE32BE(z[1], x + 8);
+   STORE32BE(z[0], x + 12);
+}
+
+
+/**
+ * @brief XOR operation
+ * @param[out] x Block resulting from the XOR operation
+ * @param[in] a First block
+ * @param[in] b Second block
+ * @param[in] n Size of the block
+ **/
+
+void gcmXorBlock(uint8_t *x, const uint8_t *a, const uint8_t *b, size_t n)
+{
+   size_t i;
+
+   //Perform XOR operation
+   for(i = 0; i < n; i++)
+      x[i] = a[i] ^ b[i];
+}
+
+
+/**
+ * @brief Increment counter block
+ * @param[in,out] x Pointer to the counter block
+ **/
+
+void gcmIncCounter(uint8_t *x)
+{
+   size_t i;
+
+   //The function increments the right-most 32 bits of the block. The remaining
+   //left-most 96 bits remain unchanged
+   for(i = 0; i < 4; i++)
+   {
+      //Increment the current byte and propagate the carry if necessary
+      if(++(x[15 - i]) != 0)
+         break;
+   }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/cipher_mode_gcm.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,62 @@
+/**
+ * @file cipher_mode_gcm.h
+ * @brief Galois/Counter Mode (GCM)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _CIPHER_MODE_GCM_H
+#define _CIPHER_MODE_GCM_H
+
+//Dependencies
+#include "crypto.h"
+
+
+/**
+ * @brief GCM context
+ **/
+
+typedef struct
+{
+   const CipherAlgo *cipherAlgo; ///<Cipher algorithm
+   void *cipherContext;          ///<Cipher algorithm context
+   uint32_t m[16][4];            ///<Precalculated table
+} GcmContext;
+
+
+//GCM related functions
+error_t gcmInit(GcmContext *context, const CipherAlgo *cipherAlgo, void *cipherContext);
+
+error_t gcmEncrypt(GcmContext *context, const uint8_t *iv, size_t ivLen, const uint8_t *a,
+   size_t aLen, const uint8_t *p, uint8_t *c, size_t length, uint8_t *t, size_t tLen);
+
+error_t gcmDecrypt(GcmContext *context, const uint8_t *iv, size_t ivLen, const uint8_t *a,
+   size_t aLen, const uint8_t *c, uint8_t *p, size_t length, const uint8_t *t, size_t tLen);
+
+void gcmMul(GcmContext *context, uint8_t *x);
+void gcmXorBlock(uint8_t *x, const uint8_t *a, const uint8_t *b, size_t n);
+void gcmIncCounter(uint8_t *x);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/cipher_mode_ofb.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,167 @@
+/**
+ * @file cipher_mode_ofb.c
+ * @brief Output Feedback (OFB) mode
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The Output Feedback (OFB) mode is a confidentiality mode that features the
+ * iteration of the forward cipher on an IV to generate a sequence of output
+ * blocks that are exclusive-ORed with the plaintext to produce the ciphertext,
+ * and vice versa. The OFB mode requires that the IV is a nonce, i.e., the IV
+ * must be unique for each execution of the mode under the given key.
+ * Refer to SP 800-38A for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "cipher_mode_ofb.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (OFB_SUPPORT == ENABLED)
+
+
+/**
+ * @brief OFB encryption
+ * @param[in] cipher Cipher algorithm
+ * @param[in] context Cipher algorithm context
+ * @param[in] s Size of the plaintext and ciphertext segments
+ * @param[in,out] iv Initialization vector
+ * @param[in] p Plaintext to be encrypted
+ * @param[out] c Ciphertext resulting from the encryption
+ * @param[in] length Total number of data bytes to be encrypted
+ * @return Error code
+ **/
+
+error_t ofbEncrypt(const CipherAlgo *cipher, void *context, uint_t s,
+   uint8_t *iv, const uint8_t *p, uint8_t *c, size_t length)
+{
+   size_t i;
+   uint8_t o[16];
+
+   //The parameter must be a multiple of 8
+   if(s % 8)
+      return ERROR_INVALID_PARAMETER;
+
+   //Determine the size, in bytes, of the plaintext and ciphertext segments
+   s = s / 8;
+
+   //Check the resulting value
+   if(s < 1 || s > cipher->blockSize)
+      return ERROR_INVALID_PARAMETER;
+
+   //Process each plaintext segment
+   while(length >= s)
+   {
+      //Compute O(j) = CIPH(I(j))
+      cipher->encryptBlock(context, iv, o);
+
+      //Compute C(j) = P(j) XOR MSB(O(j))
+      for(i = 0; i < s; i++)
+         c[i] = p[i] ^ o[i];
+
+      //Compute I(j+1) = LSB(I(j)) | O(j)
+      memmove(iv, iv + s, cipher->blockSize - s);
+      memcpy(iv + cipher->blockSize - s, o, s);
+
+      //Next block
+      p += s;
+      c += s;
+      length -= s;
+   }
+
+   //The plaintext must be a multiple of the segment size
+   if(length != 0)
+      return ERROR_INVALID_LENGTH;
+
+   //Successful encryption
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief OFB decryption
+ * @param[in] cipher Cipher algorithm
+ * @param[in] context Cipher algorithm context
+ * @param[in] s Size of the plaintext and ciphertext segments
+ * @param[in,out] iv Initialization vector
+ * @param[in] c Ciphertext to be decrypted
+ * @param[out] p Plaintext resulting from the decryption
+ * @param[in] length Total number of data bytes to be decrypted
+ * @return Error code
+ **/
+
+error_t ofbDecrypt(const CipherAlgo *cipher, void *context, uint_t s,
+   uint8_t *iv, const uint8_t *c, uint8_t *p, size_t length)
+{
+   size_t i;
+   uint8_t o[16];
+
+   //The parameter must be a multiple of 8
+   if(s % 8)
+      return ERROR_INVALID_PARAMETER;
+
+   //Determine the size, in bytes, of the plaintext and ciphertext segments
+   s = s / 8;
+
+   //Check the resulting value
+   if(s < 1 || s > cipher->blockSize)
+      return ERROR_INVALID_PARAMETER;
+
+   //Process each ciphertext segment
+   while(length >= s)
+   {
+      //Compute O(j) = CIPH(I(j))
+      cipher->encryptBlock(context, iv, o);
+
+      //Compute P(j) = C(j) XOR MSB(O(j))
+      for(i = 0; i < s; i++)
+         p[i] = c[i] ^ o[i];
+
+      //Compute I(j+1) = LSB(I(j)) | O(j)
+      memmove(iv, iv + s, cipher->blockSize - s);
+      memcpy(iv + cipher->blockSize - s, o, s);
+
+      //Next block
+      c += s;
+      p += s;
+      length -= s;
+   }
+
+   //The plaintext must be a multiple of the segment size
+   if(length != 0)
+      return ERROR_INVALID_LENGTH;
+
+   //Successful encryption
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/cipher_mode_ofb.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,43 @@
+/**
+ * @file cipher_mode_ofb.h
+ * @brief Output Feedback (OFB) mode
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _CIPHER_MODE_OFB_H
+#define _CIPHER_MODE_OFB_H
+
+//Dependencies
+#include "crypto.h"
+
+//OFB encryption and decryption routines
+error_t ofbEncrypt(const CipherAlgo *cipher, void *context, uint_t s,
+   uint8_t *iv, const uint8_t *p, uint8_t *c, size_t length);
+
+error_t ofbDecrypt(const CipherAlgo *cipher, void *context, uint_t s,
+   uint8_t *iv, const uint8_t *c, uint8_t *p, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/crypto.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,722 @@
+/**
+ * @file crypto.h
+ * @brief General definitions for cryptographic algorithms
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _CRYPTO_H
+#define _CRYPTO_H
+
+//Dependencies
+#include "os_port.h"
+#include "crypto_config.h"
+#include "cpu_endian.h"
+#include "error.h"
+
+//Multiple precision integer support
+#ifndef MPI_SUPPORT
+   #define MPI_SUPPORT ENABLED
+#elif (MPI_SUPPORT != ENABLED && MPI_SUPPORT != DISABLED)
+   #error MPI_SUPPORT parameter is not valid
+#endif
+
+//Assembly optimizations for time-critical routines
+#ifndef MPI_ASM_SUPPORT
+   #define MPI_ASM_SUPPORT DISABLED
+#elif (MPI_ASM_SUPPORT != ENABLED && MPI_ASM_SUPPORT != DISABLED)
+   #error MPI_ASM_SUPPORT parameter is not valid
+#endif
+
+//Base64 encoding support
+#ifndef BASE64_SUPPORT
+   #define BASE64_SUPPORT ENABLED
+#elif (BASE64_SUPPORT != ENABLED && BASE64_SUPPORT != DISABLED)
+   #error BASE64_SUPPORT parameter is not valid
+#endif
+
+//MD2 hash support
+#ifndef MD2_SUPPORT
+   #define MD2_SUPPORT ENABLED
+#elif (MD2_SUPPORT != ENABLED && MD2_SUPPORT != DISABLED)
+   #error MD2_SUPPORT parameter is not valid
+#endif
+
+//MD4 hash support
+#ifndef MD4_SUPPORT
+   #define MD4_SUPPORT ENABLED
+#elif (MD4_SUPPORT != ENABLED && MD4_SUPPORT != DISABLED)
+   #error MD4_SUPPORT parameter is not valid
+#endif
+
+//MD5 hash support
+#ifndef MD5_SUPPORT
+   #define MD5_SUPPORT ENABLED
+#elif (MD5_SUPPORT != ENABLED && MD5_SUPPORT != DISABLED)
+   #error MD5_SUPPORT parameter is not valid
+#endif
+
+//RIPEMD-128 hash support
+#ifndef RIPEMD128_SUPPORT
+   #define RIPEMD128_SUPPORT ENABLED
+#elif (RIPEMD128_SUPPORT != ENABLED && RIPEMD128_SUPPORT != DISABLED)
+   #error RIPEMD128_SUPPORT parameter is not valid
+#endif
+
+//RIPEMD-160 hash support
+#ifndef RIPEMD160_SUPPORT
+   #define RIPEMD160_SUPPORT ENABLED
+#elif (RIPEMD160_SUPPORT != ENABLED && RIPEMD160_SUPPORT != DISABLED)
+   #error RIPEMD160_SUPPORT parameter is not valid
+#endif
+
+//SHA-1 hash support
+#ifndef SHA1_SUPPORT
+   #define SHA1_SUPPORT ENABLED
+#elif (SHA1_SUPPORT != ENABLED && SHA1_SUPPORT != DISABLED)
+   #error SHA1_SUPPORT parameter is not valid
+#endif
+
+//SHA-224 hash support
+#ifndef SHA224_SUPPORT
+   #define SHA224_SUPPORT ENABLED
+#elif (SHA224_SUPPORT != ENABLED && SHA224_SUPPORT != DISABLED)
+   #error SHA224_SUPPORT parameter is not valid
+#endif
+
+//SHA-256 hash support
+#ifndef SHA256_SUPPORT
+   #define SHA256_SUPPORT ENABLED
+#elif (SHA256_SUPPORT != ENABLED && SHA256_SUPPORT != DISABLED)
+   #error SHA256_SUPPORT parameter is not valid
+#endif
+
+//SHA-384 hash support
+#ifndef SHA384_SUPPORT
+   #define SHA384_SUPPORT ENABLED
+#elif (SHA384_SUPPORT != ENABLED && SHA384_SUPPORT != DISABLED)
+   #error SHA384_SUPPORT parameter is not valid
+#endif
+
+//SHA-512 hash support
+#ifndef SHA512_SUPPORT
+   #define SHA512_SUPPORT ENABLED
+#elif (SHA512_SUPPORT != ENABLED && SHA512_SUPPORT != DISABLED)
+   #error SHA512_SUPPORT parameter is not valid
+#endif
+
+//SHA-512/224 hash support
+#ifndef SHA512_224_SUPPORT
+   #define SHA512_224_SUPPORT ENABLED
+#elif (SHA512_224_SUPPORT != ENABLED && SHA512_224_SUPPORT != DISABLED)
+   #error SHA512_224_SUPPORT parameter is not valid
+#endif
+
+//SHA-512/256 hash support
+#ifndef SHA512_256_SUPPORT
+   #define SHA512_256_SUPPORT ENABLED
+#elif (SHA512_256_SUPPORT != ENABLED && SHA512_256_SUPPORT != DISABLED)
+   #error SHA512_256_SUPPORT parameter is not valid
+#endif
+
+//SHA3-224 hash support
+#ifndef SHA3_224_SUPPORT
+   #define SHA3_224_SUPPORT DISABLED
+#elif (SHA3_224_SUPPORT != ENABLED && SHA3_224_SUPPORT != DISABLED)
+   #error SHA3_224_SUPPORT parameter is not valid
+#endif
+
+//SHA3-256 hash support
+#ifndef SHA3_256_SUPPORT
+   #define SHA3_256_SUPPORT DISABLED
+#elif (SHA3_256_SUPPORT != ENABLED && SHA3_256_SUPPORT != DISABLED)
+   #error SHA3_256_SUPPORT parameter is not valid
+#endif
+
+//SHA3-384 hash support
+#ifndef SHA3_384_SUPPORT
+   #define SHA3_384_SUPPORT DISABLED
+#elif (SHA3_384_SUPPORT != ENABLED && SHA3_384_SUPPORT != DISABLED)
+   #error SHA3_384_SUPPORT parameter is not valid
+#endif
+
+//SHA3-512 hash support
+#ifndef SHA3_512_SUPPORT
+   #define SHA3_512_SUPPORT DISABLED
+#elif (SHA3_512_SUPPORT != ENABLED && SHA3_512_SUPPORT != DISABLED)
+   #error SHA3_512_SUPPORT parameter is not valid
+#endif
+
+//Keccak support
+#ifndef KECCAK_SUPPORT
+   #define KECCAK_SUPPORT DISABLED
+#elif (KECCAK_SUPPORT != ENABLED && KECCAK_SUPPORT != DISABLED)
+   #error KECCAK_SUPPORT parameter is not valid
+#endif
+
+//Tiger hash support
+#ifndef TIGER_SUPPORT
+   #define TIGER_SUPPORT ENABLED
+#elif (TIGER_SUPPORT != ENABLED && TIGER_SUPPORT != DISABLED)
+   #error TIGER_SUPPORT parameter is not valid
+#endif
+
+//Whirlpool hash support
+#ifndef WHIRLPOOL_SUPPORT
+   #define WHIRLPOOL_SUPPORT ENABLED
+#elif (WHIRLPOOL_SUPPORT != ENABLED && WHIRLPOOL_SUPPORT != DISABLED)
+   #error WHIRLPOOL_SUPPORT parameter is not valid
+#endif
+
+//HMAC support
+#ifndef HMAC_SUPPORT
+   #define HMAC_SUPPORT ENABLED
+#elif (HMAC_SUPPORT != ENABLED && HMAC_SUPPORT != DISABLED)
+   #error HMAC_SUPPORT parameter is not valid
+#endif
+
+//RC4 encryption support
+#ifndef RC4_SUPPORT
+   #define RC4_SUPPORT ENABLED
+#elif (RC4_SUPPORT != ENABLED && RC4_SUPPORT != DISABLED)
+   #error RC4_SUPPORT parameter is not valid
+#endif
+
+//RC6 encryption support
+#ifndef RC6_SUPPORT
+   #define RC6_SUPPORT ENABLED
+#elif (RC6_SUPPORT != ENABLED && RC6_SUPPORT != DISABLED)
+   #error RC6_SUPPORT parameter is not valid
+#endif
+
+//IDEA encryption support
+#ifndef IDEA_SUPPORT
+   #define IDEA_SUPPORT ENABLED
+#elif (IDEA_SUPPORT != ENABLED && IDEA_SUPPORT != DISABLED)
+   #error IDEA_SUPPORT parameter is not valid
+#endif
+
+//DES encryption support
+#ifndef DES_SUPPORT
+   #define DES_SUPPORT ENABLED
+#elif (DES_SUPPORT != ENABLED && DES_SUPPORT != DISABLED)
+   #error DES_SUPPORT parameter is not valid
+#endif
+
+//Triple DES encryption support
+#ifndef DES3_SUPPORT
+   #define DES3_SUPPORT ENABLED
+#elif (DES3_SUPPORT != ENABLED && DES3_SUPPORT != DISABLED)
+   #error DES3_SUPPORT parameter is not valid
+#endif
+
+//AES encryption support
+#ifndef AES_SUPPORT
+   #define AES_SUPPORT ENABLED
+#elif (AES_SUPPORT != ENABLED && AES_SUPPORT != DISABLED)
+   #error AES_SUPPORT parameter is not valid
+#endif
+
+//Camellia encryption support
+#ifndef CAMELLIA_SUPPORT
+   #define CAMELLIA_SUPPORT ENABLED
+#elif (CAMELLIA_SUPPORT != ENABLED && CAMELLIA_SUPPORT != DISABLED)
+   #error CAMELLIA_SUPPORT parameter is not valid
+#endif
+
+//SEED encryption support
+#ifndef SEED_SUPPORT
+   #define SEED_SUPPORT ENABLED
+#elif (SEED_SUPPORT != ENABLED && SEED_SUPPORT != DISABLED)
+   #error SEED_SUPPORT parameter is not valid
+#endif
+
+//ARIA encryption support
+#ifndef ARIA_SUPPORT
+   #define ARIA_SUPPORT ENABLED
+#elif (ARIA_SUPPORT != ENABLED && ARIA_SUPPORT != DISABLED)
+   #error ARIA_SUPPORT parameter is not valid
+#endif
+
+//ECB mode support
+#ifndef ECB_SUPPORT
+   #define ECB_SUPPORT ENABLED
+#elif (ECB_SUPPORT != ENABLED && ECB_SUPPORT != DISABLED)
+   #error ECB_SUPPORT parameter is not valid
+#endif
+
+//CBC mode support
+#ifndef CBC_SUPPORT
+   #define CBC_SUPPORT ENABLED
+#elif (CBC_SUPPORT != ENABLED && CBC_SUPPORT != DISABLED)
+   #error CBC_SUPPORT parameter is not valid
+#endif
+
+//CFB mode support
+#ifndef CFB_SUPPORT
+   #define CFB_SUPPORT ENABLED
+#elif (CFB_SUPPORT != ENABLED && CFB_SUPPORT != DISABLED)
+   #error CFB_SUPPORT parameter is not valid
+#endif
+
+//OFB mode support
+#ifndef OFB_SUPPORT
+   #define OFB_SUPPORT ENABLED
+#elif (OFB_SUPPORT != ENABLED && OFB_SUPPORT != DISABLED)
+   #error OFB_SUPPORT parameter is not valid
+#endif
+
+//CTR mode support
+#ifndef CTR_SUPPORT
+   #define CTR_SUPPORT ENABLED
+#elif (CTR_SUPPORT != ENABLED && CTR_SUPPORT != DISABLED)
+   #error CTR_SUPPORT parameter is not valid
+#endif
+
+//CCM mode support
+#ifndef CCM_SUPPORT
+   #define CCM_SUPPORT ENABLED
+#elif (CCM_SUPPORT != ENABLED && CCM_SUPPORT != DISABLED)
+   #error CCM_SUPPORT parameter is not valid
+#endif
+
+//GCM mode support
+#ifndef GCM_SUPPORT
+   #define GCM_SUPPORT ENABLED
+#elif (GCM_SUPPORT != ENABLED && GCM_SUPPORT != DISABLED)
+   #error GCM_SUPPORT parameter is not valid
+#endif
+
+//Chacha support
+#ifndef CHACHA_SUPPORT
+   #define CHACHA_SUPPORT DISABLED
+#elif (CHACHA_SUPPORT != ENABLED && CHACHA_SUPPORT != DISABLED)
+   #error CHACHA_SUPPORT parameter is not valid
+#endif
+
+//Poly1305 support
+#ifndef POLY1305_SUPPORT
+   #define POLY1305_SUPPORT DISABLED
+#elif (POLY1305_SUPPORT != ENABLED && POLY1305_SUPPORT != DISABLED)
+   #error POLY1305_SUPPORT parameter is not valid
+#endif
+
+//Chacha20Poly1305 support
+#ifndef CHACHA20_POLY1305_SUPPORT
+   #define CHACHA20_POLY1305_SUPPORT DISABLED
+#elif (CHACHA20_POLY1305_SUPPORT != ENABLED && CHACHA20_POLY1305_SUPPORT != DISABLED)
+   #error CHACHA20_POLY1305_SUPPORT parameter is not valid
+#endif
+
+//Diffie-Hellman support
+#ifndef DH_SUPPORT
+   #define DH_SUPPORT ENABLED
+#elif (DH_SUPPORT != ENABLED && DH_SUPPORT != DISABLED)
+   #error DH_SUPPORT parameter is not valid
+#endif
+
+//RSA support
+#ifndef RSA_SUPPORT
+   #define RSA_SUPPORT ENABLED
+#elif (RSA_SUPPORT != ENABLED && RSA_SUPPORT != DISABLED)
+   #error RSA_SUPPORT parameter is not valid
+#endif
+
+//DSA support
+#ifndef DSA_SUPPORT
+   #define DSA_SUPPORT ENABLED
+#elif (DSA_SUPPORT != ENABLED && DSA_SUPPORT != DISABLED)
+   #error DSA_SUPPORT parameter is not valid
+#endif
+
+//Elliptic curve cryptography support
+#ifndef EC_SUPPORT
+   #define EC_SUPPORT ENABLED
+#elif (EC_SUPPORT != ENABLED && EC_SUPPORT != DISABLED)
+   #error EC_SUPPORT parameter is not valid
+#endif
+
+//ECDH support
+#ifndef ECDH_SUPPORT
+   #define ECDH_SUPPORT ENABLED
+#elif (ECDH_SUPPORT != ENABLED && ECDH_SUPPORT != DISABLED)
+   #error ECDH_SUPPORT parameter is not valid
+#endif
+
+//ECDSA support
+#ifndef ECDSA_SUPPORT
+   #define ECDSA_SUPPORT ENABLED
+#elif (ECDSA_SUPPORT != ENABLED && ECDSA_SUPPORT != DISABLED)
+   #error ECDSA_SUPPORT parameter is not valid
+#endif
+
+//PKCS #5 support
+#ifndef PKCS5_SUPPORT
+   #define PKCS5_SUPPORT ENABLED
+#elif (PKCS5_SUPPORT != ENABLED && PKCS5_SUPPORT != DISABLED)
+   #error PKCS5_SUPPORT parameter is not valid
+#endif
+
+//Yarrow PRNG support
+#ifndef YARROW_SUPPORT
+   #define YARROW_SUPPORT ENABLED
+#elif (YARROW_SUPPORT != ENABLED && YARROW_SUPPORT != DISABLED)
+   #error YARROW_SUPPORT parameter is not valid
+#endif
+
+//Object identifier support
+#ifndef OID_SUPPORT
+   #define OID_SUPPORT ENABLED
+#elif (OID_SUPPORT != ENABLED && OID_SUPPORT != DISABLED)
+   #error OID_SUPPORT parameter is not valid
+#endif
+
+//ASN.1 syntax support
+#ifndef ASN1_SUPPORT
+   #define ASN1_SUPPORT ENABLED
+#elif (ASN1_SUPPORT != ENABLED && ASN1_SUPPORT != DISABLED)
+   #error ASN1_SUPPORT parameter is not valid
+#endif
+
+//PEM file support
+#ifndef PEM_SUPPORT
+   #define PEM_SUPPORT ENABLED
+#elif (PEM_SUPPORT != ENABLED && PEM_SUPPORT != DISABLED)
+   #error PEM_SUPPORT parameter is not valid
+#endif
+
+//X.509 certificate support
+#ifndef X509_SUPPORT
+   #define X509_SUPPORT ENABLED
+#elif (X509_SUPPORT != ENABLED && X509_SUPPORT != DISABLED)
+   #error X509_SUPPORT parameter is not valid
+#endif
+
+//Memory allocation
+#ifndef cryptoAllocMem
+   #define cryptoAllocMem(size) osAllocMem(size)
+#endif
+
+//Memory deallocation
+#ifndef cryptoFreeMem
+   #define cryptoFreeMem(p) osFreeMem(p)
+#endif
+
+//Maximum context size (hash algorithms)
+#if (SHA3_512_SUPPORT == ENABLED)
+   #define MAX_HASH_CONTEXT_SIZE sizeof(Sha3_512Context)
+#elif (SHA3_384_SUPPORT == ENABLED)
+   #define MAX_HASH_CONTEXT_SIZE sizeof(Sha3_384Context)
+#elif (SHA3_256_SUPPORT == ENABLED)
+   #define MAX_HASH_CONTEXT_SIZE sizeof(Sha3_256Context)
+#elif (SHA3_224_SUPPORT == ENABLED)
+   #define MAX_HASH_CONTEXT_SIZE sizeof(Sha3_224Context)
+#elif (WHIRLPOOL_SUPPORT == ENABLED)
+   #define MAX_HASH_CONTEXT_SIZE sizeof(WhirlpoolContext)
+#elif (SHA512_SUPPORT == ENABLED)
+   #define MAX_HASH_CONTEXT_SIZE sizeof(Sha512Context)
+#elif (SHA384_SUPPORT == ENABLED)
+   #define MAX_HASH_CONTEXT_SIZE sizeof(Sha384Context)
+#elif (SHA512_256_SUPPORT == ENABLED)
+   #define MAX_HASH_CONTEXT_SIZE sizeof(Sha512_256Context)
+#elif (SHA512_224_SUPPORT == ENABLED)
+   #define MAX_HASH_CONTEXT_SIZE sizeof(Sha512_224Context)
+#elif (SHA256_SUPPORT == ENABLED)
+   #define MAX_HASH_CONTEXT_SIZE sizeof(Sha256Context)
+#elif (SHA224_SUPPORT == ENABLED)
+   #define MAX_HASH_CONTEXT_SIZE sizeof(Sha224Context)
+#elif (TIGER_SUPPORT == ENABLED)
+   #define MAX_HASH_CONTEXT_SIZE sizeof(TigerContext)
+#elif (SHA1_SUPPORT == ENABLED)
+   #define MAX_HASH_CONTEXT_SIZE sizeof(Sha1Context)
+#elif (RIPEMD160_SUPPORT == ENABLED)
+   #define MAX_HASH_CONTEXT_SIZE sizeof(Ripemd160Context)
+#elif (RIPEMD128_SUPPORT == ENABLED)
+   #define MAX_HASH_CONTEXT_SIZE sizeof(Ripemd128Context)
+#elif (MD5_SUPPORT == ENABLED)
+   #define MAX_HASH_CONTEXT_SIZE sizeof(Md5Context)
+#elif (MD4_SUPPORT == ENABLED)
+   #define MAX_HASH_CONTEXT_SIZE sizeof(Md4Context)
+#elif (MD2_SUPPORT == ENABLED)
+   #define MAX_HASH_CONTEXT_SIZE sizeof(Md2Context)
+#endif
+
+//Maximum block size (hash algorithms)
+#if (SHA3_224_SUPPORT == ENABLED)
+   #define MAX_HASH_BLOCK_SIZE SHA3_224_BLOCK_SIZE
+#elif (SHA3_256_SUPPORT == ENABLED)
+   #define MAX_HASH_BLOCK_SIZE SHA3_256_BLOCK_SIZE
+#elif (SHA512_SUPPORT == ENABLED)
+   #define MAX_HASH_BLOCK_SIZE SHA512_BLOCK_SIZE
+#elif (SHA384_SUPPORT == ENABLED)
+   #define MAX_HASH_BLOCK_SIZE SHA384_BLOCK_SIZE
+#elif (SHA512_256_SUPPORT == ENABLED)
+   #define MAX_HASH_BLOCK_SIZE SHA512_256_BLOCK_SIZE
+#elif (SHA512_224_SUPPORT == ENABLED)
+   #define MAX_HASH_BLOCK_SIZE SHA512_224_BLOCK_SIZE
+#elif (SHA3_384_SUPPORT == ENABLED)
+   #define MAX_HASH_BLOCK_SIZE SHA3_384_BLOCK_SIZE
+#elif (SHA3_512_SUPPORT == ENABLED)
+   #define MAX_HASH_BLOCK_SIZE SHA3_512_BLOCK_SIZE
+#elif (SHA256_SUPPORT == ENABLED)
+   #define MAX_HASH_BLOCK_SIZE SHA256_BLOCK_SIZE
+#elif (SHA224_SUPPORT == ENABLED)
+   #define MAX_HASH_BLOCK_SIZE SHA224_BLOCK_SIZE
+#elif (SHA1_SUPPORT == ENABLED)
+   #define MAX_HASH_BLOCK_SIZE SHA1_BLOCK_SIZE
+#elif (WHIRLPOOL_SUPPORT == ENABLED)
+   #define MAX_HASH_BLOCK_SIZE WHIRLPOOL_BLOCK_SIZE
+#elif (TIGER_SUPPORT == ENABLED)
+   #define MAX_HASH_BLOCK_SIZE TIGER_BLOCK_SIZE
+#elif (RIPEMD160_SUPPORT == ENABLED)
+   #define MAX_HASH_BLOCK_SIZE RIPEMD160_BLOCK_SIZE
+#elif (RIPEMD128_SUPPORT == ENABLED)
+   #define MAX_HASH_BLOCK_SIZE RIPEMD128_BLOCK_SIZE
+#elif (MD5_SUPPORT == ENABLED)
+   #define MAX_HASH_BLOCK_SIZE MD5_BLOCK_SIZE
+#elif (MD4_SUPPORT == ENABLED)
+   #define MAX_HASH_BLOCK_SIZE MD4_BLOCK_SIZE
+#elif (MD2_SUPPORT == ENABLED)
+   #define MAX_HASH_BLOCK_SIZE MD2_BLOCK_SIZE
+#endif
+
+//Maximum digest size (hash algorithms)
+#if (WHIRLPOOL_SUPPORT == ENABLED)
+   #define MAX_HASH_DIGEST_SIZE WHIRLPOOL_DIGEST_SIZE
+#elif (SHA3_512_SUPPORT == ENABLED)
+   #define MAX_HASH_DIGEST_SIZE SHA3_512_DIGEST_SIZE
+#elif (SHA512_SUPPORT == ENABLED)
+   #define MAX_HASH_DIGEST_SIZE SHA512_DIGEST_SIZE
+#elif (SHA3_384_SUPPORT == ENABLED)
+   #define MAX_HASH_DIGEST_SIZE SHA3_384_DIGEST_SIZE
+#elif (SHA384_SUPPORT == ENABLED)
+   #define MAX_HASH_DIGEST_SIZE SHA384_DIGEST_SIZE
+#elif (SHA3_256_SUPPORT == ENABLED)
+   #define MAX_HASH_DIGEST_SIZE SHA3_256_DIGEST_SIZE
+#elif (SHA512_256_SUPPORT == ENABLED)
+   #define MAX_HASH_DIGEST_SIZE SHA512_256_DIGEST_SIZE
+#elif (SHA256_SUPPORT == ENABLED)
+   #define MAX_HASH_DIGEST_SIZE SHA256_DIGEST_SIZE
+#elif (SHA3_224_SUPPORT == ENABLED)
+   #define MAX_HASH_DIGEST_SIZE SHA3_224_DIGEST_SIZE
+#elif (SHA512_224_SUPPORT == ENABLED)
+   #define MAX_HASH_DIGEST_SIZE SHA512_224_DIGEST_SIZE
+#elif (SHA224_SUPPORT == ENABLED)
+   #define MAX_HASH_DIGEST_SIZE SHA224_DIGEST_SIZE
+#elif (TIGER_SUPPORT == ENABLED)
+   #define MAX_HASH_DIGEST_SIZE TIGER_DIGEST_SIZE
+#elif (SHA1_SUPPORT == ENABLED)
+   #define MAX_HASH_DIGEST_SIZE SHA1_DIGEST_SIZE
+#elif (RIPEMD160_SUPPORT == ENABLED)
+   #define MAX_HASH_DIGEST_SIZE RIPEMD160_DIGEST_SIZE
+#elif (RIPEMD128_SUPPORT == ENABLED)
+   #define MAX_HASH_DIGEST_SIZE RIPEMD128_DIGEST_SIZE
+#elif (MD5_SUPPORT == ENABLED)
+   #define MAX_HASH_DIGEST_SIZE MD5_DIGEST_SIZE
+#elif (MD4_SUPPORT == ENABLED)
+   #define MAX_HASH_DIGEST_SIZE MD4_DIGEST_SIZE
+#elif (MD2_SUPPORT == ENABLED)
+   #define MAX_HASH_DIGEST_SIZE MD2_DIGEST_SIZE
+#endif
+
+//Maximum context size (cipher algorithms)
+#if (ARIA_SUPPORT == ENABLED)
+   #define MAX_CIPHER_CONTEXT_SIZE sizeof(AriaContext)
+#elif (AES_SUPPORT == ENABLED)
+   #define MAX_CIPHER_CONTEXT_SIZE sizeof(AesContext)
+#elif (DES3_SUPPORT == ENABLED)
+   #define MAX_CIPHER_CONTEXT_SIZE sizeof(Des3Context)
+#elif (CAMELLIA_SUPPORT == ENABLED)
+   #define MAX_CIPHER_CONTEXT_SIZE sizeof(CamelliaContext)
+#elif (RC4_SUPPORT == ENABLED)
+   #define MAX_CIPHER_CONTEXT_SIZE sizeof(Rc4Context)
+#elif (RC6_SUPPORT == ENABLED)
+   #define MAX_CIPHER_CONTEXT_SIZE sizeof(Rc6Context)
+#elif (IDEA_SUPPORT == ENABLED)
+   #define MAX_CIPHER_CONTEXT_SIZE sizeof(IdeaContext)
+#elif (DES_SUPPORT == ENABLED)
+   #define MAX_CIPHER_CONTEXT_SIZE sizeof(DesContext)
+#elif (SEED_SUPPORT == ENABLED)
+   #define MAX_CIPHER_CONTEXT_SIZE sizeof(SeedContext)
+#endif
+
+//Maximum block size (cipher algorithms)
+#if (CAMELLIA_SUPPORT == ENABLED)
+   #define MAX_CIPHER_BLOCK_SIZE CAMELLIA_BLOCK_SIZE
+#elif (AES_SUPPORT == ENABLED)
+   #define MAX_CIPHER_BLOCK_SIZE AES_BLOCK_SIZE
+#elif (ARIA_SUPPORT == ENABLED)
+   #define MAX_CIPHER_BLOCK_SIZE ARIA_BLOCK_SIZE
+#elif (SEED_SUPPORT == ENABLED)
+   #define MAX_CIPHER_BLOCK_SIZE SEED_BLOCK_SIZE
+#elif (RC6_SUPPORT == ENABLED)
+   #define MAX_CIPHER_BLOCK_SIZE RC6_BLOCK_SIZE
+#elif (DES3_SUPPORT == ENABLED)
+   #define MAX_CIPHER_BLOCK_SIZE DES3_BLOCK_SIZE
+#elif (DES_SUPPORT == ENABLED)
+   #define MAX_CIPHER_BLOCK_SIZE DES_BLOCK_SIZE
+#elif (IDEA_SUPPORT == ENABLED)
+   #define MAX_CIPHER_BLOCK_SIZE IDEA_BLOCK_SIZE
+#endif
+
+//Rotate left operation
+#define ROL8(a, n) (((a) << (n)) | ((a) >> (8 - (n))))
+#define ROL16(a, n) (((a) << (n)) | ((a) >> (16 - (n))))
+#define ROL32(a, n) (((a) << (n)) | ((a) >> (32 - (n))))
+#define ROL64(a, n) (((a) << (n)) | ((a) >> (64 - (n))))
+
+//Rotate right operation
+#define ROR8(a, n) (((a) >> (n)) | ((a) << (8 - (n))))
+#define ROR16(a, n) (((a) >> (n)) | ((a) << (16 - (n))))
+#define ROR32(a, n) (((a) >> (n)) | ((a) << (32 - (n))))
+#define ROR64(a, n) (((a) >> (n)) | ((a) << (64 - (n))))
+
+//Shift left operation
+#define SHL8(a, n) ((a) << (n))
+#define SHL16(a, n) ((a) << (n))
+#define SHL32(a, n) ((a) << (n))
+#define SHL64(a, n) ((a) << (n))
+
+//Shift right operation
+#define SHR8(a, n) ((a) >> (n))
+#define SHR16(a, n) ((a) >> (n))
+#define SHR32(a, n) ((a) >> (n))
+#define SHR64(a, n) ((a) >> (n))
+
+
+/**
+ * @brief Encryption algorithm type
+ **/
+
+typedef enum
+{
+   CIPHER_ALGO_TYPE_STREAM = 0,
+   CIPHER_ALGO_TYPE_BLOCK  = 1
+} CipherAlgoType;
+
+
+/**
+ * @brief Cipher operation modes
+ **/
+
+typedef enum
+{
+   CIPHER_MODE_NULL              = 0,
+   CIPHER_MODE_STREAM            = 1,
+   CIPHER_MODE_ECB               = 2,
+   CIPHER_MODE_CBC               = 3,
+   CIPHER_MODE_CFB               = 4,
+   CIPHER_MODE_OFB               = 5,
+   CIPHER_MODE_CTR               = 6,
+   CIPHER_MODE_CCM               = 7,
+   CIPHER_MODE_GCM               = 8,
+   CIPHER_MODE_CHACHA20_POLY1305 = 9,
+} CipherMode;
+
+
+//Common API for hash algorithms
+typedef error_t (*HashAlgoCompute)(const void *data, size_t length, uint8_t *digest);
+typedef void (*HashAlgoInit)(void *context);
+typedef void (*HashAlgoUpdate)(void *context, const void *data, size_t length);
+typedef void (*HashAlgoFinal)(void *context, uint8_t *digest);
+
+//Common API for encryption algorithms
+typedef error_t (*CipherAlgoInit)(void *context, const uint8_t *key, size_t keyLength);
+typedef void (*CipherAlgoEncryptStream)(void *context, const uint8_t *input, uint8_t *output, size_t length);
+typedef void (*CipherAlgoDecryptStream)(void *context, const uint8_t *input, uint8_t *output, size_t length);
+typedef void (*CipherAlgoEncryptBlock)(void *context, const uint8_t *input, uint8_t *output);
+typedef void (*CipherAlgoDecryptBlock)(void *context, const uint8_t *input, uint8_t *output);
+
+//Common API for pseudo-random number generators
+typedef error_t (*PrngAlgoInit)(void *context);
+typedef void (*PrngAlgoRelease)(void *context);
+typedef error_t (*PrngAlgoSeed)(void *context, const uint8_t *input, size_t length);
+typedef error_t (*PrngAlgoAddEntropy)(void *context, uint_t source, const uint8_t *input, size_t length, size_t entropy);
+typedef error_t (*PrngAlgoRead)(void *context, uint8_t *output, size_t length);
+
+
+/**
+ * @brief Generic hash algorithm context
+ **/
+
+typedef struct
+{
+   uint8_t digest[1];
+} HashContext;
+
+
+/**
+ * @brief Common interface for hash algorithms
+ **/
+
+typedef struct
+{
+   const char_t *name;
+   const uint8_t *oid;
+   size_t oidSize;
+   size_t contextSize;
+   size_t blockSize;
+   size_t digestSize;
+   HashAlgoCompute compute;
+   HashAlgoInit init;
+   HashAlgoUpdate update;
+   HashAlgoFinal final;
+} HashAlgo;
+
+
+/**
+ * @brief Common interface for encryption algorithms
+ **/
+
+typedef struct
+{
+   const char_t *name;
+   size_t contextSize;
+   CipherAlgoType type;
+   size_t blockSize;
+   CipherAlgoInit init;
+   CipherAlgoEncryptStream encryptStream;
+   CipherAlgoDecryptStream decryptStream;
+   CipherAlgoEncryptBlock encryptBlock;
+   CipherAlgoDecryptBlock decryptBlock;
+} CipherAlgo;
+
+
+/**
+ * @brief Common interface for pseudo-random number generators
+ **/
+
+typedef struct
+{
+   const char_t *name;
+   size_t contextSize;
+   PrngAlgoInit init;
+   PrngAlgoRelease release;
+   PrngAlgoSeed seed;
+   PrngAlgoAddEntropy addEntropy;
+   PrngAlgoRead read;
+} PrngAlgo;
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/des.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,413 @@
+/**
+ * @file des.c
+ * @brief DES (Data Encryption Standard)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * DES is an encryption algorithm designed to encipher and decipher blocks of
+ * 64 bits under control of a 64-bit key. Refer to FIPS 46-3 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "des.h"
+
+//Check crypto library configuration
+#if (DES_SUPPORT == ENABLED || DES3_SUPPORT == ENABLED)
+
+//Rotate left operation
+#define ROL28(a, n) ((((a) << (n)) | ((a) >> (28 - (n)))) & 0x0FFFFFFF)
+
+//Initial permutation
+#define DES_IP(left, right) \
+{ \
+   temp = ((left >> 4) ^ right) & 0x0F0F0F0F; \
+   right ^= temp; \
+   left ^= temp << 4; \
+   temp = ((left >> 16) ^ right) & 0x0000FFFF; \
+   right ^= temp; \
+   left ^= temp << 16; \
+   temp = ((right >> 2) ^ left) & 0x33333333; \
+   left ^= temp; \
+   right ^= temp << 2; \
+   temp = ((right >> 8) ^ left) & 0x00FF00FF; \
+   left ^= temp; \
+   right ^= temp << 8; \
+   temp = ((left >> 1) ^ right) & 0x55555555; \
+   right ^= temp; \
+   left ^= temp << 1; \
+   left = ROL32(left, 1); \
+   right = ROL32(right, 1); \
+}
+
+//Final permutation
+#define DES_FP(left, right) \
+{ \
+   left = ROR32(left, 1); \
+   right = ROR32(right, 1); \
+   temp = ((left >> 1) ^ right) & 0x55555555; \
+   right ^= temp; \
+   left ^= temp << 1; \
+   temp = ((right >> 8) ^ left) & 0x00FF00FF; \
+   left ^= temp; \
+   right ^= temp << 8; \
+   temp = ((right >> 2) ^ left) & 0x33333333; \
+   left ^= temp; \
+   right ^= temp << 2; \
+   temp = ((left >> 16) ^ right) & 0x0000FFFF; \
+   right ^= temp; \
+   left ^= temp << 16; \
+   temp = ((left >> 4) ^ right) & 0x0F0F0F0F; \
+   right ^= temp; \
+   left ^= temp << 4; \
+}
+
+//DES round
+#define DES_ROUND(left, right, ks) \
+{ \
+   temp = right ^ *(ks); \
+   left ^= sp2[(temp >> 24) & 0x3F]; \
+   left ^= sp4[(temp >> 16) & 0x3F]; \
+   left ^= sp6[(temp >> 8) & 0x3F]; \
+   left ^= sp8[temp & 0x3F]; \
+   temp = ROR32(right, 4) ^ *(ks + 1); \
+   left ^= sp1[(temp >> 24) & 0x3F]; \
+   left ^= sp3[(temp >> 16) & 0x3F]; \
+   left ^= sp5[(temp >> 8) & 0x3F]; \
+   left ^= sp7[temp & 0x3F]; \
+   temp = right; \
+   right = left; \
+   left = temp; \
+}
+
+//Permuted choice 1
+#define DES_PC1(left, right) \
+{ \
+   uint32_t temp; \
+   temp = ((left >> 4) ^ right) & 0x0F0F0F0F; \
+   right ^= temp; \
+   left ^= (temp << 4); \
+   temp = ((right >> 16) ^ left) & 0x0000FFFF; \
+   left ^= temp; \
+   right ^= (temp << 16); \
+   temp = ((left >> 2) ^ right) & 0x33333333; \
+   right ^= temp; \
+   left ^= (temp << 2); \
+   temp = ((right >> 16) ^ left) & 0x0000FFFF; \
+   left ^= temp; \
+   right ^= (temp << 16); \
+   temp = ((left >> 1) ^ right) & 0x55555555; \
+   right ^= temp; \
+   left ^= (temp << 1); \
+   temp = ((right >> 8) ^ left) & 0x00FF00FF; \
+   left ^= temp; \
+   right ^= (temp << 8); \
+   temp = ((left >> 1) ^ right) & 0x55555555; \
+   right ^= temp; \
+   left ^= (temp << 1); \
+   temp = (left << 8) | ((right >> 20) & 0x000000F0); \
+   left = ((right << 20) & 0x0FF00000); \
+   left |= ((right << 4) & 0x000FF000); \
+   left |= ((right >> 12) & 0x00000FF0); \
+   left |= ((right >> 28) & 0x0000000F); \
+   right = temp >> 4; \
+}
+
+//Selection function 1
+static const uint32_t sp1[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
+};
+
+//Selection function 2
+static const uint32_t sp2[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
+};
+
+//Selection function 3
+static const uint32_t sp3[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
+};
+
+//Selection function 4
+static const uint32_t sp4[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
+};
+
+//Selection function 5
+static const uint32_t sp5[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
+};
+
+//Selection function 6
+static const uint32_t sp6[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
+};
+
+//Selection function 7
+static const uint32_t sp7[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
+};
+
+//Selection function 8
+static const uint32_t sp8[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
+};
+
+//Common interface for encryption algorithms
+const CipherAlgo desCipherAlgo =
+{
+   "DES",
+   sizeof(DesContext),
+   CIPHER_ALGO_TYPE_BLOCK,
+   DES_BLOCK_SIZE,
+   (CipherAlgoInit) desInit,
+   NULL,
+   NULL,
+   (CipherAlgoEncryptBlock) desEncryptBlock,
+   (CipherAlgoDecryptBlock) desDecryptBlock
+};
+
+
+/**
+ * @brief Initialize a DES context using the supplied key
+ * @param[in] context Pointer to the DES context to initialize
+ * @param[in] key Pointer to the key
+ * @param[in] keyLength Length of the key (must be set to 8)
+ * @return Error code
+ **/
+
+error_t desInit(DesContext *context, const uint8_t *key, size_t keyLength)
+{
+   uint_t i;
+   uint32_t c;
+   uint32_t d;
+
+   //Check key length
+   if(keyLength != 8)
+      return ERROR_INVALID_KEY_LENGTH;
+
+   //Copy the key
+   c = LOAD32BE(key + 0);
+   d = LOAD32BE(key + 4);
+
+   //Permuted choice 1
+   DES_PC1(c, d);
+
+   //Generate the key schedule
+   for(i = 0; i < 16; i++)
+   {
+      //Individual blocks are shifted left
+      if(i == 0 || i == 1 || i == 8 || i == 15)
+      {
+         c = ROL28(c, 1);
+         d = ROL28(d, 1);
+      }
+      else
+      {
+         c = ROL28(c, 2);
+         d = ROL28(d, 2);
+      }
+
+      //Permuted choice 2
+      context->ks[2 * i] =
+         ((c << 4)  & 0x24000000) | ((c << 28) & 0x10000000) |
+         ((c << 14) & 0x08000000) | ((c << 18) & 0x02080000) |
+         ((c << 6)  & 0x01000000) | ((c << 9)  & 0x00200000) |
+         ((c >> 1)  & 0x00100000) | ((c << 10) & 0x00040000) |
+         ((c << 2)  & 0x00020000) | ((c >> 10) & 0x00010000) |
+         ((d >> 13) & 0x00002000) | ((d >> 4)  & 0x00001000) |
+         ((d << 6)  & 0x00000800) | ((d >> 1)  & 0x00000400) |
+         ((d >> 14) & 0x00000200) | ((d)       & 0x00000100) |
+         ((d >> 5)  & 0x00000020) | ((d >> 10) & 0x00000010) |
+         ((d >> 3)  & 0x00000008) | ((d >> 18) & 0x00000004) |
+         ((d >> 26) & 0x00000002) | ((d >> 24) & 0x00000001);
+
+      context->ks[2 * i + 1] =
+         ((c << 15) & 0x20000000) | ((c << 17) & 0x10000000) |
+         ((c << 10) & 0x08000000) | ((c << 22) & 0x04000000) |
+         ((c >> 2)  & 0x02000000) | ((c << 1)  & 0x01000000) |
+         ((c << 16) & 0x00200000) | ((c << 11) & 0x00100000) |
+         ((c << 3)  & 0x00080000) | ((c >> 6)  & 0x00040000) |
+         ((c << 15) & 0x00020000) | ((c >> 4)  & 0x00010000) |
+         ((d >> 2)  & 0x00002000) | ((d << 8)  & 0x00001000) |
+         ((d >> 14) & 0x00000808) | ((d >> 9)  & 0x00000400) |
+         ((d)       & 0x00000200) | ((d << 7)  & 0x00000100) |
+         ((d >> 7)  & 0x00000020) | ((d >> 3)  & 0x00000011) |
+         ((d << 2)  & 0x00000004) | ((d >> 21) & 0x00000002);
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Encrypt a 8-byte block using DES algorithm
+ * @param[in] context Pointer to the DES context
+ * @param[in] input Plaintext block to encrypt
+ * @param[out] output Ciphertext block resulting from encryption
+ **/
+
+void desEncryptBlock(DesContext *context, const uint8_t *input, uint8_t *output)
+{
+   uint_t i;
+   uint32_t left;
+   uint32_t right;
+   uint32_t temp;
+
+   //Key schedule
+   uint32_t *ks = context->ks;
+
+   //Copy the plaintext from the input buffer
+   left = LOAD32BE(input + 0);
+   right = LOAD32BE(input + 4);
+
+   //Initial permutation
+   DES_IP(left, right);
+
+   //16 rounds of computation are needed
+   for(i = 0; i < 16; i++, ks += 2)
+   {
+      DES_ROUND(left, right, ks);
+   }
+
+   //Inverse IP permutation
+   DES_FP(right, left);
+
+   //Copy the resulting ciphertext
+   STORE32BE(right, output + 0);
+   STORE32BE(left, output + 4);
+}
+
+
+/**
+ * @brief Decrypt a 8-byte block using DES algorithm
+ * @param[in] context Pointer to the DES context
+ * @param[in] input Ciphertext block to decrypt
+ * @param[out] output Plaintext block resulting from decryption
+ **/
+
+void desDecryptBlock(DesContext *context, const uint8_t *input, uint8_t *output)
+{
+   uint_t i;
+   uint32_t left;
+   uint32_t right;
+   uint32_t temp;
+
+   //Keys in the key schedule must be applied in reverse order
+   uint32_t *ks = context->ks + 30;
+
+   //Copy the ciphertext from the input buffer
+   left = LOAD32BE(input + 0);
+   right = LOAD32BE(input + 4);
+
+   //Initial permutation
+   DES_IP(left, right);
+
+   //16 rounds of computation are needed
+   for(i = 0; i < 16; i++, ks -= 2)
+   {
+      DES_ROUND(left, right, ks);
+   }
+
+   //Inverse IP permutation
+   DES_FP(right, left);
+
+   //Copy the resulting plaintext
+   STORE32BE(right, output + 0);
+   STORE32BE(left, output + 4);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/des.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,60 @@
+/**
+ * @file des.h
+ * @brief DES (Data Encryption Standard)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DES_H
+#define _DES_H
+
+//Dependencies
+#include "crypto.h"
+
+//DES block size
+#define DES_BLOCK_SIZE 8
+//Common interface for encryption algorithms
+#define DES_CIPHER_ALGO (&desCipherAlgo)
+
+
+/**
+ * @brief DES algorithm context
+ **/
+
+typedef struct
+{
+   uint32_t ks[32];
+} DesContext;
+
+
+//DES related constants
+extern const CipherAlgo desCipherAlgo;
+
+//DES related functions
+error_t desInit(DesContext *context, const uint8_t *key, size_t keyLength);
+void desEncryptBlock(DesContext *context, const uint8_t *input, uint8_t *output);
+void desDecryptBlock(DesContext *context, const uint8_t *input, uint8_t *output);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/des3.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,144 @@
+/**
+ * @file des3.c
+ * @brief Triple DES (Triple Data Encryption Algorithm)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * Triple DES is an encryption algorithm designed to encipher and decipher blocks
+ * of 64 bits under control of a 192-bit key. Refer to FIPS 46-3 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "des3.h"
+#include "des.h"
+
+//Check crypto library configuration
+#if (DES3_SUPPORT == ENABLED)
+
+//Common interface for encryption algorithms
+const CipherAlgo des3CipherAlgo =
+{
+   "3DES",
+   sizeof(Des3Context),
+   CIPHER_ALGO_TYPE_BLOCK,
+   DES3_BLOCK_SIZE,
+   (CipherAlgoInit) des3Init,
+   NULL,
+   NULL,
+   (CipherAlgoEncryptBlock) des3EncryptBlock,
+   (CipherAlgoDecryptBlock) des3DecryptBlock
+};
+
+
+/**
+ * @brief Initialize a Triple DES context using the supplied key
+ * @param[in] context Pointer to the Triple DES context to initialize
+ * @param[in] key Pointer to the key
+ * @param[in] keyLength Length of the key
+ * @return Error code
+ **/
+
+error_t des3Init(Des3Context *context, const uint8_t *key, size_t keyLength)
+{
+   //Check key length
+   if(keyLength == 8)
+   {
+      //This option provides backward compatibility with DES, because the
+      //first and second DES operations cancel out
+      desInit(&context->k1, key, 8);
+      desInit(&context->k2, key, 8);
+      desInit(&context->k3, key, 8);
+   }
+   else if(keyLength == 16)
+   {
+      //If the key length is 128 bits including parity, the first 8 bytes of the
+      //encoding represent the key used for the two outer DES operations, and
+      //the second 8 bytes represent the key used for the inner DES operation
+      desInit(&context->k1, key, 8);
+      desInit(&context->k2, key + 8, 8);
+      desInit(&context->k3, key, 8);
+   }
+   else if(keyLength == 24)
+   {
+      //If the key length is 192 bits including parity, then three independent DES
+      //keys are represented, in the order in which they are used for encryption
+      desInit(&context->k1, key, 8);
+      desInit(&context->k2, key + 8, 8);
+      desInit(&context->k3, key + 16, 8);
+   }
+   else
+   {
+      //Invalid key length...
+      return ERROR_INVALID_KEY_LENGTH;
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Encrypt a 8-byte block using Triple DES algorithm
+ * @param[in] context Pointer to the Triple DES context
+ * @param[in] input Plaintext block to encrypt
+ * @param[out] output Ciphertext block resulting from encryption
+ **/
+
+void des3EncryptBlock(Des3Context *context, const uint8_t *input, uint8_t *output)
+{
+   //The first pass is a DES encryption
+   desEncryptBlock(&context->k1, input, output);
+   //The second pass is a DES decryption of the first ciphertext result
+   desDecryptBlock(&context->k2, output, output);
+   //The third pass is a DES encryption of the second pass result
+   desEncryptBlock(&context->k3, output, output);
+}
+
+
+/**
+ * @brief Decrypt a 8-byte block using Triple DES algorithm
+ * @param[in] context Pointer to the Triple DES context
+ * @param[in] input Ciphertext block to decrypt
+ * @param[out] output Plaintext block resulting from decryption
+ **/
+
+void des3DecryptBlock(Des3Context *context, const uint8_t *input, uint8_t *output)
+{
+   //The first pass is a DES decryption
+   desDecryptBlock(&context->k3, input, output);
+   //The second pass is a DES encryption of the first pass result
+   desEncryptBlock(&context->k2, output, output);
+   //The third pass is a DES decryption of the second ciphertext result
+   desDecryptBlock(&context->k1, output, output);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/des3.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,63 @@
+/**
+ * @file des3.h
+ * @brief Triple DES (Triple Data Encryption Algorithm)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DES3_H
+#define _DES3_H
+
+//Dependencies
+#include "crypto.h"
+#include "des.h"
+
+//Triple DES block size
+#define DES3_BLOCK_SIZE 8
+//Common interface for encryption algorithms
+#define DES3_CIPHER_ALGO (&des3CipherAlgo)
+
+
+/**
+ * @brief Triple DES algorithm context
+ **/
+
+typedef struct
+{
+   DesContext k1;
+   DesContext k2;
+   DesContext k3;
+} Des3Context;
+
+
+//Triple DES related constants
+extern const CipherAlgo des3CipherAlgo;
+
+//Triple DES related functions
+error_t des3Init(Des3Context *context, const uint8_t *key, size_t keyLength);
+void des3EncryptBlock(Des3Context *context, const uint8_t *input, uint8_t *output);
+void des3DecryptBlock(Des3Context *context, const uint8_t *input, uint8_t *output);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/dh.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,241 @@
+/**
+ * @file dh.c
+ * @brief Diffie-Hellman key exchange
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The Diffie-Hellman key agreement protocol allows two users to exchange a
+ * secret key over an insecure medium without any prior secrets. Refer to
+ * PKCS #3 (Diffie-Hellman Key-Agreement Standard)
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include "crypto.h"
+#include "dh.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (DH_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Initialize Diffie-Hellman context
+ * @param[in] context Pointer to the Diffie-Hellman context
+ **/
+
+void dhInit(DhContext *context)
+{
+   //Initialize Diffie-Hellman parameters
+   mpiInit(&context->params.p);
+   mpiInit(&context->params.g);
+   //Initialize private and public values
+   mpiInit(&context->xa);
+   mpiInit(&context->ya);
+   mpiInit(&context->yb);
+}
+
+
+/**
+ * @brief Release Diffie-Hellman context
+ * @param[in] context Pointer to the Diffie-Hellman context
+ **/
+
+void dhFree(DhContext *context)
+{
+   //Release Diffie-Hellman parameters
+   mpiFree(&context->params.p);
+   mpiFree(&context->params.g);
+   //Release private and public values
+   mpiFree(&context->xa);
+   mpiFree(&context->ya);
+   mpiFree(&context->yb);
+}
+
+
+/**
+ * @brief Diffie-Hellman key pair generation
+ * @param[in] context Pointer to the Diffie-Hellman context
+ * @param[in] prngAlgo PRNG algorithm
+ * @param[in] prngContext Pointer to the PRNG context
+ * @return Error code
+ **/
+
+error_t dhGenerateKeyPair(DhContext *context,
+   const PrngAlgo *prngAlgo, void *prngContext)
+{
+   error_t error;
+   uint_t k;
+
+   //Debug message
+   TRACE_DEBUG("Generating Diffie-Hellman key pair...\r\n");
+
+   //Get the length in bits of the prime p
+   k = mpiGetBitLength(&context->params.p);
+   //Ensure the length is valid
+   if(k == 0)
+      return ERROR_INVALID_PARAMETER;
+
+   //The private value shall be randomly generated
+   error = mpiRand(&context->xa, k, prngAlgo, prngContext);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //The private value shall be less than p
+   if(mpiComp(&context->xa, &context->params.p) >= 0)
+   {
+      //Shift value to the right
+      error = mpiShiftRight(&context->xa, 1);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+
+   //Debug message
+   TRACE_DEBUG("  Private value:\r\n");
+   TRACE_DEBUG_MPI("    ", &context->xa);
+
+   //Calculate the corresponding public value (ya = g ^ xa mod p)
+   error = mpiExpMod(&context->ya, &context->params.g, &context->xa, &context->params.p);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Debug message
+   TRACE_DEBUG("  Public value:\r\n");
+   TRACE_DEBUG_MPI("    ", &context->ya);
+
+   //Check public value
+   error = dhCheckPublicKey(&context->params, &context->ya);
+   //Weak public value?
+   if(error)
+      return error;
+
+   //Public value successfully generated
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Check Diffie-Hellman public value
+ * @param[in] params Pointer to the Diffie-Hellman parameters
+ * @param[in] publicKey Public value to be checked
+ * @return Error code
+ **/
+
+error_t dhCheckPublicKey(DhParameters *params, const Mpi *publicKey)
+{
+   error_t error;
+   Mpi a;
+
+   //Initialize multiple precision integer
+   mpiInit(&a);
+   //Precompute p - 1
+   error = mpiSubInt(&a, &params->p, 1);
+
+   //Check status
+   if(!error)
+   {
+      //Reject weak public values 1 and p - 1
+      if(mpiCompInt(publicKey, 1) <= 0)
+         error = ERROR_ILLEGAL_PARAMETER;
+      else if(mpiComp(publicKey, &a) >= 0)
+         error = ERROR_ILLEGAL_PARAMETER;
+   }
+
+   //Free previously allocated resources
+   mpiFree(&a);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Compute Diffie-Hellman shared secret
+ * @param[in] context Pointer to the Diffie-Hellman context
+ * @param[out] output Buffer where to store the shared secret
+ * @param[in] outputSize Size of the buffer in bytes
+ * @param[out] outputLength Length of the resulting shared secret
+ * @return Error code
+ **/
+
+error_t dhComputeSharedSecret(DhContext *context,
+   uint8_t *output, size_t outputSize, size_t *outputLength)
+{
+   error_t error;
+   size_t k;
+   Mpi z;
+
+   //Debug message
+   TRACE_DEBUG("Computing Diffie-Hellman shared secret...\r\n");
+
+   //Get the length in octets of the prime modulus
+   k = mpiGetByteLength(&context->params.p);
+
+   //Make sure that the output buffer is large enough
+   if(outputSize < k)
+      return ERROR_INVALID_LENGTH;
+
+   //The multiple precision integer must be initialized before it can be used
+   mpiInit(&z);
+
+   //Start of exception handling block
+   do
+   {
+      //Calculate the shared secret key (k = yb ^ xa mod p)
+      error = mpiExpMod(&z, &context->yb, &context->xa, &context->params.p);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Convert the resulting integer to an octet string
+      error = mpiWriteRaw(&z, output, k);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Length of the resulting shared secret
+      *outputLength = k;
+
+      //Debug message
+      TRACE_DEBUG("  Shared secret (%" PRIuSIZE " bytes):\r\n", *outputLength);
+      TRACE_DEBUG_ARRAY("    ", output, *outputLength);
+
+      //End of exception handling block
+   } while(0);
+
+   //Release previously allocated resources
+   mpiFree(&z);
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/dh.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,74 @@
+/**
+ * @file dh.h
+ * @brief Diffie-Hellman key exchange
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DH_H
+#define _DH_H
+
+//Dependencies
+#include "crypto.h"
+#include "mpi.h"
+
+
+/**
+ * @brief Diffie-Hellman parameters
+ **/
+
+typedef struct
+{
+   Mpi p; ///<Prime modulus
+   Mpi g; ///<Generator
+} DhParameters;
+
+
+/**
+ * @brief Diffie-Hellman context
+ **/
+
+typedef struct
+{
+   DhParameters params; //Diffie-Hellman parameters
+   Mpi xa;              ///<One's own private value
+   Mpi ya;              ///<One's own public value
+   Mpi yb;              ///<Peer's public value
+} DhContext;
+
+
+//Diffie-Hellman related functions
+void dhInit(DhContext *context);
+void dhFree(DhContext *context);
+
+error_t dhGenerateKeyPair(DhContext *context,
+   const PrngAlgo *prngAlgo, void *prngContext);
+
+error_t dhCheckPublicKey(DhParameters *params, const Mpi *publicKey);
+
+error_t dhComputeSharedSecret(DhContext *context,
+   uint8_t *output, size_t outputSize, size_t *outputLength);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/dsa.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,630 @@
+/**
+ * @file dsa.c
+ * @brief DSA (Digital Signature Algorithm)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The Digital Signature Algorithm (DSA) is a an algorithm developed by the
+ * NSA to generate a digital signature for the authentication of electronic
+ * documents. Refer to FIPS 186-3 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include "crypto.h"
+#include "dsa.h"
+#include "mpi.h"
+#include "asn1.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (DSA_SUPPORT == ENABLED)
+
+//DSA OID (1.2.840.10040.4.1)
+const uint8_t DSA_OID[7] = {0x2A, 0x86, 0x48, 0xCE, 0x38, 0x04, 0x01};
+//DSA with SHA-1 OID (1.2.840.10040.4.3)
+const uint8_t DSA_WITH_SHA1_OID[7] = {0x2A, 0x86, 0x48, 0xCE, 0x38, 0x04, 0x03};
+//DSA with SHA-224 OID (2.16.840.1.101.3.4.3.1)
+const uint8_t DSA_WITH_SHA224_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x01};
+//DSA with SHA-256 OID (2.16.840.1.101.3.4.3.2)
+const uint8_t DSA_WITH_SHA256_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x02};
+//DSA with SHA-384 OID (2.16.840.1.101.3.4.3.3)
+const uint8_t DSA_WITH_SHA384_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x03};
+//DSA with SHA-512 OID (2.16.840.1.101.3.4.3.4)
+const uint8_t DSA_WITH_SHA512_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x04};
+//DSA with SHA-3-224 OID (2.16.840.1.101.3.4.3.5)
+const uint8_t DSA_WITH_SHA3_224_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x05};
+//DSA with SHA-3-256 OID (2.16.840.1.101.3.4.3.6)
+const uint8_t DSA_WITH_SHA3_256_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x06};
+//DSA with SHA-3-384 OID (2.16.840.1.101.3.4.3.7)
+const uint8_t DSA_WITH_SHA3_384_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x07};
+//DSA with SHA-3-512 OID (2.16.840.1.101.3.4.3.8)
+const uint8_t DSA_WITH_SHA3_512_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x08};
+
+
+/**
+ * @brief Initialize a DSA public key
+ * @param[in] key Pointer to the DSA public key to initialize
+ **/
+
+void dsaInitPublicKey(DsaPublicKey *key)
+{
+   //Initialize multiple precision integers
+   mpiInit(&key->p);
+   mpiInit(&key->q);
+   mpiInit(&key->g);
+   mpiInit(&key->y);
+}
+
+
+/**
+ * @brief Release a DSA public key
+ * @param[in] key Pointer to the DSA public key to free
+ **/
+
+void dsaFreePublicKey(DsaPublicKey *key)
+{
+   //Free multiple precision integers
+   mpiFree(&key->p);
+   mpiFree(&key->q);
+   mpiFree(&key->g);
+   mpiFree(&key->y);
+}
+
+
+/**
+ * @brief Initialize a DSA private key
+ * @param[in] key Pointer to the DSA private key to initialize
+ **/
+
+void dsaInitPrivateKey(DsaPrivateKey *key)
+{
+   //Initialize multiple precision integers
+   mpiInit(&key->p);
+   mpiInit(&key->q);
+   mpiInit(&key->g);
+   mpiInit(&key->x);
+}
+
+
+/**
+ * @brief Release a DSA private key
+ * @param[in] key Pointer to the DSA public key to free
+ **/
+
+void dsaFreePrivateKey(DsaPrivateKey *key)
+{
+   //Free multiple precision integers
+   mpiFree(&key->p);
+   mpiFree(&key->q);
+   mpiFree(&key->g);
+   mpiFree(&key->x);
+}
+
+
+/**
+ * @brief Initialize a DSA signature
+ * @param[in] signature Pointer to the DSA signature to initialize
+ **/
+
+void dsaInitSignature(DsaSignature *signature)
+{
+   //Initialize multiple precision integers
+   mpiInit(&signature->r);
+   mpiInit(&signature->s);
+}
+
+
+/**
+ * @brief Release a DSA signature
+ * @param[in] signature Pointer to the DSA signature to free
+ **/
+
+void dsaFreeSignature(DsaSignature *signature)
+{
+   //Release multiple precision integers
+   mpiFree(&signature->r);
+   mpiFree(&signature->s);
+}
+
+
+/**
+ * @brief Encode DSA signature using ASN.1
+ * @param[in] signature (R, S) integer pair
+ * @param[out] data Pointer to the buffer where to store the resulting ASN.1 structure
+ * @param[out] length Length of the ASN.1 structure
+ * @return Error code
+ **/
+
+error_t dsaWriteSignature(const DsaSignature *signature, uint8_t *data, size_t *length)
+{
+   error_t error;
+   size_t k;
+   size_t n;
+   size_t rLen;
+   size_t sLen;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("Writing DSA signature...\r\n");
+
+   //Dump (R, S) integer pair
+   TRACE_DEBUG("  r:\r\n");
+   TRACE_DEBUG_MPI("    ", &signature->r);
+   TRACE_DEBUG("  s:\r\n");
+   TRACE_DEBUG_MPI("    ", &signature->s);
+
+   //Calculate the length of R
+   rLen = mpiGetByteLength(&signature->r);
+   //Calculate the length of S
+   sLen = mpiGetByteLength(&signature->s);
+
+   //Make sure the (R, S) integer pair is valid
+   if(rLen == 0 || sLen == 0)
+      return ERROR_INVALID_LENGTH;
+
+   //R and S are always encoded in the smallest possible number of octets
+   if(mpiGetBitValue(&signature->r, (rLen * 8) - 1))
+      rLen++;
+   if(mpiGetBitValue(&signature->s, (sLen * 8) - 1))
+      sLen++;
+
+   //The first pass computes the length of the ASN.1 sequence
+   n = 0;
+
+   //The parameter R is encapsulated within an ASN.1 structure
+   tag.constructed = FALSE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_INTEGER;
+   tag.length = rLen;
+   tag.value = NULL;
+
+   //Compute the length of the corresponding ASN.1 structure
+   error = asn1WriteTag(&tag, FALSE, NULL, NULL);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Update the length of the ASN.1 sequence
+   n += tag.totalLength;
+
+   //The parameter S is encapsulated within an ASN.1 structure
+   tag.constructed = FALSE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_INTEGER;
+   tag.length = sLen;
+   tag.value = NULL;
+
+   //Compute the length of the corresponding ASN.1 structure
+   error = asn1WriteTag(&tag, FALSE, NULL, NULL);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Update the length of the ASN.1 sequence
+   n += tag.totalLength;
+
+   //The second pass encodes the ASN.1 structure
+   k = 0;
+
+   //The (R, S) integer pair is encapsulated within a sequence
+   tag.constructed = TRUE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_SEQUENCE;
+   tag.length = n;
+   tag.value = NULL;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, FALSE, data + k, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Advance write pointer
+   k += n;
+
+   //Encode the parameter R using ASN.1
+   tag.constructed = FALSE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_INTEGER;
+   tag.length = rLen;
+   tag.value = NULL;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, FALSE, data + k, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Advance write pointer
+   k += n;
+
+   //Convert R to an octet string
+   error = mpiWriteRaw(&signature->r, data + k, rLen);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Advance write pointer
+   k += rLen;
+
+   //Encode the parameter S using ASN.1
+   tag.constructed = FALSE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_INTEGER;
+   tag.length = sLen;
+   tag.value = NULL;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, FALSE, data + k, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Advance write pointer
+   k += n;
+
+   //Convert S to an octet string
+   error = mpiWriteRaw(&signature->s, data + k, sLen);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Advance write pointer
+   k += sLen;
+
+   //Dump DSA signature
+   TRACE_DEBUG("  signature:\r\n");
+   TRACE_DEBUG_ARRAY("    ", data, k);
+
+   //Total length of the ASN.1 structure
+   *length = k;
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Read an ASN.1 encoded DSA signature
+ * @param[in] data Pointer to the ASN.1 structure to decode
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] signature (R, S) integer pair
+ * @return Error code
+ **/
+
+error_t dsaReadSignature(const uint8_t *data, size_t length, DsaSignature *signature)
+{
+   error_t error;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("Reading DSA signature...\r\n");
+
+   //Dump DSA signature
+   TRACE_DEBUG("  signature:\r\n");
+   TRACE_DEBUG_ARRAY("    ", data, length);
+
+   //Start of exception handling block
+   do
+   {
+      //Display ASN.1 structure
+      error = asn1DumpObject(data, length, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Read the contents of the ASN.1 structure
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Point to the first field
+      data = tag.value;
+      length = tag.length;
+
+      //Read the parameter R
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Convert the octet string to a multiple precision integer
+      error = mpiReadRaw(&signature->r, tag.value, tag.length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Point to the next field
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the parameter S
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Convert the octet string to a multiple precision integer
+      error = mpiReadRaw(&signature->s, tag.value, tag.length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Dump (R, S) integer pair
+      TRACE_DEBUG("  r:\r\n");
+      TRACE_DEBUG_MPI("    ", &signature->r);
+      TRACE_DEBUG("  s:\r\n");
+      TRACE_DEBUG_MPI("    ", &signature->s);
+
+      //End of exception handling block
+   } while(0);
+
+   //Clean up side effects if necessary
+   if(error)
+      dsaFreeSignature(signature);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief DSA signature generation
+ * @param[in] prngAlgo PRNG algorithm
+ * @param[in] prngContext Pointer to the PRNG context
+ * @param[in] key Signer's DSA private key
+ * @param[in] digest Digest of the message to be signed
+ * @param[in] digestLength Length in octets of the digest
+ * @param[out] signature (R, S) integer pair
+ * @return Error code
+ **/
+
+error_t dsaGenerateSignature(const PrngAlgo *prngAlgo, void *prngContext,
+   const DsaPrivateKey *key, const uint8_t *digest, size_t digestLength,
+   DsaSignature *signature)
+{
+   error_t error;
+   uint_t n;
+   Mpi k;
+   Mpi z;
+
+   //Check parameters
+   if(key == NULL || digest == NULL || signature == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_DEBUG("DSA signature generation...\r\n");
+   TRACE_DEBUG("  p:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->p);
+   TRACE_DEBUG("  q:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->q);
+   TRACE_DEBUG("  g:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->g);
+   TRACE_DEBUG("  x:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->x);
+   TRACE_DEBUG("  digest:\r\n");
+   TRACE_DEBUG_ARRAY("    ", digest, digestLength);
+
+   //Initialize multiple precision integers
+   mpiInit(&k);
+   mpiInit(&z);
+
+   //Let N be the bit length of q
+   n = mpiGetBitLength(&key->q);
+
+   //Generated a pseudorandom number
+   MPI_CHECK(mpiRand(&k, n, prngAlgo, prngContext));
+
+   //Make sure that 0 < k < q
+   if(mpiComp(&k, &key->q) >= 0)
+      mpiShiftRight(&k, 1);
+
+   //Debug message
+   TRACE_DEBUG("  k:\r\n");
+   TRACE_DEBUG_MPI("    ", &k);
+
+   //Compute N = MIN(N, outlen)
+   n = MIN(n, digestLength * 8);
+
+   //Convert the digest to a multiple precision integer
+   MPI_CHECK(mpiReadRaw(&z, digest, (n + 7) / 8));
+
+   //Keep the leftmost N bits of the hash value
+   if(n % 8)
+   {
+      MPI_CHECK(mpiShiftRight(&z, 8 - (n % 8)));
+   }
+
+   //Debug message
+   TRACE_DEBUG("  z:\r\n");
+   TRACE_DEBUG_MPI("    ", &z);
+
+   //Compute r = (g ^ k mod p) mod q
+   MPI_CHECK(mpiExpMod(&signature->r, &key->g, &k, &key->p));
+   MPI_CHECK(mpiMod(&signature->r, &signature->r, &key->q));
+
+   //Compute k ^ -1 mod q
+   MPI_CHECK(mpiInvMod(&k, &k, &key->q));
+
+   //Compute s = k ^ -1 * (z + x * r) mod q
+   MPI_CHECK(mpiMul(&signature->s, &key->x, &signature->r));
+   MPI_CHECK(mpiAdd(&signature->s, &signature->s, &z));
+   MPI_CHECK(mpiMod(&signature->s, &signature->s, &key->q));
+   MPI_CHECK(mpiMulMod(&signature->s, &signature->s, &k, &key->q));
+
+   //Dump DSA signature
+   TRACE_DEBUG("  r:\r\n");
+   TRACE_DEBUG_MPI("    ", &signature->r);
+   TRACE_DEBUG("  s:\r\n");
+   TRACE_DEBUG_MPI("    ", &signature->s);
+
+end:
+   //Release multiple precision integers
+   mpiFree(&k);
+   mpiFree(&z);
+
+   //Clean up side effects if necessary
+   if(error)
+   {
+      //Release (R, S) integer pair
+      mpiFree(&signature->r);
+      mpiFree(&signature->r);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief DSA signature verification
+ * @param[in] key Signer's DSA public key
+ * @param[in] digest Digest of the message whose signature is to be verified
+ * @param[in] digestLength Length in octets of the digest
+ * @param[in] signature (R, S) integer pair
+ * @return Error code
+ **/
+
+error_t dsaVerifySignature(const DsaPublicKey *key,
+   const uint8_t *digest, size_t digestLength, const DsaSignature *signature)
+{
+   error_t error;
+   uint_t n;
+   Mpi w;
+   Mpi z;
+   Mpi u1;
+   Mpi u2;
+   Mpi v;
+
+   //Check parameters
+   if(key == NULL || digest == NULL || signature == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_DEBUG("DSA signature verification...\r\n");
+   TRACE_DEBUG("  p:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->p);
+   TRACE_DEBUG("  q:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->q);
+   TRACE_DEBUG("  g:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->g);
+   TRACE_DEBUG("  y:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->y);
+   TRACE_DEBUG("  digest:\r\n");
+   TRACE_DEBUG_ARRAY("    ", digest, digestLength);
+   TRACE_DEBUG("  r:\r\n");
+   TRACE_DEBUG_MPI("    ", &signature->r);
+   TRACE_DEBUG("  s:\r\n");
+   TRACE_DEBUG_MPI("    ", &signature->s);
+
+   //The verifier shall check that 0 < r < q and 0 < s < q. If either
+   //condition is violated, the signature shall be rejected as invalid
+   if(mpiCompInt(&signature->r, 0) <= 0 || mpiComp(&signature->r, &key->q) >= 0)
+      return ERROR_INVALID_SIGNATURE;
+   if(mpiCompInt(&signature->s, 0) <= 0 || mpiComp(&signature->s, &key->q) >= 0)
+      return ERROR_INVALID_SIGNATURE;
+
+   //Initialize multiple precision integers
+   mpiInit(&w);
+   mpiInit(&z);
+   mpiInit(&u1);
+   mpiInit(&u2);
+   mpiInit(&v);
+
+   //Let N be the bit length of q
+   n = mpiGetBitLength(&key->q);
+   //Compute N = MIN(N, outlen)
+   n = MIN(n, digestLength * 8);
+
+   //Convert the digest to a multiple precision integer
+   MPI_CHECK(mpiReadRaw(&z, digest, (n + 7) / 8));
+
+   //Keep the leftmost N bits of the hash value
+   if(n % 8)
+   {
+      MPI_CHECK(mpiShiftRight(&z, 8 - (n % 8)));
+   }
+
+   //Compute w = s ^ -1 mod q
+   MPI_CHECK(mpiInvMod(&w, &signature->s, &key->q));
+   //Compute u1 = z * w mod q
+   MPI_CHECK(mpiMulMod(&u1, &z, &w, &key->q));
+   //Compute u2 = r * w mod q
+   MPI_CHECK(mpiMulMod(&u2, &signature->r, &w, &key->q));
+
+   //Compute v = ((g ^ u1) * (y ^ u2) mod p) mod q
+   MPI_CHECK(mpiExpMod(&v, &key->g, &u1, &key->p));
+   MPI_CHECK(mpiExpMod(&w, &key->y, &u2, &key->p));
+   MPI_CHECK(mpiMulMod(&v, &v, &w, &key->p));
+   MPI_CHECK(mpiMod(&v, &v, &key->q));
+
+   //Debug message
+   TRACE_DEBUG("  v:\r\n");
+   TRACE_DEBUG_MPI("    ", &v);
+
+   //If v = r, then the signature is verified. If v does not equal r,
+   //then the message or the signature may have been modified
+   if(!mpiComp(&v, &signature->r))
+      error = NO_ERROR;
+   else
+      error = ERROR_INVALID_SIGNATURE;
+
+end:
+   //Release multiple precision integers
+   mpiFree(&w);
+   mpiFree(&z);
+   mpiFree(&u1);
+   mpiFree(&u2);
+   mpiFree(&v);
+
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/dsa.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,107 @@
+/**
+ * @file dsa.h
+ * @brief DSA (Digital Signature Algorithm)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DSA_H
+#define _DSA_H
+
+//Dependencies
+#include "crypto.h"
+#include "mpi.h"
+
+
+/**
+ * @brief DSA public key
+ **/
+
+typedef struct
+{
+   Mpi p; ///<Prime modulus
+   Mpi q; ///<Prime divisor
+   Mpi g; ///<Generator of the subgroup
+   Mpi y; ///<Public key
+} DsaPublicKey;
+
+
+/**
+ * @brief DSA private key
+ **/
+
+typedef struct
+{
+   Mpi p; ///<Prime modulus
+   Mpi q; ///<<Prime divisor
+   Mpi g; ///<Generator of the subgroup
+   Mpi x; ///<Private key
+} DsaPrivateKey;
+
+
+/**
+ * @brief DSA signature
+ **/
+
+typedef struct
+{
+   Mpi r;
+   Mpi s;
+} DsaSignature;
+
+
+//DSA related constants
+extern const uint8_t DSA_OID[7];
+extern const uint8_t DSA_WITH_SHA1_OID[7];
+extern const uint8_t DSA_WITH_SHA224_OID[9];
+extern const uint8_t DSA_WITH_SHA256_OID[9];
+extern const uint8_t DSA_WITH_SHA384_OID[9];
+extern const uint8_t DSA_WITH_SHA512_OID[9];
+extern const uint8_t DSA_WITH_SHA3_224_OID[9];
+extern const uint8_t DSA_WITH_SHA3_256_OID[9];
+extern const uint8_t DSA_WITH_SHA3_384_OID[9];
+extern const uint8_t DSA_WITH_SHA3_512_OID[9];
+
+//DSA related functions
+void dsaInitPublicKey(DsaPublicKey *key);
+void dsaFreePublicKey(DsaPublicKey *key);
+
+void dsaInitPrivateKey(DsaPrivateKey *key);
+void dsaFreePrivateKey(DsaPrivateKey *key);
+
+void dsaInitSignature(DsaSignature *signature);
+void dsaFreeSignature(DsaSignature *signature);
+
+error_t dsaWriteSignature(const DsaSignature *signature, uint8_t *data, size_t *length);
+error_t dsaReadSignature(const uint8_t *data, size_t length, DsaSignature *signature);
+
+error_t dsaGenerateSignature(const PrngAlgo *prngAlgo, void *prngContext,
+   const DsaPrivateKey *key, const uint8_t *digest, size_t digestLength,
+   DsaSignature *signature);
+
+error_t dsaVerifySignature(const DsaPublicKey *key,
+   const uint8_t *digest, size_t digestLength, const DsaSignature *signature);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/ec.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1159 @@
+/**
+ * @file ec.c
+ * @brief ECC (Elliptic Curve Cryptography)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include "crypto.h"
+#include "ec.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (EC_SUPPORT == ENABLED)
+
+//EC Public Key OID (1.2.840.10045.2.1)
+const uint8_t EC_PUBLIC_KEY_OID[7] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01};
+
+
+/**
+ * @brief Initialize EC domain parameters
+ * @param[in] params Pointer to the EC domain parameters to be initialized
+ **/
+
+void ecInitDomainParameters(EcDomainParameters *params)
+{
+   //Initialize structure
+   params->type = EC_CURVE_TYPE_NONE;
+   params->mod = NULL;
+
+   //Initialize EC domain parameters
+   mpiInit(&params->p);
+   mpiInit(&params->a);
+   mpiInit(&params->b);
+   ecInit(&params->g);
+   mpiInit(&params->q);
+}
+
+
+/**
+ * @brief Release EC domain parameters
+ * @param[in] params Pointer to the EC domain parameters to free
+ **/
+
+void ecFreeDomainParameters(EcDomainParameters *params)
+{
+   //Release previously allocated resources
+   mpiFree(&params->p);
+   mpiFree(&params->a);
+   mpiFree(&params->b);
+   ecFree(&params->g);
+   mpiFree(&params->q);
+}
+
+
+/**
+ * @brief Load EC domain parameters
+ * @param[out] params Pointer to the structure to be initialized
+ * @param[in] curveInfo Elliptic curve parameters
+ * @return Error code
+ **/
+
+error_t ecLoadDomainParameters(EcDomainParameters *params, const EcCurveInfo *curveInfo)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_DEBUG("Loading %s EC domain parameters...\r\n", curveInfo->name);
+
+   //Curve type
+   params->type = curveInfo->type;
+
+   //Import prime modulus
+   MPI_CHECK(mpiReadRaw(&params->p, curveInfo->p, curveInfo->pLen));
+   //Import parameter a
+   MPI_CHECK(mpiReadRaw(&params->a, curveInfo->a, curveInfo->aLen));
+   //Import parameter b
+   MPI_CHECK(mpiReadRaw(&params->b, curveInfo->b, curveInfo->bLen));
+   //Import the x-coordinate of the base point G
+   MPI_CHECK(mpiReadRaw(&params->g.x, curveInfo->gx, curveInfo->gxLen));
+   //Import the y-coordinate of the base point G
+   MPI_CHECK(mpiReadRaw(&params->g.y, curveInfo->gy, curveInfo->gyLen));
+   //Import base point order q
+   MPI_CHECK(mpiReadRaw(&params->q, curveInfo->q, curveInfo->qLen));
+
+   //Normalize base point G
+   MPI_CHECK(mpiSetValue(&params->g.z, 1));
+
+   //Fast modular reduction
+   params->mod = curveInfo->mod;
+
+   //Debug message
+   TRACE_DEBUG("  p:\r\n");
+   TRACE_DEBUG_MPI("    ", &params->p);
+   TRACE_DEBUG("  a:\r\n");
+   TRACE_DEBUG_MPI("    ", &params->a);
+   TRACE_DEBUG("  b:\r\n");
+   TRACE_DEBUG_MPI("    ", &params->b);
+   TRACE_DEBUG("  Gx:\r\n");
+   TRACE_DEBUG_MPI("    ", &params->g.x);
+   TRACE_DEBUG("  Gy:\r\n");
+   TRACE_DEBUG_MPI("    ", &params->g.y);
+   TRACE_DEBUG("  q:\r\n");
+   TRACE_DEBUG_MPI("    ", &params->q);
+
+end:
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Initialize elliptic curve point
+ * @param[in,out] r Pointer to the EC point to be initialized
+ **/
+
+void ecInit(EcPoint *r)
+{
+   //Initialize structure
+   mpiInit(&r->x);
+   mpiInit(&r->y);
+   mpiInit(&r->z);
+}
+
+
+/**
+ * @brief Release an elliptic curve point
+ * @param[in,out] r Pointer to the EC point to initialize to free
+ **/
+
+void ecFree(EcPoint *r)
+{
+   //Release previously allocated resources
+   mpiFree(&r->x);
+   mpiFree(&r->y);
+   mpiFree(&r->z);
+}
+
+
+/**
+ * @brief Copy EC point
+ * @param[out] r Destination EC point
+ * @param[in] s Source EC point
+ * @return Error code
+ **/
+
+error_t ecCopy(EcPoint *r, const EcPoint *s)
+{
+   error_t error;
+
+   //R and S are the same instance?
+   if(r == s)
+      return NO_ERROR;
+
+   //Copy coordinates
+   MPI_CHECK(mpiCopy(&r->x, &s->x));
+   MPI_CHECK(mpiCopy(&r->y, &s->y));
+   MPI_CHECK(mpiCopy(&r->z, &s->z));
+
+end:
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Convert an octet string to an EC point
+ * @param[in] params EC domain parameters
+ * @param[out] r EC point resulting from the conversion
+ * @param[in] data Pointer to the octet string
+ * @param[in] length Length of the octet string
+ * @return Error code
+ **/
+
+error_t ecImport(const EcDomainParameters *params,
+   EcPoint *r, const uint8_t *data, size_t length)
+{
+   error_t error;
+   size_t k;
+
+   //Get the length in octets of the prime
+   k = mpiGetByteLength(&params->p);
+
+   //Check the length of the octet string
+   if(length != (k * 2 + 1))
+      return ERROR_DECODING_FAILED;
+
+   //Compressed point representation is not supported
+   if(data[0] != 0x04)
+      return ERROR_ILLEGAL_PARAMETER;
+
+   //Convert the x-coordinate to a multiple precision integer
+   error = mpiReadRaw(&r->x, data + 1, k);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Convert the y-coordinate to a multiple precision integer
+   error = mpiReadRaw(&r->y, data + k + 1, k);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Convert an EC point to an octet string
+ * @param[in] params EC domain parameters
+ * @param[in] a EC point to be converted
+ * @param[out] data Pointer to the octet string
+ * @param[out] length Length of the resulting octet string
+ * @return Error code
+ **/
+
+error_t ecExport(const EcDomainParameters *params,
+   const EcPoint *a, uint8_t *data, size_t *length)
+{
+   error_t error;
+   size_t k;
+
+   //Get the length in octets of the prime
+   k = mpiGetByteLength(&params->p);
+
+   //Point compression is not used
+   data[0] = 0x04;
+
+   //Convert the x-coordinate to an octet string
+   error = mpiWriteRaw(&a->x, data + 1, k);
+   //Conversion failed?
+   if(error)
+      return error;
+
+   //Convert the y-coordinate to an octet string
+   error = mpiWriteRaw(&a->y, data + k + 1, k);
+   //Conversion failed?
+   if(error)
+      return error;
+
+   //Return the total number of bytes that have been written
+   *length = k * 2 + 1;
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Compute projective representation
+ * @param[in] params EC domain parameters
+ * @param[out] r Projective representation of the point
+ * @param[in] s Affine representation of the point
+ * @return Error code
+ **/
+
+error_t ecProjectify(const EcDomainParameters *params, EcPoint *r, const EcPoint *s)
+{
+   error_t error;
+
+   //Copy point
+   EC_CHECK(ecCopy(r, s));
+   //Map the point to projective space
+   MPI_CHECK(mpiSetValue(&r->z, 1));
+
+end:
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Recover affine representation
+ * @param[in] params EC domain parameters
+ * @param[out] r Affine representation of the point
+ * @param[in] s Projective representation of the point
+ * @return Error code
+ **/
+
+error_t ecAffinify(const EcDomainParameters *params, EcPoint *r, const EcPoint *s)
+{
+   error_t error;
+   Mpi a;
+   Mpi b;
+
+   //Point at the infinity?
+   if(!mpiCompInt(&s->z, 0))
+      return ERROR_INVALID_PARAMETER;
+
+   //Initialize multiple precision integers
+   mpiInit(&a);
+   mpiInit(&b);
+
+   //Compute a = 1/Sz mod p
+   MPI_CHECK(mpiInvMod(&a, &s->z, &params->p));
+
+   //Set Rx = a^2 * Sx mod p
+   EC_CHECK(ecSqrMod(params, &b, &a));
+   EC_CHECK(ecMulMod(params, &r->x, &b, &s->x));
+
+   //Set Ry = a^3 * Sy mod p
+   EC_CHECK(ecMulMod(params, &b, &b, &a));
+   EC_CHECK(ecMulMod(params, &r->y, &b, &s->y));
+
+   //Set Rz = 1
+   MPI_CHECK(mpiSetValue(&r->z, 1));
+
+end:
+   //Release multiple precision integers
+   mpiFree(&a);
+   mpiFree(&b);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Check whether the affine point S is on the curve
+ * @param[in] params EC domain parameters
+ * @param[in] s Affine representation of the point
+ * @return TRUE if the affine point S is on the curve, else FALSE
+ **/
+
+bool_t ecIsPointAffine(const EcDomainParameters *params, const EcPoint *s)
+{
+   error_t error;
+   Mpi t1;
+   Mpi t2;
+
+   //Initialize multiple precision integers
+   mpiInit(&t1);
+   mpiInit(&t2);
+
+   //Compute t1 = (Sx^3 + a * Sx + b) mod p
+   EC_CHECK(ecSqrMod(params, &t1, &s->x));
+   EC_CHECK(ecMulMod(params, &t1, &t1, &s->x));
+   EC_CHECK(ecMulMod(params, &t2, &params->a, &s->x));
+   EC_CHECK(ecAddMod(params, &t1, &t1, &t2));
+   EC_CHECK(ecAddMod(params, &t1, &t1, &params->b));
+
+   //Compute t2 = Sy^2
+   EC_CHECK(ecSqrMod(params, &t2, &s->y));
+
+   //Check whether the point is on the elliptic curve
+   if(mpiComp(&t1, &t2))
+      error = ERROR_FAILURE;
+
+end:
+   //Release multiple precision integers
+   mpiFree(&t1);
+   mpiFree(&t2);
+
+   //Return TRUE if the affine point S is on the curve, else FALSE
+   return error ? FALSE : TRUE;
+}
+
+
+/**
+ * @brief Point doubling
+ * @param[in] params EC domain parameters
+ * @param[out] r Resulting point R = 2S
+ * @param[in] s Point S
+ * @return Error code
+ **/
+
+error_t ecDouble(const EcDomainParameters *params, EcPoint *r, const EcPoint *s)
+{
+   error_t error;
+   Mpi t1;
+   Mpi t2;
+   Mpi t3;
+   Mpi t4;
+   Mpi t5;
+
+   //Initialize multiple precision integers
+   mpiInit(&t1);
+   mpiInit(&t2);
+   mpiInit(&t3);
+   mpiInit(&t4);
+   mpiInit(&t5);
+
+   //Set t1 = Sx
+   MPI_CHECK(mpiCopy(&t1, &s->x));
+   //Set t2 = Sy
+   MPI_CHECK(mpiCopy(&t2, &s->y));
+   //Set t3 = Sz
+   MPI_CHECK(mpiCopy(&t3, &s->z));
+
+   //Point at the infinity?
+   if(!mpiCompInt(&t3, 0))
+   {
+      //Set R = (1, 1, 0)
+      MPI_CHECK(mpiSetValue(&r->x, 1));
+      MPI_CHECK(mpiSetValue(&r->y, 1));
+      MPI_CHECK(mpiSetValue(&r->z, 0));
+   }
+   else
+   {
+      //SECP K1 elliptic curve?
+      if(params->type == EC_CURVE_TYPE_SECP_K1)
+      {
+         //Compute t5 = t1^2
+         EC_CHECK(ecSqrMod(params, &t5, &t1));
+         //Compute t4 = 3 * t5
+         EC_CHECK(ecAddMod(params, &t4, &t5, &t5));
+         EC_CHECK(ecAddMod(params, &t4, &t4, &t5));
+      }
+      //SECP R1 elliptic curve?
+      else if(params->type == EC_CURVE_TYPE_SECP_R1)
+      {
+         //Compute t4 = t3^2
+         EC_CHECK(ecSqrMod(params, &t4, &t3));
+         //Compute t5 = t1 - t4
+         EC_CHECK(ecSubMod(params, &t5, &t1, &t4));
+         //Compute t4 = t1 + t4
+         EC_CHECK(ecAddMod(params, &t4, &t1, &t4));
+         //Compute t5 = t4 * t5
+         EC_CHECK(ecMulMod(params, &t5, &t4, &t5));
+         //Compute t4 = 3 * t5
+         EC_CHECK(ecAddMod(params, &t4, &t5, &t5));
+         EC_CHECK(ecAddMod(params, &t4, &t4, &t5));
+      }
+      else
+      {
+         //Compute t4 = t3^4
+         EC_CHECK(ecSqrMod(params, &t4, &t3));
+         EC_CHECK(ecSqrMod(params, &t4, &t4));
+         //Compute t4 = a * t4
+         EC_CHECK(ecMulMod(params, &t4, &t4, &params->a));
+         //Compute t5 = t1^2
+         EC_CHECK(ecSqrMod(params, &t5, &t1));
+         //Compute t4 = t4 + 3 * t5
+         EC_CHECK(ecAddMod(params, &t4, &t4, &t5));
+         EC_CHECK(ecAddMod(params, &t4, &t4, &t5));
+         EC_CHECK(ecAddMod(params, &t4, &t4, &t5));
+      }
+
+      //Compute t3 = t3 * t2
+      EC_CHECK(ecMulMod(params, &t3, &t3, &t2));
+      //Compute t3 = 2 * t3
+      EC_CHECK(ecAddMod(params, &t3, &t3, &t3));
+      //Compute t2 = t2^2
+      EC_CHECK(ecSqrMod(params, &t2, &t2));
+      //Compute t5 = t1 * t2
+      EC_CHECK(ecMulMod(params, &t5, &t1, &t2));
+      //Compute t5 = 4 * t5
+      EC_CHECK(ecAddMod(params, &t5, &t5, &t5));
+      EC_CHECK(ecAddMod(params, &t5, &t5, &t5));
+      //Compute t1 = t4^2
+      EC_CHECK(ecSqrMod(params, &t1, &t4));
+      //Compute t1 = t1 - 2 * t5
+      EC_CHECK(ecSubMod(params, &t1, &t1, &t5));
+      EC_CHECK(ecSubMod(params, &t1, &t1, &t5));
+      //Compute t2 = t2^2
+      EC_CHECK(ecSqrMod(params, &t2, &t2));
+      //Compute t2 = 8 * t2
+      EC_CHECK(ecAddMod(params, &t2, &t2, &t2));
+      EC_CHECK(ecAddMod(params, &t2, &t2, &t2));
+      EC_CHECK(ecAddMod(params, &t2, &t2, &t2));
+      //Compute t5 = t5 - t1
+      EC_CHECK(ecSubMod(params, &t5, &t5, &t1));
+      //Compute t5 = t4 * t5
+      EC_CHECK(ecMulMod(params, &t5, &t4, &t5));
+      //Compute t2 = t5 - t2
+      EC_CHECK(ecSubMod(params, &t2, &t5, &t2));
+
+      //Set Rx = t1
+      MPI_CHECK(mpiCopy(&r->x, &t1));
+      //Set Ry = t2
+      MPI_CHECK(mpiCopy(&r->y, &t2));
+      //Set Rz = t3
+      MPI_CHECK(mpiCopy(&r->z, &t3));
+   }
+
+end:
+   //Release multiple precision integers
+   mpiFree(&t1);
+   mpiFree(&t2);
+   mpiFree(&t3);
+   mpiFree(&t4);
+   mpiFree(&t5);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Point addition (helper routine)
+ * @param[in] params EC domain parameters
+ * @param[out] r Resulting point R = S + T
+ * @param[in] s First operand
+ * @param[in] t Second operand
+ * @return Error code
+ **/
+
+error_t ecAdd(const EcDomainParameters *params, EcPoint *r, const EcPoint *s, const EcPoint *t)
+{
+   error_t error;
+   Mpi t1;
+   Mpi t2;
+   Mpi t3;
+   Mpi t4;
+   Mpi t5;
+   Mpi t6;
+   Mpi t7;
+
+   //Initialize multiple precision integers
+   mpiInit(&t1);
+   mpiInit(&t2);
+   mpiInit(&t3);
+   mpiInit(&t4);
+   mpiInit(&t5);
+   mpiInit(&t6);
+   mpiInit(&t7);
+
+   //Set t1 = Sx
+   MPI_CHECK(mpiCopy(&t1, &s->x));
+   //Set t2 = Sy
+   MPI_CHECK(mpiCopy(&t2, &s->y));
+   //Set t3 = Sz
+   MPI_CHECK(mpiCopy(&t3, &s->z));
+   //Set t4 = Tx
+   MPI_CHECK(mpiCopy(&t4, &t->x));
+   //Set t5 = Ty
+   MPI_CHECK(mpiCopy(&t5, &t->y));
+
+   //Check whether Tz != 1
+   if(mpiCompInt(&t->z, 1))
+   {
+      //Compute t6 = Tz
+      MPI_CHECK(mpiCopy(&t6, &t->z));
+      //Compute t7 = t6^2
+      EC_CHECK(ecSqrMod(params, &t7, &t6));
+      //Compute t1 = t1 * t7
+      EC_CHECK(ecMulMod(params, &t1, &t1, &t7));
+      //Compute t7 = t6 * t7
+      EC_CHECK(ecMulMod(params, &t7, &t6, &t7));
+      //Compute t2 = t2 * t7
+      EC_CHECK(ecMulMod(params, &t2, &t2, &t7));
+   }
+
+   //Compute t7 = t3^2
+   EC_CHECK(ecSqrMod(params, &t7, &t3));
+   //Compute t4 = t4 * t7
+   EC_CHECK(ecMulMod(params, &t4, &t4, &t7));
+   //Compute t7 = t3 * t7
+   EC_CHECK(ecMulMod(params, &t7, &t3, &t7));
+   //Compute t5 = t5 * t7
+   EC_CHECK(ecMulMod(params, &t5, &t5, &t7));
+   //Compute t4 = t1 - t4
+   EC_CHECK(ecSubMod(params, &t4, &t1, &t4));
+   //Compute t5 = t2 - t5
+   EC_CHECK(ecSubMod(params, &t5, &t2, &t5));
+
+   //Check whether t4 == 0
+   if(!mpiCompInt(&t4, 0))
+   {
+      //Check whether t5 == 0
+      if(!mpiCompInt(&t5, 0))
+      {
+         //Set R = (0, 0, 0)
+         MPI_CHECK(mpiSetValue(&r->x, 0));
+         MPI_CHECK(mpiSetValue(&r->y, 0));
+         MPI_CHECK(mpiSetValue(&r->z, 0));
+      }
+      else
+      {
+         //Set R = (1, 1, 0)
+         MPI_CHECK(mpiSetValue(&r->x, 1));
+         MPI_CHECK(mpiSetValue(&r->y, 1));
+         MPI_CHECK(mpiSetValue(&r->z, 0));
+      }
+   }
+   else
+   {
+      //Compute t1 = 2 * t1 - t4
+      EC_CHECK(ecAddMod(params, &t1, &t1, &t1));
+      EC_CHECK(ecSubMod(params, &t1, &t1, &t4));
+      //Compute t2 = 2 * t2 - t5
+      EC_CHECK(ecAddMod(params, &t2, &t2, &t2));
+      EC_CHECK(ecSubMod(params, &t2, &t2, &t5));
+
+      //Check whether Tz != 1
+      if(mpiCompInt(&t->z, 1))
+      {
+         //Compute t3 = t3 * t6
+         EC_CHECK(ecMulMod(params, &t3, &t3, &t6));
+      }
+
+      //Compute t3 = t3 * t4
+      EC_CHECK(ecMulMod(params, &t3, &t3, &t4));
+      //Compute t7 = t4^2
+      EC_CHECK(ecSqrMod(params, &t7, &t4));
+      //Compute t4 = t4 * t7
+      EC_CHECK(ecMulMod(params, &t4, &t4, &t7));
+      //Compute t7 = t1 * t7
+      EC_CHECK(ecMulMod(params, &t7, &t1, &t7));
+      //Compute t1 = t5^2
+      EC_CHECK(ecSqrMod(params, &t1, &t5));
+      //Compute t1 = t1 - t7
+      EC_CHECK(ecSubMod(params, &t1, &t1, &t7));
+      //Compute t7 = t7 - 2 * t1
+      EC_CHECK(ecAddMod(params, &t6, &t1, &t1));
+      EC_CHECK(ecSubMod(params, &t7, &t7, &t6));
+      //Compute t5 = t5 * t7
+      EC_CHECK(ecMulMod(params, &t5, &t5, &t7));
+      //Compute t4 = t2 * t4
+      EC_CHECK(ecMulMod(params, &t4, &t2, &t4));
+      //Compute t2 = t5 - t4
+      EC_CHECK(ecSubMod(params, &t2, &t5, &t4));
+
+      //Compute t2 = t2 / 2
+      if(mpiIsEven(&t2))
+      {
+         MPI_CHECK(mpiShiftRight(&t2, 1));
+      }
+      else
+      {
+         MPI_CHECK(mpiAdd(&t2, &t2, &params->p));
+         MPI_CHECK(mpiShiftRight(&t2, 1));
+      }
+
+      //Set Rx = t1
+      MPI_CHECK(mpiCopy(&r->x, &t1));
+      //Set Ry = t2
+      MPI_CHECK(mpiCopy(&r->y, &t2));
+      //Set Rz = t3
+      MPI_CHECK(mpiCopy(&r->z, &t3));
+   }
+
+end:
+   //Release multiple precision integers
+   mpiFree(&t1);
+   mpiFree(&t2);
+   mpiFree(&t3);
+   mpiFree(&t4);
+   mpiFree(&t5);
+   mpiFree(&t6);
+   mpiFree(&t7);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Point addition
+ * @param[in] params EC domain parameters
+ * @param[out] r Resulting point R = S + T
+ * @param[in] s First operand
+ * @param[in] t Second operand
+ * @return Error code
+ **/
+
+error_t ecFullAdd(const EcDomainParameters *params, EcPoint *r, const EcPoint *s, const EcPoint *t)
+{
+   error_t error;
+
+   //Check whether Sz == 0
+   if(!mpiCompInt(&s->z, 0))
+   {
+      //Set R = T
+      MPI_CHECK(mpiCopy(&r->x, &t->x));
+      MPI_CHECK(mpiCopy(&r->y, &t->y));
+      MPI_CHECK(mpiCopy(&r->z, &t->z));
+   }
+   //Check whether Tz == 0
+   else if(!mpiCompInt(&t->z, 0))
+   {
+      //Set R = S
+      MPI_CHECK(mpiCopy(&r->x, &s->x));
+      MPI_CHECK(mpiCopy(&r->y, &s->y));
+      MPI_CHECK(mpiCopy(&r->z, &s->z));
+   }
+   else
+   {
+      //Compute R = S + T
+      EC_CHECK(ecAdd(params, r, s, t));
+
+      //Check whether R == (0, 0, 0)
+      if(!mpiCompInt(&r->x, 0) && !mpiCompInt(&r->y, 0) && !mpiCompInt(&r->z, 0))
+      {
+         //Compute R = 2 * S
+         EC_CHECK(ecDouble(params, r, s));
+      }
+   }
+
+end:
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Point subtraction
+ * @param[in] params EC domain parameters
+ * @param[out] r Resulting point R = S - T
+ * @param[in] s First operand
+ * @param[in] t Second operand
+ * @return Error code
+ **/
+
+error_t ecFullSub(const EcDomainParameters *params, EcPoint *r, const EcPoint *s, const EcPoint *t)
+{
+   error_t error;
+   EcPoint u;
+
+   //Initialize EC point
+   ecInit(&u);
+
+   //Set Ux = Tx and Uz = Tz
+   MPI_CHECK(mpiCopy(&u.x, &t->x));
+   MPI_CHECK(mpiCopy(&u.z, &t->z));
+   //Set Uy = p - Ty
+   MPI_CHECK(mpiSub(&u.y, &params->p, &t->y));
+
+   //Compute R = S + U
+   EC_CHECK(ecFullAdd(params, r, s, &u));
+
+end:
+   //Release EC point
+   ecFree(&u);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Scalar multiplication
+ * @param[in] params EC domain parameters
+ * @param[out] r Resulting point R = d.S
+ * @param[in] d An integer d such as 0 <= d < p
+ * @param[in] s EC point
+ * @return Error code
+ **/
+
+error_t ecMult(const EcDomainParameters *params, EcPoint *r, const Mpi *d, const EcPoint *s)
+{
+   error_t error;
+   uint_t i;
+   Mpi h;
+
+   //Initialize multiple precision integer
+   mpiInit(&h);
+
+   //Check whether d == 0
+   if(!mpiCompInt(d, 0))
+   {
+      //Set R = (1, 1, 0)
+      MPI_CHECK(mpiSetValue(&r->x, 1));
+      MPI_CHECK(mpiSetValue(&r->y, 1));
+      MPI_CHECK(mpiSetValue(&r->z, 0));
+   }
+   //Check whether d == 1
+   else if(!mpiCompInt(d, 1))
+   {
+      //Set R = S
+      MPI_CHECK(mpiCopy(&r->x, &s->x));
+      MPI_CHECK(mpiCopy(&r->y, &s->y));
+      MPI_CHECK(mpiCopy(&r->z, &s->z));
+   }
+   //Check whether Sz == 0
+   else if(!mpiCompInt(&s->z, 0))
+   {
+      //Set R = (1, 1, 0)
+      MPI_CHECK(mpiSetValue(&r->x, 1));
+      MPI_CHECK(mpiSetValue(&r->y, 1));
+      MPI_CHECK(mpiSetValue(&r->z, 0));
+   }
+   else
+   {
+      //Check whether Sz != 1
+      if(mpiCompInt(&s->z, 1))
+      {
+         //Normalize S
+         EC_CHECK(ecAffinify(params, r, s));
+         EC_CHECK(ecProjectify(params, r, r));
+      }
+      else
+      {
+         //Set R = S
+         MPI_CHECK(mpiCopy(&r->x, &s->x));
+         MPI_CHECK(mpiCopy(&r->y, &s->y));
+         MPI_CHECK(mpiCopy(&r->z, &s->z));
+      }
+
+//Left-to-right binary method
+#if 0
+      for(i = mpiGetBitLength(d) - 1; i >= 1; i--)
+      {
+         //Point doubling
+         EC_CHECK(ecDouble(params, r, r));
+
+         if(mpiGetBitValue(d, i - 1))
+         {
+            //Compute R = R + S
+            EC_CHECK(ecFullAdd(params, r, r, s));
+         }
+      }
+//Fast left-to-right binary method
+#else
+      //Precompute h = 3 * d
+      MPI_CHECK(mpiAdd(&h, d, d));
+      MPI_CHECK(mpiAdd(&h, &h, d));
+
+      //Scalar multiplication
+      for(i = mpiGetBitLength(&h) - 2; i >= 1; i--)
+      {
+         //Point doubling
+         EC_CHECK(ecDouble(params, r, r));
+
+         //Check whether h(i) == 1 and k(i) == 0
+         if(mpiGetBitValue(&h, i) && !mpiGetBitValue(d, i))
+         {
+            //Compute R = R + S
+            EC_CHECK(ecFullAdd(params, r, r, s));
+         }
+         //Check whether h(i) == 0 and k(i) == 1
+         else if(!mpiGetBitValue(&h, i) && mpiGetBitValue(d, i))
+         {
+            //Compute R = R - S
+            EC_CHECK(ecFullSub(params, r, r, s));
+         }
+      }
+#endif
+   }
+
+end:
+   //Release multiple precision integer
+   mpiFree(&h);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief An auxiliary function for the twin multiplication
+ * @param[in] t An integer T such as 0 <= T <= 31
+ * @return Output value
+ **/
+
+uint_t ecTwinMultF(uint_t t)
+{
+   if(18 <= t && t < 22)
+      return 9;
+   else if(14 <= t && t < 18)
+      return 10;
+   else if(22 <= t && t < 24)
+      return 11;
+   else if(4 <= t && t < 12)
+      return 14;
+   else
+      return 12;
+}
+
+
+/**
+ * @brief Twin multiplication
+ * @param[in] params EC domain parameters
+ * @param[out] r Resulting point R = d0.S + d1.T
+ * @param[in] d0 An integer d such as 0 <= d0 < p
+ * @param[in] s EC point
+ * @param[in] d1 An integer d such as 0 <= d1 < p
+ * @param[in] t EC point
+ * @return Error code
+ **/
+
+error_t ecTwinMult(const EcDomainParameters *params, EcPoint *r,
+   const Mpi *d0, const EcPoint *s, const Mpi *d1, const EcPoint *t)
+{
+   error_t error;
+   int_t k;
+   uint_t m;
+   uint_t m0;
+   uint_t m1;
+   uint_t c0;
+   uint_t c1;
+   uint_t h0;
+   uint_t h1;
+   int_t u0;
+   int_t u1;
+   EcPoint spt;
+   EcPoint smt;
+
+   //Initialize EC points
+   ecInit(&spt);
+   ecInit(&smt);
+
+   //Precompute SpT = S + T
+   EC_CHECK(ecFullAdd(params, &spt, s, t));
+   //Precompute SmT = S - T
+   EC_CHECK(ecFullSub(params, &smt, s, t));
+
+   //Let m0 be the bit length of d0
+   m0 = mpiGetBitLength(d0);
+   //Let m1 be the bit length of d1
+   m1 = mpiGetBitLength(d1);
+   //Let m = MAX(m0, m1)
+   m = MAX(m0, m1);
+
+   //Let c be a 2 x 6 binary matrix
+   c0 = mpiGetBitValue(d0, m - 4);
+   c0 |= mpiGetBitValue(d0, m - 3) << 1;
+   c0 |= mpiGetBitValue(d0, m - 2) << 2;
+   c0 |= mpiGetBitValue(d0, m - 1) << 3;
+   c1 = mpiGetBitValue(d1, m - 4);
+   c1 |= mpiGetBitValue(d1, m - 3) << 1;
+   c1 |= mpiGetBitValue(d1, m - 2) << 2;
+   c1 |= mpiGetBitValue(d1, m - 1) << 3;
+
+   //Set R = (1, 1, 0)
+   MPI_CHECK(mpiSetValue(&r->x, 1));
+   MPI_CHECK(mpiSetValue(&r->y, 1));
+   MPI_CHECK(mpiSetValue(&r->z, 0));
+
+   //Calculate both multiplications at the same time
+   for(k = m; k >= 0; k--)
+   {
+      //Compute h(0) = 16 * c(0,1) + 8 * c(0,2) + 4 * c(0,3) + 2 * c(0,4) + c(0,5)
+      h0 = c0 & 0x1F;
+      //Check whether c(0,0) == 1
+      if(c0 & 0x20)
+         h0 = 31 - h0;
+
+      //Compute h(1) = 16 * c(1,1) + 8 * c(1,2) + 4 * c(1,3) + 2 * c(1,4) + c(1,5)
+      h1 = c1 & 0x1F;
+      //Check whether c(1,0) == 1
+      if(c1 & 0x20)
+         h1 = 31 - h1;
+
+      //Compute u(0)
+      if(h0 < ecTwinMultF(h1))
+         u0 = 0;
+      else if(c0 & 0x20)
+         u0 = -1;
+      else
+         u0 = 1;
+
+      //Compute u(1)
+      if(h1 < ecTwinMultF(h0))
+         u1 = 0;
+      else if(c1 & 0x20)
+         u1 = -1;
+      else
+         u1 = 1;
+
+      //Update c matrix
+      c0 <<= 1;
+      c0 |= mpiGetBitValue(d0, k - 5);
+      c0 ^= u0 ? 0x20 : 0x00;
+      c1 <<= 1;
+      c1 |= mpiGetBitValue(d1, k - 5);
+      c1 ^= u1 ? 0x20 : 0x00;
+
+      //Point doubling
+      EC_CHECK(ecDouble(params, r, r));
+
+      //Check u(0) and u(1)
+      if(u0 == -1 && u1 == -1)
+      {
+         //Compute R = R - SpT
+         EC_CHECK(ecFullSub(params, r, r, &spt));
+      }
+      else if(u0 == -1 && u1 == 0)
+      {
+         //Compute R = R - S
+         EC_CHECK(ecFullSub(params, r, r, s));
+      }
+      else if(u0 == -1 && u1 == 1)
+      {
+         //Compute R = R - SmT
+         EC_CHECK(ecFullSub(params, r, r, &smt));
+      }
+      else if(u0 == 0 && u1 == -1)
+      {
+         //Compute R = R - T
+         EC_CHECK(ecFullSub(params, r, r, t));
+      }
+      else if(u0 == 0 && u1 == 1)
+      {
+         //Compute R = R + T
+         EC_CHECK(ecFullAdd(params, r, r, t));
+      }
+      else if(u0 == 1 && u1 == -1)
+      {
+         //Compute R = R + SmT
+         EC_CHECK(ecFullAdd(params, r, r, &smt));
+      }
+      else if(u0 == 1 && u1 == 0)
+      {
+         //Compute R = R + S
+         EC_CHECK(ecFullAdd(params, r, r, s));
+      }
+      else if(u0 == 1 && u1 == 1)
+      {
+         //Compute R = R + SpT
+         EC_CHECK(ecFullAdd(params, r, r, &spt));
+      }
+   }
+
+end:
+   //Release EC points
+   ecFree(&spt);
+   ecFree(&smt);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Fast modular addition
+ * @param[in] params EC domain parameters
+ * @param[out] r Resulting integer R = (A + B) mod p
+ * @param[in] a An integer such as 0 <= A < p
+ * @param[in] b An integer such as 0 <= B < p
+ * @return Error code
+ **/
+
+error_t ecAddMod(const EcDomainParameters *params, Mpi *r, const Mpi *a, const Mpi *b)
+{
+   error_t error;
+
+   //Compute R = A + B
+   MPI_CHECK(mpiAdd(r, a, b));
+
+   //Compute R = (A + B) mod p
+   if(mpiComp(r, &params->p) >= 0)
+   {
+       MPI_CHECK(mpiSub(r, r, &params->p));
+   }
+
+end:
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Fast modular subtraction
+ * @param[in] params EC domain parameters
+ * @param[out] r Resulting integer R = (A - B) mod p
+ * @param[in] a An integer such as 0 <= A < p
+ * @param[in] b An integer such as 0 <= B < p
+ * @return Error code
+ **/
+
+error_t ecSubMod(const EcDomainParameters *params, Mpi *r, const Mpi *a, const Mpi *b)
+{
+   error_t error;
+
+   //Compute R = A - B
+   MPI_CHECK(mpiSub(r, a, b));
+
+   //Compute R = (A - B) mod p
+   if(mpiCompInt(r, 0) < 0)
+   {
+       MPI_CHECK(mpiAdd(r, r, &params->p));
+   }
+
+end:
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Fast modular multiplication
+ * @param[in] params EC domain parameters
+ * @param[out] r Resulting integer R = (A * B) mod p
+ * @param[in] a An integer such as 0 <= A < p
+ * @param[in] b An integer such as 0 <= B < p
+ * @return Error code
+ **/
+
+error_t ecMulMod(const EcDomainParameters *params, Mpi *r, const Mpi *a, const Mpi *b)
+{
+   error_t error;
+
+   //Compute R = A * B
+   MPI_CHECK(mpiMul(r, a, b));
+
+   //Compute R = (A * B) mod p
+   if(params->mod != NULL)
+   {
+      MPI_CHECK(params->mod(r, &params->p));
+   }
+   else
+   {
+      MPI_CHECK(mpiMod(r, r, &params->p));
+   }
+
+end:
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Fast modular squaring
+ * @param[in] params EC domain parameters
+ * @param[out] r Resulting integer R = (A ^ 2) mod p
+ * @param[in] a An integer such as 0 <= A < p
+ * @return Error code
+ **/
+
+error_t ecSqrMod(const EcDomainParameters *params, Mpi *r, const Mpi *a)
+{
+   error_t error;
+
+   //Compute R = A ^ 2
+   MPI_CHECK(mpiMul(r, a, a));
+
+   //Compute R = (A ^ 2) mod p
+   if(params->mod != NULL)
+   {
+      MPI_CHECK(params->mod(r, &params->p));
+   }
+   else
+   {
+      MPI_CHECK(mpiMod(r, r, &params->p));
+   }
+
+end:
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/ec.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,110 @@
+/**
+ * @file ec.h
+ * @brief ECC (Elliptic Curve Cryptography)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _EC_H
+#define _EC_H
+
+//Dependencies
+#include "crypto.h"
+#include "ec_curves.h"
+
+//Error code checking
+#define EC_CHECK(f) if((error = f) != NO_ERROR) goto end
+
+
+/**
+ * @brief Elliptic curve point
+ **/
+
+typedef struct
+{
+   Mpi x; ///<x-coordinate
+   Mpi y; ///<y-coordinate
+   Mpi z; ///<z-coordinate
+} EcPoint;
+
+
+/**
+ * @brief EC domain parameters
+ **/
+
+typedef struct
+{
+   EcCurveType type;  ///<Curve type
+   Mpi p;             ///<Prime
+   Mpi a;             ///<Curve parameter a
+   Mpi b;             ///<Curve parameter b
+   EcPoint g;         ///<Base point G
+   Mpi q;             ///<Order of the point G
+   EcFastModAlgo mod; ///<Fast modular reduction
+} EcDomainParameters;
+
+
+//EC related constants
+extern const uint8_t EC_PUBLIC_KEY_OID[7];
+
+//EC related functions
+void ecInitDomainParameters(EcDomainParameters *params);
+void ecFreeDomainParameters(EcDomainParameters *params);
+
+error_t ecLoadDomainParameters(EcDomainParameters *params, const EcCurveInfo *curveInfo);
+
+void ecInit(EcPoint *r);
+void ecFree(EcPoint *r);
+
+error_t ecCopy(EcPoint *r, const EcPoint *s);
+
+error_t ecImport(const EcDomainParameters *params,
+   EcPoint *r, const uint8_t *data, size_t length);
+
+error_t ecExport(const EcDomainParameters *params,
+   const EcPoint *a, uint8_t *data, size_t *length);
+
+error_t ecProjectify(const EcDomainParameters *params, EcPoint *r, const EcPoint *s);
+error_t ecAffinify(const EcDomainParameters *params, EcPoint *r, const EcPoint *s);
+
+bool_t ecIsPointAffine(const EcDomainParameters *params, const EcPoint *s);
+
+error_t ecDouble(const EcDomainParameters *params, EcPoint *r, const EcPoint *s);
+
+error_t ecAdd(const EcDomainParameters *params, EcPoint *r, const EcPoint *s, const EcPoint *t);
+error_t ecFullAdd(const EcDomainParameters *params, EcPoint *r, const EcPoint *s, const EcPoint *t);
+error_t ecFullSub(const EcDomainParameters *params, EcPoint *r, const EcPoint *s, const EcPoint *t);
+
+error_t ecMult(const EcDomainParameters *params, EcPoint *r, const Mpi *d, const EcPoint *s);
+
+error_t ecTwinMult(const EcDomainParameters *params, EcPoint *r,
+   const Mpi *d0, const EcPoint *s, const Mpi *d1, const EcPoint *t);
+
+error_t ecAddMod(const EcDomainParameters *params, Mpi *r, const Mpi *a, const Mpi *b);
+error_t ecSubMod(const EcDomainParameters *params, Mpi *r, const Mpi *a, const Mpi *b);
+error_t ecMulMod(const EcDomainParameters *params, Mpi *r, const Mpi *a, const Mpi *b);
+error_t ecSqrMod(const EcDomainParameters *params, Mpi *r, const Mpi *a);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/ec_curves.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1958 @@
+/**
+ * @file ec_curves.c
+ * @brief Elliptic curves
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include <string.h>
+#include "crypto.h"
+#include "ec_curves.h"
+#include "oid.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (EC_SUPPORT == ENABLED)
+
+//Macro definition
+#define CLEAR_WORD32(a, i, n) memset((a)->data + i, 0, n * MPI_INT_SIZE);
+#define COPY_WORD32(a, i, b, j, n) memcpy((a)->data + i, (b)->data + j, n * MPI_INT_SIZE);
+
+//secp112r1 OID (1.3.132.0.6)
+const uint8_t SECP112R1_OID[5] = {0x2B, 0x81, 0x04, 0x00, 0x06};
+//secp112r2 OID (1.3.132.0.7)
+const uint8_t SECP112R2_OID[5] = {0x2B, 0x81, 0x04, 0x00, 0x07};
+//secp128r1 OID (1.3.132.0.28)
+const uint8_t SECP128R1_OID[5] = {0x2B, 0x81, 0x04, 0x00, 0x1C};
+//secp128r2 OID (1.3.132.0.29)
+const uint8_t SECP128R2_OID[5] = {0x2B, 0x81, 0x04, 0x00, 0x1D};
+//secp160k1 OID (1.3.132.0.9)
+const uint8_t SECP160K1_OID[5] = {0x2B, 0x81, 0x04, 0x00, 0x09};
+//secp160r1 OID (1.3.132.0.8)
+const uint8_t SECP160R1_OID[5] = {0x2B, 0x81, 0x04, 0x00, 0x08};
+//secp160r2 OID (1.3.132.0.30)
+const uint8_t SECP160R2_OID[5] = {0x2B, 0x81, 0x04, 0x00, 0x1E};
+//secp192k1 OID (1.3.132.0.31)
+const uint8_t SECP192K1_OID[5] = {0x2B, 0x81, 0x04, 0x00, 0x1F};
+//secp192r1 OID (1.2.840.10045.3.1.1)
+const uint8_t SECP192R1_OID[8] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01};
+//secp224k1 OID (1.3.132.0.32)
+const uint8_t SECP224K1_OID[5] = {0x2B, 0x81, 0x04, 0x00, 0x20};
+//secp224r1 OID (1.3.132.0.33)
+const uint8_t SECP224R1_OID[5] = {0x2B, 0x81, 0x04, 0x00, 0x21};
+//secp256k1 OID (1.3.132.0.10)
+const uint8_t SECP256K1_OID[5] = {0x2B, 0x81, 0x04, 0x00, 0x0A};
+//secp256r1 OID (1.2.840.10045.3.1.7)
+const uint8_t SECP256R1_OID[8] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07};
+//secp384r1 OID (1.3.132.0.34)
+const uint8_t SECP384R1_OID[5] = {0x2B, 0x81, 0x04, 0x00, 0x22};
+//secp521r1 OID (1.3.132.0.35)
+const uint8_t SECP521R1_OID[5] = {0x2B, 0x81, 0x04, 0x00, 0x23};
+//brainpoolP160r1 OID (1.3.36.3.3.2.8.1.1.1)
+const uint8_t BRAINPOOLP160R1_OID[10] = {0x2B, 0x03, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x01};
+//brainpoolP192r1 OID (1.3.36.3.3.2.8.1.1.3)
+const uint8_t BRAINPOOLP192R1_OID[10] = {0x2B, 0x03, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x03};
+//brainpoolP224r1 OID (1.3.36.3.3.2.8.1.1.5)
+const uint8_t BRAINPOOLP224R1_OID[10] = {0x2B, 0x03, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x05};
+//brainpoolP256r1 OID (1.3.36.3.3.2.8.1.1.7)
+const uint8_t BRAINPOOLP256R1_OID[10] = {0x2B, 0x03, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07};
+//brainpoolP320r1 OID (1.3.36.3.3.2.8.1.1.9)
+const uint8_t BRAINPOOLP320R1_OID[10] = {0x2B, 0x03, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x09};
+//brainpoolP384r1 OID (1.3.36.3.3.2.8.1.1.11)
+const uint8_t BRAINPOOLP384R1_OID[10] = {0x2B, 0x03, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B};
+//brainpoolP512r1 OID (1.3.36.3.3.2.8.1.1.13)
+const uint8_t BRAINPOOLP512R1_OID[10] = {0x2B, 0x03, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D};
+
+
+/**
+ * @brief secp112r1 elliptic curve
+ **/
+
+const EcCurveInfo secp112r1Curve =
+{
+   //Curve name
+   "secp112r1",
+   //Object identifier
+   SECP112R1_OID,
+   sizeof(SECP112R1_OID),
+   //Curve type
+   EC_CURVE_TYPE_SECP_R1,
+   //Prime modulus p
+   {0xDB, 0x7C, 0x2A, 0xBF, 0x62, 0xE3, 0x5E, 0x66, 0x80, 0x76, 0xBE, 0xAD, 0x20, 0x8B},
+   14,
+   //Curve parameter a
+   {0xDB, 0x7C, 0x2A, 0xBF, 0x62, 0xE3, 0x5E, 0x66, 0x80, 0x76, 0xBE, 0xAD, 0x20, 0x88},
+   14,
+   //Curve parameter b
+   {0x65, 0x9E, 0xF8, 0xBA, 0x04, 0x39, 0x16, 0xEE, 0xDE, 0x89, 0x11, 0x70, 0x2B, 0x22},
+   14,
+   //x-coordinate of the base point G
+   {0x09, 0x48, 0x72, 0x39, 0x99, 0x5A, 0x5E, 0xE7, 0x6B, 0x55, 0xF9, 0xC2, 0xF0, 0x98},
+   14,
+   //y-coordinate of the base point G
+   {0xA8, 0x9C, 0xE5, 0xAF, 0x87, 0x24, 0xC0, 0xA2, 0x3E, 0x0E, 0x0F, 0xF7, 0x75, 0x00},
+   14,
+   //Base point order q
+   {0xDB, 0x7C, 0x2A, 0xBF, 0x62, 0xE3, 0x5E, 0x76, 0x28, 0xDF, 0xAC, 0x65, 0x61, 0xC5},
+   14,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   NULL
+};
+
+
+/**
+ * @brief secp112r2 elliptic curve
+ **/
+
+const EcCurveInfo secp112r2Curve =
+{
+   //Curve name
+   "secp112r2",
+   //Object identifier
+   SECP112R2_OID,
+   sizeof(SECP112R2_OID),
+   //Curve type
+   EC_CURVE_TYPE_SECP_R2,
+   //Prime modulus p
+   {0xDB, 0x7C, 0x2A, 0xBF, 0x62, 0xE3, 0x5E, 0x66, 0x80, 0x76, 0xBE, 0xAD, 0x20, 0x8B},
+   14,
+   //Curve parameter a
+   {0x61, 0x27, 0xC2, 0x4C, 0x05, 0xF3, 0x8A, 0x0A, 0xAA, 0xF6, 0x5C, 0x0E, 0xF0, 0x2C},
+   14,
+   //Curve parameter b
+   {0x51, 0xDE, 0xF1, 0x81, 0x5D, 0xB5, 0xED, 0x74, 0xFC, 0xC3, 0x4C, 0x85, 0xD7, 0x09},
+   14,
+   //x-coordinate of the base point G
+   {0x4B, 0xA3, 0x0A, 0xB5, 0xE8, 0x92, 0xB4, 0xE1, 0x64, 0x9D, 0xD0, 0x92, 0x86, 0x43},
+   14,
+   //y-coordinate of the base point G
+   {0xAD, 0xCD, 0x46, 0xF5, 0x88, 0x2E, 0x37, 0x47, 0xDE, 0xF3, 0x6E, 0x95, 0x6E, 0x97},
+   14,
+   //Base point order q
+   {0x36, 0xDF, 0x0A, 0xAF, 0xD8, 0xB8, 0xD7, 0x59, 0x7C, 0xA1, 0x05, 0x20, 0xD0, 0x4B},
+   14,
+   //Cofactor
+   4,
+   //Fast modular reduction
+   NULL
+};
+
+
+/**
+ * @brief secp128r1 elliptic curve
+ **/
+
+const EcCurveInfo secp128r1Curve =
+{
+   //Curve name
+   "secp128r1",
+   //Object identifier
+   SECP128R1_OID,
+   sizeof(SECP128R1_OID),
+   //Curve type
+   EC_CURVE_TYPE_SECP_R1,
+   //Prime modulus p
+   {0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+   16,
+   //Curve parameter a
+   {0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC},
+   16,
+   //Curve parameter b
+   {0xE8, 0x75, 0x79, 0xC1, 0x10, 0x79, 0xF4, 0x3D, 0xD8, 0x24, 0x99, 0x3C, 0x2C, 0xEE, 0x5E, 0xD3},
+   16,
+   //x-coordinate of the base point G
+   {0x16, 0x1F, 0xF7, 0x52, 0x8B, 0x89, 0x9B, 0x2D, 0x0C, 0x28, 0x60, 0x7C, 0xA5, 0x2C, 0x5B, 0x86},
+   16,
+   //y-coordinate of the base point G
+   {0xCF, 0x5A, 0xC8, 0x39, 0x5B, 0xAF, 0xEB, 0x13, 0xC0, 0x2D, 0xA2, 0x92, 0xDD, 0xED, 0x7A, 0x83},
+   16,
+   //Base point order q
+   {0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x75, 0xA3, 0x0D, 0x1B, 0x90, 0x38, 0xA1, 0x15},
+   16,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   secp128r1Mod
+};
+
+
+/**
+ * @brief secp128r2 elliptic curve
+ **/
+
+const EcCurveInfo secp128r2Curve =
+{
+   //Curve name
+   "secp128r2",
+   //Object identifier
+   SECP128R2_OID,
+   sizeof(SECP128R2_OID),
+   //Curve type
+   EC_CURVE_TYPE_SECP_R2,
+   //Prime modulus p
+   {0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+   16,
+   //Curve parameter a
+   {0xD6, 0x03, 0x19, 0x98, 0xD1, 0xB3, 0xBB, 0xFE, 0xBF, 0x59, 0xCC, 0x9B, 0xBF, 0xF9, 0xAE, 0xE1},
+   16,
+   //Curve parameter b
+   {0x5E, 0xEE, 0xFC, 0xA3, 0x80, 0xD0, 0x29, 0x19, 0xDC, 0x2C, 0x65, 0x58, 0xBB, 0x6D, 0x8A, 0x5D},
+   16,
+   //x-coordinate of the base point G
+   {0x7B, 0x6A, 0xA5, 0xD8, 0x5E, 0x57, 0x29, 0x83, 0xE6, 0xFB, 0x32, 0xA7, 0xCD, 0xEB, 0xC1, 0x40},
+   16,
+   //y-coordinate of the base point G
+   {0x27, 0xB6, 0x91, 0x6A, 0x89, 0x4D, 0x3A, 0xEE, 0x71, 0x06, 0xFE, 0x80, 0x5F, 0xC3, 0x4B, 0x44},
+   16,
+   //Base point order q
+   {0x3F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xBE, 0x00, 0x24, 0x72, 0x06, 0x13, 0xB5, 0xA3},
+   16,
+   //Cofactor
+   4,
+   //Fast modular reduction
+   secp128r2Mod
+};
+
+
+/**
+ * @brief secp160k1 elliptic curve
+ **/
+
+const EcCurveInfo secp160k1Curve =
+{
+   //Curve name
+   "secp160k1",
+   //Object identifier
+   SECP160K1_OID,
+   sizeof(SECP160K1_OID),
+   //Curve type
+   EC_CURVE_TYPE_SECP_K1,
+   //Prime modulus p
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+    0xFF, 0xFF, 0xAC, 0x73},
+   20,
+   //Curve parameter a
+   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00},
+   20,
+   //Curve parameter b
+   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x07},
+   20,
+   //x-coordinate of the base point G
+   {0x3B, 0x4C, 0x38, 0x2C, 0xE3, 0x7A, 0xA1, 0x92, 0xA4, 0x01, 0x9E, 0x76, 0x30, 0x36, 0xF4, 0xF5,
+    0xDD, 0x4D, 0x7E, 0xBB},
+   20,
+   //y-coordinate of the base point G
+   {0x93, 0x8C, 0xF9, 0x35, 0x31, 0x8F, 0xDC, 0xED, 0x6B, 0xC2, 0x82, 0x86, 0x53, 0x17, 0x33, 0xC3,
+    0xF0, 0x3C, 0x4F, 0xEE},
+   20,
+   //Base point order q
+   {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xB8, 0xFA, 0x16, 0xDF, 0xAB,
+    0x9A, 0xCA, 0x16, 0xB6, 0xB3},
+   21,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   secp160k1Mod
+};
+
+
+/**
+ * @brief secp160r1 elliptic curve
+ **/
+
+const EcCurveInfo secp160r1Curve =
+{
+   //Curve name
+   "secp160r1",
+   //Object identifier
+   SECP160R1_OID,
+   sizeof(SECP160R1_OID),
+   //Curve type
+   EC_CURVE_TYPE_SECP_R1,
+   //Prime modulus p
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0x7F, 0xFF, 0xFF, 0xFF},
+   20,
+   //Curve parameter a
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0x7F, 0xFF, 0xFF, 0xFC},
+   20,
+   //Curve parameter b
+   {0x1C, 0x97, 0xBE, 0xFC, 0x54, 0xBD, 0x7A, 0x8B, 0x65, 0xAC, 0xF8, 0x9F, 0x81, 0xD4, 0xD4, 0xAD,
+    0xC5, 0x65, 0xFA, 0x45},
+   20,
+   //x-coordinate of the base point G
+   {0x4A, 0x96, 0xB5, 0x68, 0x8E, 0xF5, 0x73, 0x28, 0x46, 0x64, 0x69, 0x89, 0x68, 0xC3, 0x8B, 0xB9,
+    0x13, 0xCB, 0xFC, 0x82},
+   20,
+   //y-coordinate of the base point G
+   {0x23, 0xA6, 0x28, 0x55, 0x31, 0x68, 0x94, 0x7D, 0x59, 0xDC, 0xC9, 0x12, 0x04, 0x23, 0x51, 0x37,
+    0x7A, 0xC5, 0xFB, 0x32},
+   20,
+   //Base point order q
+   {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF4, 0xC8, 0xF9, 0x27, 0xAE,
+    0xD3, 0xCA, 0x75, 0x22, 0x57},
+   21,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   secp160r1Mod
+};
+
+
+/**
+ * @brief secp160r2 elliptic curve
+ **/
+
+const EcCurveInfo secp160r2Curve =
+{
+   //Curve name
+   "secp160r2",
+   //Object identifier
+   SECP160R2_OID,
+   sizeof(SECP160R2_OID),
+   //Curve type
+   EC_CURVE_TYPE_SECP_R2,
+   //Prime modulus p
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+    0xFF, 0xFF, 0xAC, 0x73},
+   20,
+   //Curve parameter a
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+    0xFF, 0xFF, 0xAC, 0x70},
+   20,
+   //Curve parameter b
+   {0xB4, 0xE1, 0x34, 0xD3, 0xFB, 0x59, 0xEB, 0x8B, 0xAB, 0x57, 0x27, 0x49, 0x04, 0x66, 0x4D, 0x5A,
+    0xF5, 0x03, 0x88, 0xBA},
+   20,
+   //x-coordinate of the base point G
+   {0x52, 0xDC, 0xB0, 0x34, 0x29, 0x3A, 0x11, 0x7E, 0x1F, 0x4F, 0xF1, 0x1B, 0x30, 0xF7, 0x19, 0x9D,
+    0x31, 0x44, 0xCE, 0x6D},
+   20,
+   //y-coordinate of the base point G
+   {0xFE, 0xAF, 0xFE, 0xF2, 0xE3, 0x31, 0xF2, 0x96, 0xE0, 0x71, 0xFA, 0x0D, 0xF9, 0x98, 0x2C, 0xFE,
+    0xA7, 0xD4, 0x3F, 0x2E},
+   20,
+   //Base point order q
+   {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x1E, 0xE7, 0x86, 0xA8,
+    0x18, 0xF3, 0xA1, 0xA1, 0x6B},
+   21,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   secp160r2Mod
+};
+
+
+/**
+ * @brief secp192k1 elliptic curve
+ **/
+
+const EcCurveInfo secp192k1Curve =
+{
+   //Curve name
+   "secp192k1",
+   //Object identifier
+   SECP192K1_OID,
+   sizeof(SECP192K1_OID),
+   //Curve type
+   EC_CURVE_TYPE_SECP_K1,
+   //Prime modulus p
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xEE, 0x37},
+   24,
+   //Curve parameter a
+   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+   24,
+   //Curve parameter b
+   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03},
+   24,
+   //x-coordinate of the base point G
+   {0xDB, 0x4F, 0xF1, 0x0E, 0xC0, 0x57, 0xE9, 0xAE, 0x26, 0xB0, 0x7D, 0x02, 0x80, 0xB7, 0xF4, 0x34,
+    0x1D, 0xA5, 0xD1, 0xB1, 0xEA, 0xE0, 0x6C, 0x7D},
+   24,
+   //y-coordinate of the base point G
+   {0x9B, 0x2F, 0x2F, 0x6D, 0x9C, 0x56, 0x28, 0xA7, 0x84, 0x41, 0x63, 0xD0, 0x15, 0xBE, 0x86, 0x34,
+    0x40, 0x82, 0xAA, 0x88, 0xD9, 0x5E, 0x2F, 0x9D},
+   24,
+   //Base point order q
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x26, 0xF2, 0xFC, 0x17,
+    0x0F, 0x69, 0x46, 0x6A, 0x74, 0xDE, 0xFD, 0x8D},
+   24,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   secp192k1Mod
+};
+
+
+/**
+ * @brief secp192r1 elliptic curve
+ **/
+
+const EcCurveInfo secp192r1Curve =
+{
+   //Curve name
+   "secp192r1",
+   //Object identifier
+   SECP192R1_OID,
+   sizeof(SECP192R1_OID),
+   //Curve type
+   EC_CURVE_TYPE_SECP_R1,
+   //Prime modulus p
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+   24,
+   //Curve parameter a
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC},
+   24,
+   //Curve parameter b
+   {0x64, 0x21, 0x05, 0x19, 0xE5, 0x9C, 0x80, 0xE7, 0x0F, 0xA7, 0xE9, 0xAB, 0x72, 0x24, 0x30, 0x49,
+    0xFE, 0xB8, 0xDE, 0xEC, 0xC1, 0x46, 0xB9, 0xB1},
+   24,
+   //x-coordinate of the base point G
+   {0x18, 0x8D, 0xA8, 0x0E, 0xB0, 0x30, 0x90, 0xF6, 0x7C, 0xBF, 0x20, 0xEB, 0x43, 0xA1, 0x88, 0x00,
+    0xF4, 0xFF, 0x0A, 0xFD, 0x82, 0xFF, 0x10, 0x12},
+   24,
+   //y-coordinate of the base point G
+   {0x07, 0x19, 0x2B, 0x95, 0xFF, 0xC8, 0xDA, 0x78, 0x63, 0x10, 0x11, 0xED, 0x6B, 0x24, 0xCD, 0xD5,
+    0x73, 0xF9, 0x77, 0xA1, 0x1E, 0x79, 0x48, 0x11},
+   24,
+   //Base point order q
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0xDE, 0xF8, 0x36,
+    0x14, 0x6B, 0xC9, 0xB1, 0xB4, 0xD2, 0x28, 0x31},
+   24,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   secp192r1Mod
+};
+
+
+/**
+ * @brief secp224k1 elliptic curve
+ **/
+
+const EcCurveInfo secp224k1Curve =
+{
+   //Curve name
+   "secp224k1",
+   //Object identifier
+   SECP224K1_OID,
+   sizeof(SECP224K1_OID),
+   //Curve type
+   EC_CURVE_TYPE_SECP_K1,
+   //Prime modulus p
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xE5, 0x6D},
+   28,
+   //Curve parameter a
+   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+   28,
+   //Curve parameter b
+   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05},
+   28,
+   //x-coordinate of the base point G
+   {0xA1, 0x45, 0x5B, 0x33, 0x4D, 0xF0, 0x99, 0xDF, 0x30, 0xFC, 0x28, 0xA1, 0x69, 0xA4, 0x67, 0xE9,
+    0xE4, 0x70, 0x75, 0xA9, 0x0F, 0x7E, 0x65, 0x0E, 0xB6, 0xB7, 0xA4, 0x5C},
+   28,
+   //y-coordinate of the base point G
+   {0x7E, 0x08, 0x9F, 0xED, 0x7F, 0xBA, 0x34, 0x42, 0x82, 0xCA, 0xFB, 0xD6, 0xF7, 0xE3, 0x19, 0xF7,
+    0xC0, 0xB0, 0xBD, 0x59, 0xE2, 0xCA, 0x4B, 0xDB, 0x55, 0x6D, 0x61, 0xA5},
+   28,
+   //Base point order q
+   {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xDC,
+    0xE8, 0xD2, 0xEC, 0x61, 0x84, 0xCA, 0xF0, 0xA9, 0x71, 0x76, 0x9F, 0xB1, 0xF7},
+   29,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   secp224k1Mod
+};
+
+
+/**
+ * @brief secp224r1 elliptic curve
+ **/
+
+const EcCurveInfo secp224r1Curve =
+{
+   //Curve name
+   "secp224r1",
+   //Object identifier
+   SECP224R1_OID,
+   sizeof(SECP224R1_OID),
+   //Curve type
+   EC_CURVE_TYPE_SECP_R1,
+   //Prime modulus p
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+   28,
+   //Curve parameter a
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE},
+   28,
+   //Curve parameter b
+   {0xB4, 0x05, 0x0A, 0x85, 0x0C, 0x04, 0xB3, 0xAB, 0xF5, 0x41, 0x32, 0x56, 0x50, 0x44, 0xB0, 0xB7,
+    0xD7, 0xBF, 0xD8, 0xBA, 0x27, 0x0B, 0x39, 0x43, 0x23, 0x55, 0xFF, 0xB4},
+   28,
+   //x-coordinate of the base point G
+   {0xB7, 0x0E, 0x0C, 0xBD, 0x6B, 0xB4, 0xBF, 0x7F, 0x32, 0x13, 0x90, 0xB9, 0x4A, 0x03, 0xC1, 0xD3,
+    0x56, 0xC2, 0x11, 0x22, 0x34, 0x32, 0x80, 0xD6, 0x11, 0x5C, 0x1D, 0x21},
+   28,
+   //y-coordinate of the base point G
+   {0xBD, 0x37, 0x63, 0x88, 0xB5, 0xF7, 0x23, 0xFB, 0x4C, 0x22, 0xDF, 0xE6, 0xCD, 0x43, 0x75, 0xA0,
+    0x5A, 0x07, 0x47, 0x64, 0x44, 0xD5, 0x81, 0x99, 0x85, 0x00, 0x7E, 0x34},
+   28,
+   //Base point order q
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x16, 0xA2,
+    0xE0, 0xB8, 0xF0, 0x3E, 0x13, 0xDD, 0x29, 0x45, 0x5C, 0x5C, 0x2A, 0x3D},
+   28,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   secp224r1Mod
+};
+
+
+/**
+ * @brief secp256k1 elliptic curve
+ **/
+
+const EcCurveInfo secp256k1Curve =
+{
+   //Curve name
+   "secp256k1",
+   //Object identifier
+   SECP256K1_OID,
+   sizeof(SECP256K1_OID),
+   //Curve type
+   EC_CURVE_TYPE_SECP_K1,
+   //Prime modulus p
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F},
+   32,
+   //Curve parameter a
+   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+   32,
+   //Curve parameter b
+   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07},
+   32,
+   //x-coordinate of the base point G
+   {0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07,
+    0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98},
+   32,
+   //y-coordinate of the base point G
+   {0x48, 0x3A, 0xDA, 0x77, 0x26, 0xA3, 0xC4, 0x65, 0x5D, 0xA4, 0xFB, 0xFC, 0x0E, 0x11, 0x08, 0xA8,
+    0xFD, 0x17, 0xB4, 0x48, 0xA6, 0x85, 0x54, 0x19, 0x9C, 0x47, 0xD0, 0x8F, 0xFB, 0x10, 0xD4, 0xB8},
+   32,
+   //Base point order q
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+    0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41},
+   32,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   secp256k1Mod
+};
+
+
+/**
+ * @brief secp256r1 elliptic curve
+ **/
+
+const EcCurveInfo secp256r1Curve =
+{
+   //Curve name
+   "secp256r1",
+   //Object identifier
+   SECP256R1_OID,
+   sizeof(SECP256R1_OID),
+   //Curve type
+   EC_CURVE_TYPE_SECP_R1,
+   //Prime modulus p
+   {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+   32,
+   //Curve parameter a
+   {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC},
+   32,
+   //Curve parameter b
+   {0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3, 0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC,
+    0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27, 0xD2, 0x60, 0x4B},
+   32,
+   //x-coordinate of the base point G
+   {0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40, 0xF2,
+    0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2, 0x96},
+   32,
+   //y-coordinate of the base point G
+   {0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16,
+    0x2B, 0xCE, 0x33, 0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5},
+   32,
+   //Base point order q
+   {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51},
+   32,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   secp256r1Mod
+};
+
+
+/**
+ * @brief secp384r1 elliptic curve
+ **/
+
+const EcCurveInfo secp384r1Curve =
+{
+   //Curve name
+   "secp384r1",
+   //Object identifier
+   SECP384R1_OID,
+   sizeof(SECP384R1_OID),
+   //Curve type
+   EC_CURVE_TYPE_SECP_R1,
+   //Prime modulus p
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF},
+   48,
+   //Curve parameter a
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFC},
+   48,
+   //Curve parameter b
+   {0xB3, 0x31, 0x2F, 0xA7, 0xE2, 0x3E, 0xE7, 0xE4, 0x98, 0x8E, 0x05, 0x6B, 0xE3, 0xF8, 0x2D, 0x19,
+    0x18, 0x1D, 0x9C, 0x6E, 0xFE, 0x81, 0x41, 0x12, 0x03, 0x14, 0x08, 0x8F, 0x50, 0x13, 0x87, 0x5A,
+    0xC6, 0x56, 0x39, 0x8D, 0x8A, 0x2E, 0xD1, 0x9D, 0x2A, 0x85, 0xC8, 0xED, 0xD3, 0xEC, 0x2A, 0xEF},
+   48,
+   //x-coordinate of the base point G
+   {0xAA, 0x87, 0xCA, 0x22, 0xBE, 0x8B, 0x05, 0x37, 0x8E, 0xB1, 0xC7, 0x1E, 0xF3, 0x20, 0xAD, 0x74,
+    0x6E, 0x1D, 0x3B, 0x62, 0x8B, 0xA7, 0x9B, 0x98, 0x59, 0xF7, 0x41, 0xE0, 0x82, 0x54, 0x2A, 0x38,
+    0x55, 0x02, 0xF2, 0x5D, 0xBF, 0x55, 0x29, 0x6C, 0x3A, 0x54, 0x5E, 0x38, 0x72, 0x76, 0x0A, 0xB7},
+   48,
+   //y-coordinate of the base point G
+   {0x36, 0x17, 0xDE, 0x4A, 0x96, 0x26, 0x2C, 0x6F, 0x5D, 0x9E, 0x98, 0xBF, 0x92, 0x92, 0xDC, 0x29,
+    0xF8, 0xF4, 0x1D, 0xBD, 0x28, 0x9A, 0x14, 0x7C, 0xE9, 0xDA, 0x31, 0x13, 0xB5, 0xF0, 0xB8, 0xC0,
+    0x0A, 0x60, 0xB1, 0xCE, 0x1D, 0x7E, 0x81, 0x9D, 0x7A, 0x43, 0x1D, 0x7C, 0x90, 0xEA, 0x0E, 0x5F},
+   48,
+   //Base point order q
+   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0x63, 0x4D, 0x81, 0xF4, 0x37, 0x2D, 0xDF,
+    0x58, 0x1A, 0x0D, 0xB2, 0x48, 0xB0, 0xA7, 0x7A, 0xEC, 0xEC, 0x19, 0x6A, 0xCC, 0xC5, 0x29, 0x73},
+   48,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   secp384r1Mod
+};
+
+
+/**
+ * @brief secp521r1 elliptic curve
+ **/
+
+const EcCurveInfo secp521r1Curve =
+{
+   //Curve name
+   "secp521r1",
+   //Object identifier
+   SECP521R1_OID,
+   sizeof(SECP521R1_OID),
+   //Curve type
+   EC_CURVE_TYPE_SECP_R1,
+   //Prime modulus p
+   {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF},
+   66,
+   //Curve parameter a
+   {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFC},
+   66,
+   //Curve parameter b
+   {0x00, 0x51, 0x95, 0x3E, 0xB9, 0x61, 0x8E, 0x1C, 0x9A, 0x1F, 0x92, 0x9A, 0x21, 0xA0, 0xB6, 0x85,
+    0x40, 0xEE, 0xA2, 0xDA, 0x72, 0x5B, 0x99, 0xB3, 0x15, 0xF3, 0xB8, 0xB4, 0x89, 0x91, 0x8E, 0xF1,
+    0x09, 0xE1, 0x56, 0x19, 0x39, 0x51, 0xEC, 0x7E, 0x93, 0x7B, 0x16, 0x52, 0xC0, 0xBD, 0x3B, 0xB1,
+    0xBF, 0x07, 0x35, 0x73, 0xDF, 0x88, 0x3D, 0x2C, 0x34, 0xF1, 0xEF, 0x45, 0x1F, 0xD4, 0x6B, 0x50,
+    0x3F, 0x00},
+   66,
+   //x-coordinate of the base point G
+   {0x00, 0xC6, 0x85, 0x8E, 0x06, 0xB7, 0x04, 0x04, 0xE9, 0xCD, 0x9E, 0x3E, 0xCB, 0x66, 0x23, 0x95,
+    0xB4, 0x42, 0x9C, 0x64, 0x81, 0x39, 0x05, 0x3F, 0xB5, 0x21, 0xF8, 0x28, 0xAF, 0x60, 0x6B, 0x4D,
+    0x3D, 0xBA, 0xA1, 0x4B, 0x5E, 0x77, 0xEF, 0xE7, 0x59, 0x28, 0xFE, 0x1D, 0xC1, 0x27, 0xA2, 0xFF,
+    0xA8, 0xDE, 0x33, 0x48, 0xB3, 0xC1, 0x85, 0x6A, 0x42, 0x9B, 0xF9, 0x7E, 0x7E, 0x31, 0xC2, 0xE5,
+    0xBD, 0x66},
+   66,
+   //y-coordinate of the base point G
+   {0x01, 0x18, 0x39, 0x29, 0x6A, 0x78, 0x9A, 0x3B, 0xC0, 0x04, 0x5C, 0x8A, 0x5F, 0xB4, 0x2C, 0x7D,
+    0x1B, 0xD9, 0x98, 0xF5, 0x44, 0x49, 0x57, 0x9B, 0x44, 0x68, 0x17, 0xAF, 0xBD, 0x17, 0x27, 0x3E,
+    0x66, 0x2C, 0x97, 0xEE, 0x72, 0x99, 0x5E, 0xF4, 0x26, 0x40, 0xC5, 0x50, 0xB9, 0x01, 0x3F, 0xAD,
+    0x07, 0x61, 0x35, 0x3C, 0x70, 0x86, 0xA2, 0x72, 0xC2, 0x40, 0x88, 0xBE, 0x94, 0x76, 0x9F, 0xD1,
+    0x66, 0x50},
+   66,
+   //Base point order q
+   {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFA, 0x51, 0x86, 0x87, 0x83, 0xBF, 0x2F, 0x96, 0x6B, 0x7F, 0xCC, 0x01, 0x48, 0xF7, 0x09,
+    0xA5, 0xD0, 0x3B, 0xB5, 0xC9, 0xB8, 0x89, 0x9C, 0x47, 0xAE, 0xBB, 0x6F, 0xB7, 0x1E, 0x91, 0x38,
+    0x64, 0x09},
+   66,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   secp521r1Mod
+};
+
+
+/**
+ * @brief brainpoolP160r1 elliptic curve
+ **/
+
+const EcCurveInfo brainpoolP160r1Curve =
+{
+   //Curve name
+   "brainpoolP160r1",
+   //Object identifier
+   BRAINPOOLP160R1_OID,
+   sizeof(BRAINPOOLP160R1_OID),
+   //Curve type
+   EC_CURVE_TYPE_BRAINPOOLP_R1,
+   //Prime modulus p
+   {0xE9, 0x5E, 0x4A, 0x5F, 0x73, 0x70, 0x59, 0xDC, 0x60, 0xDF, 0xC7, 0xAD, 0x95, 0xB3, 0xD8, 0x13,
+    0x95, 0x15, 0x62, 0x0F},
+   20,
+   //Curve parameter a
+   {0x34, 0x0E, 0x7B, 0xE2, 0xA2, 0x80, 0xEB, 0x74, 0xE2, 0xBE, 0x61, 0xBA, 0xDA, 0x74, 0x5D, 0x97,
+    0xE8, 0xF7, 0xC3, 0x00},
+   20,
+   //Curve parameter b
+   {0x1E, 0x58, 0x9A, 0x85, 0x95, 0x42, 0x34, 0x12, 0x13, 0x4F, 0xAA, 0x2D, 0xBD, 0xEC, 0x95, 0xC8,
+    0xD8, 0x67, 0x5E, 0x58},
+   20,
+   //x-coordinate of the base point G
+   {0xBE, 0xD5, 0xAF, 0x16, 0xEA, 0x3F, 0x6A, 0x4F, 0x62, 0x93, 0x8C, 0x46, 0x31, 0xEB, 0x5A, 0xF7,
+    0xBD, 0xBC, 0xDB, 0xC3},
+   20,
+   //y-coordinate of the base point G
+   {0x16, 0x67, 0xCB, 0x47, 0x7A, 0x1A, 0x8E, 0xC3, 0x38, 0xF9, 0x47, 0x41, 0x66, 0x9C, 0x97, 0x63,
+    0x16, 0xDA, 0x63, 0x21},
+   20,
+   //Base point order q
+   {0xE9, 0x5E, 0x4A, 0x5F, 0x73, 0x70, 0x59, 0xDC, 0x60, 0xDF, 0x59, 0x91, 0xD4, 0x50, 0x29, 0x40,
+    0x9E, 0x60, 0xFC, 0x09},
+   20,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   NULL
+};
+
+
+/**
+ * @brief brainpoolP192r1 elliptic curve
+ **/
+
+const EcCurveInfo brainpoolP192r1Curve =
+{
+   //Curve name
+   "brainpoolP192r1",
+   //Object identifier
+   BRAINPOOLP192R1_OID,
+   sizeof(BRAINPOOLP192R1_OID),
+   //Curve type
+   EC_CURVE_TYPE_BRAINPOOLP_R1,
+   //Prime modulus p
+   {0xC3, 0x02, 0xF4, 0x1D, 0x93, 0x2A, 0x36, 0xCD, 0xA7, 0xA3, 0x46, 0x30, 0x93, 0xD1, 0x8D, 0xB7,
+    0x8F, 0xCE, 0x47, 0x6D, 0xE1, 0xA8, 0x62, 0x97},
+   24,
+   //Curve parameter a
+   {0x6A, 0x91, 0x17, 0x40, 0x76, 0xB1, 0xE0, 0xE1, 0x9C, 0x39, 0xC0, 0x31, 0xFE, 0x86, 0x85, 0xC1,
+    0xCA, 0xE0, 0x40, 0xE5, 0xC6, 0x9A, 0x28, 0xEF},
+   24,
+   //Curve parameter b
+   {0x46, 0x9A, 0x28, 0xEF, 0x7C, 0x28, 0xCC, 0xA3, 0xDC, 0x72, 0x1D, 0x04, 0x4F, 0x44, 0x96, 0xBC,
+    0xCA, 0x7E, 0xF4, 0x14, 0x6F, 0xBF, 0x25, 0xC9},
+   24,
+   //x-coordinate of the base point G
+   {0xC0, 0xA0, 0x64, 0x7E, 0xAA, 0xB6, 0xA4, 0x87, 0x53, 0xB0, 0x33, 0xC5, 0x6C, 0xB0, 0xF0, 0x90,
+    0x0A, 0x2F, 0x5C, 0x48, 0x53, 0x37, 0x5F, 0xD6},
+   24,
+   //y-coordinate of the base point G
+   {0x14, 0xB6, 0x90, 0x86, 0x6A, 0xBD, 0x5B, 0xB8, 0x8B, 0x5F, 0x48, 0x28, 0xC1, 0x49, 0x00, 0x02,
+    0xE6, 0x77, 0x3F, 0xA2, 0xFA, 0x29, 0x9B, 0x8F},
+   24,
+   //Base point order q
+   {0xC3, 0x02, 0xF4, 0x1D, 0x93, 0x2A, 0x36, 0xCD, 0xA7, 0xA3, 0x46, 0x2F, 0x9E, 0x9E, 0x91, 0x6B,
+    0x5B, 0xE8, 0xF1, 0x02, 0x9A, 0xC4, 0xAC, 0xC1},
+   24,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   NULL
+};
+
+
+/**
+ * @brief brainpoolP224r1 elliptic curve
+ **/
+
+const EcCurveInfo brainpoolP224r1Curve =
+{
+   //Curve name
+   "brainpoolP224r1",
+   //Object identifier
+   BRAINPOOLP224R1_OID,
+   sizeof(BRAINPOOLP224R1_OID),
+   //Curve type
+   EC_CURVE_TYPE_BRAINPOOLP_R1,
+   //Prime modulus p
+   {0xD7, 0xC1, 0x34, 0xAA, 0x26, 0x43, 0x66, 0x86, 0x2A, 0x18, 0x30, 0x25, 0x75, 0xD1, 0xD7, 0x87,
+    0xB0, 0x9F, 0x07, 0x57, 0x97, 0xDA, 0x89, 0xF5, 0x7E, 0xC8, 0xC0, 0xFF},
+   28,
+   //Curve parameter a
+   {0x68, 0xA5, 0xE6, 0x2C, 0xA9, 0xCE, 0x6C, 0x1C, 0x29, 0x98, 0x03, 0xA6, 0xC1, 0x53, 0x0B, 0x51,
+    0x4E, 0x18, 0x2A, 0xD8, 0xB0, 0x04, 0x2A, 0x59, 0xCA, 0xD2, 0x9F, 0x43},
+   28,
+   //Curve parameter b
+   {0x25, 0x80, 0xF6, 0x3C, 0xCF, 0xE4, 0x41, 0x38, 0x87, 0x07, 0x13, 0xB1, 0xA9, 0x23, 0x69, 0xE3,
+    0x3E, 0x21, 0x35, 0xD2, 0x66, 0xDB, 0xB3, 0x72, 0x38, 0x6C, 0x40, 0x0B},
+   28,
+   //x-coordinate of the base point G
+   {0x0D, 0x90, 0x29, 0xAD, 0x2C, 0x7E, 0x5C, 0xF4, 0x34, 0x08, 0x23, 0xB2, 0xA8, 0x7D, 0xC6, 0x8C,
+    0x9E, 0x4C, 0xE3, 0x17, 0x4C, 0x1E, 0x6E, 0xFD, 0xEE, 0x12, 0xC0, 0x7D},
+   28,
+   //y-coordinate of the base point G
+   {0x58, 0xAA, 0x56, 0xF7, 0x72, 0xC0, 0x72, 0x6F, 0x24, 0xC6, 0xB8, 0x9E, 0x4E, 0xCD, 0xAC, 0x24,
+    0x35, 0x4B, 0x9E, 0x99, 0xCA, 0xA3, 0xF6, 0xD3, 0x76, 0x14, 0x02, 0xCD},
+   28,
+   //Base point order q
+   {0xD7, 0xC1, 0x34, 0xAA, 0x26, 0x43, 0x66, 0x86, 0x2A, 0x18, 0x30, 0x25, 0x75, 0xD0, 0xFB, 0x98,
+    0xD1, 0x16, 0xBC, 0x4B, 0x6D, 0xDE, 0xBC, 0xA3, 0xA5, 0xA7, 0x93, 0x9F},
+   28,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   NULL
+};
+
+
+/**
+ * @brief brainpoolP256r1 elliptic curve
+ **/
+
+const EcCurveInfo brainpoolP256r1Curve =
+{
+   //Curve name
+   "brainpoolP256r1",
+   //Object identifier
+   BRAINPOOLP256R1_OID,
+   sizeof(BRAINPOOLP256R1_OID),
+   //Curve type
+   EC_CURVE_TYPE_BRAINPOOLP_R1,
+   //Prime modulus p
+   {0xA9, 0xFB, 0x57, 0xDB, 0xA1, 0xEE, 0xA9, 0xBC, 0x3E, 0x66, 0x0A, 0x90, 0x9D, 0x83, 0x8D, 0x72,
+    0x6E, 0x3B, 0xF6, 0x23, 0xD5, 0x26, 0x20, 0x28, 0x20, 0x13, 0x48, 0x1D, 0x1F, 0x6E, 0x53, 0x77},
+   32,
+   //Curve parameter a
+   {0x7D, 0x5A, 0x09, 0x75, 0xFC, 0x2C, 0x30, 0x57, 0xEE, 0xF6, 0x75, 0x30, 0x41, 0x7A, 0xFF, 0xE7,
+    0xFB, 0x80, 0x55, 0xC1, 0x26, 0xDC, 0x5C, 0x6C, 0xE9, 0x4A, 0x4B, 0x44, 0xF3, 0x30, 0xB5, 0xD9},
+   32,
+   //Curve parameter b
+   {0x26, 0xDC, 0x5C, 0x6C, 0xE9, 0x4A, 0x4B, 0x44, 0xF3, 0x30, 0xB5, 0xD9, 0xBB, 0xD7, 0x7C, 0xBF,
+    0x95, 0x84, 0x16, 0x29, 0x5C, 0xF7, 0xE1, 0xCE, 0x6B, 0xCC, 0xDC, 0x18, 0xFF, 0x8C, 0x07, 0xB6},
+   32,
+   //x-coordinate of the base point G
+   {0x8B, 0xD2, 0xAE, 0xB9, 0xCB, 0x7E, 0x57, 0xCB, 0x2C, 0x4B, 0x48, 0x2F, 0xFC, 0x81, 0xB7, 0xAF,
+    0xB9, 0xDE, 0x27, 0xE1, 0xE3, 0xBD, 0x23, 0xC2, 0x3A, 0x44, 0x53, 0xBD, 0x9A, 0xCE, 0x32, 0x62},
+   32,
+   //y-coordinate of the base point G
+   {0x54, 0x7E, 0xF8, 0x35, 0xC3, 0xDA, 0xC4, 0xFD, 0x97, 0xF8, 0x46, 0x1A, 0x14, 0x61, 0x1D, 0xC9,
+    0xC2, 0x77, 0x45, 0x13, 0x2D, 0xED, 0x8E, 0x54, 0x5C, 0x1D, 0x54, 0xC7, 0x2F, 0x04, 0x69, 0x97},
+   32,
+   //Base point order q
+   {0xA9, 0xFB, 0x57, 0xDB, 0xA1, 0xEE, 0xA9, 0xBC, 0x3E, 0x66, 0x0A, 0x90, 0x9D, 0x83, 0x8D, 0x71,
+    0x8C, 0x39, 0x7A, 0xA3, 0xB5, 0x61, 0xA6, 0xF7, 0x90, 0x1E, 0x0E, 0x82, 0x97, 0x48, 0x56, 0xA7},
+   32,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   NULL
+};
+
+
+/**
+ * @brief brainpoolP320r1 elliptic curve
+ **/
+
+const EcCurveInfo brainpoolP320r1Curve =
+{
+   //Curve name
+   "brainpoolP320r1",
+   //Object identifier
+   BRAINPOOLP320R1_OID,
+   sizeof(BRAINPOOLP320R1_OID),
+   //Curve type
+   EC_CURVE_TYPE_BRAINPOOLP_R1,
+   //Prime modulus p
+   {0xD3, 0x5E, 0x47, 0x20, 0x36, 0xBC, 0x4F, 0xB7, 0xE1, 0x3C, 0x78, 0x5E, 0xD2, 0x01, 0xE0, 0x65,
+    0xF9, 0x8F, 0xCF, 0xA6, 0xF6, 0xF4, 0x0D, 0xEF, 0x4F, 0x92, 0xB9, 0xEC, 0x78, 0x93, 0xEC, 0x28,
+    0xFC, 0xD4, 0x12, 0xB1, 0xF1, 0xB3, 0x2E, 0x27},
+   40,
+   //Curve parameter a
+   {0x3E, 0xE3, 0x0B, 0x56, 0x8F, 0xBA, 0xB0, 0xF8, 0x83, 0xCC, 0xEB, 0xD4, 0x6D, 0x3F, 0x3B, 0xB8,
+    0xA2, 0xA7, 0x35, 0x13, 0xF5, 0xEB, 0x79, 0xDA, 0x66, 0x19, 0x0E, 0xB0, 0x85, 0xFF, 0xA9, 0xF4,
+    0x92, 0xF3, 0x75, 0xA9, 0x7D, 0x86, 0x0E, 0xB4},
+   40,
+   //Curve parameter b
+   {0x52, 0x08, 0x83, 0x94, 0x9D, 0xFD, 0xBC, 0x42, 0xD3, 0xAD, 0x19, 0x86, 0x40, 0x68, 0x8A, 0x6F,
+    0xE1, 0x3F, 0x41, 0x34, 0x95, 0x54, 0xB4, 0x9A, 0xCC, 0x31, 0xDC, 0xCD, 0x88, 0x45, 0x39, 0x81,
+    0x6F, 0x5E, 0xB4, 0xAC, 0x8F, 0xB1, 0xF1, 0xA6},
+   40,
+   //x-coordinate of the base point G
+   {0x43, 0xBD, 0x7E, 0x9A, 0xFB, 0x53, 0xD8, 0xB8, 0x52, 0x89, 0xBC, 0xC4, 0x8E, 0xE5, 0xBF, 0xE6,
+    0xF2, 0x01, 0x37, 0xD1, 0x0A, 0x08, 0x7E, 0xB6, 0xE7, 0x87, 0x1E, 0x2A, 0x10, 0xA5, 0x99, 0xC7,
+    0x10, 0xAF, 0x8D, 0x0D, 0x39, 0xE2, 0x06, 0x11},
+   40,
+   //y-coordinate of the base point G
+   {0x14, 0xFD, 0xD0, 0x55, 0x45, 0xEC, 0x1C, 0xC8, 0xAB, 0x40, 0x93, 0x24, 0x7F, 0x77, 0x27, 0x5E,
+    0x07, 0x43, 0xFF, 0xED, 0x11, 0x71, 0x82, 0xEA, 0xA9, 0xC7, 0x78, 0x77, 0xAA, 0xAC, 0x6A, 0xC7,
+    0xD3, 0x52, 0x45, 0xD1, 0x69, 0x2E, 0x8E, 0xE1},
+   40,
+   //Base point order q
+   {0xD3, 0x5E, 0x47, 0x20, 0x36, 0xBC, 0x4F, 0xB7, 0xE1, 0x3C, 0x78, 0x5E, 0xD2, 0x01, 0xE0, 0x65,
+    0xF9, 0x8F, 0xCF, 0xA5, 0xB6, 0x8F, 0x12, 0xA3, 0x2D, 0x48, 0x2E, 0xC7, 0xEE, 0x86, 0x58, 0xE9,
+    0x86, 0x91, 0x55, 0x5B, 0x44, 0xC5, 0x93, 0x11},
+   40,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   NULL
+};
+
+
+/**
+ * @brief brainpoolP384r1 elliptic curve
+ **/
+
+const EcCurveInfo brainpoolP384r1Curve =
+{
+   //Curve name
+   "brainpoolP384r1",
+   //Object identifier
+   BRAINPOOLP384R1_OID,
+   sizeof(BRAINPOOLP384R1_OID),
+   //Curve type
+   EC_CURVE_TYPE_BRAINPOOLP_R1,
+   //Prime modulus p
+   {0x8C, 0xB9, 0x1E, 0x82, 0xA3, 0x38, 0x6D, 0x28, 0x0F, 0x5D, 0x6F, 0x7E, 0x50, 0xE6, 0x41, 0xDF,
+    0x15, 0x2F, 0x71, 0x09, 0xED, 0x54, 0x56, 0xB4, 0x12, 0xB1, 0xDA, 0x19, 0x7F, 0xB7, 0x11, 0x23,
+    0xAC, 0xD3, 0xA7, 0x29, 0x90, 0x1D, 0x1A, 0x71, 0x87, 0x47, 0x00, 0x13, 0x31, 0x07, 0xEC, 0x53},
+   48,
+   //Curve parameter a
+   {0x7B, 0xC3, 0x82, 0xC6, 0x3D, 0x8C, 0x15, 0x0C, 0x3C, 0x72, 0x08, 0x0A, 0xCE, 0x05, 0xAF, 0xA0,
+    0xC2, 0xBE, 0xA2, 0x8E, 0x4F, 0xB2, 0x27, 0x87, 0x13, 0x91, 0x65, 0xEF, 0xBA, 0x91, 0xF9, 0x0F,
+    0x8A, 0xA5, 0x81, 0x4A, 0x50, 0x3A, 0xD4, 0xEB, 0x04, 0xA8, 0xC7, 0xDD, 0x22, 0xCE, 0x28, 0x26},
+   48,
+   //Curve parameter b
+   {0x04, 0xA8, 0xC7, 0xDD, 0x22, 0xCE, 0x28, 0x26, 0x8B, 0x39, 0xB5, 0x54, 0x16, 0xF0, 0x44, 0x7C,
+    0x2F, 0xB7, 0x7D, 0xE1, 0x07, 0xDC, 0xD2, 0xA6, 0x2E, 0x88, 0x0E, 0xA5, 0x3E, 0xEB, 0x62, 0xD5,
+    0x7C, 0xB4, 0x39, 0x02, 0x95, 0xDB, 0xC9, 0x94, 0x3A, 0xB7, 0x86, 0x96, 0xFA, 0x50, 0x4C, 0x11},
+   48,
+   //x-coordinate of the base point G
+   {0x1D, 0x1C, 0x64, 0xF0, 0x68, 0xCF, 0x45, 0xFF, 0xA2, 0xA6, 0x3A, 0x81, 0xB7, 0xC1, 0x3F, 0x6B,
+    0x88, 0x47, 0xA3, 0xE7, 0x7E, 0xF1, 0x4F, 0xE3, 0xDB, 0x7F, 0xCA, 0xFE, 0x0C, 0xBD, 0x10, 0xE8,
+    0xE8, 0x26, 0xE0, 0x34, 0x36, 0xD6, 0x46, 0xAA, 0xEF, 0x87, 0xB2, 0xE2, 0x47, 0xD4, 0xAF, 0x1E},
+   48,
+   //y-coordinate of the base point G
+   {0x8A, 0xBE, 0x1D, 0x75, 0x20, 0xF9, 0xC2, 0xA4, 0x5C, 0xB1, 0xEB, 0x8E, 0x95, 0xCF, 0xD5, 0x52,
+    0x62, 0xB7, 0x0B, 0x29, 0xFE, 0xEC, 0x58, 0x64, 0xE1, 0x9C, 0x05, 0x4F, 0xF9, 0x91, 0x29, 0x28,
+    0x0E, 0x46, 0x46, 0x21, 0x77, 0x91, 0x81, 0x11, 0x42, 0x82, 0x03, 0x41, 0x26, 0x3C, 0x53, 0x15},
+   48,
+   //Base point order q
+   {0x8C, 0xB9, 0x1E, 0x82, 0xA3, 0x38, 0x6D, 0x28, 0x0F, 0x5D, 0x6F, 0x7E, 0x50, 0xE6, 0x41, 0xDF,
+    0x15, 0x2F, 0x71, 0x09, 0xED, 0x54, 0x56, 0xB3, 0x1F, 0x16, 0x6E, 0x6C, 0xAC, 0x04, 0x25, 0xA7,
+    0xCF, 0x3A, 0xB6, 0xAF, 0x6B, 0x7F, 0xC3, 0x10, 0x3B, 0x88, 0x32, 0x02, 0xE9, 0x04, 0x65, 0x65},
+   48,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   NULL
+};
+
+
+/**
+ * @brief brainpoolP512r1 elliptic curve
+ **/
+
+const EcCurveInfo brainpoolP512r1Curve =
+{
+   //Curve name
+   "brainpoolP512r1",
+   //Object identifier
+   BRAINPOOLP512R1_OID,
+   sizeof(BRAINPOOLP512R1_OID),
+   //Curve type
+   EC_CURVE_TYPE_BRAINPOOLP_R1,
+   //Prime modulus p
+   {0xAA, 0xDD, 0x9D, 0xB8, 0xDB, 0xE9, 0xC4, 0x8B, 0x3F, 0xD4, 0xE6, 0xAE, 0x33, 0xC9, 0xFC, 0x07,
+    0xCB, 0x30, 0x8D, 0xB3, 0xB3, 0xC9, 0xD2, 0x0E, 0xD6, 0x63, 0x9C, 0xCA, 0x70, 0x33, 0x08, 0x71,
+    0x7D, 0x4D, 0x9B, 0x00, 0x9B, 0xC6, 0x68, 0x42, 0xAE, 0xCD, 0xA1, 0x2A, 0xE6, 0xA3, 0x80, 0xE6,
+    0x28, 0x81, 0xFF, 0x2F, 0x2D, 0x82, 0xC6, 0x85, 0x28, 0xAA, 0x60, 0x56, 0x58, 0x3A, 0x48, 0xF3},
+   64,
+   //Curve parameter a
+   {0x78, 0x30, 0xA3, 0x31, 0x8B, 0x60, 0x3B, 0x89, 0xE2, 0x32, 0x71, 0x45, 0xAC, 0x23, 0x4C, 0xC5,
+    0x94, 0xCB, 0xDD, 0x8D, 0x3D, 0xF9, 0x16, 0x10, 0xA8, 0x34, 0x41, 0xCA, 0xEA, 0x98, 0x63, 0xBC,
+    0x2D, 0xED, 0x5D, 0x5A, 0xA8, 0x25, 0x3A, 0xA1, 0x0A, 0x2E, 0xF1, 0xC9, 0x8B, 0x9A, 0xC8, 0xB5,
+    0x7F, 0x11, 0x17, 0xA7, 0x2B, 0xF2, 0xC7, 0xB9, 0xE7, 0xC1, 0xAC, 0x4D, 0x77, 0xFC, 0x94, 0xCA},
+   64,
+   //Curve parameter b
+   {0x3D, 0xF9, 0x16, 0x10, 0xA8, 0x34, 0x41, 0xCA, 0xEA, 0x98, 0x63, 0xBC, 0x2D, 0xED, 0x5D, 0x5A,
+    0xA8, 0x25, 0x3A, 0xA1, 0x0A, 0x2E, 0xF1, 0xC9, 0x8B, 0x9A, 0xC8, 0xB5, 0x7F, 0x11, 0x17, 0xA7,
+    0x2B, 0xF2, 0xC7, 0xB9, 0xE7, 0xC1, 0xAC, 0x4D, 0x77, 0xFC, 0x94, 0xCA, 0xDC, 0x08, 0x3E, 0x67,
+    0x98, 0x40, 0x50, 0xB7, 0x5E, 0xBA, 0xE5, 0xDD, 0x28, 0x09, 0xBD, 0x63, 0x80, 0x16, 0xF7, 0x23},
+   64,
+   //x-coordinate of the base point G
+   {0x81, 0xAE, 0xE4, 0xBD, 0xD8, 0x2E, 0xD9, 0x64, 0x5A, 0x21, 0x32, 0x2E, 0x9C, 0x4C, 0x6A, 0x93,
+    0x85, 0xED, 0x9F, 0x70, 0xB5, 0xD9, 0x16, 0xC1, 0xB4, 0x3B, 0x62, 0xEE, 0xF4, 0xD0, 0x09, 0x8E,
+    0xFF, 0x3B, 0x1F, 0x78, 0xE2, 0xD0, 0xD4, 0x8D, 0x50, 0xD1, 0x68, 0x7B, 0x93, 0xB9, 0x7D, 0x5F,
+    0x7C, 0x6D, 0x50, 0x47, 0x40, 0x6A, 0x5E, 0x68, 0x8B, 0x35, 0x22, 0x09, 0xBC, 0xB9, 0xF8, 0x22},
+   64,
+   //y-coordinate of the base point G
+   {0x7D, 0xDE, 0x38, 0x5D, 0x56, 0x63, 0x32, 0xEC, 0xC0, 0xEA, 0xBF, 0xA9, 0xCF, 0x78, 0x22, 0xFD,
+    0xF2, 0x09, 0xF7, 0x00, 0x24, 0xA5, 0x7B, 0x1A, 0xA0, 0x00, 0xC5, 0x5B, 0x88, 0x1F, 0x81, 0x11,
+    0xB2, 0xDC, 0xDE, 0x49, 0x4A, 0x5F, 0x48, 0x5E, 0x5B, 0xCA, 0x4B, 0xD8, 0x8A, 0x27, 0x63, 0xAE,
+    0xD1, 0xCA, 0x2B, 0x2F, 0xA8, 0xF0, 0x54, 0x06, 0x78, 0xCD, 0x1E, 0x0F, 0x3A, 0xD8, 0x08, 0x92},
+   64,
+   //Base point order q
+   {0xAA, 0xDD, 0x9D, 0xB8, 0xDB, 0xE9, 0xC4, 0x8B, 0x3F, 0xD4, 0xE6, 0xAE, 0x33, 0xC9, 0xFC, 0x07,
+    0xCB, 0x30, 0x8D, 0xB3, 0xB3, 0xC9, 0xD2, 0x0E, 0xD6, 0x63, 0x9C, 0xCA, 0x70, 0x33, 0x08, 0x70,
+    0x55, 0x3E, 0x5C, 0x41, 0x4C, 0xA9, 0x26, 0x19, 0x41, 0x86, 0x61, 0x19, 0x7F, 0xAC, 0x10, 0x47,
+    0x1D, 0xB1, 0xD3, 0x81, 0x08, 0x5D, 0xDA, 0xDD, 0xB5, 0x87, 0x96, 0x82, 0x9C, 0xA9, 0x00, 0x69},
+   64,
+   //Cofactor
+   1,
+   //Fast modular reduction
+   NULL
+};
+
+
+/**
+ * @brief Fast modular reduction (secp128r1 curve)
+ * @param[in,out] a This function accept an integer less than p^2 as
+ *   input and return (a mod p) as output
+ * @param[in] p Prime modulus
+ **/
+
+error_t secp128r1Mod(Mpi *a, const Mpi *p)
+{
+   error_t error;
+   Mpi t;
+
+   //Initialize multiple precision integers
+   mpiInit(&t);
+
+   //Ajust the size of the integers
+   MPI_CHECK(mpiGrow(a, 32 / MPI_INT_SIZE));
+   MPI_CHECK(mpiGrow(&t, 32 / MPI_INT_SIZE));
+
+   //Perform modular reduction
+   do
+   {
+      //Compute T = 0 | 0 | 0 | 0 | A7 | A6 | A5 | A4
+      COPY_WORD32(&t, 0, a, 4, 4);
+      CLEAR_WORD32(&t, 4, 4);
+
+      //Clear A7 | A6 | A5 | A4
+      CLEAR_WORD32(a, 4, 4);
+
+      //Compute A = A + T + (T << 97)
+      MPI_CHECK(mpiAdd(a, a, &t));
+      MPI_CHECK(mpiShiftLeft(&t, 97));
+      MPI_CHECK(mpiAdd(a, a, &t));
+
+      //Check for end condition
+   } while(mpiComp(a, p) > 0);
+
+end:
+   //Release multiple precision integers
+   mpiFree(&t);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Fast modular reduction (secp128r2 curve)
+ * @param[in,out] a This function accept an integer less than p^2 as
+ *   input and return (a mod p) as output
+ * @param[in] p Prime modulus
+ **/
+
+error_t secp128r2Mod(Mpi *a, const Mpi *p)
+{
+   error_t error;
+   Mpi t;
+
+   //Initialize multiple precision integers
+   mpiInit(&t);
+
+   //Ajust the size of the integers
+   MPI_CHECK(mpiGrow(a, 32 / MPI_INT_SIZE));
+   MPI_CHECK(mpiGrow(&t, 32 / MPI_INT_SIZE));
+
+   //Perform modular reduction
+   do
+   {
+      //Compute T = 0 | 0 | 0 | 0 | A7 | A6 | A5 | A4
+      COPY_WORD32(&t, 0, a, 4, 4);
+      CLEAR_WORD32(&t, 4, 4);
+
+      //Clear A7 | A6 | A5 | A4
+      CLEAR_WORD32(a, 4, 4);
+
+      //Compute A = A + T + (T << 97)
+      MPI_CHECK(mpiAdd(a, a, &t));
+      MPI_CHECK(mpiShiftLeft(&t, 97));
+      MPI_CHECK(mpiAdd(a, a, &t));
+
+      //Check for end condition
+   } while(mpiComp(a, p) > 0);
+
+end:
+   //Release multiple precision integers
+   mpiFree(&t);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Fast modular reduction (secp160k1 curve)
+ * @param[in,out] a This function accept an integer less than p^2 as
+ *   input and return (a mod p) as output
+ * @param[in] p Prime modulus
+ **/
+
+error_t secp160k1Mod(Mpi *a, const Mpi *p)
+{
+   error_t error;
+   Mpi t;
+
+   //Initialize multiple precision integers
+   mpiInit(&t);
+
+   //Ajust the size of the integers
+   MPI_CHECK(mpiGrow(a, 40 / MPI_INT_SIZE));
+   MPI_CHECK(mpiGrow(&t, 24 / MPI_INT_SIZE));
+
+   //Perform modular reduction
+   do
+   {
+      //Compute T = A9 | A8 | A7 | A6 | A5 | 0
+      CLEAR_WORD32(&t, 0, 1);
+      COPY_WORD32(&t, 1, a, 5, 5);
+
+      //Clear A9 | A8 | A7 | A6 | A5
+      CLEAR_WORD32(a, 5, 5);
+
+      //Compute A = A + T
+      MPI_CHECK(mpiAdd(a, a, &t));
+      //Compute T = T >> 32
+      MPI_CHECK(mpiShiftRight(&t, 32));
+      //Compute A = A + (21389 * T)
+      MPI_CHECK(mpiMulInt(&t, &t, 21389));
+      MPI_CHECK(mpiAdd(a, a, &t));
+
+      //Check for end condition
+   } while(mpiComp(a, p) > 0);
+
+end:
+   //Release multiple precision integers
+   mpiFree(&t);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Fast modular reduction (secp160r1 curve)
+ * @param[in,out] a This function accept an integer less than p^2 as
+ *   input and return (a mod p) as output
+ * @param[in] p Prime modulus
+ **/
+
+error_t secp160r1Mod(Mpi *a, const Mpi *p)
+{
+   error_t error;
+   Mpi t;
+
+   //Initialize multiple precision integers
+   mpiInit(&t);
+
+   //Ajust the size of the integers
+   MPI_CHECK(mpiGrow(a, 40 / MPI_INT_SIZE));
+   MPI_CHECK(mpiGrow(&t, 24 / MPI_INT_SIZE));
+
+   //Perform modular reduction
+   do
+   {
+      //Compute T = 0 | A9 | A8 | A7 | A6 | A5
+      COPY_WORD32(&t, 0, a, 5, 5);
+      CLEAR_WORD32(&t, 5, 1);
+
+      //Clear A9 | A8 | A7 | A6 | A5
+      CLEAR_WORD32(a, 5, 5);
+
+      //Compute A = A + T + (T << 31)
+      MPI_CHECK(mpiAdd(a, a, &t));
+      MPI_CHECK(mpiShiftLeft(&t, 31));
+      MPI_CHECK(mpiAdd(a, a, &t));
+
+      //Check for end condition
+   } while(mpiComp(a, p) > 0);
+
+end:
+   //Release multiple precision integers
+   mpiFree(&t);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Fast modular reduction (secp160r2 curve)
+ * @param[in,out] a This function accept an integer less than p^2 as
+ *   input and return (a mod p) as output
+ * @param[in] p Prime modulus
+ **/
+
+error_t secp160r2Mod(Mpi *a, const Mpi *p)
+{
+   error_t error;
+   Mpi t;
+
+   //Initialize multiple precision integers
+   mpiInit(&t);
+
+   //Ajust the size of the integers
+   MPI_CHECK(mpiGrow(a, 40 / MPI_INT_SIZE));
+   MPI_CHECK(mpiGrow(&t, 24 / MPI_INT_SIZE));
+
+   //Perform modular reduction
+   do
+   {
+      //Compute T = A9 | A8 | A7 | A6 | A5 | 0
+      CLEAR_WORD32(&t, 0, 1);
+      COPY_WORD32(&t, 1, a, 5, 5);
+
+      //Clear A9 | A8 | A7 | A6 | A5
+      CLEAR_WORD32(a, 5, 5);
+
+      //Compute A = A + T
+      MPI_CHECK(mpiAdd(a, a, &t));
+      //Compute T = T >> 32
+      MPI_CHECK(mpiShiftRight(&t, 32));
+      //Compute A = A + (21389 * T)
+      MPI_CHECK(mpiMulInt(&t, &t, 21389));
+      MPI_CHECK(mpiAdd(a, a, &t));
+
+      //Check for end condition
+   } while(mpiComp(a, p) > 0);
+
+end:
+   //Release multiple precision integers
+   mpiFree(&t);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Fast modular reduction (secp192k1 curve)
+ * @param[in,out] a This function accept an integer less than p^2 as
+ *   input and return (a mod p) as output
+ * @param[in] p Prime modulus
+ **/
+
+error_t secp192k1Mod(Mpi *a, const Mpi *p)
+{
+   error_t error;
+   Mpi t;
+
+   //Initialize multiple precision integers
+   mpiInit(&t);
+
+   //Ajust the size of the integers
+   MPI_CHECK(mpiGrow(a, 48 / MPI_INT_SIZE));
+   MPI_CHECK(mpiGrow(&t, 28 / MPI_INT_SIZE));
+
+   //Perform modular reduction
+   do
+   {
+      //Compute T = A11 | A10 | A9 | A8 | A7 | A6 | 0
+      CLEAR_WORD32(&t, 0, 1);
+      COPY_WORD32(&t, 1, a, 6, 6);
+
+      //Clear A11 | A10 | A9 | A8 | A7 | A6
+      CLEAR_WORD32(a, 6, 6);
+
+      //Compute A = A + T
+      MPI_CHECK(mpiAdd(a, a, &t));
+      //Compute T = T >> 32
+      MPI_CHECK(mpiShiftRight(&t, 32));
+      //Compute A = A + (4553 * T)
+      MPI_CHECK(mpiMulInt(&t, &t, 4553));
+      MPI_CHECK(mpiAdd(a, a, &t));
+
+      //Check for end condition
+   } while(mpiComp(a, p) > 0);
+
+end:
+   //Release multiple precision integers
+   mpiFree(&t);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Fast modular reduction (secp192r1 curve)
+ * @param[in,out] a This function accept an integer less than p^2 as
+ *   input and return (a mod p) as output
+ * @param[in] p Prime modulus
+ **/
+
+error_t secp192r1Mod(Mpi *a, const Mpi *p)
+{
+   error_t error;
+   Mpi s;
+   Mpi t;
+
+   //Initialize multiple precision integers
+   mpiInit(&s);
+   mpiInit(&t);
+
+   //Ajust the size of the integers
+   MPI_CHECK(mpiGrow(a, 48 / MPI_INT_SIZE));
+   MPI_CHECK(mpiGrow(&s, 24 / MPI_INT_SIZE));
+   MPI_CHECK(mpiGrow(&t, 24 / MPI_INT_SIZE));
+
+   //Compute T = A5 | A4 | A3 | A2 | A1 | A0
+   COPY_WORD32(&t, 0, a, 0, 6);
+
+   //Compute S1 = 0 | 0 | A7 | A6 | A7 | A6
+   COPY_WORD32(&s, 0, a, 6, 2);
+   COPY_WORD32(&s, 2, a, 6, 2);
+   CLEAR_WORD32(&s, 4, 2);
+   //Compute T = T + S1
+   MPI_CHECK(mpiAdd(&t, &t, &s));
+
+   //Compute S2 = A9 | A8 | A9 | A8 | 0 | 0
+   CLEAR_WORD32(&s, 0, 2);
+   COPY_WORD32(&s, 2, a, 8, 2);
+   COPY_WORD32(&s, 4, a, 8, 2);
+   //Compute T = T + S2
+   MPI_CHECK(mpiAdd(&t, &t, &s));
+
+   //Compute S3 = A11 | A10 | A11 | A10 | A11 | A10
+   COPY_WORD32(&s, 0, a, 10, 2);
+   COPY_WORD32(&s, 2, a, 10, 2);
+   COPY_WORD32(&s, 4, a, 10, 2);
+   //Compute T = T + S3
+   MPI_CHECK(mpiAdd(&t, &t, &s));
+
+   //Compute (T + S1 + S2 + S3) mod p
+   while(mpiComp(&t, p) >= 0)
+   {
+       MPI_CHECK(mpiSub(&t, &t, p));
+   }
+
+   //Save result
+   MPI_CHECK(mpiCopy(a, &t));
+
+end:
+   //Release multiple precision integers
+   mpiFree(&s);
+   mpiFree(&t);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Fast modular reduction (secp224k1 curve)
+ * @param[in,out] a This function accept an integer less than p^2 as
+ *   input and return (a mod p) as output
+ * @param[in] p Prime modulus
+ **/
+
+error_t secp224k1Mod(Mpi *a, const Mpi *p)
+{
+   error_t error;
+   Mpi t;
+
+   //Initialize multiple precision integers
+   mpiInit(&t);
+
+   //Ajust the size of the integers
+   MPI_CHECK(mpiGrow(a, 56 / MPI_INT_SIZE));
+   MPI_CHECK(mpiGrow(&t, 32 / MPI_INT_SIZE));
+
+   //Perform modular reduction
+   do
+   {
+      //Compute T = A13 | A12 | A11 | A10 | A9 | A8 | A7 | 0
+      CLEAR_WORD32(&t, 0, 1);
+      COPY_WORD32(&t, 1, a, 7, 7);
+
+      //Clear A13 | A12 | A11 | A10 | A9 | A8 | A7
+      CLEAR_WORD32(a, 7, 7);
+
+      //Compute A = A + T
+      MPI_CHECK(mpiAdd(a, a, &t));
+      //Compute T = T >> 32
+      MPI_CHECK(mpiShiftRight(&t, 32));
+      //Compute A = A + (6803 * T)
+      MPI_CHECK(mpiMulInt(&t, &t, 6803));
+      MPI_CHECK(mpiAdd(a, a, &t));
+
+      //Check for end condition
+   } while(mpiComp(a, p) > 0);
+
+end:
+   //Release multiple precision integers
+   mpiFree(&t);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Fast modular reduction (secp224r1 curve)
+ * @param[in,out] a This function accept an integer less than p^2 as
+ *   input and return (a mod p) as output
+ * @param[in] p Prime modulus
+ **/
+
+error_t secp224r1Mod(Mpi *a, const Mpi *p)
+{
+   error_t error;
+   Mpi s;
+   Mpi t;
+
+   //Initialize multiple precision integers
+   mpiInit(&s);
+   mpiInit(&t);
+
+   //Ajust the size of the integers
+   MPI_CHECK(mpiGrow(a, 56 / MPI_INT_SIZE));
+   MPI_CHECK(mpiGrow(&s, 28 / MPI_INT_SIZE));
+   MPI_CHECK(mpiGrow(&t, 28 / MPI_INT_SIZE));
+
+   //Compute T = A6 | A5 | A4 | A3 | A2 | A1 | A0
+   COPY_WORD32(&t, 0, a, 0, 7);
+
+   //Compute S1 = A10 | A9 | A8 | A7 | 0 | 0 | 0
+   CLEAR_WORD32(&s, 0, 3);
+   COPY_WORD32(&s, 3, a, 7, 4);
+   //Compute T = T + S1
+   MPI_CHECK(mpiAdd(&t, &t, &s));
+
+   //Compute S2 = 0 | A13 | A12 | A11 | 0 | 0 | 0
+   CLEAR_WORD32(&s, 0, 3);
+   COPY_WORD32(&s, 3, a, 11, 3);
+   CLEAR_WORD32(&s, 6, 1);
+   //Compute T = T + S2
+   MPI_CHECK(mpiAdd(&t, &t, &s));
+
+   //Compute D1 = A13 | A12 | A11 | A10 | A9 | A8 | A7
+   COPY_WORD32(&s, 0, a, 7, 7);
+   //Compute T = T - D1
+   MPI_CHECK(mpiSub(&t, &t, &s));
+
+   //Compute D2 = 0 | 0 | 0 | 0 | A13 | A12 | A11
+   COPY_WORD32(&s, 0, a, 11, 3);
+   CLEAR_WORD32(&s, 3, 4);
+   //Compute T = T - D2
+   MPI_CHECK(mpiSub(&t, &t, &s));
+
+   //Compute (T + S1 + S2 - D1 - D2) mod p
+   while(mpiComp(&t, p) >= 0)
+   {
+       MPI_CHECK(mpiSub(&t, &t, p));
+   }
+
+   while(mpiCompInt(&t, 0) < 0)
+   {
+       MPI_CHECK(mpiAdd(&t, &t, p));
+   }
+
+   //Save result
+   MPI_CHECK(mpiCopy(a, &t));
+
+end:
+   //Release multiple precision integers
+   mpiFree(&s);
+   mpiFree(&t);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Fast modular reduction (secp256k1 curve)
+ * @param[in,out] a This function accept an integer less than p^2 as
+ *   input and return (a mod p) as output
+ * @param[in] p Prime modulus
+ **/
+
+error_t secp256k1Mod(Mpi *a, const Mpi *p)
+{
+   error_t error;
+   Mpi t;
+
+   //Initialize multiple precision integers
+   mpiInit(&t);
+
+   //Ajust the size of the integers
+   MPI_CHECK(mpiGrow(a, 64 / MPI_INT_SIZE));
+   MPI_CHECK(mpiGrow(&t, 36 / MPI_INT_SIZE));
+
+   //Perform modular reduction
+   do
+   {
+      //Compute T = A15 | A14 | A13 | A12 | A11 | A10 | A9 | A8 | 0
+      CLEAR_WORD32(&t, 0, 1);
+      COPY_WORD32(&t, 1, a, 8, 8);
+
+      //Clear A15 | A14 | A13 | A12 | A11 | A10 | A9 | A8
+      CLEAR_WORD32(a, 8, 8);
+
+      //Compute A = A + T
+      MPI_CHECK(mpiAdd(a, a, &t));
+      //Compute T = T >> 32
+      MPI_CHECK(mpiShiftRight(&t, 32));
+      //Compute A = A + (977 * T)
+      MPI_CHECK(mpiMulInt(&t, &t, 977));
+      MPI_CHECK(mpiAdd(a, a, &t));
+
+      //Check for end condition
+   } while(mpiComp(a, p) > 0);
+
+end:
+   //Release multiple precision integers
+   mpiFree(&t);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Fast modular reduction (secp256r1 curve)
+ * @param[in,out] a This function accept an integer less than p^2 as
+ *   input and return (a mod p) as output
+ * @param[in] p Prime modulus
+ **/
+
+error_t secp256r1Mod(Mpi *a, const Mpi *p)
+{
+   error_t error;
+   Mpi s;
+   Mpi t;
+   Mpi b;
+
+   //Initialize multiple precision integers
+   mpiInit(&s);
+   mpiInit(&t);
+   mpiInit(&b);
+
+   //Ajust the size of the integers
+   MPI_CHECK(mpiGrow(a, 64 / MPI_INT_SIZE));
+   MPI_CHECK(mpiGrow(&s, 32 / MPI_INT_SIZE));
+   MPI_CHECK(mpiGrow(&t, 32 / MPI_INT_SIZE));
+
+   //Compute T = A7 | A6 | A5 | A4 | A3 | A2 | A1 | A0
+   COPY_WORD32(&t, 0, a, 0, 8);
+
+   //Compute S1 = A15 | A14 | A13 | A12 | A11 | 0 | 0 | 0
+   CLEAR_WORD32(&s, 0, 3);
+   COPY_WORD32(&s, 3, a, 11, 5);
+   //Compute T = T + 2 * S1
+   MPI_CHECK(mpiAdd(&t, &t, &s));
+   MPI_CHECK(mpiAdd(&t, &t, &s));
+
+   //Compute S2 = 0 | A15 | A14 | A13 | A12 | 0 | 0 | 0
+   CLEAR_WORD32(&s, 0, 3);
+   COPY_WORD32(&s, 3, a, 12, 4);
+   CLEAR_WORD32(&s, 7, 1);
+   //Compute T = T + 2 * S2
+   MPI_CHECK(mpiAdd(&t, &t, &s));
+   MPI_CHECK(mpiAdd(&t, &t, &s));
+
+   //Compute S3 = A15 | A14 | 0 | 0 | 0 | A10 | A9 | A8
+   COPY_WORD32(&s, 0, a, 8, 3);
+   CLEAR_WORD32(&s, 3, 3);
+   COPY_WORD32(&s, 6, a, 14, 2);
+   //Compute T = T + S3
+   MPI_CHECK(mpiAdd(&t, &t, &s));
+
+   //Compute S4 = A8 | A13 | A15 | A14 | A13 | A11 | A10 | A9
+   COPY_WORD32(&s, 0, a, 9, 3);
+   COPY_WORD32(&s, 3, a, 13, 3);
+   COPY_WORD32(&s, 6, a, 13, 1);
+   COPY_WORD32(&s, 7, a, 8, 1);
+   //Compute T = T + S4
+   MPI_CHECK(mpiAdd(&t, &t, &s));
+
+   //Compute D1 = A10 | A8 | 0 | 0 | 0 | A13 | A12 | A11
+   COPY_WORD32(&s, 0, a, 11, 3);
+   CLEAR_WORD32(&s, 3, 3);
+   COPY_WORD32(&s, 6, a, 8, 1);
+   COPY_WORD32(&s, 7, a, 10, 1);
+   //Compute T = T - D1
+   MPI_CHECK(mpiSub(&t, &t, &s));
+
+   //Compute D2 = A11 | A9 | 0 | 0 | A15 | A14 | A13 | A12
+   COPY_WORD32(&s, 0, a, 12, 4);
+   CLEAR_WORD32(&s, 4, 2);
+   COPY_WORD32(&s, 6, a, 9, 1);
+   COPY_WORD32(&s, 7, a, 11, 1);
+   //Compute T = T - D2
+   MPI_CHECK(mpiSub(&t, &t, &s));
+
+   //Compute D3 = A12 | 0 | A10 | A9 | A8 | A15 | A14 | A13
+   COPY_WORD32(&s, 0, a, 13, 3);
+   COPY_WORD32(&s, 3, a, 8, 3);
+   CLEAR_WORD32(&s, 6, 1);
+   COPY_WORD32(&s, 7, a, 12, 1);
+   //Compute T = T - D3
+   MPI_CHECK(mpiSub(&t, &t, &s));
+
+   //Compute D4 = A13 | 0 | A11 | A10 | A9 | 0 | A15 | A14
+   COPY_WORD32(&s, 0, a, 14, 2);
+   CLEAR_WORD32(&s, 2, 1);
+   COPY_WORD32(&s, 3, a, 9, 3);
+   CLEAR_WORD32(&s, 6, 1);
+   COPY_WORD32(&s, 7, a, 13, 1);
+   //Compute T = T - D4
+   MPI_CHECK(mpiSub(&t, &t, &s));
+
+   //Compute (T + 2 * S1 + 2 * S2 + S3 + S4 - D1 - D2 - D3 - D4) mod p
+   while(mpiComp(&t, p) >= 0)
+   {
+       MPI_CHECK(mpiSub(&t, &t, p));
+   }
+
+   while(mpiCompInt(&t, 0) < 0)
+   {
+       MPI_CHECK(mpiAdd(&t, &t, p));
+   }
+
+   //Save result
+   MPI_CHECK(mpiCopy(a, &t));
+
+end:
+   //Release multiple precision integers
+   mpiFree(&s);
+   mpiFree(&t);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Fast modular reduction (secp384r1 curve)
+ * @param[in,out] a This function accept an integer less than p^2 as
+ *   input and return (a mod p) as output
+ * @param[in] p Prime modulus
+ **/
+
+error_t secp384r1Mod(Mpi *a, const Mpi *p)
+{
+   error_t error;
+   Mpi s;
+   Mpi t;
+
+   //Initialize multiple precision integers
+   mpiInit(&s);
+   mpiInit(&t);
+
+   //Ajust the size of the integers
+   MPI_CHECK(mpiGrow(a, 96 / MPI_INT_SIZE));
+   MPI_CHECK(mpiGrow(&s, 48 / MPI_INT_SIZE));
+   MPI_CHECK(mpiGrow(&t, 48 / MPI_INT_SIZE));
+
+   //Compute T = A11 | A10 | A9 | A8 | A7 | A6 | A5 | A4 | A3 | A2 | A1 | A0
+   COPY_WORD32(&t, 0, a, 0, 12);
+
+   //Compute S1 = 0 | 0 | 0 | 0 | 0 | A23 | A22 | A21 | 0 | 0 | 0 | 0
+   CLEAR_WORD32(&s, 0, 4);
+   COPY_WORD32(&s, 4, a, 21, 3);
+   CLEAR_WORD32(&s, 7, 5);
+   //Compute T = T + 2 * S1
+   MPI_CHECK(mpiAdd(&t, &t, &s));
+   MPI_CHECK(mpiAdd(&t, &t, &s));
+
+   //Compute S2 = A23 | A22 | A21 | A20 | A19 | A18 | A17 | A16 | A15 | A14 | A13 | A12
+   COPY_WORD32(&s, 0, a, 12, 12);
+   //Compute T = T + S2
+   MPI_CHECK(mpiAdd(&t, &t, &s));
+
+   //Compute S3 = A20 | A19 | A18 | A17 | A16 | A15 | A14 | A13 | A12 | A23| A22 | A21
+   COPY_WORD32(&s, 0, a, 21, 3);
+   COPY_WORD32(&s, 3, a, 12, 9);
+   //Compute T = T + S3
+   MPI_CHECK(mpiAdd(&t, &t, &s));
+
+   //Compute S4 = A19 | A18 | A17 | A16 | A15 | A14 | A13 | A12 | A20 | 0 | A23 | 0
+   CLEAR_WORD32(&s, 0, 1);
+   COPY_WORD32(&s, 1, a, 23, 1);
+   CLEAR_WORD32(&s, 2, 1);
+   COPY_WORD32(&s, 3, a, 20, 1);
+   COPY_WORD32(&s, 4, a, 12, 8);
+   //Compute T = T + S4
+   MPI_CHECK(mpiAdd(&t, &t, &s));
+
+   //Compute S5 = 0 | 0 | 0 | 0 | A23 | A22 | A21 | A20 | 0 | 0 | 0 | 0
+   CLEAR_WORD32(&s, 0, 4);
+   COPY_WORD32(&s, 4, a, 20, 4);
+   CLEAR_WORD32(&s, 8, 4);
+   //Compute T = T + S5
+   MPI_CHECK(mpiAdd(&t, &t, &s));
+
+   //Compute S6 = 0 | 0 | 0 | 0 | 0 | 0 | A23 | A22 | A21 | 0 | 0 | A20
+   COPY_WORD32(&s, 0, a, 20, 1);
+   CLEAR_WORD32(&s, 1, 2);
+   COPY_WORD32(&s, 3, a, 21, 3);
+   CLEAR_WORD32(&s, 6, 6);
+   //Compute T = T + S6
+   MPI_CHECK(mpiAdd(&t, &t, &s));
+
+   //Compute D1 = A22 | A21 | A20 | A19 | A18 | A17 | A16 | A15 | A14 | A13 | A12 | A23
+   COPY_WORD32(&s, 0, a, 23, 1);
+   COPY_WORD32(&s, 1, a, 12, 11);
+   //Compute T = T - D1
+   MPI_CHECK(mpiSub(&t, &t, &s));
+
+   //Compute D2 = 0 | 0 | 0 | 0 | 0 | 0 | 0 | A23 | A22 | A21 | A20 | 0
+   CLEAR_WORD32(&s, 0, 1);
+   COPY_WORD32(&s, 1, a, 20, 4);
+   CLEAR_WORD32(&s, 5, 7);
+   //Compute T = T - D2
+   MPI_CHECK(mpiSub(&t, &t, &s));
+
+   //Compute D3 = 0 | 0 | 0 | 0 | 0 | 0 | 0 | A23 | A23 | 0 | 0 | 0
+   CLEAR_WORD32(&s, 0, 3);
+   COPY_WORD32(&s, 3, a, 23, 1);
+   COPY_WORD32(&s, 4, a, 23, 1);
+   CLEAR_WORD32(&s, 5, 7);
+   //Compute T = T - D3
+   MPI_CHECK(mpiSub(&t, &t, &s));
+
+   //Compute (T + 2 * S1 + S2 + S3 + S4 + S5 + S6 - D1 - D2 - D3) mod p
+   while(mpiComp(&t, p) >= 0)
+   {
+       MPI_CHECK(mpiSub(&t, &t, p));
+   }
+
+   while(mpiCompInt(&t, 0) < 0)
+   {
+       MPI_CHECK(mpiAdd(&t, &t, p));
+   }
+
+   //Save result
+   MPI_CHECK(mpiCopy(a, &t));
+
+end:
+   //Release multiple precision integers
+   mpiFree(&s);
+   mpiFree(&t);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Fast modular reduction (secp521r1 curve)
+ * @param[in,out] a This function accept an integer less than p^2 as
+ *   input and return (a mod p) as output
+ * @param[in] p Prime modulus
+ **/
+
+error_t secp521r1Mod(Mpi *a, const Mpi *p)
+{
+   error_t error;
+   Mpi t;
+
+   //Initialize multiple precision integer
+   mpiInit(&t);
+
+   //Ajust the size of the integers
+   MPI_CHECK(mpiGrow(a, 132 / MPI_INT_SIZE));
+   MPI_CHECK(mpiGrow(&t, 68 / MPI_INT_SIZE));
+
+   //Compute A0
+   COPY_WORD32(&t, 0, a, 0, 17);
+   t.data[16] &= 0x000001FF;
+
+   //Compute A1
+   MPI_CHECK(mpiShiftRight(a, 521));
+
+   //Compute A0 + A1
+   MPI_CHECK(mpiAdd(a, a, &t));
+
+   //Compute (A0 + A1) mod p
+   while(mpiComp(a, p) >= 0)
+   {
+       MPI_CHECK(mpiSub(a, a, p));
+   }
+
+end:
+   //Release multiple precision integer
+   mpiFree(&t);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Get the elliptic curve that matches the specified OID
+ * @param[in] oid Object identifier
+ * @param[in] length OID length
+ * @return Elliptic curve domain parameters
+ **/
+
+const EcCurveInfo *ecGetCurveInfo(const uint8_t *oid, size_t length)
+{
+   //secp112r1 elliptic curve?
+   if(!oidComp(oid, length, SECP112R1_OID, sizeof(SECP112R1_OID)))
+      return SECP112R1_CURVE;
+   //secp112r2 elliptic curve?
+   if(!oidComp(oid, length, SECP112R2_OID, sizeof(SECP112R2_OID)))
+      return SECP112R2_CURVE;
+   //secp128r1 elliptic curve?
+   if(!oidComp(oid, length, SECP128R1_OID, sizeof(SECP128R1_OID)))
+      return SECP128R1_CURVE;
+   //secp128r2 elliptic curve?
+   if(!oidComp(oid, length, SECP128R2_OID, sizeof(SECP128R2_OID)))
+      return SECP128R2_CURVE;
+   //secp160k1 elliptic curve?
+   if(!oidComp(oid, length, SECP160K1_OID, sizeof(SECP160K1_OID)))
+      return SECP160K1_CURVE;
+   //secp160r1 elliptic curve?
+   else if(!oidComp(oid, length, SECP160R1_OID, sizeof(SECP160R1_OID)))
+      return SECP160R1_CURVE;
+   //secp160r2 elliptic curve?
+   else if(!oidComp(oid, length, SECP160R2_OID, sizeof(SECP160R2_OID)))
+      return SECP160R2_CURVE;
+   //secp192k1 elliptic curve?
+   else if(!oidComp(oid, length, SECP192K1_OID, sizeof(SECP192K1_OID)))
+      return SECP192K1_CURVE;
+   //secp192r1 elliptic curve?
+   else if(!oidComp(oid, length, SECP192R1_OID, sizeof(SECP192R1_OID)))
+      return SECP192R1_CURVE;
+   //secp224k1 elliptic curve?
+   else if(!oidComp(oid, length, SECP224K1_OID, sizeof(SECP224K1_OID)))
+      return SECP224K1_CURVE;
+   //secp224r1 elliptic curve?
+   else if(!oidComp(oid, length, SECP224R1_OID, sizeof(SECP224R1_OID)))
+      return SECP224R1_CURVE;
+   //secp256k1 elliptic curve?
+   else if(!oidComp(oid, length, SECP256K1_OID, sizeof(SECP256K1_OID)))
+      return SECP256K1_CURVE;
+   //secp256r1 elliptic curve?
+   else if(!oidComp(oid, length, SECP256R1_OID, sizeof(SECP256R1_OID)))
+      return SECP256R1_CURVE;
+   //secp384r1 elliptic curve?
+   else if(!oidComp(oid, length, SECP384R1_OID, sizeof(SECP384R1_OID)))
+      return SECP384R1_CURVE;
+   //secp521r1 elliptic curve?
+   else if(!oidComp(oid, length, SECP521R1_OID, sizeof(SECP521R1_OID)))
+      return SECP521R1_CURVE;
+   //brainpoolP160r1 elliptic curve?
+   else if(!oidComp(oid, length, BRAINPOOLP160R1_OID, sizeof(BRAINPOOLP160R1_OID)))
+      return BRAINPOOLP160R1_CURVE;
+   //brainpoolP192r1 elliptic curve?
+   else if(!oidComp(oid, length, BRAINPOOLP192R1_OID, sizeof(BRAINPOOLP192R1_OID)))
+      return BRAINPOOLP192R1_CURVE;
+   //brainpoolP224r1 elliptic curve?
+   else if(!oidComp(oid, length, BRAINPOOLP224R1_OID, sizeof(BRAINPOOLP224R1_OID)))
+      return BRAINPOOLP224R1_CURVE;
+   //brainpoolP256r1 elliptic curve?
+   else if(!oidComp(oid, length, BRAINPOOLP256R1_OID, sizeof(BRAINPOOLP256R1_OID)))
+      return BRAINPOOLP256R1_CURVE;
+   //brainpoolP320r1 elliptic curve?
+   else if(!oidComp(oid, length, BRAINPOOLP320R1_OID, sizeof(BRAINPOOLP320R1_OID)))
+      return BRAINPOOLP320R1_CURVE;
+   //brainpoolP384r1 elliptic curve?
+   else if(!oidComp(oid, length, BRAINPOOLP384R1_OID, sizeof(BRAINPOOLP384R1_OID)))
+      return BRAINPOOLP384R1_CURVE;
+   //brainpoolP512r1 elliptic curve?
+   else if(!oidComp(oid, length, BRAINPOOLP512R1_OID, sizeof(BRAINPOOLP512R1_OID)))
+      return BRAINPOOLP512R1_CURVE;
+   //Unknown identifier?
+   else
+      return NULL;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/ec_curves.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,179 @@
+/**
+ * @file ec_curves.h
+ * @brief Elliptic curves
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _EC_CURVES_H
+#define _EC_CURVES_H
+
+//Dependencies
+#include "crypto.h"
+#include "mpi.h"
+
+//SECG curves
+#define SECP112R1_CURVE (&secp112r1Curve)
+#define SECP112R2_CURVE (&secp112r2Curve)
+#define SECP128R1_CURVE (&secp128r1Curve)
+#define SECP128R2_CURVE (&secp128r2Curve)
+#define SECP160K1_CURVE (&secp160k1Curve)
+#define SECP160R1_CURVE (&secp160r1Curve)
+#define SECP160R2_CURVE (&secp160r2Curve)
+#define SECP192K1_CURVE (&secp192k1Curve)
+#define SECP192R1_CURVE (&secp192r1Curve)
+#define SECP224K1_CURVE (&secp224k1Curve)
+#define SECP224R1_CURVE (&secp224r1Curve)
+#define SECP256K1_CURVE (&secp256k1Curve)
+#define SECP256R1_CURVE (&secp256r1Curve)
+#define SECP384R1_CURVE (&secp384r1Curve)
+#define SECP521R1_CURVE (&secp521r1Curve)
+
+//Brainpool curves
+#define BRAINPOOLP160R1_CURVE (&brainpoolP160r1Curve)
+#define BRAINPOOLP192R1_CURVE (&brainpoolP192r1Curve)
+#define BRAINPOOLP224R1_CURVE (&brainpoolP224r1Curve)
+#define BRAINPOOLP256R1_CURVE (&brainpoolP256r1Curve)
+#define BRAINPOOLP320R1_CURVE (&brainpoolP320r1Curve)
+#define BRAINPOOLP384R1_CURVE (&brainpoolP384r1Curve)
+#define BRAINPOOLP512R1_CURVE (&brainpoolP512r1Curve)
+
+
+/**
+ * @brief Elliptic curve type
+ **/
+
+typedef enum
+{
+   EC_CURVE_TYPE_NONE          = 0,
+   EC_CURVE_TYPE_SECT_K1       = 1,
+   EC_CURVE_TYPE_SECT_R1       = 2,
+   EC_CURVE_TYPE_SECT_R2       = 3,
+   EC_CURVE_TYPE_SECP_K1       = 4,
+   EC_CURVE_TYPE_SECP_R1       = 5,
+   EC_CURVE_TYPE_SECP_R2       = 6,
+   EC_CURVE_TYPE_BRAINPOOLP_R1 = 7
+} EcCurveType;
+
+
+/**
+ * @brief Fast modular reduction
+ **/
+
+typedef error_t (*EcFastModAlgo)(Mpi *a, const Mpi *p);
+
+
+/**
+ * @brief Elliptic curve parameters
+ **/
+
+typedef struct
+{
+   const char_t *name;   ///<Curve name
+   const uint8_t *oid;   ///<Object identifier
+   size_t oidSize;       ///<OID size
+   EcCurveType type;     ///<Curve type
+   const uint8_t p[66];  ///<Prime modulus p
+   size_t pLen;          ///<Length of p
+   const uint8_t a[66];  ///<Curve parameter a
+   size_t aLen;          ///<Length of a
+   const uint8_t b[66];  ///<Curve parameter b
+   size_t bLen;          ///<Length of b
+   const uint8_t gx[66]; ///<x-coordinate of the base point G
+   size_t gxLen;         ///<Length of Gx
+   const uint8_t gy[66]; ///<y-coordinate of the base point G
+   size_t gyLen;         ///<Length of Gy
+   const uint8_t q[66];  ///<Order of the base point G
+   size_t qLen;          ///<Length of q
+   uint32_t h;           ///<Cofactor h
+   EcFastModAlgo mod;    ///<Fast modular reduction
+} EcCurveInfo;
+
+
+//Constants
+extern const uint8_t SECP112R1_OID[5];
+extern const uint8_t SECP112R2_OID[5];
+extern const uint8_t SECP128R1_OID[5];
+extern const uint8_t SECP128R2_OID[5];
+extern const uint8_t SECP160K1_OID[5];
+extern const uint8_t SECP160R1_OID[5];
+extern const uint8_t SECP160R2_OID[5];
+extern const uint8_t SECP192K1_OID[5];
+extern const uint8_t SECP192R1_OID[8];
+extern const uint8_t SECP224K1_OID[5];
+extern const uint8_t SECP224R1_OID[5];
+extern const uint8_t SECP256K1_OID[5];
+extern const uint8_t SECP256R1_OID[8];
+extern const uint8_t SECP384R1_OID[5];
+extern const uint8_t SECP521R1_OID[5];
+extern const uint8_t BRAINPOOLP160R1_OID[10];
+extern const uint8_t BRAINPOOLP192R1_OID[10];
+extern const uint8_t BRAINPOOLP224R1_OID[10];
+extern const uint8_t BRAINPOOLP256R1_OID[10];
+extern const uint8_t BRAINPOOLP320R1_OID[10];
+extern const uint8_t BRAINPOOLP384R1_OID[10];
+extern const uint8_t BRAINPOOLP512R1_OID[10];
+
+extern const EcCurveInfo secp112r1Curve;
+extern const EcCurveInfo secp112r2Curve;
+extern const EcCurveInfo secp128r1Curve;
+extern const EcCurveInfo secp128r2Curve;
+extern const EcCurveInfo secp160k1Curve;
+extern const EcCurveInfo secp160r1Curve;
+extern const EcCurveInfo secp160r2Curve;
+extern const EcCurveInfo secp192k1Curve;
+extern const EcCurveInfo secp192r1Curve;
+extern const EcCurveInfo secp224k1Curve;
+extern const EcCurveInfo secp224r1Curve;
+extern const EcCurveInfo secp256k1Curve;
+extern const EcCurveInfo secp256r1Curve;
+extern const EcCurveInfo secp384r1Curve;
+extern const EcCurveInfo secp521r1Curve;
+extern const EcCurveInfo brainpoolP160r1Curve;
+extern const EcCurveInfo brainpoolP192r1Curve;
+extern const EcCurveInfo brainpoolP224r1Curve;
+extern const EcCurveInfo brainpoolP256r1Curve;
+extern const EcCurveInfo brainpoolP320r1Curve;
+extern const EcCurveInfo brainpoolP384r1Curve;
+extern const EcCurveInfo brainpoolP512r1Curve;
+
+//Fast modular reduction
+error_t secp128r1Mod(Mpi *a, const Mpi *p);
+error_t secp128r2Mod(Mpi *a, const Mpi *p);
+error_t secp160k1Mod(Mpi *a, const Mpi *p);
+error_t secp160r1Mod(Mpi *a, const Mpi *p);
+error_t secp160r2Mod(Mpi *a, const Mpi *p);
+error_t secp192k1Mod(Mpi *a, const Mpi *p);
+error_t secp192r1Mod(Mpi *a, const Mpi *p);
+error_t secp224k1Mod(Mpi *a, const Mpi *p);
+error_t secp224r1Mod(Mpi *a, const Mpi *p);
+error_t secp256k1Mod(Mpi *a, const Mpi *p);
+error_t secp256r1Mod(Mpi *a, const Mpi *p);
+error_t secp384r1Mod(Mpi *a, const Mpi *p);
+error_t secp521r1Mod(Mpi *a, const Mpi *p);
+
+const EcCurveInfo *ecGetCurveInfo(const uint8_t *oid, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/ecdh.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,205 @@
+/**
+ * @file ecdh.c
+ * @brief ECDH (Elliptic Curve Diffie-Hellman) key exchange
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include "crypto.h"
+#include "ecdh.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (ECDH_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Initialize ECDH context
+ * @param[in] context Pointer to the ECDH context
+ **/
+
+void ecdhInit(EcdhContext *context)
+{
+   //Initialize EC domain parameters
+   ecInitDomainParameters(&context->params);
+   //Initialize private and public keys
+   mpiInit(&context->da);
+   ecInit(&context->qa);
+   ecInit(&context->qb);
+}
+
+
+/**
+ * @brief Release ECDH context
+ * @param[in] context Pointer to the ECDH context
+ **/
+
+void ecdhFree(EcdhContext *context)
+{
+   //Release EC domain parameters
+   ecFreeDomainParameters(&context->params);
+   //Release private and public keys
+   mpiFree(&context->da);
+   ecFree(&context->qa);
+   ecFree(&context->qb);
+}
+
+
+/**
+ * @brief ECDH key pair generation
+ * @param[in] context Pointer to the ECDH context
+ * @param[in] prngAlgo PRNG algorithm
+ * @param[in] prngContext Pointer to the PRNG context
+ * @return Error code
+ **/
+
+error_t ecdhGenerateKeyPair(EcdhContext *context,
+   const PrngAlgo *prngAlgo, void *prngContext)
+{
+   error_t error;
+   uint_t n;
+
+   //Debug message
+   TRACE_DEBUG("Generating ECDH key pair...\r\n");
+
+   //Let N be the bit length of q
+   n = mpiGetBitLength(&context->params.q);
+
+   //Generated a pseudorandom number
+   MPI_CHECK(mpiRand(&context->da, n, prngAlgo, prngContext));
+
+   //Make sure that 0 < da < q
+   if(mpiComp(&context->da, &context->params.q) >= 0)
+      mpiShiftRight(&context->da, 1);
+
+   //Debug message
+   TRACE_DEBUG("  Private key:\r\n");
+   TRACE_DEBUG_MPI("    ", &context->da);
+
+   //Compute Qa = da.G
+   EC_CHECK(ecMult(&context->params, &context->qa, &context->da, &context->params.g));
+   EC_CHECK(ecAffinify(&context->params, &context->qa, &context->qa));
+
+   //Debug message
+   TRACE_DEBUG("  Public key X:\r\n");
+   TRACE_DEBUG_MPI("    ", &context->qa.x);
+   TRACE_DEBUG("  Public key Y:\r\n");
+   TRACE_DEBUG_MPI("    ", &context->qa.y);
+
+end:
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Check ECDH public key
+ * @param[in] params EC domain parameters
+ * @param[in] publicKey Public key to be checked
+ * @return Error code
+ **/
+
+error_t ecdhCheckPublicKey(const EcDomainParameters *params, EcPoint *publicKey)
+{
+   bool_t valid;
+
+   //Verify that 0 <= Qx < p
+   if(mpiCompInt(&publicKey->x, 0) < 0)
+      return ERROR_ILLEGAL_PARAMETER;
+   else if(mpiComp(&publicKey->x, &params->p) >= 0)
+      return ERROR_ILLEGAL_PARAMETER;
+
+   //Verify that 0 <= Qy < p
+   if(mpiCompInt(&publicKey->y, 0) < 0)
+      return ERROR_ILLEGAL_PARAMETER;
+   else if(mpiComp(&publicKey->y, &params->p) >= 0)
+      return ERROR_ILLEGAL_PARAMETER;
+
+   //Check whether the point is on the curve
+   valid = ecIsPointAffine(params, publicKey);
+
+   //Return status code
+   return valid ? NO_ERROR : ERROR_FAILURE;
+}
+
+
+/**
+ * @brief Compute ECDH shared secret
+ * @param[in] context Pointer to the ECDH context
+ * @param[out] output Buffer where to store the shared secret
+ * @param[in] outputSize Size of the buffer in bytes
+ * @param[out] outputLength Length of the resulting shared secret
+ * @return Error code
+ **/
+
+error_t ecdhComputeSharedSecret(EcdhContext *context,
+   uint8_t *output, size_t outputSize, size_t *outputLength)
+{
+   error_t error;
+   size_t k;
+   EcPoint z;
+
+   //Debug message
+   TRACE_DEBUG("Computing Diffie-Hellman shared secret...\r\n");
+
+   //Get the length in octets of the prime modulus
+   k = mpiGetByteLength(&context->params.p);
+
+   //Make sure that the output buffer is large enough
+   if(outputSize < k)
+      return ERROR_INVALID_LENGTH;
+
+   //Initialize EC points
+   ecInit(&z);
+
+   //Compute Z = da.Qb
+   EC_CHECK(ecProjectify(&context->params, &context->qb, &context->qb));
+   EC_CHECK(ecMult(&context->params, &z, &context->da, &context->qb));
+   EC_CHECK(ecAffinify(&context->params, &z, &z));
+
+   //Convert the x-coordinate of Z to an octet string
+   MPI_CHECK(mpiWriteRaw(&z.x, output, k));
+
+   //Length of the resulting shared secret
+   *outputLength = k;
+
+   //Debug message
+   TRACE_DEBUG("  Shared secret (%" PRIuSIZE " bytes):\r\n", *outputLength);
+   TRACE_DEBUG_ARRAY("    ", output, *outputLength);
+
+end:
+   //Release EC points
+   ecFree(&z);
+
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/ecdh.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,63 @@
+/**
+ * @file ecdh.h
+ * @brief ECDH (Elliptic Curve Diffie-Hellman) key exchange
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _ECDH_H
+#define _ECDH_H
+
+//Dependencies
+#include "crypto.h"
+#include "ec.h"
+
+
+/**
+ * @brief ECDH context
+ **/
+
+typedef struct
+{
+   EcDomainParameters params; //EC domain parameters
+   Mpi da;                    ///<One's own private key
+   EcPoint qa;                ///<One's own public key
+   EcPoint qb;                ///<Peer's public key
+} EcdhContext;
+
+
+//ECDH related functions
+void ecdhInit(EcdhContext *context);
+void ecdhFree(EcdhContext *context);
+
+error_t ecdhGenerateKeyPair(EcdhContext *context,
+   const PrngAlgo *prngAlgo, void *prngContext);
+
+error_t ecdhCheckPublicKey(const EcDomainParameters *params, EcPoint *publicKey);
+
+error_t ecdhComputeSharedSecret(EcdhContext *context,
+   uint8_t *output, size_t outputSize, size_t *outputLength);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/ecdsa.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,584 @@
+/**
+ * @file ecdsa.c
+ * @brief ECDSA (Elliptic Curve Digital Signature Algorithm)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include "crypto.h"
+#include "ecdsa.h"
+#include "mpi.h"
+#include "asn1.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (ECDSA_SUPPORT == ENABLED)
+
+//ECDSA with SHA-1 OID (1.2.840.10045.4.1)
+const uint8_t ECDSA_WITH_SHA1_OID[7] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x01};
+//ECDSA with SHA-224 OID (1.2.840.10045.4.3.1)
+const uint8_t ECDSA_WITH_SHA224_OID[8] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01};
+//ECDSA with SHA-224 OID (1.2.840.10045.4.3.2)
+const uint8_t ECDSA_WITH_SHA256_OID[8] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02};
+//ECDSA with SHA-384 OID (1.2.840.10045.4.3.3)
+const uint8_t ECDSA_WITH_SHA384_OID[8] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03};
+//ECDSA with SHA-512 OID (1.2.840.10045.4.3.4)
+const uint8_t ECDSA_WITH_SHA512_OID[8] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04};
+//ECDSA with SHA-3-224 OID (2.16.840.1.101.3.4.3.9)
+const uint8_t ECDSA_WITH_SHA3_224_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x09};
+//ECDSA with SHA-3-256 OID (2.16.840.1.101.3.4.3.10)
+const uint8_t ECDSA_WITH_SHA3_256_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x0A};
+//ECDSA with SHA-3-384 OID (2.16.840.1.101.3.4.3.11)
+const uint8_t ECDSA_WITH_SHA3_384_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x0B};
+//ECDSA with SHA-3-512 OID (2.16.840.1.101.3.4.3.12)
+const uint8_t ECDSA_WITH_SHA3_512_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x0C};
+
+
+/**
+ * @brief Initialize an ECDSA signature
+ * @param[in] signature Pointer to the ECDSA signature to initialize
+ **/
+
+void ecdsaInitSignature(EcdsaSignature *signature)
+{
+   //Initialize multiple precision integers
+   mpiInit(&signature->r);
+   mpiInit(&signature->s);
+}
+
+
+/**
+ * @brief Release an ECDSA signature
+ * @param[in] signature Pointer to the ECDSA signature to free
+ **/
+
+void ecdsaFreeSignature(EcdsaSignature *signature)
+{
+   //Release multiple precision integers
+   mpiFree(&signature->r);
+   mpiFree(&signature->s);
+}
+
+
+/**
+ * @brief Encode ECDSA signature using ASN.1
+ * @param[in] signature (R, S) integer pair
+ * @param[out] data Pointer to the buffer where to store the resulting ASN.1 structure
+ * @param[out] length Length of the ASN.1 structure
+ * @return Error code
+ **/
+
+error_t ecdsaWriteSignature(const EcdsaSignature *signature, uint8_t *data, size_t *length)
+{
+   error_t error;
+   size_t k;
+   size_t n;
+   size_t rLen;
+   size_t sLen;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("Writing ECDSA signature...\r\n");
+
+   //Dump (R, S) integer pair
+   TRACE_DEBUG("  r:\r\n");
+   TRACE_DEBUG_MPI("    ", &signature->r);
+   TRACE_DEBUG("  s:\r\n");
+   TRACE_DEBUG_MPI("    ", &signature->s);
+
+   //Calculate the length of R
+   rLen = mpiGetByteLength(&signature->r);
+   //Calculate the length of S
+   sLen = mpiGetByteLength(&signature->s);
+
+   //Make sure the (R, S) integer pair is valid
+   if(rLen == 0 || sLen == 0)
+      return ERROR_INVALID_LENGTH;
+
+   //R and S are always encoded in the smallest possible number of octets
+   if(mpiGetBitValue(&signature->r, (rLen * 8) - 1))
+      rLen++;
+   if(mpiGetBitValue(&signature->s, (sLen * 8) - 1))
+      sLen++;
+
+   //The first pass computes the length of the ASN.1 sequence
+   n = 0;
+
+   //The parameter R is encapsulated within an ASN.1 structure
+   tag.constructed = FALSE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_INTEGER;
+   tag.length = rLen;
+   tag.value = NULL;
+
+   //Compute the length of the corresponding ASN.1 structure
+   error = asn1WriteTag(&tag, FALSE, NULL, NULL);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Update the length of the ASN.1 sequence
+   n += tag.totalLength;
+
+   //The parameter S is encapsulated within an ASN.1 structure
+   tag.constructed = FALSE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_INTEGER;
+   tag.length = sLen;
+   tag.value = NULL;
+
+   //Compute the length of the corresponding ASN.1 structure
+   error = asn1WriteTag(&tag, FALSE, NULL, NULL);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Update the length of the ASN.1 sequence
+   n += tag.totalLength;
+
+   //The second pass encodes the ASN.1 structure
+   k = 0;
+
+   //The (R, S) integer pair is encapsulated within a sequence
+   tag.constructed = TRUE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_SEQUENCE;
+   tag.length = n;
+   tag.value = NULL;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, FALSE, data + k, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Advance write pointer
+   k += n;
+
+   //Encode the parameter R using ASN.1
+   tag.constructed = FALSE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_INTEGER;
+   tag.length = rLen;
+   tag.value = NULL;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, FALSE, data + k, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Advance write pointer
+   k += n;
+
+   //Convert R to an octet string
+   error = mpiWriteRaw(&signature->r, data + k, rLen);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Advance write pointer
+   k += rLen;
+
+   //Encode the parameter S using ASN.1
+   tag.constructed = FALSE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_INTEGER;
+   tag.length = sLen;
+   tag.value = NULL;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, FALSE, data + k, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Advance write pointer
+   k += n;
+
+   //Convert S to an octet string
+   error = mpiWriteRaw(&signature->s, data + k, sLen);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Advance write pointer
+   k += sLen;
+
+   //Dump ECDSA signature
+   TRACE_DEBUG("  signature:\r\n");
+   TRACE_DEBUG_ARRAY("    ", data, k);
+
+   //Total length of the ASN.1 structure
+   *length = k;
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Read an ASN.1 encoded ECDSA signature
+ * @param[in] data Pointer to the ASN.1 structure to decode
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] signature (R, S) integer pair
+ * @return Error code
+ **/
+
+error_t ecdsaReadSignature(const uint8_t *data, size_t length, EcdsaSignature *signature)
+{
+   error_t error;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("Reading ECDSA signature...\r\n");
+
+   //Dump ECDSA signature
+   TRACE_DEBUG("  signature:\r\n");
+   TRACE_DEBUG_ARRAY("    ", data, length);
+
+   //Start of exception handling block
+   do
+   {
+      //Display ASN.1 structure
+      error = asn1DumpObject(data, length, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Read the contents of the ASN.1 structure
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Point to the first field
+      data = tag.value;
+      length = tag.length;
+
+      //Read the parameter R
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Convert the octet string to a multiple precision integer
+      error = mpiReadRaw(&signature->r, tag.value, tag.length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Point to the next field
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the parameter S
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Convert the octet string to a multiple precision integer
+      error = mpiReadRaw(&signature->s, tag.value, tag.length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Dump (R, S) integer pair
+      TRACE_DEBUG("  r:\r\n");
+      TRACE_DEBUG_MPI("    ", &signature->r);
+      TRACE_DEBUG("  s:\r\n");
+      TRACE_DEBUG_MPI("    ", &signature->s);
+
+      //End of exception handling block
+   } while(0);
+
+   //Clean up side effects if necessary
+   if(error)
+      ecdsaFreeSignature(signature);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief ECDSA signature generation
+ * @param[in] params EC domain parameters
+ * @param[in] prngAlgo PRNG algorithm
+ * @param[in] prngContext Pointer to the PRNG context
+ * @param[in] privateKey Signer's ECDSA private key
+ * @param[in] digest Digest of the message to be signed
+ * @param[in] digestLength Length in octets of the digest
+ * @param[out] signature (R, S) integer pair
+ * @return Error code
+ **/
+
+error_t ecdsaGenerateSignature(const EcDomainParameters *params,
+   const PrngAlgo *prngAlgo, void *prngContext, const Mpi *privateKey,
+   const uint8_t *digest, size_t digestLength, EcdsaSignature *signature)
+{
+   error_t error;
+   uint_t n;
+   Mpi k;
+   Mpi z;
+   EcPoint r1;
+
+   //Check parameters
+   if(params == NULL || privateKey == NULL || digest == NULL || signature == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_DEBUG("ECDSA signature generation...\r\n");
+   TRACE_DEBUG("  private key:\r\n");
+   TRACE_DEBUG_MPI("    ", privateKey);
+   TRACE_DEBUG("  digest:\r\n");
+   TRACE_DEBUG_ARRAY("    ", digest, digestLength);
+
+   //Initialize multiple precision integers
+   mpiInit(&k);
+   mpiInit(&z);
+   //Initialize EC point
+   ecInit(&r1);
+
+   //Let N be the bit length of q
+   n = mpiGetBitLength(&params->q);
+
+   //Generated a pseudorandom number
+   MPI_CHECK(mpiRand(&k, n, prngAlgo, prngContext));
+
+   //Make sure that 0 < k < q
+   if(mpiComp(&k, &params->q) >= 0)
+      mpiShiftRight(&k, 1);
+
+   //Debug message
+   TRACE_DEBUG("  k:\r\n");
+   TRACE_DEBUG_MPI("    ", &k);
+
+   //Compute N = MIN(N, outlen)
+   n = MIN(n, digestLength * 8);
+
+   //Convert the digest to a multiple precision integer
+   MPI_CHECK(mpiReadRaw(&z, digest, (n + 7) / 8));
+
+   //Keep the leftmost N bits of the hash value
+   if(n % 8)
+   {
+      MPI_CHECK(mpiShiftRight(&z, 8 - (n % 8)));
+   }
+
+   //Debug message
+   TRACE_DEBUG("  z:\r\n");
+   TRACE_DEBUG_MPI("    ", &z);
+
+   //Compute R1 = (x1, y1) = k.G
+   EC_CHECK(ecMult(params, &r1, &k, &params->g));
+   EC_CHECK(ecAffinify(params, &r1, &r1));
+
+   //Debug message
+   TRACE_DEBUG("  x1:\r\n");
+   TRACE_DEBUG_MPI("    ", &r1.x);
+   TRACE_DEBUG("  y1:\r\n");
+   TRACE_DEBUG_MPI("    ", &r1.y);
+
+   //Compute r = x1 mod q
+   MPI_CHECK(mpiMod(&signature->r, &r1.x, &params->q));
+
+   //Compute k ^ -1 mod q
+   MPI_CHECK(mpiInvMod(&k, &k, &params->q));
+
+   //Compute s = k ^ -1 * (z + x * r) mod q
+   MPI_CHECK(mpiMul(&signature->s, privateKey, &signature->r));
+   MPI_CHECK(mpiAdd(&signature->s, &signature->s, &z));
+   MPI_CHECK(mpiMod(&signature->s, &signature->s, &params->q));
+   MPI_CHECK(mpiMulMod(&signature->s, &signature->s, &k, &params->q));
+
+   //Dump ECDSA signature
+   TRACE_DEBUG("  r:\r\n");
+   TRACE_DEBUG_MPI("    ", &signature->r);
+   TRACE_DEBUG("  s:\r\n");
+   TRACE_DEBUG_MPI("    ", &signature->s);
+
+end:
+   //Release multiple precision integers
+   mpiFree(&k);
+   mpiFree(&z);
+   //Release EC point
+   ecFree(&r1);
+
+   //Clean up side effects if necessary
+   if(error)
+   {
+      //Release (R, S) integer pair
+      mpiFree(&signature->r);
+      mpiFree(&signature->s);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief ECDSA signature verification
+ * @param[in] params EC domain parameters
+ * @param[in] publicKey Signer's ECDSA public key
+ * @param[in] digest Digest of the message whose signature is to be verified
+ * @param[in] digestLength Length in octets of the digest
+ * @param[in] signature (R, S) integer pair
+ * @return Error code
+ **/
+
+error_t ecdsaVerifySignature(const EcDomainParameters *params, const EcPoint *publicKey,
+   const uint8_t *digest, size_t digestLength, const EcdsaSignature *signature)
+{
+   error_t error;
+   uint_t n;
+   Mpi w;
+   Mpi z;
+   Mpi u1;
+   Mpi u2;
+   Mpi v;
+   EcPoint v0;
+   EcPoint v1;
+
+   //Check parameters
+   if(params == NULL || publicKey == NULL || digest == NULL || signature == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_DEBUG("ECDSA signature verification...\r\n");
+   TRACE_DEBUG("  public key X:\r\n");
+   TRACE_DEBUG_MPI("    ", &publicKey->x);
+   TRACE_DEBUG("  public key Y:\r\n");
+   TRACE_DEBUG_MPI("    ", &publicKey->y);
+   TRACE_DEBUG("  digest:\r\n");
+   TRACE_DEBUG_ARRAY("    ", digest, digestLength);
+   TRACE_DEBUG("  r:\r\n");
+   TRACE_DEBUG_MPI("    ", &signature->r);
+   TRACE_DEBUG("  s:\r\n");
+   TRACE_DEBUG_MPI("    ", &signature->s);
+
+   //The verifier shall check that 0 < r < q and 0 < s < q. If either
+   //condition is violated, the signature shall be rejected as invalid
+   if(mpiCompInt(&signature->r, 0) <= 0 || mpiComp(&signature->r, &params->q) >= 0)
+      return ERROR_INVALID_SIGNATURE;
+   if(mpiCompInt(&signature->s, 0) <= 0 || mpiComp(&signature->s, &params->q) >= 0)
+      return ERROR_INVALID_SIGNATURE;
+
+   //Initialize multiple precision integers
+   mpiInit(&w);
+   mpiInit(&z);
+   mpiInit(&u1);
+   mpiInit(&u2);
+   mpiInit(&v);
+   //Initialize EC points
+   ecInit(&v0);
+   ecInit(&v1);
+
+   //Let N be the bit length of q
+   n = mpiGetBitLength(&params->q);
+   //Compute N = MIN(N, outlen)
+   n = MIN(n, digestLength * 8);
+
+   //Convert the digest to a multiple precision integer
+   MPI_CHECK(mpiReadRaw(&z, digest, (n + 7) / 8));
+
+   //Keep the leftmost N bits of the hash value
+   if(n % 8)
+   {
+      MPI_CHECK(mpiShiftRight(&z, 8 - (n % 8)));
+   }
+
+   //Compute w = s ^ -1 mod q
+   MPI_CHECK(mpiInvMod(&w, &signature->s, &params->q));
+   //Compute u1 = z * w mod q
+   MPI_CHECK(mpiMulMod(&u1, &z, &w, &params->q));
+   //Compute u2 = r * w mod q
+   MPI_CHECK(mpiMulMod(&u2, &signature->r, &w, &params->q));
+
+   //Compute V0 = (x0, y0) = u1.G + u2.Q
+   EC_CHECK(ecProjectify(params, &v1, publicKey));
+   EC_CHECK(ecTwinMult(params, &v0, &u1, &params->g, &u2, &v1));
+   EC_CHECK(ecAffinify(params, &v0, &v0));
+
+   //Debug message
+   TRACE_DEBUG("  x0:\r\n");
+   TRACE_DEBUG_MPI("    ", &v0.x);
+   TRACE_DEBUG("  y0:\r\n");
+   TRACE_DEBUG_MPI("    ", &v0.y);
+
+   //Compute v = x0 mod q
+   MPI_CHECK(mpiMod(&v, &v0.x, &params->q));
+
+   //Debug message
+   TRACE_DEBUG("  v:\r\n");
+   TRACE_DEBUG_MPI("    ", &v);
+
+   //If v = r, then the signature is verified. If v does not equal r,
+   //then the message or the signature may have been modified
+   if(!mpiComp(&v, &signature->r))
+      error = NO_ERROR;
+   else
+      error = ERROR_INVALID_SIGNATURE;
+
+end:
+   //Release multiple precision integers
+   mpiFree(&w);
+   mpiFree(&z);
+   mpiFree(&u1);
+   mpiFree(&u2);
+   mpiFree(&v);
+   //Release EC points
+   ecFree(&v0);
+   ecFree(&v1);
+
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/ecdsa.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,74 @@
+/**
+ * @file ecdsa.h
+ * @brief ECDSA (Elliptic Curve Digital Signature Algorithm)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _ECDSA_H
+#define _ECDSA_H
+
+//Dependencies
+#include "crypto.h"
+#include "ec.h"
+
+
+/**
+ * @brief ECDSA signature
+ **/
+
+typedef struct
+{
+   Mpi r;
+   Mpi s;
+} EcdsaSignature;
+
+
+//ECDSA related constants
+extern const uint8_t ECDSA_WITH_SHA1_OID[7];
+extern const uint8_t ECDSA_WITH_SHA224_OID[8];
+extern const uint8_t ECDSA_WITH_SHA256_OID[8];
+extern const uint8_t ECDSA_WITH_SHA384_OID[8];
+extern const uint8_t ECDSA_WITH_SHA512_OID[8];
+extern const uint8_t ECDSA_WITH_SHA3_224_OID[9];
+extern const uint8_t ECDSA_WITH_SHA3_256_OID[9];
+extern const uint8_t ECDSA_WITH_SHA3_384_OID[9];
+extern const uint8_t ECDSA_WITH_SHA3_512_OID[9];
+
+//ECDSA related functions
+void ecdsaInitSignature(EcdsaSignature *signature);
+void ecdsaFreeSignature(EcdsaSignature *signature);
+
+error_t ecdsaWriteSignature(const EcdsaSignature *signature, uint8_t *data, size_t *length);
+error_t ecdsaReadSignature(const uint8_t *data, size_t length, EcdsaSignature *signature);
+
+error_t ecdsaGenerateSignature(const EcDomainParameters *params,
+   const PrngAlgo *prngAlgo, void *prngContext, const Mpi *privateKey,
+   const uint8_t *digest, size_t digestLength, EcdsaSignature *signature);
+
+error_t ecdsaVerifySignature(const EcDomainParameters *params, const EcPoint *publicKey,
+   const uint8_t *digest, size_t digestLength, const EcdsaSignature *signature);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/hmac.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,204 @@
+/**
+ * @file hmac.c
+ * @brief HMAC (Keyed-Hashing for Message Authentication)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * HMAC is a mechanism for message authentication using cryptographic hash
+ * functions. HMAC can be used with any iterative cryptographic hash
+ * function (MD5, SHA-1 or SHA-256) in combination with a secret shared
+ * key. Refer to RFC 2104 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "hmac.h"
+
+//Check crypto library configuration
+#if (HMAC_SUPPORT == ENABLED)
+
+//HMAC with MD5 OID (1.3.6.1.5.5.8.1.1)
+const uint8_t HMAC_WITH_MD5_OID[8] = {0x2B, 0x06, 0x01, 0x05, 0x05, 0x08, 0x01, 0x01};
+//HMAC with Tiger OID (1.3.6.1.5.5.8.1.3)
+const uint8_t HMAC_WITH_TIGER_OID[8] = {0x2B, 0x06, 0x01, 0x05, 0x05, 0x08, 0x01, 0x03};
+//HMAC with RIPEMD-160 OID (1.3.6.1.5.5.8.1.4)
+const uint8_t HMAC_WITH_RIPEMD160_OID[8] = {0x2B, 0x06, 0x01, 0x05, 0x05, 0x08, 0x01, 0x04};
+//HMAC with SHA-1 OID (1.2.840.113549.2.7)
+const uint8_t HMAC_WITH_SHA1_OID[8] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x07};
+//HMAC with SHA-224 OID (1.2.840.113549.2.8)
+const uint8_t HMAC_WITH_SHA224_OID[8] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x08};
+//HMAC with SHA-256 OID (1.2.840.113549.2.9)
+const uint8_t HMAC_WITH_SHA256_OID[8] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x09};
+//HMAC with SHA-384 OID (1.2.840.113549.2.10)
+const uint8_t HMAC_WITH_SHA384_OID[8] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x0A};
+//HMAC with SHA-512 OID (1.2.840.113549.2.11)
+const uint8_t HMAC_WITH_SHA512_OID[8] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x0B};
+//HMAC with SHA-3-224 object identifier (2.16.840.1.101.3.4.2.13)
+const uint8_t HMAC_WITH_SHA3_224_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0D};
+//HMAC with SHA-3-256 object identifier (2.16.840.1.101.3.4.2.14)
+const uint8_t HMAC_WITH_SHA3_256_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0E};
+//HMAC with SHA-3-384 object identifier (2.16.840.1.101.3.4.2.15)
+const uint8_t HMAC_WITH_SHA3_384_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0F};
+//HMAC with SHA-3-512 object identifier (2.16.840.1.101.3.4.2.16)
+const uint8_t HMAC_WITH_SHA3_512_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x10};
+
+
+/**
+ * @brief Compute HMAC using the specified hash function
+ * @param[in] hash Hash algorithm used to compute HMAC
+ * @param[in] key Key to use in the hash algorithm
+ * @param[in] keyLength Length of the key
+ * @param[in] data The input data for which to compute the hash code
+ * @param[in] dataLength Length of the input data
+ * @param[out] digest The computed HMAC value
+ * @return Error code
+ **/
+
+error_t hmacCompute(const HashAlgo *hash, const void *key, size_t keyLength,
+   const void *data, size_t dataLength, uint8_t *digest)
+{
+   //Allocate a memory buffer to hold the HMAC context
+   HmacContext *context = cryptoAllocMem(sizeof(HmacContext));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the HMAC context
+   hmacInit(context, hash, key, keyLength);
+   //Digest the message
+   hmacUpdate(context, data, dataLength);
+   //Finalize the HMAC computation
+   hmacFinal(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize HMAC calculation
+ * @param[in] context Pointer to the HMAC context to initialize
+ * @param[in] hash Hash algorithm used to compute HMAC
+ * @param[in] key Key to use in the hash algorithm
+ * @param[in] keyLength Length of the key
+ **/
+
+void hmacInit(HmacContext *context, const HashAlgo *hash,
+   const void *key, size_t keyLength)
+{
+   uint_t i;
+
+   //Hash algorithm used to compute HMAC
+   context->hash = hash;
+
+   //The key is longer than the block size?
+   if(keyLength > hash->blockSize)
+   {
+      //Initialize the hash function context
+      hash->init(context->hashContext);
+      //Digest the original key
+      hash->update(context->hashContext, key, keyLength);
+      //Finalize the message digest computation
+      hash->final(context->hashContext, context->key);
+      //Key is padded to the right with extra zeros
+      memset(context->key + hash->digestSize, 0, hash->blockSize - hash->digestSize);
+   }
+   else
+   {
+      //Copy the key
+      memcpy(context->key, key, keyLength);
+      //Key is padded to the right with extra zeros
+      memset(context->key + keyLength, 0, hash->blockSize - keyLength);
+   }
+
+   //XOR the resulting key with ipad
+   for(i = 0; i < hash->blockSize; i++)
+      context->key[i] ^= HMAC_IPAD;
+
+   //Initialize context for the first pass
+   hash->init(context->hashContext);
+   //Start with the inner pad
+   hash->update(context->hashContext, context->key, hash->blockSize);
+}
+
+
+/**
+ * @brief Update the HMAC context with a portion of the message being hashed
+ * @param[in] context Pointer to the HMAC context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void hmacUpdate(HmacContext *context, const void *data, size_t length)
+{
+   //Hash algorithm used to compute HMAC
+   const HashAlgo *hash = context->hash;
+   //Digest the message (first pass)
+   hash->update(context->hashContext, data, length);
+}
+
+
+/**
+ * @brief Finish the HMAC calculation
+ * @param[in] context Pointer to the HMAC context
+ * @param[out] digest Calculated HMAC value (optional parameter)
+ **/
+
+void hmacFinal(HmacContext *context, uint8_t *digest)
+{
+   uint_t i;
+
+   //Hash algorithm used to compute HMAC
+   const HashAlgo *hash = context->hash;
+   //Finish the first pass
+   hash->final(context->hashContext, context->digest);
+
+   //XOR the original key with opad
+   for(i = 0; i < hash->blockSize; i++)
+      context->key[i] ^= HMAC_IPAD ^ HMAC_OPAD;
+
+   //Initialize context for the second pass
+   hash->init(context->hashContext);
+   //Start with outer pad
+   hash->update(context->hashContext, context->key, hash->blockSize);
+   //Then digest the result of the first hash
+   hash->update(context->hashContext, context->digest, hash->digestSize);
+   //Finish the second pass
+   hash->final(context->hashContext, context->digest);
+
+   //Copy the resulting HMAC value
+   if(digest != NULL)
+      memcpy(digest, context->digest, hash->digestSize);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/hmac.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,169 @@
+/**
+ * @file hmac.h
+ * @brief HMAC (Keyed-Hashing for Message Authentication)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _HMAC_H
+#define _HMAC_H
+
+//Dependencies
+#include "crypto.h"
+
+//MD2 hash support?
+#if (MD2_SUPPORT == ENABLED)
+   #include "md2.h"
+#endif
+
+//MD4 hash support?
+#if (MD4_SUPPORT == ENABLED)
+   #include "md4.h"
+#endif
+
+//MD5 hash support?
+#if (MD5_SUPPORT == ENABLED)
+   #include "md5.h"
+#endif
+
+//RIPEMD-128 hash support?
+#if (RIPEMD128_SUPPORT == ENABLED)
+   #include "ripemd128.h"
+#endif
+
+//RIPEMD-160 hash support?
+#if (RIPEMD160_SUPPORT == ENABLED)
+   #include "ripemd160.h"
+#endif
+
+//SHA-1 hash support?
+#if (SHA1_SUPPORT == ENABLED)
+   #include "sha1.h"
+#endif
+
+//SHA-224 hash support?
+#if (SHA224_SUPPORT == ENABLED)
+   #include "sha224.h"
+#endif
+
+//SHA-256 hash support?
+#if (SHA256_SUPPORT == ENABLED)
+   #include "sha256.h"
+#endif
+
+//SHA-384 hash support?
+#if (SHA384_SUPPORT == ENABLED)
+   #include "sha384.h"
+#endif
+
+//SHA-512 hash support?
+#if (SHA512_SUPPORT == ENABLED)
+   #include "sha512.h"
+#endif
+
+//SHA-512/224 hash support?
+#if (SHA512_224_SUPPORT == ENABLED)
+   #include "sha512_224.h"
+#endif
+
+//SHA-512/256 hash support?
+#if (SHA512_256_SUPPORT == ENABLED)
+   #include "sha512_256.h"
+#endif
+
+//SHA3-224 hash support?
+#if (SHA3_224_SUPPORT == ENABLED)
+   #include "sha3_224.h"
+#endif
+
+//SHA3-256 hash support?
+#if (SHA3_256_SUPPORT == ENABLED)
+   #include "sha3_256.h"
+#endif
+
+//SHA3-384 hash support?
+#if (SHA3_384_SUPPORT == ENABLED)
+   #include "sha3_384.h"
+#endif
+
+//SHA3-512 hash support?
+#if (SHA3_512_SUPPORT == ENABLED)
+   #include "sha3_512.h"
+#endif
+
+//Tiger hash support?
+#if (TIGER_SUPPORT == ENABLED)
+   #include "tiger.h"
+#endif
+
+//Whirlpool hash support?
+#if (WHIRLPOOL_SUPPORT == ENABLED)
+   #include "whirlpool.h"
+#endif
+
+//Inner padding (ipad)
+#define HMAC_IPAD 0x36
+//Outer padding (opad)
+#define HMAC_OPAD 0x5C
+
+
+/**
+ * @brief HMAC algorithm context
+ **/
+
+typedef struct
+{
+   const HashAlgo *hash;
+   uint8_t hashContext[MAX_HASH_CONTEXT_SIZE];
+   uint8_t key[MAX_HASH_BLOCK_SIZE];
+   uint8_t digest[MAX_HASH_DIGEST_SIZE];
+} HmacContext;
+
+
+//HMAC related constants
+extern const uint8_t HMAC_WITH_MD5_OID[8];
+extern const uint8_t HMAC_WITH_TIGER_OID[8];
+extern const uint8_t HMAC_WITH_RIPEMD160_OID[8];
+extern const uint8_t HMAC_WITH_SHA1_OID[8];
+extern const uint8_t HMAC_WITH_SHA224_OID[8];
+extern const uint8_t HMAC_WITH_SHA256_OID[8];
+extern const uint8_t HMAC_WITH_SHA384_OID[8];
+extern const uint8_t HMAC_WITH_SHA512_OID[8];
+extern const uint8_t HMAC_WITH_SHA3_224_OID[9];
+extern const uint8_t HMAC_WITH_SHA3_256_OID[9];
+extern const uint8_t HMAC_WITH_SHA3_384_OID[9];
+extern const uint8_t HMAC_WITH_SHA3_512_OID[9];
+
+//HMAC related functions
+error_t hmacCompute(const HashAlgo *hash, const void *key, size_t keyLength,
+   const void *data, size_t dataLength, uint8_t *digest);
+
+void hmacInit(HmacContext *context, const HashAlgo *hash,
+   const void *key, size_t length);
+
+void hmacUpdate(HmacContext *context, const void *data, size_t length);
+void hmacFinal(HmacContext *context, uint8_t *digest);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/idea.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,320 @@
+/**
+ * @file idea.c
+ * @brief IDEA encryption algorithm
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "idea.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (IDEA_SUPPORT == ENABLED)
+
+//Common interface for encryption algorithms
+const CipherAlgo ideaCipherAlgo =
+{
+   "IDEA",
+   sizeof(IdeaContext),
+   CIPHER_ALGO_TYPE_BLOCK,
+   IDEA_BLOCK_SIZE,
+   (CipherAlgoInit) ideaInit,
+   NULL,
+   NULL,
+   (CipherAlgoEncryptBlock) ideaEncryptBlock,
+   (CipherAlgoDecryptBlock) ideaDecryptBlock
+};
+
+
+/**
+ * @brief Modular multiplication
+ * @param[in] a First operand
+ * @param[in] b Second operand
+ * @return Resulting value
+ **/
+
+static uint16_t ideaMul(uint16_t a, uint16_t b)
+{
+   uint32_t c = a * b;
+
+   if(c)
+   {
+      c = (ROL32(c, 16) - c) >> 16;
+      return (c + 1) & 0xFFFF;
+   }
+   else
+   {
+      return (1 - a - b) & 0xFFFF;
+   }
+}
+
+
+/**
+ * @brief Compute modular inverse
+ * @param[in] a Operand
+ * @return Resulting value
+ **/
+
+static uint16_t ideaInv(uint16_t a)
+{
+   uint32_t b;
+   uint32_t q;
+   uint32_t r;
+   int32_t t;
+   int32_t u;
+   int32_t v;
+
+   b = 0x10001;
+   u = 0;
+   v = 1;
+
+   while(a > 0)
+   {
+      q = b / a;
+      r = b % a;
+
+      b = a;
+      a = r;
+
+      t = v;
+      v = u - q * v;
+      u = t;
+   }
+
+   if(u < 0)
+      u += 0x10001;
+
+   return u;
+}
+
+
+/**
+ * @brief Initialize a IDEA context using the supplied key
+ * @param[in] context Pointer to the IDEA context to initialize
+ * @param[in] key Pointer to the key
+ * @param[in] keyLength Length of the key
+ * @return Error code
+ **/
+
+error_t ideaInit(IdeaContext *context, const uint8_t *key, size_t keyLength)
+{
+   uint_t i;
+   uint16_t *ek;
+   uint16_t *dk;
+
+   //Invalid key length?
+   if(keyLength != 16)
+      return ERROR_INVALID_KEY_LENGTH;
+
+   //Point to the encryption and decryption subkeys
+   ek = context->ek;
+   dk = context->dk;
+
+   //First, the 128-bit key is partitioned into eight 16-bit sub-blocks
+   for(i = 0; i < 8; i++)
+      ek[i] = LOAD16BE(key + i * 2);
+
+   //Expand encryption subkeys
+   for(i = 8; i < 52; i++)
+   {
+      if((i % 8) == 6)
+         ek[i] = (ek[i - 7] << 9) | (ek[i - 14] >> 7);
+      else if((i % 8) == 7)
+         ek[i] = (ek[i - 15] << 9) | (ek[i - 14] >> 7);
+      else
+         ek[i] = (ek[i - 7] << 9) | (ek[i - 6] >> 7);
+   }
+
+   //Generate subkeys for decryption
+   for(i = 0; i < 52; i += 6)
+   {
+      dk[i] = ideaInv(ek[48 - i]);
+
+      if(i == 0 || i == 48)
+      {
+         dk[i + 1] = -ek[49 - i];
+         dk[i + 2] = -ek[50 - i];
+      }
+      else
+      {
+         dk[i + 1] = -ek[50 - i];
+         dk[i + 2] = -ek[49 - i];
+      }
+
+      dk[i + 3] = ideaInv(ek[51 - i]);
+
+      if(i < 48)
+      {
+         dk[i + 4] = ek[46 - i];
+         dk[i + 5] = ek[47 - i];
+      }
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Encrypt a 16-byte block using IDEA algorithm
+ * @param[in] context Pointer to the IDEA context
+ * @param[in] input Plaintext block to encrypt
+ * @param[out] output Ciphertext block resulting from encryption
+ **/
+
+void ideaEncryptBlock(IdeaContext *context, const uint8_t *input, uint8_t *output)
+{
+   uint_t i;
+   uint16_t e;
+   uint16_t f;
+   uint16_t *k;
+
+   //The plaintext is divided into four 16-bit registers
+   uint16_t a = LOAD16BE(input + 0);
+   uint16_t b = LOAD16BE(input + 2);
+   uint16_t c = LOAD16BE(input + 4);
+   uint16_t d = LOAD16BE(input + 6);
+
+   //Point to the key schedule
+   k = context->ek;
+
+   //The process consists of eight identical encryption steps
+   for(i = 0; i < 8; i++)
+   {
+      //Apply a round
+      a = ideaMul(a, k[0]);
+      b += k[1];
+      c += k[2];
+      d = ideaMul(d, k[3]);
+
+      e = a ^ c;
+      f = b ^ d;
+
+      e = ideaMul(e, k[4]);
+      f += e;
+      f = ideaMul(f, k[5]);
+      e += f;
+
+      a ^= f;
+      d ^= e;
+      e ^= b;
+      f ^= c;
+
+      b = f;
+      c = e;
+
+      //Advance current location in key schedule
+      k += 6;
+   }
+
+   //The four 16-bit values produced at the end of the 8th encryption
+   //round are combined with the last four of the 52 key sub-blocks
+   a = ideaMul(a, k[0]);
+   c += k[1];
+   b += k[2];
+   d = ideaMul(d, k[3]);
+
+   //The resulting value is the ciphertext
+   STORE16BE(a, output + 0);
+   STORE16BE(c, output + 2);
+   STORE16BE(b, output + 4);
+   STORE16BE(d, output + 6);
+}
+
+
+/**
+ * @brief Decrypt a 16-byte block using IDEA algorithm
+ * @param[in] context Pointer to the IDEA context
+ * @param[in] input Ciphertext block to decrypt
+ * @param[out] output Plaintext block resulting from decryption
+ **/
+
+void ideaDecryptBlock(IdeaContext *context, const uint8_t *input, uint8_t *output)
+{
+   uint_t i;
+   uint16_t e;
+   uint16_t f;
+   uint16_t *k;
+
+   //The plaintext is divided into four 16-bit registers
+   uint16_t a = LOAD16BE(input + 0);
+   uint16_t b = LOAD16BE(input + 2);
+   uint16_t c = LOAD16BE(input + 4);
+   uint16_t d = LOAD16BE(input + 6);
+
+   //Point to the key schedule
+   k = context->dk;
+
+   //The computational process used for decryption of the ciphertext is
+   //essentially the same as that used for encryption of the plaintext
+   for(i = 0; i < 8; i++)
+   {
+      //Apply a round
+      a = ideaMul(a, k[0]);
+      b += k[1];
+      c += k[2];
+      d = ideaMul(d, k[3]);
+
+      e = a ^ c;
+      f = b ^ d;
+
+      e = ideaMul(e, k[4]);
+      f += e;
+      f = ideaMul(f, k[5]);
+      e += f;
+
+      a ^= f;
+      d ^= e;
+      e ^= b;
+      f ^= c;
+
+      b = f;
+      c = e;
+
+      //Advance current location in key schedule
+      k += 6;
+   }
+
+   //The four 16-bit values produced at the end of the 8th encryption
+   //round are combined with the last four of the 52 key sub-blocks
+   a = ideaMul(a, k[0]);
+   c += k[1];
+   b += k[2];
+   d = ideaMul(d, k[3]);
+
+   //The resulting value is the plaintext
+   STORE16BE(a, output + 0);
+   STORE16BE(c, output + 2);
+   STORE16BE(b, output + 4);
+   STORE16BE(d, output + 6);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/idea.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,61 @@
+/**
+ * @file idea.h
+ * @brief IDEA encryption algorithm
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _IDEA_H
+#define _IDEA_H
+
+//Dependencies
+#include "crypto.h"
+
+//IDEA block size
+#define IDEA_BLOCK_SIZE 8
+//Common interface for encryption algorithms
+#define IDEA_CIPHER_ALGO (&ideaCipherAlgo)
+
+
+/**
+ * @brief IDEA algorithm context
+ **/
+
+typedef struct
+{
+   uint16_t ek[52];
+   uint16_t dk[52];
+} IdeaContext;
+
+
+//IDEA related constants
+extern const CipherAlgo ideaCipherAlgo;
+
+//IDEA related functions
+error_t ideaInit(IdeaContext *context, const uint8_t *key, size_t keyLength);
+void ideaEncryptBlock(IdeaContext *context, const uint8_t *input, uint8_t *output);
+void ideaDecryptBlock(IdeaContext *context, const uint8_t *input, uint8_t *output);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/keccak.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,481 @@
+/**
+ * @file keccak.c
+ * @brief Keccak sponge function
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "keccak.h"
+
+//Check crypto library configuration
+#if (KECCAK_SUPPORT == ENABLED)
+
+//Keccak round constants
+static const keccak_lane_t rc[KECCAK_NR] =
+{
+   (keccak_lane_t) 0x0000000000000001,
+   (keccak_lane_t) 0x0000000000008082,
+   (keccak_lane_t) 0x800000000000808A,
+   (keccak_lane_t) 0x8000000080008000,
+   (keccak_lane_t) 0x000000000000808B,
+   (keccak_lane_t) 0x0000000080000001,
+   (keccak_lane_t) 0x8000000080008081,
+   (keccak_lane_t) 0x8000000000008009,
+   (keccak_lane_t) 0x000000000000008A,
+   (keccak_lane_t) 0x0000000000000088,
+   (keccak_lane_t) 0x0000000080008009,
+   (keccak_lane_t) 0x000000008000000A,
+   (keccak_lane_t) 0x000000008000808B,
+   (keccak_lane_t) 0x800000000000008B,
+   (keccak_lane_t) 0x8000000000008089,
+   (keccak_lane_t) 0x8000000000008003,
+   (keccak_lane_t) 0x8000000000008002,
+   (keccak_lane_t) 0x8000000000000080,
+#if (KECCAK_L >= 4)
+   (keccak_lane_t) 0x000000000000800A,
+   (keccak_lane_t) 0x800000008000000A,
+#endif
+#if (KECCAK_L >= 5)
+   (keccak_lane_t) 0x8000000080008081,
+   (keccak_lane_t) 0x8000000000008080,
+#endif
+#if (KECCAK_L >= 6)
+   (keccak_lane_t) 0x0000000080000001,
+   (keccak_lane_t) 0x8000000080008008
+#endif
+};
+
+
+/**
+ * @brief Apply theta transformation
+ * @param[in,out] a State array
+ **/
+
+static void theta(keccak_lane_t a[5][5])
+{
+   keccak_lane_t c[5];
+   keccak_lane_t d[5];
+
+   //The effect of the theta transformation is to XOR each bit in the
+   //state with the parities of two columns in the array
+   c[0] = a[0][0] ^ a[1][0] ^ a[2][0] ^ a[3][0] ^ a[4][0];
+   c[1] = a[0][1] ^ a[1][1] ^ a[2][1] ^ a[3][1] ^ a[4][1];
+   c[2] = a[0][2] ^ a[1][2] ^ a[2][2] ^ a[3][2] ^ a[4][2];
+   c[3] = a[0][3] ^ a[1][3] ^ a[2][3] ^ a[3][3] ^ a[4][3];
+   c[4] = a[0][4] ^ a[1][4] ^ a[2][4] ^ a[3][4] ^ a[4][4];
+
+   d[0] = c[4] ^ KECCAK_ROL(c[1], 1);
+   d[1] = c[0] ^ KECCAK_ROL(c[2], 1);
+   d[2] = c[1] ^ KECCAK_ROL(c[3], 1);
+   d[3] = c[2] ^ KECCAK_ROL(c[4], 1);
+   d[4] = c[3] ^ KECCAK_ROL(c[0], 1);
+
+   a[0][0] ^= d[0];
+   a[1][0] ^= d[0];
+   a[2][0] ^= d[0];
+   a[3][0] ^= d[0];
+   a[4][0] ^= d[0];
+
+   a[0][1] ^= d[1];
+   a[1][1] ^= d[1];
+   a[2][1] ^= d[1];
+   a[3][1] ^= d[1];
+   a[4][1] ^= d[1];
+
+   a[0][2] ^= d[2];
+   a[1][2] ^= d[2];
+   a[2][2] ^= d[2];
+   a[3][2] ^= d[2];
+   a[4][2] ^= d[2];
+
+   a[0][3] ^= d[3];
+   a[1][3] ^= d[3];
+   a[2][3] ^= d[3];
+   a[3][3] ^= d[3];
+   a[4][3] ^= d[3];
+
+   a[0][4] ^= d[4];
+   a[1][4] ^= d[4];
+   a[2][4] ^= d[4];
+   a[3][4] ^= d[4];
+   a[4][4] ^= d[4];
+}
+
+
+/**
+ * @brief Apply rho transformation
+ * @param[in,out] a State array
+ **/
+
+static void rho(keccak_lane_t a[5][5])
+{
+   //The effect of the rho transformation is to rotate the bits of each lane by
+   //an offset, which depends on the fixed x and y coordinates of the lane
+   a[0][1] = KECCAK_ROL(a[0][1], 1   % KECCAK_W);
+   a[0][2] = KECCAK_ROL(a[0][2], 190 % KECCAK_W);
+   a[0][3] = KECCAK_ROL(a[0][3], 28  % KECCAK_W);
+   a[0][4] = KECCAK_ROL(a[0][4], 91  % KECCAK_W);
+
+   a[1][0] = KECCAK_ROL(a[1][0], 36  % KECCAK_W);
+   a[1][1] = KECCAK_ROL(a[1][1], 300 % KECCAK_W);
+   a[1][2] = KECCAK_ROL(a[1][2], 6   % KECCAK_W);
+   a[1][3] = KECCAK_ROL(a[1][3], 55  % KECCAK_W);
+   a[1][4] = KECCAK_ROL(a[1][4], 276 % KECCAK_W);
+
+   a[2][0] = KECCAK_ROL(a[2][0], 3   % KECCAK_W);
+   a[2][1] = KECCAK_ROL(a[2][1], 10  % KECCAK_W);
+   a[2][2] = KECCAK_ROL(a[2][2], 171 % KECCAK_W);
+   a[2][3] = KECCAK_ROL(a[2][3], 153 % KECCAK_W);
+   a[2][4] = KECCAK_ROL(a[2][4], 231 % KECCAK_W);
+
+   a[3][0] = KECCAK_ROL(a[3][0], 105 % KECCAK_W);
+   a[3][1] = KECCAK_ROL(a[3][1], 45  % KECCAK_W);
+   a[3][2] = KECCAK_ROL(a[3][2], 15  % KECCAK_W);
+   a[3][3] = KECCAK_ROL(a[3][3], 21  % KECCAK_W);
+   a[3][4] = KECCAK_ROL(a[3][4], 136 % KECCAK_W);
+
+   a[4][0] = KECCAK_ROL(a[4][0], 210 % KECCAK_W);
+   a[4][1] = KECCAK_ROL(a[4][1], 66  % KECCAK_W);
+   a[4][2] = KECCAK_ROL(a[4][2], 253 % KECCAK_W);
+   a[4][3] = KECCAK_ROL(a[4][3], 120 % KECCAK_W);
+   a[4][4] = KECCAK_ROL(a[4][4], 78  % KECCAK_W);
+}
+
+
+/**
+ * @brief Apply pi transformation
+ * @param[in,out] a State array
+ **/
+
+static void pi(keccak_lane_t a[5][5])
+{
+   keccak_lane_t temp;
+
+   //The effect of the pi transformation is to rearrange the
+   //positions of the lanes
+   temp = a[0][1];
+   a[0][1] = a[1][1];
+   a[1][1] = a[1][4];
+   a[1][4] = a[4][2];
+   a[4][2] = a[2][4];
+   a[2][4] = a[4][0];
+   a[4][0] = a[0][2];
+   a[0][2] = a[2][2];
+   a[2][2] = a[2][3];
+   a[2][3] = a[3][4];
+   a[3][4] = a[4][3];
+   a[4][3] = a[3][0];
+   a[3][0] = a[0][4];
+   a[0][4] = a[4][4];
+   a[4][4] = a[4][1];
+   a[4][1] = a[1][3];
+   a[1][3] = a[3][1];
+   a[3][1] = a[1][0];
+   a[1][0] = a[0][3];
+   a[0][3] = a[3][3];
+   a[3][3] = a[3][2];
+   a[3][2] = a[2][1];
+   a[2][1] = a[1][2];
+   a[1][2] = a[2][0];
+   a[2][0] = temp;
+}
+
+
+/**
+ * @brief Apply chi transformation
+ * @param[in,out] a State array
+ **/
+
+static void chi(keccak_lane_t a[5][5])
+{
+   keccak_lane_t temp1;
+   keccak_lane_t temp2;
+
+   //The effect of the chi transformation is to XOR each bit with
+   //a non linear function of two other bits in its row
+   temp1 = a[0][0];
+   temp2 = a[0][1];
+   a[0][0] ^= ~a[0][1] & a[0][2];
+   a[0][1] ^= ~a[0][2] & a[0][3];
+   a[0][2] ^= ~a[0][3] & a[0][4];
+   a[0][3] ^= ~a[0][4] & temp1;
+   a[0][4] ^= ~temp1 & temp2;
+
+   temp1 = a[1][0];
+   temp2 = a[1][1];
+   a[1][0] ^= ~a[1][1] & a[1][2];
+   a[1][1] ^= ~a[1][2] & a[1][3];
+   a[1][2] ^= ~a[1][3] & a[1][4];
+   a[1][3] ^= ~a[1][4] & temp1;
+   a[1][4] ^= ~temp1 & temp2;
+
+   temp1 = a[2][0];
+   temp2 = a[2][1];
+   a[2][0] ^= ~a[2][1] & a[2][2];
+   a[2][1] ^= ~a[2][2] & a[2][3];
+   a[2][2] ^= ~a[2][3] & a[2][4];
+   a[2][3] ^= ~a[2][4] & temp1;
+   a[2][4] ^= ~temp1 & temp2;
+
+   temp1 = a[3][0];
+   temp2 = a[3][1];
+   a[3][0] ^= ~a[3][1] & a[3][2];
+   a[3][1] ^= ~a[3][2] & a[3][3];
+   a[3][2] ^= ~a[3][3] & a[3][4];
+   a[3][3] ^= ~a[3][4] & temp1;
+   a[3][4] ^= ~temp1 & temp2;
+
+   temp1 = a[4][0];
+   temp2 = a[4][1];
+   a[4][0] ^= ~a[4][1] & a[4][2];
+   a[4][1] ^= ~a[4][2] & a[4][3];
+   a[4][2] ^= ~a[4][3] & a[4][4];
+   a[4][3] ^= ~a[4][4] & temp1;
+   a[4][4] ^= ~temp1 & temp2;
+}
+
+
+/**
+ * @brief Apply iota transformation
+ * @param[in,out] a State array
+ * @param[index] round Round index
+ **/
+
+static iota(keccak_lane_t a[5][5], uint_t index)
+{
+   //The iota transformation is parameterized by the round index
+   a[0][0] ^= rc[index];
+}
+
+
+/**
+ * @brief Initialize Keccak context
+ * @param[in] context Pointer to the Keccak context to initialize
+ * @param[in] capacity Capacity of the sponge function
+ **/
+
+error_t keccakInit(KeccakContext *context, uint_t capacity)
+{
+   uint_t rate;
+
+   //Clear Keccak context
+   memset(context, 0, sizeof(KeccakContext));
+
+   //The capacity cannot exceed the width of a Keccak-p permutation
+   if(capacity > KECCAK_B)
+      return ERROR_INVALID_PARAMETER;
+
+   //The rate depends on the capacity of the sponge function
+   rate = KECCAK_B - capacity;
+
+   //The rate must be multiple of the lane size
+   if(rate % KECCAK_W)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save the block size, in bytes
+   context->blockSize = rate / 8;
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Absorb data
+ * @param[in] context Pointer to the Keccak context
+ * @param[in] input Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void keccakAbsorb(KeccakContext *context, const void *input, size_t length)
+{
+   uint_t i;
+   size_t n;
+   keccak_lane_t *a;
+
+   //Point to the state array
+   a = (keccak_lane_t *) context->a;
+
+   //Absorbing phase
+   while(length > 0)
+   {
+      //Limit the number of bytes to process at a time
+      n = MIN(length, context->blockSize - context->length);
+
+      //Copy the data to the buffer
+      memcpy(context->buffer + context->length, input, n);
+
+      //Number of data bytes that have been buffered
+      context->length += n;
+
+      //Advance the data pointer
+      input = (uint8_t *) input + n;
+      //Remaining bytes to process
+      length -= n;
+
+      //Absorb the message block by block
+      if(context->length == context->blockSize)
+      {
+         //Absorb the current block
+         for(i = 0; i < context->blockSize / sizeof(keccak_lane_t); i++)
+            a[i] ^= KECCAK_LETOH(context->block[i]);
+
+         //Apply block permutation function
+         keccakPermutBlock(context);
+
+         //The input buffer is empty
+         context->length = 0;
+      }
+   }
+}
+
+
+/**
+ * @brief Finish absorbing phase
+ * @param[in] context Pointer to the Keccak context
+ * @param[in] pad Value of the padding byte (0x01 for Keccak, 0x06 for SHA-3 and 0x1F for XOF)
+ **/
+
+void keccakFinal(KeccakContext *context, uint8_t pad)
+{
+   uint_t i;
+   size_t q;
+   keccak_lane_t *a;
+
+   //Point to the state array
+   a = (keccak_lane_t *) context->a;
+
+   //Compute the number of padding bytes
+   q = context->blockSize - context->length;
+
+   //Append padding
+   memset(context->buffer + context->length, 0, q);
+   context->buffer[context->length] |= pad;
+   context->buffer[context->blockSize - 1] |= 0x80;
+
+   //Absorb the final block
+   for(i = 0; i < context->blockSize / sizeof(keccak_lane_t); i++)
+      a[i] ^= KECCAK_LETOH(context->block[i]);
+
+   //Apply block permutation function
+   keccakPermutBlock(context);
+
+   //Convert lanes to little-endian byte order
+   for(i = 0; i < context->blockSize / sizeof(keccak_lane_t); i++)
+      a[i] = KECCAK_HTOLE(a[i]);
+
+   //Number of bytes available in the output buffer
+   context->length = context->blockSize;
+}
+
+
+/**
+ * @brief Extract data from the squeezing phase
+ * @param[in] context Pointer to the Keccak context
+ * @param[out] output Output string
+ * @param[in] length Desired output length, in bytes
+ **/
+
+void keccakSqueeze(KeccakContext *context, uint8_t *output, size_t length)
+{
+   uint_t i;
+   size_t n;
+   keccak_lane_t *a;
+
+   //Point to the state array
+   a = (keccak_lane_t *) context->a;
+
+   //An arbitrary number of output bits can be squeezed out of the state
+   while(length > 0)
+   {
+      //Check whether more data is required
+      if(context->length == 0)
+      {
+         //Convert lanes to host byte order
+         for(i = 0; i < context->blockSize / sizeof(keccak_lane_t); i++)
+            a[i] = KECCAK_LETOH(a[i]);
+
+         //Apply block permutation function
+         keccakPermutBlock(context);
+
+         //Convert lanes to little-endian byte order
+         for(i = 0; i < context->blockSize / sizeof(keccak_lane_t); i++)
+            a[i] = KECCAK_HTOLE(a[i]);
+
+         //Number of bytes available in the output buffer
+         context->length = context->blockSize;
+      }
+
+      //Compute the number of bytes to process at a time
+      n = MIN(length, context->length);
+
+      //Copy the output string
+      if(output != NULL)
+         memcpy(output, context->digest + context->blockSize - context->length, n);
+
+      //Number of bytes available in the output buffer
+      context->length -= n;
+
+      //Advance the data pointer
+      output = (uint8_t *) output + n;
+      //Number of bytes that remains to be written
+      length -= n;
+   }
+}
+
+
+/**
+ * @brief Block permutation
+ * @param[in] context Pointer to the Keccak context
+ **/
+
+void keccakPermutBlock(KeccakContext *context)
+{
+   uint_t i;
+
+   //Each round consists of a sequence of five transformations,
+   //which are called the step mappings
+   for(i = 0; i < KECCAK_NR; i++)
+   {
+      //Apply theta step mapping
+      theta(context->a);
+      //Apply rho step mapping
+      rho(context->a);
+      //Apply pi step mapping
+      pi(context->a);
+      //Apply chi step mapping
+      chi(context->a);
+      //Apply iota step mapping
+      iota(context->a, i);
+   }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/keccak.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,118 @@
+/**
+ * @file keccak.h
+ * @brief Keccak sponge function
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _KECCAK_H
+#define _KECCAK_H
+
+//Dependencies
+#include "crypto.h"
+
+//The binary logarithm of the lane size
+#ifndef KECCAK_L
+   #define KECCAK_L 6
+#endif
+
+//Check lane size
+#if (KECCAK_L == 3)
+   //Base type that represents a lane
+   typedef uint8_t keccak_lane_t;
+   //Rotate left operation
+   #define KECCAK_ROL(a, n) ROL8(a, n)
+   //Host byte order to little-endian byte order
+   #define KECCAK_HTOLE(a) (a)
+   //Little-endian byte order to host byte order
+   #define KECCAK_LETOH(a) (a)
+#elif (KECCAK_L == 4)
+   //Base type that represents a lane
+   #define keccak_lane_t uint16_t
+   //Rotate left operation
+   #define KECCAK_ROL(a, n) ROL16(a, n)
+   //Host byte order to little-endian byte order
+   #define KECCAK_HTOLE(a) htole16(a)
+   //Little-endian byte order to host byte order
+   #define KECCAK_LETOH(a) letoh16(a)
+#elif (KECCAK_L == 5)
+   //Base type that represents a lane
+   #define keccak_lane_t uint32_t
+   //Rotate left operation
+   #define KECCAK_ROL(a, n) ROL32(a, n)
+   //Host byte order to little-endian byte order
+   #define KECCAK_HTOLE(a) htole32(a)
+   //Little-endian byte order to host byte order
+   #define KECCAK_LETOH(a) letoh32(a)
+#elif (KECCAK_L == 6)
+   //Base type that represents a lane
+   #define keccak_lane_t uint64_t
+   //Rotate left operation
+   #define KECCAK_ROL(a, n) ROL64(a, n)
+   //Host byte order to little-endian byte order conversion
+   #define KECCAK_HTOLE(a) htole64(a)
+   //Little-endian byte order to host byte order conversion
+   #define KECCAK_LETOH(a) letoh64(a)
+#else
+   #error KECCAK_L parameter is not valid
+#endif
+
+//The lane size of a Keccak-p permutation in bits
+#define KECCAK_W (1 << KECCAK_L)
+//The width of a Keccak-p permutation
+#define KECCAK_B (KECCAK_W * 25)
+//The number of rounds for a Keccak-p permutation
+#define KECCAK_NR (12 + 2 * KECCAK_L)
+
+
+/**
+ * @brief Keccak context
+ **/
+
+typedef struct
+{
+   union
+   {
+      keccak_lane_t a[5][5];
+      uint8_t digest[1];
+   };
+   union
+   {
+      keccak_lane_t block[24];
+      uint8_t buffer[1];
+   };
+   uint_t blockSize;
+   size_t length;
+} KeccakContext;
+
+
+//Keccak related functions
+error_t keccakInit(KeccakContext *context, uint_t capacity);
+void keccakAbsorb(KeccakContext *context, const void *input, size_t length);
+void keccakFinal(KeccakContext *context, uint8_t pad);
+void keccakSqueeze(KeccakContext *context, uint8_t *output, size_t length);
+void keccakPermutBlock(KeccakContext *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/md2.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,242 @@
+/**
+ * @file md2.c
+ * @brief MD2 (Message-Digest Algorithm)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The MD2 algorithm takes as input a message of arbitrary length and produces
+ * as output a 128-bit message digest of the input. Refer to RFC 1319
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "md2.h"
+
+//Check crypto library configuration
+#if (MD2_SUPPORT == ENABLED)
+
+//MD2 constants
+static const uint8_t s[256] =
+{
+   0x29, 0x2E, 0x43, 0xC9, 0xA2, 0xD8, 0x7C, 0x01, 0x3D, 0x36, 0x54, 0xA1, 0xEC, 0xF0, 0x06, 0x13,
+   0x62, 0xA7, 0x05, 0xF3, 0xC0, 0xC7, 0x73, 0x8C, 0x98, 0x93, 0x2B, 0xD9, 0xBC, 0x4C, 0x82, 0xCA,
+   0x1E, 0x9B, 0x57, 0x3C, 0xFD, 0xD4, 0xE0, 0x16, 0x67, 0x42, 0x6F, 0x18, 0x8A, 0x17, 0xE5, 0x12,
+   0xBE, 0x4E, 0xC4, 0xD6, 0xDA, 0x9E, 0xDE, 0x49, 0xA0, 0xFB, 0xF5, 0x8E, 0xBB, 0x2F, 0xEE, 0x7A,
+   0xA9, 0x68, 0x79, 0x91, 0x15, 0xB2, 0x07, 0x3F, 0x94, 0xC2, 0x10, 0x89, 0x0B, 0x22, 0x5F, 0x21,
+   0x80, 0x7F, 0x5D, 0x9A, 0x5A, 0x90, 0x32, 0x27, 0x35, 0x3E, 0xCC, 0xE7, 0xBF, 0xF7, 0x97, 0x03,
+   0xFF, 0x19, 0x30, 0xB3, 0x48, 0xA5, 0xB5, 0xD1, 0xD7, 0x5E, 0x92, 0x2A, 0xAC, 0x56, 0xAA, 0xC6,
+   0x4F, 0xB8, 0x38, 0xD2, 0x96, 0xA4, 0x7D, 0xB6, 0x76, 0xFC, 0x6B, 0xE2, 0x9C, 0x74, 0x04, 0xF1,
+   0x45, 0x9D, 0x70, 0x59, 0x64, 0x71, 0x87, 0x20, 0x86, 0x5B, 0xCF, 0x65, 0xE6, 0x2D, 0xA8, 0x02,
+   0x1B, 0x60, 0x25, 0xAD, 0xAE, 0xB0, 0xB9, 0xF6, 0x1C, 0x46, 0x61, 0x69, 0x34, 0x40, 0x7E, 0x0F,
+   0x55, 0x47, 0xA3, 0x23, 0xDD, 0x51, 0xAF, 0x3A, 0xC3, 0x5C, 0xF9, 0xCE, 0xBA, 0xC5, 0xEA, 0x26,
+   0x2C, 0x53, 0x0D, 0x6E, 0x85, 0x28, 0x84, 0x09, 0xD3, 0xDF, 0xCD, 0xF4, 0x41, 0x81, 0x4D, 0x52,
+   0x6A, 0xDC, 0x37, 0xC8, 0x6C, 0xC1, 0xAB, 0xFA, 0x24, 0xE1, 0x7B, 0x08, 0x0C, 0xBD, 0xB1, 0x4A,
+   0x78, 0x88, 0x95, 0x8B, 0xE3, 0x63, 0xE8, 0x6D, 0xE9, 0xCB, 0xD5, 0xFE, 0x3B, 0x00, 0x1D, 0x39,
+   0xF2, 0xEF, 0xB7, 0x0E, 0x66, 0x58, 0xD0, 0xE4, 0xA6, 0x77, 0x72, 0xF8, 0xEB, 0x75, 0x4B, 0x0A,
+   0x31, 0x44, 0x50, 0xB4, 0x8F, 0xED, 0x1F, 0x1A, 0xDB, 0x99, 0x8D, 0x33, 0x9F, 0x11, 0x83, 0x14,
+};
+
+//MD2 object identifier (1.2.840.113549.2.2)
+static const uint8_t md2Oid[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x02};
+
+//Common interface for hash algorithms
+const HashAlgo md2HashAlgo =
+{
+   "MD2",
+   md2Oid,
+   sizeof(md2Oid),
+   sizeof(Md2Context),
+   MD2_BLOCK_SIZE,
+   MD2_DIGEST_SIZE,
+   (HashAlgoCompute) md2Compute,
+   (HashAlgoInit) md2Init,
+   (HashAlgoUpdate) md2Update,
+   (HashAlgoFinal) md2Final
+};
+
+
+/**
+ * @brief Digest a message using MD2
+ * @param[in] data Pointer to the message being hashed
+ * @param[in] length Length of the message
+ * @param[out] digest Pointer to the calculated digest
+ * @return Error code
+ **/
+
+error_t md2Compute(const void *data, size_t length, uint8_t *digest)
+{
+   //Allocate a memory buffer to hold the MD2 context
+   Md2Context *context = cryptoAllocMem(sizeof(Md2Context));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the MD2 context
+   md2Init(context);
+   //Digest the message
+   md2Update(context, data, length);
+   //Finalize the MD2 message digest
+   md2Final(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize MD2 message digest context
+ * @param[in] context Pointer to the MD2 context to initialize
+ **/
+
+void md2Init(Md2Context *context)
+{
+   //Initialize the 48-byte buffer X
+   memset(context->x, 0, 48);
+   //Clear checksum
+   memset(context->c, 0, 16);
+   //Number of bytes in the buffer
+   context->size = 0;
+}
+
+
+/**
+ * @brief Update the MD2 context with a portion of the message being hashed
+ * @param[in] context Pointer to the MD2 context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void md2Update(Md2Context *context, const void *data, size_t length)
+{
+   size_t n;
+
+   //Process the incoming data
+   while(length > 0)
+   {
+      //The buffer can hold at most 16 bytes
+      n = MIN(length, 16 - context->size);
+
+      //Copy the data to the buffer
+      memcpy(context->m + context->size, data, n);
+
+      //Update the MD2 context
+      context->size += n;
+      //Advance the data pointer
+      data = (uint8_t *) data + n;
+      //Remaining bytes to process
+      length -= n;
+
+      //Process message in 16-word blocks
+      if(context->size == 16)
+      {
+         //Transform the 16-word block
+         md2ProcessBlock(context->m, context->x, context->c);
+         //Empty the buffer
+         context->size = 0;
+      }
+   }
+}
+
+
+/**
+ * @brief Finish the MD2 message digest
+ * @param[in] context Pointer to the MD2 context
+ * @param[out] digest Calculated digest (optional parameter)
+ **/
+
+void md2Final(Md2Context *context, uint8_t *digest)
+{
+   uint_t n;
+
+   //Pad the message so that its length is congruent to 0 modulo 16
+   n = 16 - context->size;
+
+   //Append padding bytes
+   memset(context->m + context->size, n, n);
+   //Transform the 16-word block
+   md2ProcessBlock(context->m, context->x, context->c);
+
+   //Append the checksum
+   memcpy(context->m, context->c, 16);
+   //Transform the 16-word block
+   md2ProcessBlock(context->m, context->x, context->c);
+
+   //Copy the resulting digest
+   if(digest != NULL)
+      memcpy(digest, context->digest, MD2_DIGEST_SIZE);
+}
+
+
+/**
+ * @brief Process message in 16-word blocks
+ * @param[in] m 16-byte data block to process
+ * @param[in,out] x 48-byte buffer
+ * @param[in,out] c 16-byte checksum
+ **/
+
+void md2ProcessBlock(const uint8_t *m, uint8_t *x, uint8_t *c)
+{
+   uint_t j;
+   uint_t k;
+   uint8_t t;
+
+   //Update checksum
+   for(t = c[15], j = 0; j < 16; j++)
+   {
+      c[j] ^= s[m[j] ^ t];
+      t = c[j];
+   }
+
+   //Copy current block into X
+   for(j = 0; j < 16; j++)
+   {
+      x[16 + j] = m[j];
+      x[32 + j] = x[16 + j] ^ x[j];
+   }
+
+   //Encrypt block (18 rounds)
+   for(t = 0, j = 0; j < 18; j++)
+   {
+      //Round j
+      for(k = 0; k < 48; k++)
+      {
+         x[k] ^= s[t];
+         t = x[k];
+      }
+
+      //Set t to (t + j) modulo 256
+      t = (t + j) & 0xFF;
+   }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/md2.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,72 @@
+/**
+ * @file md2.h
+ * @brief MD2 (Message-Digest Algorithm)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MD2_H
+#define _MD2_H
+
+//Dependencies
+#include "crypto.h"
+
+//MD2 block size
+#define MD2_BLOCK_SIZE 16
+//MD2 digest size
+#define MD2_DIGEST_SIZE 16
+//Common interface for hash algorithms
+#define MD2_HASH_ALGO (&md2HashAlgo)
+
+
+/**
+ * @brief MD2 algorithm context
+ **/
+
+typedef struct
+{
+
+   union
+   {
+      uint8_t x[48];
+      uint8_t digest[16];
+   };
+   uint8_t m[16];
+   uint8_t c[16];
+   size_t size;
+} Md2Context;
+
+
+//MD2 related constants
+extern const HashAlgo md2HashAlgo;
+
+//MD2 related functions
+error_t md2Compute(const void *data, size_t length, uint8_t *digest);
+void md2Init(Md2Context *context);
+void md2Update(Md2Context *context, const void *data, size_t length);
+void md2Final(Md2Context *context, uint8_t *digest);
+void md2ProcessBlock(const uint8_t *m, uint8_t *x, uint8_t *c);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/md4.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,297 @@
+/**
+ * @file md4.c
+ * @brief MD4 (Message-Digest Algorithm)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The MD4 algorithm takes as input a message of arbitrary length and produces
+ * as output a 128-bit message digest of the input. Refer to RFC 1320
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "md4.h"
+
+//Check crypto library configuration
+#if (MD4_SUPPORT == ENABLED)
+
+//MD4 auxiliary functions
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+
+#define FF(a, b, c, d, x, s) a += F(b, c, d) + (x), a = ROL32(a, s)
+#define GG(a, b, c, d, x, s) a += G(b, c, d) + (x) + 0x5A827999, a = ROL32(a, s)
+#define HH(a, b, c, d, x, s) a += H(b, c, d) + (x) + 0x6ED9EBA1, a = ROL32(a, s)
+
+//MD4 padding
+static const uint8_t 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
+};
+
+//MD4 object identifier (1.2.840.113549.2.4)
+static const uint8_t md4Oid[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x04};
+
+//Common interface for hash algorithms
+const HashAlgo md4HashAlgo =
+{
+   "MD4",
+   md4Oid,
+   sizeof(md4Oid),
+   sizeof(Md4Context),
+   MD4_BLOCK_SIZE,
+   MD4_DIGEST_SIZE,
+   (HashAlgoCompute) md4Compute,
+   (HashAlgoInit) md4Init,
+   (HashAlgoUpdate) md4Update,
+   (HashAlgoFinal) md4Final
+};
+
+
+/**
+ * @brief Digest a message using MD4
+ * @param[in] data Pointer to the message being hashed
+ * @param[in] length Length of the message
+ * @param[out] digest Pointer to the calculated digest
+ * @return Error code
+ **/
+
+error_t md4Compute(const void *data, size_t length, uint8_t *digest)
+{
+   //Allocate a memory buffer to hold the MD4 context
+   Md4Context *context = cryptoAllocMem(sizeof(Md4Context));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the MD4 context
+   md4Init(context);
+   //Digest the message
+   md4Update(context, data, length);
+   //Finalize the MD4 message digest
+   md4Final(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize MD4 message digest context
+ * @param[in] context Pointer to the MD4 context to initialize
+ **/
+
+void md4Init(Md4Context *context)
+{
+   //Set initial hash value
+   context->h[0] = 0x67452301;
+   context->h[1] = 0xEFCDAB89;
+   context->h[2] = 0x98BADCFE;
+   context->h[3] = 0x10325476;
+
+   //Number of bytes in the buffer
+   context->size = 0;
+   //Total length of the message
+   context->totalSize = 0;
+}
+
+
+/**
+ * @brief Update the MD4 context with a portion of the message being hashed
+ * @param[in] context Pointer to the MD4 context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void md4Update(Md4Context *context, const void *data, size_t length)
+{
+   size_t n;
+
+   //Process the incoming data
+   while(length > 0)
+   {
+      //The buffer can hold at most 64 bytes
+      n = MIN(length, 64 - context->size);
+
+      //Copy the data to the buffer
+      memcpy(context->buffer + context->size, data, n);
+
+      //Update the MD4 context
+      context->size += n;
+      context->totalSize += n;
+      //Advance the data pointer
+      data = (uint8_t *) data + n;
+      //Remaining bytes to process
+      length -= n;
+
+      //Process message in 16-word blocks
+      if(context->size == 64)
+      {
+         //Transform the 16-word block
+         md4ProcessBlock(context);
+         //Empty the buffer
+         context->size = 0;
+      }
+   }
+}
+
+
+/**
+ * @brief Finish the MD4 message digest
+ * @param[in] context Pointer to the MD4 context
+ * @param[out] digest Calculated digest (optional parameter)
+ **/
+
+void md4Final(Md4Context *context, uint8_t *digest)
+{
+   uint_t i;
+   size_t paddingSize;
+   uint64_t totalSize;
+
+   //Length of the original message (before padding)
+   totalSize = context->totalSize * 8;
+
+   //Pad the message so that its length is congruent to 56 modulo 64
+   if(context->size < 56)
+      paddingSize = 56 - context->size;
+   else
+      paddingSize = 64 + 56 - context->size;
+
+   //Append padding
+   md4Update(context, padding, paddingSize);
+
+   //Append the length of the original message
+   context->x[14] = htole32((uint32_t) totalSize);
+   context->x[15] = htole32((uint32_t) (totalSize >> 32));
+
+   //Calculate the message digest
+   md4ProcessBlock(context);
+
+   //Convert from host byte order to little-endian byte order
+   for(i = 0; i < 4; i++)
+      context->h[i] = htole32(context->h[i]);
+
+   //Copy the resulting digest
+   if(digest != NULL)
+      memcpy(digest, context->digest, MD4_DIGEST_SIZE);
+}
+
+
+/**
+ * @brief Process message in 16-word blocks
+ * @param[in] context Pointer to the MD4 context
+ **/
+
+void md4ProcessBlock(Md4Context *context)
+{
+   uint_t i;
+
+   //Initialize the 4 working registers
+   uint32_t a = context->h[0];
+   uint32_t b = context->h[1];
+   uint32_t c = context->h[2];
+   uint32_t d = context->h[3];
+
+   //Process message in 16-word blocks
+   uint32_t *x = context->x;
+
+   //Convert from little-endian byte order to host byte order
+   for(i = 0; i < 16; i++)
+      x[i] = letoh32(x[i]);
+
+   //Round 1
+   FF(a, b, c, d, x[0],  3);
+   FF(d, a, b, c, x[1],  7);
+   FF(c, d, a, b, x[2],  11);
+   FF(b, c, d, a, x[3],  19);
+   FF(a, b, c, d, x[4],  3);
+   FF(d, a, b, c, x[5],  7);
+   FF(c, d, a, b, x[6],  11);
+   FF(b, c, d, a, x[7],  19);
+   FF(a, b, c, d, x[8],  3);
+   FF(d, a, b, c, x[9],  7);
+   FF(c, d, a, b, x[10], 11);
+   FF(b, c, d, a, x[11], 19);
+   FF(a, b, c, d, x[12], 3);
+   FF(d, a, b, c, x[13], 7);
+   FF(c, d, a, b, x[14], 11);
+   FF(b, c, d, a, x[15], 19);
+
+   //Round 2
+   GG(a, b, c, d, x[0],  3);
+   GG(d, a, b, c, x[4],  5);
+   GG(c, d, a, b, x[8],  9);
+   GG(b, c, d, a, x[12], 13);
+   GG(a, b, c, d, x[1],  3);
+   GG(d, a, b, c, x[5],  5);
+   GG(c, d, a, b, x[9],  9);
+   GG(b, c, d, a, x[13], 13);
+   GG(a, b, c, d, x[2],  3);
+   GG(d, a, b, c, x[6],  5);
+   GG(c, d, a, b, x[10], 9);
+   GG(b, c, d, a, x[14], 13);
+   GG(a, b, c, d, x[3],  3);
+   GG(d, a, b, c, x[7],  5);
+   GG(c, d, a, b, x[11], 9);
+   GG(b, c, d, a, x[15], 13);
+
+   //Round 3
+   HH(a, b, c, d, x[0],  3);
+   HH(d, a, b, c, x[8],  9);
+   HH(c, d, a, b, x[4],  11);
+   HH(b, c, d, a, x[12], 15);
+   HH(a, b, c, d, x[2],  3);
+   HH(d, a, b, c, x[10], 9);
+   HH(c, d, a, b, x[6],  11);
+   HH(b, c, d, a, x[14], 15);
+   HH(a, b, c, d, x[1],  3);
+   HH(d, a, b, c, x[9],  9);
+   HH(c, d, a, b, x[5],  11);
+   HH(b, c, d, a, x[13], 15);
+   HH(a, b, c, d, x[3],  3);
+   HH(d, a, b, c, x[11], 9);
+   HH(c, d, a, b, x[7],  11);
+   HH(b, c, d, a, x[15], 15);
+
+   //Update the hash value
+   context->h[0] += a;
+   context->h[1] += b;
+   context->h[2] += c;
+   context->h[3] += d;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/md4.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,75 @@
+/**
+ * @file md4.h
+ * @brief MD4 (Message-Digest Algorithm)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MD4_H
+#define _MD4_H
+
+//Dependencies
+#include "crypto.h"
+
+//MD4 block size
+#define MD4_BLOCK_SIZE 64
+//MD4 digest size
+#define MD4_DIGEST_SIZE 16
+//Common interface for hash algorithms
+#define MD4_HASH_ALGO (&md4HashAlgo)
+
+
+/**
+ * @brief MD4 algorithm context
+ **/
+
+typedef struct
+{
+   union
+   {
+      uint32_t h[4];
+      uint8_t digest[16];
+   };
+   union
+   {
+      uint32_t x[16];
+      uint8_t buffer[64];
+   };
+   size_t size;
+   uint64_t totalSize;
+} Md4Context;
+
+
+//MD4 related constants
+extern const HashAlgo md4HashAlgo;
+
+//MD4 related functions
+error_t md4Compute(const void *data, size_t length, uint8_t *digest);
+void md4Init(Md4Context *context);
+void md4Update(Md4Context *context, const void *data, size_t length);
+void md4Final(Md4Context *context, uint8_t *digest);
+void md4ProcessBlock(Md4Context *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/md5.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,330 @@
+/**
+ * @file md5.c
+ * @brief MD5 (Message-Digest Algorithm)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The MD5 algorithm takes as input a message of arbitrary length and produces
+ * as output a 128-bit message digest of the input. Refer to RFC 1321
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "md5.h"
+
+//Check crypto library configuration
+#if (MD5_SUPPORT == ENABLED)
+
+//MD5 auxiliary 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)))
+
+#define FF(a, b, c, d, x, s, k) a += F(b, c, d) + (x) + (k), a = ROL32(a, s) + (b)
+#define GG(a, b, c, d, x, s, k) a += G(b, c, d) + (x) + (k), a = ROL32(a, s) + (b)
+#define HH(a, b, c, d, x, s, k) a += H(b, c, d) + (x) + (k), a = ROL32(a, s) + (b)
+#define II(a, b, c, d, x, s, k) a += I(b, c, d) + (x) + (k), a = ROL32(a, s) + (b)
+
+//MD5 padding
+static const uint8_t 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
+};
+
+//MD5 constants
+static const uint32_t k[64] =
+{
+   0xD76AA478, 0xE8C7B756, 0x242070DB, 0xC1BDCEEE, 0xF57C0FAF, 0x4787C62A, 0xA8304613, 0xFD469501,
+   0x698098D8, 0x8B44F7AF, 0xFFFF5BB1, 0x895CD7BE, 0x6B901122, 0xFD987193, 0xA679438E, 0x49B40821,
+   0xF61E2562, 0xC040B340, 0x265E5A51, 0xE9B6C7AA, 0xD62F105D, 0x02441453, 0xD8A1E681, 0xE7D3FBC8,
+   0x21E1CDE6, 0xC33707D6, 0xF4D50D87, 0x455A14ED, 0xA9E3E905, 0xFCEFA3F8, 0x676F02D9, 0x8D2A4C8A,
+   0xFFFA3942, 0x8771F681, 0x6D9D6122, 0xFDE5380C, 0xA4BEEA44, 0x4BDECFA9, 0xF6BB4B60, 0xBEBFBC70,
+   0x289B7EC6, 0xEAA127FA, 0xD4EF3085, 0x04881D05, 0xD9D4D039, 0xE6DB99E5, 0x1FA27CF8, 0xC4AC5665,
+   0xF4292244, 0x432AFF97, 0xAB9423A7, 0xFC93A039, 0x655B59C3, 0x8F0CCC92, 0xFFEFF47D, 0x85845DD1,
+   0x6FA87E4F, 0xFE2CE6E0, 0xA3014314, 0x4E0811A1, 0xF7537E82, 0xBD3AF235, 0x2AD7D2BB, 0xEB86D391
+};
+
+//MD5 object identifier (1.2.840.113549.2.5)
+static const uint8_t md5Oid[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05};
+
+//Common interface for hash algorithms
+const HashAlgo md5HashAlgo =
+{
+   "MD5",
+   md5Oid,
+   sizeof(md5Oid),
+   sizeof(Md5Context),
+   MD5_BLOCK_SIZE,
+   MD5_DIGEST_SIZE,
+   (HashAlgoCompute) md5Compute,
+   (HashAlgoInit) md5Init,
+   (HashAlgoUpdate) md5Update,
+   (HashAlgoFinal) md5Final
+};
+
+
+/**
+ * @brief Digest a message using MD5
+ * @param[in] data Pointer to the message being hashed
+ * @param[in] length Length of the message
+ * @param[out] digest Pointer to the calculated digest
+ * @return Error code
+ **/
+
+error_t md5Compute(const void *data, size_t length, uint8_t *digest)
+{
+   //Allocate a memory buffer to hold the MD5 context
+   Md5Context *context = cryptoAllocMem(sizeof(Md5Context));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the MD5 context
+   md5Init(context);
+   //Digest the message
+   md5Update(context, data, length);
+   //Finalize the MD5 message digest
+   md5Final(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize MD5 message digest context
+ * @param[in] context Pointer to the MD5 context to initialize
+ **/
+
+void md5Init(Md5Context *context)
+{
+   //Set initial hash value
+   context->h[0] = 0x67452301;
+   context->h[1] = 0xEFCDAB89;
+   context->h[2] = 0x98BADCFE;
+   context->h[3] = 0x10325476;
+
+   //Number of bytes in the buffer
+   context->size = 0;
+   //Total length of the message
+   context->totalSize = 0;
+}
+
+
+/**
+ * @brief Update the MD5 context with a portion of the message being hashed
+ * @param[in] context Pointer to the MD5 context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void md5Update(Md5Context *context, const void *data, size_t length)
+{
+   size_t n;
+
+   //Process the incoming data
+   while(length > 0)
+   {
+      //The buffer can hold at most 64 bytes
+      n = MIN(length, 64 - context->size);
+
+      //Copy the data to the buffer
+      memcpy(context->buffer + context->size, data, n);
+
+      //Update the MD5 context
+      context->size += n;
+      context->totalSize += n;
+      //Advance the data pointer
+      data = (uint8_t *) data + n;
+      //Remaining bytes to process
+      length -= n;
+
+      //Process message in 16-word blocks
+      if(context->size == 64)
+      {
+         //Transform the 16-word block
+         md5ProcessBlock(context);
+         //Empty the buffer
+         context->size = 0;
+      }
+   }
+}
+
+
+/**
+ * @brief Finish the MD5 message digest
+ * @param[in] context Pointer to the MD5 context
+ * @param[out] digest Calculated digest (optional parameter)
+ **/
+
+void md5Final(Md5Context *context, uint8_t *digest)
+{
+   uint_t i;
+   size_t paddingSize;
+   uint64_t totalSize;
+
+   //Length of the original message (before padding)
+   totalSize = context->totalSize * 8;
+
+   //Pad the message so that its length is congruent to 56 modulo 64
+   if(context->size < 56)
+      paddingSize = 56 - context->size;
+   else
+      paddingSize = 64 + 56 - context->size;
+
+   //Append padding
+   md5Update(context, padding, paddingSize);
+
+   //Append the length of the original message
+   context->x[14] = htole32((uint32_t) totalSize);
+   context->x[15] = htole32((uint32_t) (totalSize >> 32));
+
+   //Calculate the message digest
+   md5ProcessBlock(context);
+
+   //Convert from host byte order to little-endian byte order
+   for(i = 0; i < 4; i++)
+      context->h[i] = htole32(context->h[i]);
+
+   //Copy the resulting digest
+   if(digest != NULL)
+      memcpy(digest, context->digest, MD5_DIGEST_SIZE);
+}
+
+
+/**
+ * @brief Process message in 16-word blocks
+ * @param[in] context Pointer to the MD5 context
+ **/
+
+void md5ProcessBlock(Md5Context *context)
+{
+   uint_t i;
+
+   //Initialize the 4 working registers
+   uint32_t a = context->h[0];
+   uint32_t b = context->h[1];
+   uint32_t c = context->h[2];
+   uint32_t d = context->h[3];
+
+   //Process message in 16-word blocks
+   uint32_t *x = context->x;
+
+   //Convert from little-endian byte order to host byte order
+   for(i = 0; i < 16; i++)
+      x[i] = letoh32(x[i]);
+
+   //Round 1
+   FF(a, b, c, d, x[0],  7,  k[0]);
+   FF(d, a, b, c, x[1],  12, k[1]);
+   FF(c, d, a, b, x[2],  17, k[2]);
+   FF(b, c, d, a, x[3],  22, k[3]);
+   FF(a, b, c, d, x[4],  7,  k[4]);
+   FF(d, a, b, c, x[5],  12, k[5]);
+   FF(c, d, a, b, x[6],  17, k[6]);
+   FF(b, c, d, a, x[7],  22, k[7]);
+   FF(a, b, c, d, x[8],  7,  k[8]);
+   FF(d, a, b, c, x[9],  12, k[9]);
+   FF(c, d, a, b, x[10], 17, k[10]);
+   FF(b, c, d, a, x[11], 22, k[11]);
+   FF(a, b, c, d, x[12], 7,  k[12]);
+   FF(d, a, b, c, x[13], 12, k[13]);
+   FF(c, d, a, b, x[14], 17, k[14]);
+   FF(b, c, d, a, x[15], 22, k[15]);
+
+   //Round 2
+   GG(a, b, c, d, x[1],  5,  k[16]);
+   GG(d, a, b, c, x[6],  9,  k[17]);
+   GG(c, d, a, b, x[11], 14, k[18]);
+   GG(b, c, d, a, x[0],  20, k[19]);
+   GG(a, b, c, d, x[5],  5,  k[20]);
+   GG(d, a, b, c, x[10], 9,  k[21]);
+   GG(c, d, a, b, x[15], 14, k[22]);
+   GG(b, c, d, a, x[4],  20, k[23]);
+   GG(a, b, c, d, x[9],  5,  k[24]);
+   GG(d, a, b, c, x[14], 9,  k[25]);
+   GG(c, d, a, b, x[3],  14, k[26]);
+   GG(b, c, d, a, x[8],  20, k[27]);
+   GG(a, b, c, d, x[13], 5,  k[28]);
+   GG(d, a, b, c, x[2],  9,  k[29]);
+   GG(c, d, a, b, x[7],  14, k[30]);
+   GG(b, c, d, a, x[12], 20, k[31]);
+
+   //Round 3
+   HH(a, b, c, d, x[5],  4,  k[32]);
+   HH(d, a, b, c, x[8],  11, k[33]);
+   HH(c, d, a, b, x[11], 16, k[34]);
+   HH(b, c, d, a, x[14], 23, k[35]);
+   HH(a, b, c, d, x[1],  4,  k[36]);
+   HH(d, a, b, c, x[4],  11, k[37]);
+   HH(c, d, a, b, x[7],  16, k[38]);
+   HH(b, c, d, a, x[10], 23, k[39]);
+   HH(a, b, c, d, x[13], 4,  k[40]);
+   HH(d, a, b, c, x[0],  11, k[41]);
+   HH(c, d, a, b, x[3],  16, k[42]);
+   HH(b, c, d, a, x[6],  23, k[43]);
+   HH(a, b, c, d, x[9],  4,  k[44]);
+   HH(d, a, b, c, x[12], 11, k[45]);
+   HH(c, d, a, b, x[15], 16, k[46]);
+   HH(b, c, d, a, x[2],  23, k[47]);
+
+   //Round 4
+   II(a, b, c, d, x[0],  6,  k[48]);
+   II(d, a, b, c, x[7],  10, k[49]);
+   II(c, d, a, b, x[14], 15, k[50]);
+   II(b, c, d, a, x[5],  21, k[51]);
+   II(a, b, c, d, x[12], 6,  k[52]);
+   II(d, a, b, c, x[3],  10, k[53]);
+   II(c, d, a, b, x[10], 15, k[54]);
+   II(b, c, d, a, x[1],  21, k[55]);
+   II(a, b, c, d, x[8],  6,  k[56]);
+   II(d, a, b, c, x[15], 10, k[57]);
+   II(c, d, a, b, x[6],  15, k[58]);
+   II(b, c, d, a, x[13], 21, k[59]);
+   II(a, b, c, d, x[4],  6,  k[60]);
+   II(d, a, b, c, x[11], 10, k[61]);
+   II(c, d, a, b, x[2],  15, k[62]);
+   II(b, c, d, a, x[9],  21, k[63]);
+
+   //Update the hash value
+   context->h[0] += a;
+   context->h[1] += b;
+   context->h[2] += c;
+   context->h[3] += d;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/md5.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,75 @@
+/**
+ * @file md5.h
+ * @brief MD5 (Message-Digest Algorithm)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MD5_H
+#define _MD5_H
+
+//Dependencies
+#include "crypto.h"
+
+//MD5 block size
+#define MD5_BLOCK_SIZE 64
+//MD5 digest size
+#define MD5_DIGEST_SIZE 16
+//Common interface for hash algorithms
+#define MD5_HASH_ALGO (&md5HashAlgo)
+
+
+/**
+ * @brief MD5 algorithm context
+ **/
+
+typedef struct
+{
+   union
+   {
+      uint32_t h[4];
+      uint8_t digest[16];
+   };
+   union
+   {
+      uint32_t x[16];
+      uint8_t buffer[64];
+   };
+   size_t size;
+   uint64_t totalSize;
+} Md5Context;
+
+
+//MD5 related constants
+extern const HashAlgo md5HashAlgo;
+
+//MD5 related functions
+error_t md5Compute(const void *data, size_t length, uint8_t *digest);
+void md5Init(Md5Context *context);
+void md5Update(Md5Context *context, const void *data, size_t length);
+void md5Final(Md5Context *context, uint8_t *digest);
+void md5ProcessBlock(Md5Context *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/mpi.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1756 @@
+/**
+ * @file mpi.c
+ * @brief MPI (Multiple Precision Integer Arithmetic)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include <string.h>
+#include "crypto.h"
+#include "mpi.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (MPI_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Initialize a multiple precision integer
+ * @param[in,out] r Pointer to the multiple precision integer to be initialized
+ **/
+
+void mpiInit(Mpi *r)
+{
+   //Initialize structure
+   r->sign = 1;
+   r->size = 0;
+   r->data = NULL;
+}
+
+
+/**
+ * @brief Release a multiple precision integer
+ * @param[in,out] r Pointer to the multiple precision integer to be freed
+ **/
+
+void mpiFree(Mpi *r)
+{
+   //Any memory previously allocated?
+   if(r->data != NULL)
+   {
+      //Erase contents before releasing memory
+      memset(r->data, 0, r->size * MPI_INT_SIZE);
+      cryptoFreeMem(r->data);
+   }
+
+   //Set size to zero
+   r->size = 0;
+   r->data = NULL;
+}
+
+
+/**
+ * @brief Adjust the size of multiple precision integer
+ * @param[in,out] r A multiple precision integer whose size is to be increased
+ * @param[in] size Desired size in words
+ * @return Error code
+ **/
+
+error_t mpiGrow(Mpi *r, uint_t size)
+{
+   uint_t *data;
+
+   //Ensure the parameter is valid
+   size = MAX(size, 1);
+
+   //Check the current size
+   if(r->size >= size)
+      return NO_ERROR;
+
+   //Allocate a memory buffer
+   data = cryptoAllocMem(size * MPI_INT_SIZE);
+   //Failed to allocate memory?
+   if(data == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Clear buffer contents
+   memset(data, 0, size * MPI_INT_SIZE);
+
+   //Any data to copy?
+   if(r->size > 0)
+   {
+      //Copy original data
+      memcpy(data, r->data, r->size * MPI_INT_SIZE);
+      //Free previously allocated memory
+      cryptoFreeMem(r->data);
+   }
+
+   //Update the size of the multiple precision integer
+   r->size = size;
+   r->data = data;
+
+   //Successful operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Get the actual length in words
+ * @param[in] a Pointer to a multiple precision integer
+ * @return The actual length in words
+ **/
+
+uint_t mpiGetLength(const Mpi *a)
+{
+   int_t i;
+
+   //Check whether the specified multiple precision integer is empty
+   if(a->size == 0)
+      return 0;
+
+   //Start from the most significant word
+   for(i = a->size - 1; i >= 0; i--)
+   {
+      //Loop as long as the current word is zero
+      if(a->data[i] != 0)
+         break;
+   }
+
+   //Return the actual length
+   return i + 1;
+}
+
+
+/**
+ * @brief Get the actual length in bytes
+ * @param[in] a Pointer to a multiple precision integer
+ * @return The actual byte count
+ **/
+
+uint_t mpiGetByteLength(const Mpi *a)
+{
+   uint_t n;
+   uint32_t m;
+
+   //Check whether the specified multiple precision integer is empty
+   if(a->size == 0)
+      return 0;
+
+   //Start from the most significant word
+   for(n = a->size - 1; n > 0; n--)
+   {
+      //Loop as long as the current word is zero
+      if(a->data[n] != 0)
+         break;
+   }
+
+   //Get the current word
+   m = a->data[n];
+   //Convert the length to a byte count
+   n *= MPI_INT_SIZE;
+
+   //Adjust the byte count
+   for(; m != 0; m >>= 8) n++;
+
+   //Return the actual length in bytes
+   return n;
+}
+
+
+/**
+ * @brief Get the actual length in bits
+ * @param[in] a Pointer to a multiple precision integer
+ * @return The actual bit count
+ **/
+
+uint_t mpiGetBitLength(const Mpi *a)
+{
+   uint_t n;
+   uint32_t m;
+
+   //Check whether the specified multiple precision integer is empty
+   if(a->size == 0)
+      return 0;
+
+   //Start from the most significant word
+   for(n = a->size - 1; n > 0; n--)
+   {
+      //Loop as long as the current word is zero
+      if(a->data[n] != 0)
+         break;
+   }
+
+   //Get the current word
+   m = a->data[n];
+   //Convert the length to a bit count
+   n *= MPI_INT_SIZE * 8;
+
+   //Adjust the bit count
+   for(; m != 0; m >>= 1) n++;
+
+   //Return the actual length in bits
+   return n;
+}
+
+
+/**
+ * @brief Set the bit value at the specified index
+ * @param[in] r Pointer to a multiple precision integer
+ * @param[in] index Position of the bit to be written
+ * @param[in] value Bit value
+ * @return Error code
+ **/
+
+error_t mpiSetBitValue(Mpi *r, uint_t index, uint_t value)
+{
+   error_t error;
+   uint_t n1;
+   uint_t n2;
+
+   //Retrieve the position of the bit to be written
+   n1 = index / (MPI_INT_SIZE * 8);
+   n2 = index % (MPI_INT_SIZE * 8);
+
+   //Ajust the size of the multiple precision integer if necessary
+   error = mpiGrow(r, (index + (MPI_INT_SIZE * 8) - 1) / (MPI_INT_SIZE * 8));
+   //Failed to adjust the size?
+   if(error)
+      return error;
+
+   //Set bit value
+   if(value)
+      r->data[n1] |= (1 << n2);
+   else
+      r->data[n1] &= ~(1 << n2);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Get the bit value at the specified index
+ * @param[in] a Pointer to a multiple precision integer
+ * @param[in] index Position where to read the bit
+ * @return The actual bit value
+ **/
+
+uint_t mpiGetBitValue(const Mpi *a, uint_t index)
+{
+   uint_t n1;
+   uint_t n2;
+
+   //Retrieve the position of the bit to be read
+   n1 = index / (MPI_INT_SIZE * 8);
+   n2 = index % (MPI_INT_SIZE * 8);
+
+   //Index out of range?
+   if(n1 >= a->size)
+      return 0;
+
+   //Return the actual bit value
+   return (a->data[n1] >> n2) & 0x01;
+}
+
+
+/**
+ * @brief Compare two multiple precision integers
+ * @param[in] a The first multiple precision integer to be compared
+ * @param[in] b The second multiple precision integer to be compared
+ * @return Comparison result
+ **/
+
+int_t mpiComp(const Mpi *a, const Mpi *b)
+{
+   uint_t m;
+   uint_t n;
+
+   //Determine the actual length of A and B
+   m = mpiGetLength(a);
+   n = mpiGetLength(b);
+
+   //Compare lengths
+   if(!m && !n)
+      return 0;
+   else if(m > n)
+      return a->sign;
+   else if(m < n)
+      return -b->sign;
+
+   //Compare signs
+   if(a->sign > 0 && b->sign < 0)
+      return 1;
+   else if(a->sign < 0 && b->sign > 0)
+      return -1;
+
+   //Then compare values
+   while(n--)
+   {
+      if(a->data[n] > b->data[n])
+         return a->sign;
+      else if(a->data[n] < b->data[n])
+         return -a->sign;
+   }
+
+   //Multiple precision integers are equals
+   return 0;
+}
+
+
+/**
+ * @brief Compare a multiple precision integer with an integer
+ * @param[in] a Multiple precision integer to be compared
+ * @param[in] b Integer to be compared
+ * @return Comparison result
+ **/
+
+int_t mpiCompInt(const Mpi *a, int_t b)
+{
+   uint_t value;
+   Mpi t;
+
+   //Initialize a temporary multiple precision integer
+   value = (b >= 0) ? b : -b;
+   t.sign = (b >= 0) ? 1 : -1;
+   t.size = 1;
+   t.data = &value;
+
+   //Return comparison result
+   return mpiComp(a, &t);
+}
+
+
+/**
+ * @brief Compare the absolute value of two multiple precision integers
+ * @param[in] a The first multiple precision integer to be compared
+ * @param[in] b The second multiple precision integer to be compared
+ * @return Comparison result
+ **/
+
+int_t mpiCompAbs(const Mpi *a, const Mpi *b)
+{
+   uint_t m;
+   uint_t n;
+
+   //Determine the actual length of A and B
+   m = mpiGetLength(a);
+   n = mpiGetLength(b);
+
+   //Compare lengths
+   if(!m && !n)
+      return 0;
+   else if(m > n)
+      return 1;
+   else if(m < n)
+      return -1;
+
+   //Then compare values
+   while(n--)
+   {
+      if(a->data[n] > b->data[n])
+         return 1;
+      else if(a->data[n] < b->data[n])
+         return -1;
+   }
+
+   //Operands are equals
+   return 0;
+}
+
+
+/**
+ * @brief Copy a multiple precision integer
+ * @param[out] r Pointer to a multiple precision integer (destination)
+ * @param[in] a Pointer to a multiple precision integer (source)
+ * @return Error code
+ **/
+
+error_t mpiCopy(Mpi *r, const Mpi *a)
+{
+   error_t error;
+   uint_t n;
+
+   //R and A are the same instance?
+   if(r == a)
+      return NO_ERROR;
+
+   //Determine the actual length of A
+   n = mpiGetLength(a);
+
+   //Ajust the size of the destination operand
+   error = mpiGrow(r, n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Clear the contents of the multiple precision integer
+   memset(r->data, 0, r->size * MPI_INT_SIZE);
+   //Let R = A
+   memcpy(r->data, a->data, n * MPI_INT_SIZE);
+   //Set the sign of R
+   r->sign = a->sign;
+
+   //Successful operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set the value of a multiple precision integer
+ * @param[out] r Pointer to a multiple precision integer
+ * @param[in] a Value to be assigned to the multiple precision integer
+ * @return Error code
+ **/
+
+error_t mpiSetValue(Mpi *r, int_t a)
+{
+   error_t error;
+
+   //Ajust the size of the destination operand
+   error = mpiGrow(r, 1);
+   //Failed to adjust the size?
+   if(error)
+      return error;
+
+   //Clear the contents of the multiple precision integer
+   memset(r->data, 0, r->size * MPI_INT_SIZE);
+   //Set the value or R
+   r->data[0] = (a >= 0) ? a : -a;
+   //Set the sign of R
+   r->sign = (a >= 0) ? 1 : -1;
+
+   //Successful operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Generate a random value
+ * @param[out] r Pointer to a multiple precision integer
+ * @param[in] length Desired length in bits
+ * @param[in] prngAlgo PRNG algorithm
+ * @param[in] prngContext Pointer to the PRNG context
+ * @return Error code
+ **/
+
+error_t mpiRand(Mpi *r, uint_t length, const PrngAlgo *prngAlgo, void *prngContext)
+{
+   error_t error;
+   uint_t m;
+   uint_t n;
+
+   //Compute the required length, in words
+   n = (length + (MPI_INT_SIZE * 8) - 1) / (MPI_INT_SIZE * 8);
+   //Number of bits in the most significant word
+   m = length % (MPI_INT_SIZE * 8);
+
+   //Ajust the size of the multiple precision integer if necessary
+   error = mpiGrow(r, n);
+   //Failed to adjust the size?
+   if(error)
+      return error;
+
+   //Clear the contents of the multiple precision integer
+   memset(r->data, 0, r->size * MPI_INT_SIZE);
+   //Set the sign of R
+   r->sign = 1;
+
+   //Generate a random pattern
+   error = prngAlgo->read(prngContext, (uint8_t *) r->data, n * MPI_INT_SIZE);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Remove the meaningless bits in the most significant word
+   if(n > 0 && m > 0)
+      r->data[n - 1] &= (1 << m) - 1;
+
+   //Successful operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Octet string to integer conversion
+ *
+ * Converts an octet string to a non-negative integer
+ *
+ * @param[out] r Non-negative integer resulting from the conversion
+ * @param[in] data Octet string to be converted
+ * @param[in] length Length of the octet string
+ * @return Error code
+ **/
+
+error_t mpiReadRaw(Mpi *r, const uint8_t *data, uint_t length)
+{
+   error_t error;
+   uint_t i;
+
+   //Skip leading zeroes
+   while(length > 1 && *data == 0)
+   {
+      //Advance read pointer
+      data++;
+      length--;
+   }
+
+   //Ajust the size of the multiple precision integer
+   error = mpiGrow(r, (length + MPI_INT_SIZE - 1) / MPI_INT_SIZE);
+   //Failed to adjust the size?
+   if(error)
+      return error;
+
+   //Clear the contents of the multiple precision integer
+   memset(r->data, 0, r->size * MPI_INT_SIZE);
+   //Set sign
+   r->sign = 1;
+
+   //Start from the least significant byte
+   data += length - 1;
+
+   //Copy data
+   for(i = 0; i < length; i++, data--)
+      r->data[i / MPI_INT_SIZE] |= *data << ((i % MPI_INT_SIZE) * 8);
+
+   //The conversion succeeded
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Integer to octet string conversion
+ *
+ * Converts an integer to an octet string of a specified length
+ *
+ * @param[in] a Non-negative integer to be converted
+ * @param[out] data Octet string resulting from the conversion
+ * @param[in] length Intended length of the resulting octet string
+ * @return Error code
+ **/
+
+error_t mpiWriteRaw(const Mpi *a, uint8_t *data, uint_t length)
+{
+   uint_t i;
+
+   //Get the actual length in bytes
+   uint_t n = mpiGetByteLength(a);
+
+   //Make sure the output buffer is large enough
+   if(n > length)
+      return ERROR_INVALID_LENGTH;
+
+   //Clear output buffer
+   memset(data, 0, length);
+
+   //Start from the least significant word
+   data += length - 1;
+
+   //Copy data
+   for(i = 0; i < n; i++, data--)
+      *data = a->data[i / MPI_INT_SIZE] >> ((i % MPI_INT_SIZE) * 8);
+
+   //The conversion succeeded
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Multiple precision addition
+ * @param[out] r Resulting integer R = A + B
+ * @param[in] a First operand A
+ * @param[in] b Second operand B
+ * @return Error code
+ **/
+
+error_t mpiAdd(Mpi *r, const Mpi *a, const Mpi *b)
+{
+   error_t error;
+   int_t sign;
+
+   //Retrieve the sign of A
+   sign = a->sign;
+
+   //Both operands have the same sign?
+   if(a->sign == b->sign)
+   {
+      //Perform addition
+      error = mpiAddAbs(r, a, b);
+      //Set the sign of the resulting number
+      r->sign = sign;
+   }
+   //Operands have different signs?
+   else
+   {
+      //Compare the absolute value of A and B
+      if(mpiCompAbs(a, b) >= 0)
+      {
+         //Perform subtraction
+         error = mpiSubAbs(r, a, b);
+         //Set the sign of the resulting number
+         r->sign = sign;
+      }
+      else
+      {
+         //Perform subtraction
+         error = mpiSubAbs(r, b, a);
+         //Set the sign of the resulting number
+         r->sign = -sign;
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Add an integer to a multiple precision integer
+ * @param[out] r Resulting integer R = A + B
+ * @param[in] a First operand A
+ * @param[in] b Second operand B
+ * @return Error code
+ **/
+
+error_t mpiAddInt(Mpi *r, const Mpi *a, int_t b)
+{
+   uint_t value;
+   Mpi t;
+
+   //Convert the second operand to a multiple precision integer
+   value = (b >= 0) ? b : -b;
+   t.sign = (b >= 0) ? 1 : -1;
+   t.size = 1;
+   t.data = &value;
+
+   //Perform addition
+   return mpiAdd(r, a ,&t);
+}
+
+
+/**
+ * @brief Multiple precision subtraction
+ * @param[out] r Resulting integer R = A - B
+ * @param[in] a First operand A
+ * @param[in] b Second operand B
+ * @return Error code
+ **/
+
+error_t mpiSub(Mpi *r, const Mpi *a, const Mpi *b)
+{
+   error_t error;
+   int_t sign;
+
+   //Retrieve the sign of A
+   sign = a->sign;
+
+   //Both operands have the same sign?
+   if(a->sign == b->sign)
+   {
+      //Compare the absolute value of A and B
+      if(mpiCompAbs(a, b) >= 0)
+      {
+         //Perform subtraction
+         error = mpiSubAbs(r, a, b);
+         //Set the sign of the resulting number
+         r->sign = sign;
+      }
+      else
+      {
+         //Perform subtraction
+         error = mpiSubAbs(r, b, a);
+         //Set the sign of the resulting number
+         r->sign = -sign;
+      }
+   }
+   //Operands have different signs?
+   else
+   {
+      //Perform addition
+      error = mpiAddAbs(r, a, b);
+      //Set the sign of the resulting number
+      r->sign = sign;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Subtract an integer from a multiple precision integer
+ * @param[out] r Resulting integer R = A - B
+ * @param[in] a First operand A
+ * @param[in] b Second operand B
+ * @return Error code
+ **/
+
+error_t mpiSubInt(Mpi *r, const Mpi *a, int_t b)
+{
+   uint_t value;
+   Mpi t;
+
+   //Convert the second operand to a multiple precision integer
+   value = (b >= 0) ? b : -b;
+   t.sign = (b >= 0) ? 1 : -1;
+   t.size = 1;
+   t.data = &value;
+
+   //Perform subtraction
+   return mpiSub(r, a ,&t);
+}
+
+
+/**
+ * @brief Helper routine for multiple precision addition
+ * @param[out] r Resulting integer R = |A + B|
+ * @param[in] a First operand A
+ * @param[in] b Second operand B
+ * @return Error code
+ **/
+
+error_t mpiAddAbs(Mpi *r, const Mpi *a, const Mpi *b)
+{
+   error_t error;
+   uint_t i;
+   uint_t n;
+   uint_t c;
+   uint_t d;
+
+   //R and B are the same instance?
+   if(r == b)
+   {
+      //Swap A and B
+      const Mpi *t = a;
+      a = b;
+      b = t;
+   }
+   //R is neither A nor B?
+   else if(r != a)
+   {
+      //Copy the first operand to R
+      MPI_CHECK(mpiCopy(r, a));
+   }
+
+   //Determine the actual length of B
+   n = mpiGetLength(b);
+   //Extend the size of the destination register as needed
+   MPI_CHECK(mpiGrow(r, n));
+
+   //The result is always positive
+   r->sign = 1;
+   //Clear carry bit
+   c = 0;
+
+   //Add operands
+   for(i = 0; i < n; i++)
+   {
+      //Add carry bit
+      d = r->data[i] + c;
+      //Update carry bit
+      if(d != 0) c = 0;
+      //Perform addition
+      d += b->data[i];
+      //Update carry bit
+      if(d < b->data[i]) c = 1;
+      //Save result
+      r->data[i] = d;
+   }
+
+   //Loop as long as the carry bit is set
+   for(i = n; c && i < r->size; i++)
+   {
+      //Add carry bit
+      r->data[i] += c;
+      //Update carry bit
+      if(r->data[i] != 0) c = 0;
+   }
+
+   //Check the final carry bit
+   if(c && n >= r->size)
+   {
+      //Extend the size of the destination register
+      MPI_CHECK(mpiGrow(r, n + 1));
+      //Add carry bit
+      r->data[n] = 1;
+   }
+
+end:
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Helper routine for multiple precision subtraction
+ * @param[out] r Resulting integer R = |A - B|
+ * @param[in] a First operand A
+ * @param[in] b Second operand B
+ * @return Error code
+ **/
+
+error_t mpiSubAbs(Mpi *r, const Mpi *a, const Mpi *b)
+{
+   error_t error;
+   uint_t c;
+   uint_t d;
+   uint_t i;
+   uint_t m;
+   uint_t n;
+
+   //Check input parameters
+   if(mpiCompAbs(a, b) < 0)
+   {
+      //Swap A and B if necessary
+      const Mpi *t = b;
+      a = b;
+      b = t;
+   }
+
+   //Determine the actual length of A
+   m = mpiGetLength(a);
+   //Determine the actual length of B
+   n = mpiGetLength(b);
+
+   //Extend the size of the destination register as needed
+   MPI_CHECK(mpiGrow(r, m));
+
+   //The result is always positive
+   r->sign = 1;
+   //Clear carry bit
+   c = 0;
+
+   //Subtract operands
+   for(i = 0; i < n; i++)
+   {
+      //Read first operand
+      d = a->data[i];
+
+      //Check the carry bit
+      if(c)
+      {
+         //Update carry bit
+         if(d != 0) c = 0;
+         //Propagate carry bit
+         d -= 1;
+      }
+
+      //Update carry bit
+      if(d < b->data[i]) c = 1;
+      //Perform subtraction
+      r->data[i] = d - b->data[i];
+   }
+
+   //Loop as long as the carry bit is set
+   for(i = n; c && i < m; i++)
+   {
+      //Update carry bit
+      if(a->data[i] != 0) c = 0;
+      //Propagate carry bit
+      r->data[i] = a->data[i] - 1;
+   }
+
+   //R and A are not the same instance?
+   if(r != a)
+   {
+      //Copy the remaining words
+      for(; i < m; i++)
+         r->data[i] = a->data[i];
+
+      //Zero the upper part of R
+      for(; i < r->size; i++)
+         r->data[i] = 0;
+   }
+
+end:
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Left shift operation
+ * @param[in,out] r The multiple precision integer to be shifted to the left
+ * @param[in] n The number of bits to shift
+ * @return Error code
+ **/
+
+error_t mpiShiftLeft(Mpi *r, uint_t n)
+{
+   error_t error;
+   uint_t i;
+
+   //Number of 32-bit words to shift
+   uint_t n1 = n / (MPI_INT_SIZE * 8);
+   //Number of bits to shift
+   uint_t n2 = n % (MPI_INT_SIZE * 8);
+
+   //Check parameters
+   if(!r->size || !n)
+      return NO_ERROR;
+
+   //Increase the size of the multiple-precision number
+   error = mpiGrow(r, r->size + (n + 31) / 32);
+   //Check return code
+   if(error)
+      return error;
+
+   //First, shift words
+   if(n1 > 0)
+   {
+      //Process the most significant words
+      for(i = r->size - 1; i >= n1; i--)
+         r->data[i] = r->data[i - n1];
+      //Fill the rest with zeroes
+      for(i = 0; i < n1; i++)
+         r->data[i] = 0;
+   }
+   //Then shift bits
+   if(n2 > 0)
+   {
+      //Process the most significant words
+      for(i = r->size - 1; i >= 1; i--)
+         r->data[i] = (r->data[i] << n2) | (r->data[i - 1] >> (32 - n2));
+      //The least significant word requires a special handling
+      r->data[0] <<= n2;
+   }
+
+   //Shift operation is complete
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Right shift operation
+ * @param[in,out] r The multiple precision integer to be shifted to the right
+ * @param[in] n The number of bits to shift
+ * @return Error code
+ **/
+
+error_t mpiShiftRight(Mpi *r, uint_t n)
+{
+   uint_t i;
+   uint_t m;
+
+   //Number of 32-bit words to shift
+   uint_t n1 = n / (MPI_INT_SIZE * 8);
+   //Number of bits to shift
+   uint_t n2 = n % (MPI_INT_SIZE * 8);
+
+   //Check parameters
+   if(n1 >= r->size)
+   {
+      memset(r->data, 0, r->size * MPI_INT_SIZE);
+      return NO_ERROR;
+   }
+
+   //First, shift words
+   if(n1 > 0)
+   {
+      //Process the least significant words
+      for(m = r->size - n1, i = 0; i < m; i++)
+         r->data[i] = r->data[i + n1];
+      //Fill the rest with zeroes
+      for(i = m; i < r->size; i++)
+         r->data[i] = 0;
+   }
+   //Then shift bits
+   if(n2 > 0)
+   {
+      //Process the least significant words
+      for(m = r->size - n1 - 1, i = 0; i < m; i++)
+         r->data[i] = (r->data[i] >> n2) | (r->data[i + 1] << (32 - n2));
+      //The most significant word requires a special handling
+      r->data[m] >>= n2;
+   }
+
+   //Shift operation is complete
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Multiple precision multiplication
+ * @param[out] r Resulting integer R = A * B
+ * @param[in] a First operand A
+ * @param[in] b Second operand B
+ * @return Error code
+ **/
+
+error_t mpiMul(Mpi *r, const Mpi *a, const Mpi *b)
+{
+   error_t error;
+   int_t i;
+   int_t m;
+   int_t n;
+   Mpi ta;
+   Mpi tb;
+
+   //Initialize multiple precision integers
+   mpiInit(&ta);
+   mpiInit(&tb);
+
+   //R and A are the same instance?
+   if(r == a)
+   {
+      //Copy A to TA
+      MPI_CHECK(mpiCopy(&ta, a));
+      //Use TA instead of A
+      a = &ta;
+   }
+
+   //R and B are the same instance?
+   if(r == b)
+   {
+      //Copy B to TB
+      MPI_CHECK(mpiCopy(&tb, b));
+      //Use TB instead of B
+      b = &tb;
+   }
+
+   //Determine the actual length of A and B
+   m = mpiGetLength(a);
+   n = mpiGetLength(b);
+
+   //Adjust the size of R
+   MPI_CHECK(mpiGrow(r, m + n));
+   //Set the sign of R
+   r->sign = (a->sign == b->sign) ? 1 : -1;
+
+   //Clear the contents of the destination integer
+   memset(r->data, 0, r->size * MPI_INT_SIZE);
+
+   //Perform multiplication
+   if(m < n)
+   {
+      for(i = 0; i < m; i++)
+         mpiMulAccCore(&r->data[i], b->data, n, a->data[i]);
+   }
+   else
+   {
+      for(i = 0; i < n; i++)
+         mpiMulAccCore(&r->data[i], a->data, m, b->data[i]);
+   }
+
+end:
+   //Release multiple precision integers
+   mpiFree(&ta);
+   mpiFree(&tb);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Multiply a multiple precision integer by an integer
+ * @param[out] r Resulting integer R = A * B
+ * @param[in] a First operand A
+ * @param[in] b Second operand B
+ * @return Error code
+ **/
+
+error_t mpiMulInt(Mpi *r, const Mpi *a, int_t b)
+{
+   uint_t value;
+   Mpi t;
+
+   //Convert the second operand to a multiple precision integer
+   value = (b >= 0) ? b : -b;
+   t.sign = (b >= 0) ? 1 : -1;
+   t.size = 1;
+   t.data = &value;
+
+   //Perform multiplication
+   return mpiMul(r, a, &t);
+}
+
+
+/**
+ * @brief Multiple precision division
+ * @param[out] q The quotient Q = A / B
+ * @param[out] r The remainder R = A mod B
+ * @param[in] a The dividend A
+ * @param[in] b The divisor B
+ * @return Error code
+ **/
+
+error_t mpiDiv(Mpi *q, Mpi *r, const Mpi *a, const Mpi *b)
+{
+   error_t error;
+   uint_t m;
+   uint_t n;
+   Mpi c;
+   Mpi d;
+   Mpi e;
+
+   //Check whether the divisor is equal to zero
+   if(!mpiCompInt(b, 0))
+      return ERROR_INVALID_PARAMETER;
+
+   //Initialize multiple precision integers
+   mpiInit(&c);
+   mpiInit(&d);
+   mpiInit(&e);
+
+   MPI_CHECK(mpiCopy(&c, a));
+   MPI_CHECK(mpiCopy(&d, b));
+   MPI_CHECK(mpiSetValue(&e, 0));
+
+   m = mpiGetBitLength(&c);
+   n = mpiGetBitLength(&d);
+
+   if(m > n)
+      MPI_CHECK(mpiShiftLeft(&d, m - n));
+
+   while(n++ <= m)
+   {
+      MPI_CHECK(mpiShiftLeft(&e, 1));
+
+      if(mpiComp(&c, &d) >= 0)
+      {
+         MPI_CHECK(mpiSetBitValue(&e, 0, 1));
+         MPI_CHECK(mpiSub(&c, &c, &d));
+      }
+
+      MPI_CHECK(mpiShiftRight(&d, 1));
+   }
+
+   if(q != NULL)
+      MPI_CHECK(mpiCopy(q, &e));
+
+   if(r != NULL)
+      MPI_CHECK(mpiCopy(r, &c));
+
+end:
+   //Release previously allocated memory
+   mpiFree(&c);
+   mpiFree(&d);
+   mpiFree(&e);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Divide a multiple precision integer by an integer
+ * @param[out] q The quotient Q = A / B
+ * @param[out] r The remainder R = A mod B
+ * @param[in] a The dividend A
+ * @param[in] b The divisor B
+ * @return Error code
+ **/
+
+error_t mpiDivInt(Mpi *q, Mpi *r, const Mpi *a, int_t b)
+{
+   uint_t value;
+   Mpi t;
+
+   //Convert the divisor to a multiple precision integer
+   value = (b >= 0) ? b : -b;
+   t.sign = (b >= 0) ? 1 : -1;
+   t.size = 1;
+   t.data = &value;
+
+   //Perform division
+   return mpiDiv(q, r, a, &t);
+}
+
+
+/**
+ * @brief Modulo operation
+ * @param[out] r Resulting integer R = A mod P
+ * @param[in] a The multiple precision integer to be reduced
+ * @param[in] p The modulus P
+ * @return Error code
+ **/
+
+error_t mpiMod(Mpi *r, const Mpi *a, const Mpi *p)
+{
+   error_t error;
+   int_t sign;
+   uint_t m;
+   uint_t n;
+   Mpi c;
+
+   //Make sure the modulus is positive
+   if(mpiCompInt(p, 0) <= 0)
+      return ERROR_INVALID_PARAMETER;
+
+   //Initialize multiple precision integer
+   mpiInit(&c);
+
+   //Save the sign of A
+   sign = a->sign;
+   //Determine the actual length of A
+   m = mpiGetBitLength(a);
+   //Determine the actual length of P
+   n = mpiGetBitLength(p);
+
+   //Let R = A
+   MPI_CHECK(mpiCopy(r, a));
+
+   if(m >= n)
+   {
+      MPI_CHECK(mpiCopy(&c, p));
+      MPI_CHECK(mpiShiftLeft(&c, m - n));
+
+      while(mpiCompAbs(r, p) >= 0)
+      {
+         if(mpiCompAbs(r, &c) >= 0)
+         {
+            MPI_CHECK(mpiSubAbs(r, r, &c));
+         }
+
+         MPI_CHECK(mpiShiftRight(&c, 1));
+      }
+   }
+
+   if(sign < 0)
+   {
+      MPI_CHECK(mpiSubAbs(r, p, r));
+   }
+
+end:
+   //Release previously allocated memory
+   mpiFree(&c);
+
+   //Return status code
+   return error;
+}
+
+
+
+/**
+ * @brief Modular addition
+ * @param[out] r Resulting integer R = A + B mod P
+ * @param[in] a The first operand A
+ * @param[in] b The second operand B
+ * @param[in] p The modulus P
+ * @return Error code
+ **/
+
+error_t mpiAddMod(Mpi *r, const Mpi *a, const Mpi *b, const Mpi *p)
+{
+   error_t error;
+
+   //Perform modular addition
+   MPI_CHECK(mpiAdd(r, a, b));
+   MPI_CHECK(mpiMod(r, r, p));
+
+end:
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Modular subtraction
+ * @param[out] r Resulting integer R = A - B mod P
+ * @param[in] a The first operand A
+ * @param[in] b The second operand B
+ * @param[in] p The modulus P
+ * @return Error code
+ **/
+
+error_t mpiSubMod(Mpi *r, const Mpi *a, const Mpi *b, const Mpi *p)
+{
+   error_t error;
+
+   //Perform modular subtraction
+   MPI_CHECK(mpiSub(r, a, b));
+   MPI_CHECK(mpiMod(r, r, p));
+
+end:
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Modular multiplication
+ * @param[out] r Resulting integer R = A * B mod P
+ * @param[in] a The first operand A
+ * @param[in] b The second operand B
+ * @param[in] p The modulus P
+ * @return Error code
+ **/
+
+error_t mpiMulMod(Mpi *r, const Mpi *a, const Mpi *b, const Mpi *p)
+{
+   error_t error;
+
+   //Perform modular multiplication
+   MPI_CHECK(mpiMul(r, a, b));
+   MPI_CHECK(mpiMod(r, r, p));
+
+end:
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Modular inverse
+ * @param[out] r Resulting integer R = A^-1 mod P
+ * @param[in] a The multiple precision integer A
+ * @param[in] p The modulus P
+ * @return Error code
+ **/
+
+error_t mpiInvMod(Mpi *r, const Mpi *a, const Mpi *p)
+{
+   error_t error;
+   Mpi b;
+   Mpi c;
+   Mpi q0;
+   Mpi r0;
+   Mpi t;
+   Mpi u;
+   Mpi v;
+
+   //Initialize multiple precision integers
+   mpiInit(&b);
+   mpiInit(&c);
+   mpiInit(&q0);
+   mpiInit(&r0);
+   mpiInit(&t);
+   mpiInit(&u);
+   mpiInit(&v);
+
+   MPI_CHECK(mpiCopy(&b, p));
+   MPI_CHECK(mpiCopy(&c, a));
+   MPI_CHECK(mpiSetValue(&u, 0));
+   MPI_CHECK(mpiSetValue(&v, 1));
+
+   while(mpiCompInt(&c, 0) > 0)
+   {
+      MPI_CHECK(mpiDiv(&q0, &r0, &b, &c));
+
+      MPI_CHECK(mpiCopy(&b, &c));
+      MPI_CHECK(mpiCopy(&c, &r0));
+
+      MPI_CHECK(mpiCopy(&t, &v));
+      MPI_CHECK(mpiMul(&q0, &q0, &v));
+      MPI_CHECK(mpiSub(&v, &u, &q0));
+      MPI_CHECK(mpiCopy(&u, &t));
+   }
+
+   if(mpiCompInt(&b, 1))
+   {
+      MPI_CHECK(ERROR_FAILURE);
+   }
+
+   if(mpiCompInt(&u, 0) > 0)
+   {
+      MPI_CHECK(mpiCopy(r, &u));
+   }
+   else
+   {
+      MPI_CHECK(mpiAdd(r, &u, p));
+   }
+
+end:
+   //Release previously allocated memory
+   mpiFree(&b);
+   mpiFree(&c);
+   mpiFree(&q0);
+   mpiFree(&r0);
+   mpiFree(&t);
+   mpiFree(&u);
+   mpiFree(&v);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Modular exponentiation
+ * @param[out] r Resulting integer R = A ^ E mod P
+ * @param[in] a Pointer to a multiple precision integer
+ * @param[in] e Exponent
+ * @param[in] p Modulus
+ * @return Error code
+ **/
+
+error_t mpiExpMod(Mpi *r, const Mpi *a, const Mpi *e, const Mpi *p)
+{
+   error_t error;
+   int_t i;
+   int_t j;
+   int_t n;
+   uint_t d;
+   uint_t k;
+   uint_t u;
+   Mpi b;
+   Mpi c2;
+   Mpi t;
+   Mpi s[8];
+
+   //Initialize multiple precision integers
+   mpiInit(&b);
+   mpiInit(&c2);
+   mpiInit(&t);
+
+   //Initialize precomputed values
+   for(i = 0; i < arraysize(s); i++)
+      mpiInit(&s[i]);
+
+   //Very small exponents are often selected with low Hamming weight.
+   //The sliding window mechanism should be disabled in that case
+   d = (mpiGetBitLength(e) <= 32) ? 1 : 4;
+
+   //Even modulus?
+   if(mpiIsEven(p))
+   {
+      //Let B = A^2
+      MPI_CHECK(mpiMulMod(&b, a, a, p));
+      //Let S[0] = A
+      MPI_CHECK(mpiCopy(&s[0], a));
+
+      //Precompute S[i] = A^(2 * i + 1)
+      for(i = 1; i < (1 << (d - 1)); i++)
+      {
+         MPI_CHECK(mpiMulMod(&s[i], &s[i - 1], &b, p));
+      }
+
+      //Let R = 1
+      MPI_CHECK(mpiSetValue(r, 1));
+
+      //The exponent is processed in a right-to-left fashion
+      i = mpiGetBitLength(e) - 1;
+
+      //Perform sliding window exponentiation
+      while(i >= 0)
+      {
+         //The sliding window exponentiation algorithm decomposes E
+         //into zero and nonzero windows
+         if(!mpiGetBitValue(e, i))
+         {
+            //Compute R = R^2
+            MPI_CHECK(mpiMulMod(r, r, r, p));
+            //Next bit to be processed
+            i--;
+         }
+         else
+         {
+            //Find the longest window
+            n = MAX(i - d + 1, 0);
+
+            //The least significant bit of the window must be equal to 1
+            while(!mpiGetBitValue(e, n)) n++;
+
+            //The algorithm processes more than one bit per iteration
+            for(u = 0, j = i; j >= n; j--)
+            {
+               //Compute R = R^2
+               MPI_CHECK(mpiMulMod(r, r, r, p));
+               //Compute the relevant index to be used in the precomputed table
+               u = (u << 1) | mpiGetBitValue(e, j);
+            }
+
+            //Perform a single multiplication per iteration
+            MPI_CHECK(mpiMulMod(r, r, &s[u >> 1], p));
+            //Next bit to be processed
+            i = n - 1;
+         }
+      }
+   }
+   else
+   {
+      //Compute the smaller C = (2^32)^k such as C > P
+      k = mpiGetLength(p);
+
+      //Compute C^2 mod P
+      MPI_CHECK(mpiSetValue(&c2, 1));
+      MPI_CHECK(mpiShiftLeft(&c2, 2 * k * (MPI_INT_SIZE * 8)));
+      MPI_CHECK(mpiMod(&c2, &c2, p));
+
+      //Let B = A * C mod P
+      if(mpiComp(a, p) >= 0)
+      {
+         MPI_CHECK(mpiMod(&b, a, p));
+         MPI_CHECK(mpiMontgomeryMul(&b, &b, &c2, k, p, &t));
+      }
+      else
+      {
+         MPI_CHECK(mpiMontgomeryMul(&b, a, &c2, k, p, &t));
+      }
+
+      //Let R = B^2 * C^-1 mod P
+      MPI_CHECK(mpiMontgomeryMul(r, &b, &b, k, p, &t));
+      //Let S[0] = B
+      MPI_CHECK(mpiCopy(&s[0], &b));
+
+      //Precompute S[i] = B^(2 * i + 1) * C^-1 mod P
+      for(i = 1; i < (1 << (d - 1)); i++)
+      {
+         MPI_CHECK(mpiMontgomeryMul(&s[i], &s[i - 1], r, k, p, &t));
+      }
+
+      //Let R = C mod P
+      MPI_CHECK(mpiCopy(r, &c2));
+      MPI_CHECK(mpiMontgomeryRed(r, r, k, p, &t));
+
+      //The exponent is processed in a right-to-left fashion
+      i = mpiGetBitLength(e) - 1;
+
+      //Perform sliding window exponentiation
+      while(i >= 0)
+      {
+         //The sliding window exponentiation algorithm decomposes E
+         //into zero and nonzero windows
+         if(!mpiGetBitValue(e, i))
+         {
+            //Compute R = R^2 * C^-1 mod P
+            MPI_CHECK(mpiMontgomeryMul(r, r, r, k, p, &t));
+            //Next bit to be processed
+            i--;
+         }
+         else
+         {
+            //Find the longest window
+            n = MAX(i - d + 1, 0);
+
+            //The least significant bit of the window must be equal to 1
+            while(!mpiGetBitValue(e, n)) n++;
+
+            //The algorithm processes more than one bit per iteration
+            for(u = 0, j = i; j >= n; j--)
+            {
+               //Compute R = R^2 * C^-1 mod P
+               MPI_CHECK(mpiMontgomeryMul(r, r, r, k, p, &t));
+               //Compute the relevant index to be used in the precomputed table
+               u = (u << 1) | mpiGetBitValue(e, j);
+            }
+
+            //Compute R = R * T[u/2] * C^-1 mod P
+            MPI_CHECK(mpiMontgomeryMul(r, r, &s[u >> 1], k, p, &t));
+            //Next bit to be processed
+            i = n - 1;
+         }
+      }
+
+      //Compute R = R * C^-1 mod P
+      MPI_CHECK(mpiMontgomeryRed(r, r, k, p, &t));
+   }
+
+end:
+   //Release multiple precision integers
+   mpiFree(&b);
+   mpiFree(&c2);
+   mpiFree(&t);
+
+   //Release precomputed values
+   for(i = 0; i < arraysize(s); i++)
+      mpiFree(&s[i]);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Montgomery multiplication
+ * @param[out] r Resulting integer R = A * B / 2^k mod P
+ * @param[in] a An integer A such as 0 <= A < 2^k
+ * @param[in] b An integer B such as 0 <= B < 2^k
+ * @param[in] k An integer k such as P < 2^k
+ * @param[in] p Modulus P
+ * @param[in] t An preallocated integer T (for internal operation)
+ * @return Error code
+ **/
+
+error_t mpiMontgomeryMul(Mpi *r, const Mpi *a, const Mpi *b, uint_t k, const Mpi *p, Mpi *t)
+{
+   error_t error;
+   uint_t i;
+   uint_t m;
+   uint_t n;
+   uint_t q;
+
+   //Use Newton's method to compute the inverse of P[0] mod 2^32
+   for(m = 2 - p->data[0], i = 0; i < 4; i++)
+      m = m * (2 - m * p->data[0]);
+
+   //Precompute -1/P[0] mod 2^32;
+   m = ~m + 1;
+
+   //We assume that B is always less than 2^k
+   n = MIN(b->size, k);
+
+   //Make sure T is large enough
+   MPI_CHECK(mpiGrow(t, 2 * k + 1));
+   //Let T = 0
+   MPI_CHECK(mpiSetValue(t, 0));
+
+   //Perform Montgomery multiplication
+   for(i = 0; i < k; i++)
+   {
+      //Check current index
+      if(i < a->size)
+      {
+         //Compute q = ((T[i] + A[i] * B[0]) * m) mod 2^32
+         q = (t->data[i] + a->data[i] * b->data[0]) * m;
+         //Compute T = T + A[i] * B
+         mpiMulAccCore(t->data + i, b->data, n, a->data[i]);
+      }
+      else
+      {
+         //Compute q = (T[i] * m) mod 2^32
+         q = t->data[i] * m;
+      }
+
+      //Compute T = T + q * P
+      mpiMulAccCore(t->data + i, p->data, k, q);
+   }
+
+   //Compute R = T / 2^(32 * k)
+   MPI_CHECK(mpiShiftRight(t, k * (MPI_INT_SIZE * 8)));
+   MPI_CHECK(mpiCopy(r, t));
+
+   //A final subtraction is required
+   if(mpiComp(r, p) >= 0)
+   {
+      MPI_CHECK(mpiSub(r, r, p));
+   }
+
+end:
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Montgomery reduction
+ * @param[out] r Resulting integer R = A / 2^k mod P
+ * @param[in] a An integer A such as 0 <= A < 2^k
+ * @param[in] k An integer k such as P < 2^k
+ * @param[in] p Modulus P
+ * @param[in] t An preallocated integer T (for internal operation)
+ * @return Error code
+ **/
+
+error_t mpiMontgomeryRed(Mpi *r, const Mpi *a, uint_t k, const Mpi *p, Mpi *t)
+{
+   uint_t value;
+   Mpi b;
+
+   //Let B = 1
+   value = 1;
+   b.sign = 1;
+   b.size = 1;
+   b.data = &value;
+
+   //Compute R = A / 2^k mod P
+   return mpiMontgomeryMul(r, a, &b, k, p, t);
+}
+
+
+#if (MPI_ASM_SUPPORT == DISABLED)
+
+/**
+ * @brief Multiply-accumulate operation
+ * @param[out] r Resulting integer
+ * @param[in] a First operand A
+ * @param[in] m Size of A in words
+ * @param[in] b Second operand B
+ **/
+
+void mpiMulAccCore(uint_t *r, const uint_t *a, int_t m, const uint_t b)
+{
+   int_t i;
+   uint32_t c;
+   uint32_t u;
+   uint32_t v;
+   uint64_t p;
+
+   //Clear variables
+   c = 0;
+   u = 0;
+   v = 0;
+
+   //Perform multiplication
+   for(i = 0; i < m; i++)
+   {
+      p = (uint64_t) a[i] * b;
+      u = (uint32_t) p;
+      v = (uint32_t) (p >> 32);
+
+      u += c;
+      if(u < c) v++;
+
+      u += r[i];
+      if(u < r[i]) v++;
+
+      r[i] = u;
+      c = v;
+   }
+
+   //Propagate carry
+   for(; c != 0; i++)
+   {
+      r[i] += c;
+      c = (r[i] < c);
+   }
+}
+
+#endif
+
+
+/**
+ * @brief Display the contents of a multiple precision integer
+ * @param[in] stream Pointer to a FILE object that identifies an output stream
+ * @param[in] prepend String to prepend to the left of each line
+ * @param[in] a Pointer to a multiple precision integer
+ **/
+
+void mpiDump(FILE *stream, const char_t *prepend, const Mpi *a)
+{
+   uint_t i;
+
+   //Process each word
+   for(i = 0; i < a->size; i++)
+   {
+      //Beginning of a new line?
+      if(i == 0 || ((a->size - i - 1) % 8) == 7)
+         fprintf(stream, "%s", prepend);
+
+      //Display current data
+      fprintf(stream, "%08X ", a->data[a->size - 1 - i]);
+
+      //End of current line?
+      if(!((a->size - i - 1) % 8) || i == (a->size - 1))
+         fprintf(stream, "\r\n");
+   }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/mpi.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,117 @@
+/**
+ * @file mpi.h
+ * @brief MPI (Multiple Precision Integer Arithmetic)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MPI_H
+#define _MPI_H
+
+//Dependencies
+#include <stdio.h>
+#include "crypto.h"
+
+//Size of the sub data type
+#define MPI_INT_SIZE sizeof(uint_t)
+
+//Error code checking
+#define MPI_CHECK(f) if((error = f) != NO_ERROR) goto end
+
+//Miscellaneous macros
+#define mpiIsEven(a) !mpiGetBitValue(a, 0)
+#define mpiIsOdd(a) mpiGetBitValue(a, 0)
+
+
+/**
+ * @brief Arbitrary precision integer
+ **/
+
+typedef struct
+{
+   int_t sign;
+   uint_t size;
+   uint_t *data;
+} Mpi;
+
+
+//MPI related functions
+void mpiInit(Mpi *r);
+void mpiFree(Mpi *r);
+
+error_t mpiGrow(Mpi *r, uint_t size);
+
+uint_t mpiGetLength(const Mpi *a);
+uint_t mpiGetByteLength(const Mpi *a);
+uint_t mpiGetBitLength(const Mpi *a);
+
+error_t mpiSetBitValue(Mpi *r, uint_t index, uint_t value);
+uint_t mpiGetBitValue(const Mpi *a, uint_t index);
+
+int_t mpiComp(const Mpi *a, const Mpi *b);
+int_t mpiCompInt(const Mpi *a, int_t b);
+int_t mpiCompAbs(const Mpi *a, const Mpi *b);
+
+error_t mpiCopy(Mpi *r, const Mpi *a);
+error_t mpiSetValue(Mpi *a, int_t b);
+
+error_t mpiRand(Mpi *r, uint_t length, const PrngAlgo *prngAlgo, void *prngContext);
+
+error_t mpiReadRaw(Mpi *r, const uint8_t *data, uint_t length);
+error_t mpiWriteRaw(const Mpi *a, uint8_t *data, uint_t length);
+
+error_t mpiAdd(Mpi *r, const Mpi *a, const Mpi *b);
+error_t mpiAddInt(Mpi *r, const Mpi *a, int_t b);
+
+error_t mpiSub(Mpi *r, const Mpi *a, const Mpi *b);
+error_t mpiSubInt(Mpi *r, const Mpi *a, int_t b);
+
+error_t mpiAddAbs(Mpi *r, const Mpi *a, const Mpi *b);
+error_t mpiSubAbs(Mpi *r, const Mpi *a, const Mpi *b);
+
+error_t mpiShiftLeft(Mpi *r, uint_t n);
+error_t mpiShiftRight(Mpi *r, uint_t n);
+
+error_t mpiMul(Mpi *r, const Mpi *a, const Mpi *b);
+error_t mpiMulInt(Mpi *r, const Mpi *a, int_t b);
+
+error_t mpiDiv(Mpi *q, Mpi *r, const Mpi *a, const Mpi *b);
+error_t mpiDivInt(Mpi *q, Mpi *r, const Mpi *a, int_t b);
+
+error_t mpiMod(Mpi *r, const Mpi *a, const Mpi *p);
+error_t mpiAddMod(Mpi *r, const Mpi *a, const Mpi *b, const Mpi *p);
+error_t mpiSubMod(Mpi *r, const Mpi *a, const Mpi *b, const Mpi *p);
+error_t mpiMulMod(Mpi *r, const Mpi *a, const Mpi *b, const Mpi *p);
+error_t mpiInvMod(Mpi *r, const Mpi *a, const Mpi *p);
+error_t mpiExpMod(Mpi *r, const Mpi *a, const Mpi *e, const Mpi *p);
+
+error_t mpiMontgomeryMul(Mpi *r, const Mpi *a, const Mpi *b, uint_t k, const Mpi *p, Mpi *t);
+error_t mpiMontgomeryRed(Mpi *r, const Mpi *a, uint_t k, const Mpi *p, Mpi *t);
+
+void mpiMulAccCore(uint_t *r, const uint_t *a, int_t m, const uint_t b);
+
+void mpiDump(FILE *stream, const char_t *prepend, const Mpi *a);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/mpi_asm_ccs_cortex_m3.asm	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,92 @@
+; @file mpi_asm_ccs_cortex_m3.s
+; @brief Cortex-M3 specific routines (TI ARM compiler)
+;
+; @section License
+;
+; Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+;
+; This file is part of CycloneCrypto Open.
+;
+; 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.
+;
+; @author Oryx Embedded SARL (www.oryx-embedded.com)
+; @version 1.7.6
+
+;**********
+;* Macros *
+;**********
+
+MUL_ACC_CORE   .macro
+               mov   r5, #0
+               ldr   r6, [r1], #4
+               umlal r4, r5, r6, r3
+               ldr   r6, [r0]
+               adds  r6, r6, r4
+               adc   r4, r5, #0
+               str   r6, [r0], #4
+               .endm
+
+;***********
+;* Exports *
+;***********
+
+               .global mpiMulAccCore
+
+               .thumb
+               .text
+
+;*********************************
+;* Multiply-accumulate operation *
+;*********************************
+
+mpiMulAccCore:
+               push  {r4-r6}
+               mov   r4, #0
+               cmp   r2, #8
+               blo   next1
+loop1:
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               sub   r2, r2, #8
+               cmp   r2, #8
+               bhs   loop1
+next1:
+               cmp   r2, #1
+               blo   next2
+loop2:
+               MUL_ACC_CORE
+               subs  r2, r2, #1
+               bne   loop2
+next2:
+               cbz   r4, next3
+loop3:
+               ldr   r6, [r0]
+               adds  r6, r6, r4
+               str   r6, [r0], #4
+               mov   r4, #0
+               adcs  r4, r4, #0
+               bne   loop3
+next3:
+               pop   {r4-r6}
+               bx    r14
+
+               .end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/mpi_asm_gcc_cortex_a9.S	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,128 @@
+/**
+ * @file mpi_asm_gcc_cortex_a9.S
+ * @brief Cortex-A9 specific routines (GCC compiler)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+/*
+ * Macros
+ */
+
+.macro         MUL_ACC_CORE
+               mov   r5, #0
+               ldr   r6, [r1], #4
+               umlal r4, r5, r6, r3
+               ldr   r6, [r0]
+               adds  r6, r6, r4
+               adc   r4, r5, #0
+               str   r6, [r0], #4
+.endm
+
+/*
+ * Exports
+ */
+
+.global mpiMulAccCore
+
+.syntax unified
+.cpu cortex-a9
+.arm
+.text
+
+/*
+ * Multiply-accumulate operation
+ */
+
+mpiMulAccCore:
+               push  {r4-r6}
+               mov   r4, #0
+               cmp   r2, #16
+               blo   next1
+loop1:
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               sub   r2, r2, #16
+               cmp   r2, #16
+               bhs   loop1
+next1:
+               cmp   r2, #8
+               blo   next2
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               sub   r2, r2, #8
+next2:
+               cmp   r2, #4
+               blo   next3
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               sub   r2, r2, #4
+next3:
+               cmp   r2, #2
+               blo   next4
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               sub   r2, r2, #2
+next4:
+               cmp   r2, #1
+               blo   next5
+               MUL_ACC_CORE
+next5:
+               cmp   r4, #0
+               beq   next6
+loop2:
+               ldr   r6, [r0]
+               adds  r6, r6, r4
+               str   r6, [r0], #4
+               mov   r4, #0
+               adcs  r4, r4, #0
+               bne   loop2
+next6:
+               pop   {r4-r6}
+               bx    r14
+
+.end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/mpi_asm_gcc_cortex_m3.S	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,96 @@
+/**
+ * @file mpi_asm_gcc_cortex_m3.S
+ * @brief Cortex-M3 specific routines (GCC compiler)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+/*
+ * Macros
+ */
+
+.macro         MUL_ACC_CORE
+               mov   r5, #0
+               ldr   r6, [r1], #4
+               umlal r4, r5, r6, r3
+               ldr   r6, [r0]
+               adds  r6, r6, r4
+               adc   r4, r5, #0
+               str   r6, [r0], #4
+.endm
+
+/*
+ * Exports
+ */
+
+.global mpiMulAccCore
+
+.syntax unified
+.cpu cortex-m3
+.thumb
+.text
+
+/*
+ * Multiply-accumulate operation
+ */
+
+mpiMulAccCore:
+               push  {r4-r6}
+               mov   r4, #0
+               cmp   r2, #8
+               blo   next1
+loop1:
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               sub   r2, r2, #8
+               cmp   r2, #8
+               bhs   loop1
+next1:
+               cmp   r2, #1
+               blo   next2
+loop2:
+               MUL_ACC_CORE
+               subs  r2, r2, #1
+               bne   loop2
+next2:
+               cbz   r4, next3
+loop3:
+               ldr   r6, [r0]
+               adds  r6, r6, r4
+               str   r6, [r0], #4
+               mov   r4, #0
+               adcs  r4, r4, #0
+               bne   loop3
+next3:
+               pop   {r4-r6}
+               bx    r14
+
+.end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/mpi_asm_gcc_mips.S	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,143 @@
+/**
+ * @file mpi_asm_gcc_mips.S
+ * @brief MIPS specific routines (GCC compiler)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+/*
+ * Macros
+ */
+
+.macro         MUL_ACC_CORE
+               lw    $t3, 0($a1)
+               addiu $a1, $a1, 4
+               multu $t3, $a3
+               mflo  $t1
+               mfhi  $t2
+               lw    $t3, 0($a0)
+               addu  $t3, $t3, $t0
+               sltu  $t0, $t3, $t0
+               addu  $t3, $t3, $t1
+               sltu  $t1, $t3, $t1
+               addu  $t0, $t0, $t1
+               addu  $t0, $t0, $t2
+               sw    $t3, 0($a0)
+               addiu $a0, $a0, 4
+.endm
+
+/*
+ * Exports
+ */
+
+.global mpiMulAccCore
+
+.set nomips16
+.set noreorder
+.set noat
+.text
+
+/*
+ * Multiply-accumulate operation
+ */
+
+.ent mpiMulAccCore
+
+mpiMulAccCore:
+               li    $t0, 0
+               sltiu $t1, $a2, 16
+               bne   $t1, $zero, next1
+               nop
+loop1:
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               addiu $a2, $a2, -16
+               sltiu $t1, $a2, 16
+               beq   $t1, $zero, loop1
+               nop
+next1:
+               sltiu $t1, $a2, 8
+               bne   $t1, $zero, next2
+               nop
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               addiu $a2, $a2, -8
+next2:
+               sltiu $t1, $a2, 4
+               bne   $t1, $zero, next3
+               nop
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               addiu $a2, $a2, -4
+next3:
+               sltiu $t1, $a2, 2
+               bne   $t1, $zero, next4
+               nop
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               addiu $a2, $a2, -2
+next4:
+               sltiu $t1, $a2, 1
+               bne   $t1, $zero, next5
+               nop
+               MUL_ACC_CORE
+next5:
+               beq   $t0, $zero, next6
+               nop
+loop2:
+               lw    $t3, 0($a0)
+               addu  $t3, $t3, $t0
+               sltu  $t0, $t3, $t0
+               sw    $t3, 0($a0)
+               addiu $a0, $a0, 4
+               bne   $t0, $zero, loop2
+               nop
+next6:
+               jr    $ra
+               nop
+
+.end mpiMulAccCore
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/mpi_asm_iar_cortex_m3.s	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,92 @@
+; @file mpi_asm_iar_cortex_m3.s
+; @brief Cortex-M3 specific routines (IAR EWARM compiler)
+;
+; @section License
+;
+; Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+;
+; This file is part of CycloneCrypto Open.
+;
+; 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.
+;
+; @author Oryx Embedded SARL (www.oryx-embedded.com)
+; @version 1.7.6
+
+;**********
+;* Macros *
+;**********
+
+MUL_ACC_CORE   macro
+               mov   r5, #0
+               ldr   r6, [r1], #4
+               umlal r4, r5, r6, r3
+               ldr   r6, [r0]
+               adds  r6, r6, r4
+               adc   r4, r5, #0
+               str   r6, [r0], #4
+               endm
+
+;***********
+;* Exports *
+;***********
+
+               public mpiMulAccCore
+
+               rseg  CODE:CODE(2)
+               thumb
+
+;*********************************
+;* Multiply-accumulate operation *
+;*********************************
+
+mpiMulAccCore
+               push  {r4-r6}
+               mov   r4, #0
+               cmp   r2, #8
+               blo   next1
+loop1
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               sub   r2, r2, #8
+               cmp   r2, #8
+               bhs   loop1
+next1
+               cmp   r2, #1
+               blo   next2
+loop2
+               MUL_ACC_CORE
+               subs  r2, r2, #1
+               bne   loop2
+next2
+               cbz   r4, next3
+loop3
+               ldr   r6, [r0]
+               adds  r6, r6, r4
+               str   r6, [r0], #4
+               mov   r4, #0
+               adcs  r4, r4, #0
+               bne   loop3
+next3
+               pop   {r4-r6}
+               bx    r14
+
+               end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/mpi_asm_keil_arm7.s	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,130 @@
+; @file mpi_asm_keil_arm7.s
+; @brief ARM7 specific routines (Keil MDK-ARM compiler)
+;
+; @section License
+;
+; Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+;
+; This file is part of CycloneCrypto Open.
+;
+; 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.
+;
+; @author Oryx Embedded SARL (www.oryx-embedded.com)
+; @version 1.7.6
+
+;**********
+;* Macros *
+;**********
+
+               macro
+               MUL_ACC_CORE
+               mov   r5, #0
+               ldr   r6, [r1], #4
+               umlal r4, r5, r6, r3
+               ldr   r6, [r0]
+               adds  r6, r6, r4
+               adc   r4, r5, #0
+               str   r6, [r0], #4
+               mend
+
+;***********
+;* Exports *
+;***********
+
+               export mpiMulAccCore
+
+               preserve8
+               arm
+
+               area  |.text|, code, readonly
+
+;*********************************
+;* Multiply-accumulate operation *
+;*********************************
+
+               align
+
+mpiMulAccCore  proc
+               push  {r4-r6}
+               mov   r4, #0
+               cmp   r2, #16
+               blo   next1
+loop1
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               sub   r2, r2, #16
+               cmp   r2, #16
+               bhs   loop1
+next1
+               cmp   r2, #8
+               blo   next2
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               sub   r2, r2, #8
+next2
+               cmp   r2, #4
+               blo   next3
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               sub   r2, r2, #4
+next3
+               cmp   r2, #2
+               blo   next4
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               sub   r2, r2, #2
+next4
+               cmp   r2, #1
+               blo   next5
+               MUL_ACC_CORE
+next5
+               cmp   r4, #0
+               beq   next6
+loop2
+               ldr   r6, [r0]
+               adds  r6, r6, r4
+               str   r6, [r0], #4
+               mov   r4, #0
+               adcs  r4, r4, #0
+               bne   loop2
+next6
+               pop   {r4-r6}
+               bx    r14
+               endp
+
+               end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/mpi_asm_keil_cortex_m3.s	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,98 @@
+; @file mpi_asm_keil_cortex_m3.s
+; @brief Cortex-M3 specific routines (Keil MDK-ARM compiler)
+;
+; @section License
+;
+; Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+;
+; This file is part of CycloneCrypto Open.
+;
+; 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.
+;
+; @author Oryx Embedded SARL (www.oryx-embedded.com)
+; @version 1.7.6
+
+;**********
+;* Macros *
+;**********
+
+               macro
+               MUL_ACC_CORE
+               mov   r5, #0
+               ldr   r6, [r1], #4
+               umlal r4, r5, r6, r3
+               ldr   r6, [r0]
+               adds  r6, r6, r4
+               adc   r4, r5, #0
+               str   r6, [r0], #4
+               mend
+
+;***********
+;* Exports *
+;***********
+
+               export mpiMulAccCore
+
+               preserve8
+               thumb
+
+               area  |.text|, code, readonly
+
+;*********************************
+;* Multiply-accumulate operation *
+;*********************************
+
+               align
+
+mpiMulAccCore  proc
+               push  {r4-r6}
+               mov   r4, #0
+               cmp   r2, #8
+               blo   next1
+loop1
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               MUL_ACC_CORE
+               sub   r2, r2, #8
+               cmp   r2, #8
+               bhs   loop1
+next1
+               cmp   r2, #1
+               blo   next2
+loop2
+               MUL_ACC_CORE
+               subs  r2, r2, #1
+               bne   loop2
+next2
+               cbz   r4, next3
+loop3
+               ldr   r6, [r0]
+               adds  r6, r6, r4
+               str   r6, [r0], #4
+               mov   r4, #0
+               adcs  r4, r4, #0
+               bne   loop3
+next3
+               pop   {r4-r6}
+               bx    r14
+               endp
+
+               end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/oid.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,438 @@
+/**
+ * @file oid.c
+ * @brief OID (Object Identifier)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include <ctype.h>
+#include "crypto.h"
+#include "oid.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (OID_SUPPORT == ENABLED)
+
+
+/**
+* @brief Check whether the specified object identifier is valid
+* @param[in] oid Pointer to the object identifier
+* @param[in] oidLen Length of the OID, in bytes
+* @return Error code
+**/
+
+error_t oidCheck(const uint8_t *oid, size_t oidLen)
+{
+   size_t i;
+   size_t n;
+
+   //Check parameters
+   if(oid == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Check the length of the OID
+   if(oidLen == 0)
+   {
+      //Report an error
+      return ERROR_INVALID_SYNTAX;
+   }
+   else if(oidLen > 1)
+   {
+      //Parse the object identifier
+      for(i = 1, n = 2; i < oidLen; i++)
+      {
+         //Update the total number of nodes
+         if(!(oid[i] & OID_MORE_FLAG))
+            n++;
+
+         //SNMP limits object identifier values to a maximum of 128 nodes
+         if(n > 128)
+            return ERROR_INVALID_SYNTAX;
+      }
+
+      //Ensure that the last sub-identifier is valid
+      if(oid[oidLen - 1] & OID_MORE_FLAG)
+         return ERROR_INVALID_SYNTAX;
+   }
+
+   //The specified OID is valid
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Compare object identifiers
+ * @param[in] oid1 Pointer the first OID
+ * @param[in] oidLen1 Length of the first OID, in bytes
+ * @param[in] oid2 Pointer the second OID
+ * @param[in] oidLen2 Length of the second OID, in bytes
+ * @return Comparison result
+ * @retval 0 Objects identifiers are equal
+ * @retval -1 The first OID lexicographically precedes the second OID
+ * @retval 1 The second OID lexicographically precedes the first OID
+ **/
+
+int_t oidComp(const uint8_t *oid1, size_t oidLen1,
+   const uint8_t *oid2, size_t oidLen2)
+{
+   size_t i;
+
+   //Perform lexicographical comparison
+   for(i = 0; i < oidLen1 && i < oidLen2; i++)
+   {
+      //Compare current byte
+      if(oid1[i] < oid2[i])
+         return -1;
+      else if(oid1[i] > oid2[i])
+         return 1;
+   }
+
+   //Compare length
+   if(oidLen1 < oidLen2)
+      return -1;
+   else if(oidLen1 > oidLen2)
+      return 1;
+
+   //Object identifiers are equal
+   return 0;
+}
+
+
+/**
+ * @brief Encode OID sub-identifier
+ * @param[in] oid Pointer to the object identifier
+ * @param[in] maxOidLen Maximum number of bytes the OID can hold
+ * @param[in,out] pos Offset where to write the sub-identifier
+ * @param[in] value Value of the sub-identifier
+ * @return Error code
+ **/
+
+error_t oidEncodeSubIdentifier(uint8_t *oid,
+   size_t maxOidLen, size_t *pos, uint32_t value)
+{
+   size_t i;
+   size_t n;
+   uint8_t temp[5];
+
+   //Encode the first byte of the sub-identifier
+   temp[0] = value & OID_VALUE_MASK;
+   //Shift the value to the right
+   value >>= 7;
+
+   //Encode the remaining bytes
+   for(n = 1; value != 0; n++)
+   {
+      //Encode current byte
+      temp[n] = OID_MORE_FLAG | (value & OID_VALUE_MASK);
+      //Shift the value to the right
+      value >>= 7;
+   }
+
+   //Sanity check
+   if((*pos + n) > maxOidLen)
+      return ERROR_BUFFER_OVERFLOW;
+
+   //Write the current sub-identifier
+   for(i = 0; i < n; i++)
+      oid[*pos + i] = temp[n - i - 1];
+
+   //Update offset value
+   *pos += n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Decode OID sub-identifier
+ * @param[in] oid Pointer to the object identifier
+ * @param[in] oidLen Length of the OID, in bytes
+ * @param[in,out] pos Offset where to read the sub-identifier
+ * @param[out] value Value of the sub-identifier
+ * @return Error code
+ **/
+
+error_t oidDecodeSubIdentifier(const uint8_t *oid,
+   size_t oidLen, size_t *pos, uint32_t *value)
+{
+   size_t i;
+
+   //Initialize the value of the sub-identifier
+   *value = 0;
+
+   //Read the OID until the last byte of the sub-identifier is found
+   for(i = *pos; i < oidLen; i++)
+   {
+      //Shift the value to the left
+      *value <<= 7;
+      //Update value of the sub-identifier
+      *value |= oid[i] & OID_VALUE_MASK;
+
+      //Bit b8 is set to zero to indicate the last byte
+      if(!(oid[i] & OID_MORE_FLAG))
+      {
+         //Update offset value
+         *pos = i + 1;
+         //Successful processing
+         return NO_ERROR;
+      }
+   }
+
+   //The specified OID is not valid
+   return ERROR_INVALID_SYNTAX;
+}
+
+
+/**
+ * @brief Convert a string representation of an OID to a binary OID
+ * @param[in] str NULL-terminated string representing the OID
+ * @param[out] oid Object identifier
+ * @param[in] maxOidLen Maximum number of bytes the OID can hold
+ * @param[out] oidLen Length of the object identifier
+ * @return Error code
+ **/
+
+error_t oidFromString(const char_t *str,
+   uint8_t *oid, size_t maxOidLen, size_t *oidLen)
+{
+   error_t error;
+   size_t i;
+   size_t j;
+   size_t n;
+   uint32_t value;
+   uint8_t temp[5];
+
+   //Reset the length of the OID
+   *oidLen = 0;
+
+   //Number of nodes
+   i = 0;
+   //Initialize the value of the sub-identifier
+   value = 0;
+
+   //Parse input string
+   while(1)
+   {
+      //Digit found?
+      if(isdigit((uint8_t) *str))
+      {
+         //Update the value of the sub-identifier
+         value = (value * 10) + (*str - '0');
+      }
+      //Separator or end of string found?
+      else if(*str == '.' || *str == '\0')
+      {
+         //First node?
+         if(i == 0)
+         {
+            //Check value
+            if(value > 6)
+            {
+               //The conversion failed
+               error = ERROR_INVALID_SYNTAX;
+               break;
+            }
+
+            //Encode the first sub-identifier
+            temp[0] = value * 40;
+            //Prepare to decode the next node
+            value = 0;
+            //Do not write current sub-identifier yet
+            n = 0;
+         }
+         //Second node?
+         else if(i == 1)
+         {
+            //Check value
+            if(value > 39)
+            {
+               //The conversion failed
+               error = ERROR_INVALID_SYNTAX;
+               break;
+            }
+
+            //Encode the second sub-identifier
+            temp[0] |= value;
+            //Prepare to decode the next node
+            value = 0;
+            //Write the first two sub-identifiers
+            n = 1;
+         }
+         //Remaining nodes?
+         else
+         {
+            //Encode the first byte of the sub-identifier
+            temp[0] = value & OID_VALUE_MASK;
+            //Shift the value to the right
+            value >>= 7;
+
+            //Encode the remaining bytes
+            for(n = 1; value != 0; n++)
+            {
+               //Encode current byte
+               temp[n] = OID_MORE_FLAG | (value & OID_VALUE_MASK);
+               //Shift the value to the right
+               value >>= 7;
+            }
+         }
+
+         //Sanity check
+         if(n > maxOidLen)
+         {
+            //Report an error
+            error = ERROR_BUFFER_OVERFLOW;
+            break;
+         }
+
+         //Write the current sub-identifier
+         for(j = 0; j < n; j++)
+            oid[j] = temp[n - j - 1];
+
+         //Advance write pointer
+         oid += n;
+         *oidLen += n;
+         maxOidLen -= n;
+
+         //Number of sub-identifiers
+         i++;
+
+         //End of string detected?
+         if(*str == '\0')
+         {
+            //The conversion succeeded
+            error = NO_ERROR;
+            break;
+         }
+      }
+      //Invalid character...
+      else
+      {
+         //The conversion failed
+         error = ERROR_INVALID_SYNTAX;
+         break;
+      }
+
+      //Point to the next character
+      str++;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Convert a binary OID to a string representation
+ * @param[in] oid Object identifier
+ * @param[in] oidLen Length of the object identifier, in bytes
+ * @param[out] str NULL-terminated string representing the OID
+ * @param[in] maxStrLen Maximum length of the resulting string
+ * @return Pointer to the formatted string
+ **/
+
+char_t *oidToString(const uint8_t *oid,
+   size_t oidLen, char_t *str, size_t maxStrLen)
+{
+   static char_t buffer[64];
+   size_t i;
+   size_t n;
+   uint32_t value;
+   char_t *p;
+   char_t temp[12];
+
+   //The str parameter is optional
+   if(str == NULL)
+   {
+      //Point to the internal buffer
+      str = buffer;
+      //Maximum length of the resulting string
+      maxStrLen = sizeof(buffer) - 1;
+   }
+
+   //Point the beginning of the string
+   p = str;
+   //Properly terminate the string
+   *p = '\0';
+
+   //Check the length of the OID
+   if(oidLen > 0)
+   {
+      //Convert the first 2 bytes
+      n = sprintf(temp, "%" PRIu8 ".%" PRIu8 "", oid[0] / 40, oid[0] % 40);
+
+      //Sanity check
+      if(n <= maxStrLen)
+      {
+         //Copy the resulting string
+         strcpy(p, temp);
+         //Advance write pointer
+         p += n;
+         maxStrLen -= n;
+      }
+
+      //Initialize the value of the sub-identifier
+      value = 0;
+
+      //Convert the rest of the OID
+      for(i = 1; i < oidLen; i++)
+      {
+         //Shift the value to the left
+         value <<= 7;
+         //Update the current value
+         value |= oid[i] & OID_VALUE_MASK;
+
+         //Bit b8 is set to zero to indicate the last byte
+         if(!(oid[i] & OID_MORE_FLAG))
+         {
+            //Dump current value
+            n = sprintf(temp, ".%" PRIu32, value);
+
+            //Sanity check
+            if(n <= maxStrLen)
+            {
+               //Copy the resulting string
+               strcpy(p, temp);
+               //Advance write pointer
+               p += n;
+               maxStrLen -= n;
+            }
+
+            //Prepare to decode the next value
+            value = 0;
+         }
+      }
+   }
+
+   //Return a pointer to the formatted string
+   return str;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/oid.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,58 @@
+/**
+ * @file oid.h
+ * @brief OID (Object Identifier)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _OID_H
+#define _OID_H
+
+//Dependencies
+#include "crypto.h"
+
+//Mask definition
+#define OID_MORE_FLAG  0x80
+#define OID_VALUE_MASK 0x7F
+
+//OID related functions
+error_t oidCheck(const uint8_t *oid, size_t oidLen);
+
+int_t oidComp(const uint8_t *oid1, size_t oidLen1,
+   const uint8_t *oid2, size_t oidLen2);
+
+error_t oidEncodeSubIdentifier(uint8_t *oid,
+   size_t maxOidLen, size_t *pos, uint32_t value);
+
+error_t oidDecodeSubIdentifier(const uint8_t *oid,
+   size_t oidLen, size_t *pos, uint32_t *value);
+
+error_t oidFromString(const char_t *str,
+   uint8_t *oid, size_t maxOidLen, size_t *oidLen);
+
+char_t *oidToString(const uint8_t *oid,
+   size_t oidLen, char_t *str, size_t maxStrLen);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/pem.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1532 @@
+/**
+ * @file pem.c
+ * @brief PEM (Privacy-Enhanced Mail)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "pem.h"
+#include "asn1.h"
+#include "base64.h"
+#include "mpi.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (PEM_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Decode a PEM file containing Diffie-Hellman parameters
+ * @param[in] input Pointer to the PEM structure
+ * @param[in] length Length of the PEM structure
+ * @param[out] params Diffie-Hellman parameters resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t pemReadDhParameters(const char_t *input, size_t length, DhParameters *params)
+{
+#if (DH_SUPPORT == ENABLED)
+   error_t error;
+   size_t i;
+   size_t j;
+   int_t k;
+   char_t *buffer;
+   const uint8_t *data;
+   Asn1Tag tag;
+
+   //Check parameters
+   if(input == NULL && length != 0)
+      return ERROR_INVALID_PARAMETER;
+   if(params == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Search for the beginning tag
+   k = pemSearchTag(input, length, "-----BEGIN DH PARAMETERS-----", 29);
+   //Failed to find the specified tag?
+   if(k < 0)
+      return ERROR_INVALID_SYNTAX;
+
+   //Advance the pointer over the tag
+   input += k + 29;
+   length -= k + 29;
+
+   //Search for the end tag
+   k = pemSearchTag(input, length, "-----END DH PARAMETERS-----", 27);
+   //Invalid PEM file?
+   if(k <= 0)
+      return ERROR_INVALID_SYNTAX;
+
+   //Length of the PEM structure
+   length = k;
+
+   //Allocate a memory buffer to hold the decoded data
+   buffer = cryptoAllocMem(length);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Copy the contents of the PEM structure
+   memcpy(buffer, input, length);
+
+   //Remove carriage returns and line feeds
+   for(i = 0, j = 0; i < length; i++)
+   {
+      if(buffer[i] != '\r' && buffer[i] != '\n')
+         buffer[j++] = buffer[i];
+   }
+
+   //Start of exception handling block
+   do
+   {
+      //The PEM file is Base64 encoded...
+      error = base64Decode(buffer, j, buffer, &length);
+      //Failed to decode the file?
+      if(error)
+         break;
+
+      //Point to the resulting ASN.1 structure
+      data = (uint8_t *) buffer;
+
+      //Display ASN.1 structure
+      error = asn1DumpObject(data, length, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //The Diffie-Hellman parameters are encapsulated within a sequence
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Point to the first field of the sequence
+      data = tag.value;
+      length = tag.length;
+
+      //Read the prime modulus
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Convert the prime modulus to a multiple precision integer
+      error = mpiReadRaw(&params->p, tag.value, tag.length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Point to the next field
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the generator
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Convert the generator to a multiple precision integer
+      error = mpiReadRaw(&params->g, tag.value, tag.length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Debug message
+      TRACE_DEBUG("Diffie-Hellman parameters:\r\n");
+      TRACE_DEBUG("  Prime modulus:\r\n");
+      TRACE_DEBUG_MPI("    ", &params->p);
+      TRACE_DEBUG("  Generator:\r\n");
+      TRACE_DEBUG_MPI("    ", &params->g);
+
+      //End of exception handling block
+   } while(0);
+
+   //Release previously allocated memory
+   cryptoFreeMem(buffer);
+
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects
+      mpiFree(&params->p);
+      mpiFree(&params->g);
+   }
+
+   //Return status code
+   return error;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Decode a PEM file containing a RSA private key
+ * @param[in] input Pointer to the PEM structure
+ * @param[in] length Length of the PEM structure
+ * @param[out] key RSA private key resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t pemReadRsaPrivateKey(const char_t *input, size_t length, RsaPrivateKey *key)
+{
+#if (RSA_SUPPORT == ENABLED)
+   error_t error;
+   size_t i;
+   size_t j;
+   int_t k;
+   char_t *buffer;
+   const uint8_t *data;
+   Asn1Tag tag;
+
+   //Check parameters
+   if(input == NULL && length != 0)
+      return ERROR_INVALID_PARAMETER;
+   if(key == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Search for the beginning tag
+   k = pemSearchTag(input, length, "-----BEGIN RSA PRIVATE KEY-----", 31);
+   //Failed to find the specified tag?
+   if(k < 0)
+      return ERROR_INVALID_SYNTAX;
+
+   //Advance the pointer over the tag
+   input += k + 31;
+   length -= k + 31;
+
+   //Search for the end tag
+   k = pemSearchTag(input, length, "-----END RSA PRIVATE KEY-----", 29);
+   //Invalid PEM file?
+   if(k <= 0)
+      return ERROR_INVALID_SYNTAX;
+
+   //Length of the PEM structure
+   length = k;
+
+   //Allocate a memory buffer to hold the decoded data
+   buffer = cryptoAllocMem(length);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Copy the contents of the PEM structure
+   memcpy(buffer, input, length);
+
+   //Remove carriage returns and line feeds
+   for(i = 0, j = 0; i < length; i++)
+   {
+      if(buffer[i] != '\r' && buffer[i] != '\n')
+         buffer[j++] = buffer[i];
+   }
+
+   //Start of exception handling block
+   do
+   {
+      //The PEM file is Base64 encoded...
+      error = base64Decode(buffer, j, buffer, &length);
+      //Failed to decode the file?
+      if(error)
+         break;
+
+      //Point to the resulting ASN.1 structure
+      data = (uint8_t *) buffer;
+
+      //Display ASN.1 structure
+      error = asn1DumpObject(data, length, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //The RSA private key is encapsulated within a sequence
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Point to the first field of the sequence
+      data = tag.value;
+      length = tag.length;
+
+      //Read the version
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Skip the version field
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the modulus
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Convert the modulus to a multiple precision integer
+      error = mpiReadRaw(&key->n, tag.value, tag.length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Point to the next field
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the public exponent
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Convert the public exponent to a multiple precision integer
+      error = mpiReadRaw(&key->e, tag.value, tag.length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Point to the next field
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the private exponent
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Convert the private exponent to a multiple precision integer
+      error = mpiReadRaw(&key->d, tag.value, tag.length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Point to the next field
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the first factor
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Convert the first factor to a multiple precision integer
+      error = mpiReadRaw(&key->p, tag.value, tag.length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Point to the next field
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the second factor
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Convert the second factor to a multiple precision integer
+      error = mpiReadRaw(&key->q, tag.value, tag.length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Point to the next field
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the first exponent
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Convert the first exponent to a multiple precision integer
+      error = mpiReadRaw(&key->dp, tag.value, tag.length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Point to the next field
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the second exponent
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Convert the second exponent to a multiple precision integer
+      error = mpiReadRaw(&key->dq, tag.value, tag.length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Point to the next field
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the coefficient
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Convert the coefficient to a multiple precision integer
+      error = mpiReadRaw(&key->qinv, tag.value, tag.length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Debug message
+      TRACE_DEBUG("RSA private key:\r\n");
+      TRACE_DEBUG("  Modulus:\r\n");
+      TRACE_DEBUG_MPI("    ", &key->n);
+      TRACE_DEBUG("  Public exponent:\r\n");
+      TRACE_DEBUG_MPI("    ", &key->e);
+      TRACE_DEBUG("  Private exponent:\r\n");
+      TRACE_DEBUG_MPI("    ", &key->d);
+      TRACE_DEBUG("  Prime 1:\r\n");
+      TRACE_DEBUG_MPI("    ", &key->p);
+      TRACE_DEBUG("  Prime 2:\r\n");
+      TRACE_DEBUG_MPI("    ", &key->q);
+      TRACE_DEBUG("  Prime exponent 1:\r\n");
+      TRACE_DEBUG_MPI("    ", &key->dp);
+      TRACE_DEBUG("  Prime exponent 2:\r\n");
+      TRACE_DEBUG_MPI("    ", &key->dq);
+      TRACE_DEBUG("  Coefficient:\r\n");
+      TRACE_DEBUG_MPI("    ", &key->qinv);
+
+      //End of exception handling block
+   } while(0);
+
+   //Release previously allocated memory
+   cryptoFreeMem(buffer);
+
+   //Clean up side effects if necessary
+   if(error)
+      rsaFreePrivateKey(key);
+
+   //Return status code
+   return error;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Decode a PEM file containing a DSA private key
+ * @param[in] input Pointer to the PEM structure
+ * @param[in] length Length of the PEM structure
+ * @param[out] key DSA private key resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t pemReadDsaPrivateKey(const char_t *input, size_t length, DsaPrivateKey *key)
+{
+#if (DSA_SUPPORT == ENABLED)
+   error_t error;
+   size_t i;
+   size_t j;
+   int_t k;
+   char_t *buffer;
+   const uint8_t *data;
+   Asn1Tag tag;
+
+   //Check parameters
+   if(input == NULL && length != 0)
+      return ERROR_INVALID_PARAMETER;
+   if(key == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Search for the beginning tag
+   k = pemSearchTag(input, length, "-----BEGIN DSA PRIVATE KEY-----", 31);
+   //Failed to find the specified tag?
+   if(k < 0)
+      return ERROR_INVALID_SYNTAX;
+
+   //Advance the pointer over the tag
+   input += k + 31;
+   length -= k + 31;
+
+   //Search for the end tag
+   k = pemSearchTag(input, length, "-----END DSA PRIVATE KEY-----", 29);
+   //Invalid PEM file?
+   if(k <= 0)
+      return ERROR_INVALID_SYNTAX;
+
+   //Length of the PEM structure
+   length = k;
+
+   //Allocate a memory buffer to hold the decoded data
+   buffer = cryptoAllocMem(length);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Copy the contents of the PEM structure
+   memcpy(buffer, input, length);
+
+   //Remove carriage returns and line feeds
+   for(i = 0, j = 0; i < length; i++)
+   {
+      if(buffer[i] != '\r' && buffer[i] != '\n')
+         buffer[j++] = buffer[i];
+   }
+
+   //Start of exception handling block
+   do
+   {
+      //The PEM file is Base64 encoded...
+      error = base64Decode(buffer, j, buffer, &length);
+      //Failed to decode the file?
+      if(error)
+         break;
+
+      //Point to the resulting ASN.1 structure
+      data = (uint8_t *) buffer;
+
+      //Display ASN.1 structure
+      error = asn1DumpObject(data, length, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //The DSA private key is encapsulated within a sequence
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Point to the first field of the sequence
+      data = tag.value;
+      length = tag.length;
+
+      //Read the version
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Skip the version field
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read p
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Convert p to a multiple precision integer
+      error = mpiReadRaw(&key->p, tag.value, tag.length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Point to the next field
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read q
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Convert q to a multiple precision integer
+      error = mpiReadRaw(&key->q, tag.value, tag.length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Point to the next field
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read g
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Convert g to a multiple precision integer
+      error = mpiReadRaw(&key->g, tag.value, tag.length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Point to the next field
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the public value
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Point to the next field
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the private value
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         break;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         break;
+
+      //Convert the private value to a multiple precision integer
+      error = mpiReadRaw(&key->x, tag.value, tag.length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Debug message
+      TRACE_DEBUG("DSA private key:\r\n");
+      TRACE_DEBUG("  p:\r\n");
+      TRACE_DEBUG_MPI("    ", &key->p);
+      TRACE_DEBUG("  q:\r\n");
+      TRACE_DEBUG_MPI("    ", &key->q);
+      TRACE_DEBUG("  g:\r\n");
+      TRACE_DEBUG_MPI("    ", &key->g);
+      TRACE_DEBUG("  x:\r\n");
+      TRACE_DEBUG_MPI("    ", &key->x);
+
+      //End of exception handling block
+   } while(0);
+
+   //Release previously allocated memory
+   cryptoFreeMem(buffer);
+
+   //Clean up side effects if necessary
+   if(error)
+      dsaFreePrivateKey(key);
+
+   //Return status code
+   return error;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Decode a PEM file containing EC domain parameters
+ * @param[in] input Pointer to the PEM structure
+ * @param[in] length Length of the PEM structure
+ * @param[out] params EC domain parameters
+ * @return Error code
+ **/
+
+error_t pemReadEcParameters(const char_t *input, size_t length, EcDomainParameters *params)
+{
+#if (EC_SUPPORT == ENABLED)
+   error_t error;
+   size_t i;
+   size_t j;
+   int_t k;
+   char_t *buffer;
+   const uint8_t *data;
+   Asn1Tag tag;
+   const EcCurveInfo *curveInfo;
+
+   //Check parameters
+   if(input == NULL && length != 0)
+      return ERROR_INVALID_PARAMETER;
+   if(params == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Check the format of the PEM file
+   if(pemSearchTag(input, length, "-----BEGIN EC PARAMETERS-----", 29) >= 0)
+   {
+      //Search for the beginning tag
+      k = pemSearchTag(input, length, "-----BEGIN EC PARAMETERS-----", 29);
+      //Failed to find the specified tag?
+      if(k < 0)
+         return ERROR_INVALID_SYNTAX;
+
+      //Advance the pointer over the tag
+      input += k + 29;
+      length -= k + 29;
+
+      //Search for the end tag
+      k = pemSearchTag(input, length, "-----END EC PARAMETERS-----", 27);
+      //Invalid PEM file?
+      if(k <= 0)
+         return ERROR_INVALID_SYNTAX;
+
+      //Length of the PEM structure
+      length = k;
+
+      //Allocate a memory buffer to hold the decoded data
+      buffer = cryptoAllocMem(length);
+      //Failed to allocate memory?
+      if(buffer == NULL)
+         return ERROR_OUT_OF_MEMORY;
+
+      //Copy the contents of the PEM structure
+      memcpy(buffer, input, length);
+
+      //Remove carriage returns and line feeds
+      for(i = 0, j = 0; i < length; i++)
+      {
+         if(buffer[i] != '\r' && buffer[i] != '\n')
+            buffer[j++] = buffer[i];
+      }
+
+      //Start of exception handling block
+      do
+      {
+         //The PEM file is Base64 encoded...
+         error = base64Decode(buffer, j, buffer, &length);
+         //Failed to decode the file?
+         if(error)
+            break;
+
+         //Point to the resulting ASN.1 structure
+         data = (uint8_t *) buffer;
+
+         //Display ASN.1 structure
+         error = asn1DumpObject(data, length, 0);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Read the curve identifier
+         error = asn1ReadTag(data, length, &tag);
+         //Failed to decode ASN.1 tag?
+         if(error)
+            break;
+
+         //Enforce encoding, class and type
+         error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OBJECT_IDENTIFIER);
+         //The tag does not match the criteria?
+         if(error)
+            break;
+
+         //Retrieve EC domain parameters
+         curveInfo = ecGetCurveInfo(tag.value, tag.length);
+         //Make sure the specified elliptic curve is supported
+         if(curveInfo == NULL)
+         {
+            //Report an error
+            error = ERROR_ILLEGAL_PARAMETER;
+            //Exit immediately
+            break;
+         }
+
+         //Load EC domain parameters
+         error = ecLoadDomainParameters(params, curveInfo);
+         //Any error to report?
+         if(error)
+            break;
+
+         //End of exception handling block
+      } while(0);
+   }
+   else
+   {
+      //Search for the beginning tag
+      k = pemSearchTag(input, length, "-----BEGIN PRIVATE KEY-----", 27);
+      //Failed to find the specified tag?
+      if(k < 0)
+         return ERROR_INVALID_SYNTAX;
+
+      //Advance the pointer over the tag
+      input += k + 27;
+      length -= k + 27;
+
+      //Search for the end tag
+      k = pemSearchTag(input, length, "-----END PRIVATE KEY-----", 25);
+      //Invalid PEM file?
+      if(k <= 0)
+         return ERROR_INVALID_SYNTAX;
+
+      //Length of the PEM structure
+      length = k;
+
+      //Allocate a memory buffer to hold the decoded data
+      buffer = cryptoAllocMem(length);
+      //Failed to allocate memory?
+      if(buffer == NULL)
+         return ERROR_OUT_OF_MEMORY;
+
+      //Copy the contents of the PEM structure
+      memcpy(buffer, input, length);
+
+      //Remove carriage returns and line feeds
+      for(i = 0, j = 0; i < length; i++)
+      {
+         if(buffer[i] != '\r' && buffer[i] != '\n')
+            buffer[j++] = buffer[i];
+      }
+
+      //Start of exception handling block
+      do
+      {
+         //The PEM file is Base64 encoded...
+         error = base64Decode(buffer, j, buffer, &length);
+         //Failed to decode the file?
+         if(error)
+            break;
+
+         //Point to the resulting ASN.1 structure
+         data = (uint8_t *) buffer;
+
+         //Display ASN.1 structure
+         error = asn1DumpObject(data, length, 0);
+         //Any error to report?
+         if(error)
+            break;
+
+         //The private key is encapsulated within a sequence
+         error = asn1ReadTag(data, length, &tag);
+         //Failed to decode ASN.1 tag?
+         if(error)
+            break;
+
+         //Enforce encoding, class and type
+         error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+         //The tag does not match the criteria?
+         if(error)
+            break;
+
+         //Point to the first field of the sequence
+         data = tag.value;
+         length = tag.length;
+
+         //Read the Version field
+         error = asn1ReadTag(data, length, &tag);
+         //Failed to decode ASN.1 tag?
+         if(error)
+            break;
+
+         //Enforce encoding, class and type
+         error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+         //The tag does not match the criteria?
+         if(error)
+            break;
+
+         //Skip the Version field
+         data += tag.totalLength;
+         length -= tag.totalLength;
+
+         //Read the PrivateKeyAlgorithmIdentifier field
+         error = asn1ReadTag(data, length, &tag);
+         //Failed to decode ASN.1 tag?
+         if(error)
+            break;
+
+         //Enforce encoding, class and type
+         error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+         //The tag does not match the criteria?
+         if(error)
+            break;
+
+         //Point to the first field of the sequence
+         data = tag.value;
+         length = tag.length;
+
+         //Read the algorithm identifier (OID)
+         error = asn1ReadTag(data, length, &tag);
+         //Failed to decode ASN.1 tag?
+         if(error)
+            break;
+
+         //Check algorithm identifier
+         error = asn1CheckOid(&tag, EC_PUBLIC_KEY_OID, sizeof(EC_PUBLIC_KEY_OID));
+         //Wrong identifier?
+         if(error)
+            break;
+
+         //Point to the next field
+         data += tag.totalLength;
+         length += tag.totalLength;
+
+         //Read namedCurve field
+         error = asn1ReadTag(data, length, &tag);
+         //Failed to decode ASN.1 tag?
+         if(error)
+            break;
+
+         //Enforce encoding, class and type
+         error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OBJECT_IDENTIFIER);
+         //The tag does not match the criteria?
+         if(error)
+            break;
+
+         //Retrieve EC domain parameters
+         curveInfo = ecGetCurveInfo(tag.value, tag.length);
+         //Make sure the specified elliptic curve is supported
+         if(curveInfo == NULL)
+         {
+            //Report an error
+            error = ERROR_ILLEGAL_PARAMETER;
+            //Exit immediately
+            break;
+         }
+
+         //Load EC domain parameters
+         error = ecLoadDomainParameters(params, curveInfo);
+         //Any error to report?
+         if(error)
+            break;
+
+         //End of exception handling block
+      } while(0);
+   }
+
+   //Release previously allocated memory
+   cryptoFreeMem(buffer);
+
+   //Clean up side effects if necessary
+   if(error)
+      ecFreeDomainParameters(params);
+
+   //Return status code
+   return error;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Decode a PEM file containing an EC private key
+ * @param[in] input Pointer to the PEM structure
+ * @param[in] length Length of the PEM structure
+ * @param[out] key EC private key resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t pemReadEcPrivateKey(const char_t *input, size_t length, Mpi *key)
+{
+#if (EC_SUPPORT == ENABLED)
+   error_t error;
+   size_t i;
+   size_t j;
+   int_t k;
+   char_t *buffer;
+   const uint8_t *data;
+   Asn1Tag tag;
+
+   //Check parameters
+   if(input == NULL && length != 0)
+      return ERROR_INVALID_PARAMETER;
+   if(key == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Check the format of the PEM file
+   if(pemSearchTag(input, length, "-----BEGIN EC PRIVATE KEY-----", 30) >= 0)
+   {
+      //Search for the beginning tag
+      k = pemSearchTag(input, length, "-----BEGIN EC PRIVATE KEY-----", 30);
+      //Failed to find the specified tag?
+      if(k < 0)
+         return ERROR_INVALID_SYNTAX;
+
+      //Advance the pointer over the tag
+      input += k + 30;
+      length -= k + 30;
+
+      //Search for the end tag
+      k = pemSearchTag(input, length, "-----END EC PRIVATE KEY-----", 28);
+      //Invalid PEM file?
+      if(k <= 0)
+         return ERROR_INVALID_SYNTAX;
+
+      //Length of the PEM structure
+      length = k;
+
+      //Allocate a memory buffer to hold the decoded data
+      buffer = cryptoAllocMem(length);
+      //Failed to allocate memory?
+      if(buffer == NULL)
+         return ERROR_OUT_OF_MEMORY;
+
+      //Copy the contents of the PEM structure
+      memcpy(buffer, input, length);
+
+      //Remove carriage returns and line feeds
+      for(i = 0, j = 0; i < length; i++)
+      {
+         if(buffer[i] != '\r' && buffer[i] != '\n')
+            buffer[j++] = buffer[i];
+      }
+
+      //Start of exception handling block
+      do
+      {
+         //The PEM file is Base64 encoded...
+         error = base64Decode(buffer, j, buffer, &length);
+         //Failed to decode the file?
+         if(error)
+            break;
+
+         //Point to the resulting ASN.1 structure
+         data = (uint8_t *) buffer;
+
+         //Display ASN.1 structure
+         error = asn1DumpObject(data, length, 0);
+         //Any error to report?
+         if(error)
+            break;
+
+         //The private key is encapsulated within a sequence
+         error = asn1ReadTag(data, length, &tag);
+         //Failed to decode ASN.1 tag?
+         if(error)
+            break;
+
+         //Enforce encoding, class and type
+         error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+         //The tag does not match the criteria?
+         if(error)
+            break;
+
+         //Point to the first field of the sequence
+         data = tag.value;
+         length = tag.length;
+
+         //Read the Version field
+         error = asn1ReadTag(data, length, &tag);
+         //Failed to decode ASN.1 tag?
+         if(error)
+            break;
+
+         //Enforce encoding, class and type
+         error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+         //The tag does not match the criteria?
+         if(error)
+            break;
+
+         //Skip the Version field
+         data += tag.totalLength;
+         length -= tag.totalLength;
+
+         //Read the PrivateKey field
+         error = asn1ReadTag(data, length, &tag);
+         //Failed to decode ASN.1 tag?
+         if(error)
+            break;
+
+         //Enforce encoding, class and type
+         error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OCTET_STRING);
+         //The tag does not match the criteria?
+         if(error)
+            break;
+
+         //Read the EC private key
+         error = mpiReadRaw(key, tag.value, tag.length);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Debug message
+         TRACE_DEBUG("EC private key:\r\n");
+         TRACE_DEBUG_MPI("  ", key);
+
+         //End of exception handling block
+      } while(0);
+   }
+   else
+   {
+      //Search for the beginning tag
+      k = pemSearchTag(input, length, "-----BEGIN PRIVATE KEY-----", 27);
+      //Failed to find the specified tag?
+      if(k < 0)
+         return ERROR_INVALID_SYNTAX;
+
+      //Advance the pointer over the tag
+      input += k + 27;
+      length -= k + 27;
+
+      //Search for the end tag
+      k = pemSearchTag(input, length, "-----END PRIVATE KEY-----", 25);
+      //Invalid PEM file?
+      if(k <= 0)
+         return ERROR_INVALID_SYNTAX;
+
+      //Length of the PEM structure
+      length = k;
+
+      //Allocate a memory buffer to hold the decoded data
+      buffer = cryptoAllocMem(length);
+      //Failed to allocate memory?
+      if(buffer == NULL)
+         return ERROR_OUT_OF_MEMORY;
+
+      //Copy the contents of the PEM structure
+      memcpy(buffer, input, length);
+
+      //Remove carriage returns and line feeds
+      for(i = 0, j = 0; i < length; i++)
+      {
+         if(buffer[i] != '\r' && buffer[i] != '\n')
+            buffer[j++] = buffer[i];
+      }
+
+      //Start of exception handling block
+      do
+      {
+         //The PEM file is Base64 encoded...
+         error = base64Decode(buffer, j, buffer, &length);
+         //Failed to decode the file?
+         if(error)
+            break;
+
+         //Point to the resulting ASN.1 structure
+         data = (uint8_t *) buffer;
+
+         //Display ASN.1 structure
+         error = asn1DumpObject(data, length, 0);
+         //Any error to report?
+         if(error)
+            break;
+
+         //The private key is encapsulated within a sequence
+         error = asn1ReadTag(data, length, &tag);
+         //Failed to decode ASN.1 tag?
+         if(error)
+            break;
+
+         //Enforce encoding, class and type
+         error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+         //The tag does not match the criteria?
+         if(error)
+            break;
+
+         //Point to the first field of the sequence
+         data = tag.value;
+         length = tag.length;
+
+         //Read the Version field
+         error = asn1ReadTag(data, length, &tag);
+         //Failed to decode ASN.1 tag?
+         if(error)
+            break;
+
+         //Enforce encoding, class and type
+         error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+         //The tag does not match the criteria?
+         if(error)
+            break;
+
+         //Skip the Version field
+         data += tag.totalLength;
+         length -= tag.totalLength;
+
+         //Read the PrivateKeyAlgorithmIdentifier field
+         error = asn1ReadTag(data, length, &tag);
+         //Failed to decode ASN.1 tag?
+         if(error)
+            break;
+
+         //Enforce encoding, class and type
+         error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+         //The tag does not match the criteria?
+         if(error)
+            break;
+
+         //Skip the PrivateKeyAlgorithmIdentifier field
+         data += tag.totalLength;
+         length -= tag.totalLength;
+
+         //The PrivateKey field is encapsulated within an octet string
+         error = asn1ReadTag(data, length, &tag);
+         //Failed to decode ASN.1 tag?
+         if(error)
+            break;
+
+         //Enforce encoding, class and type
+         error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OCTET_STRING);
+         //The tag does not match the criteria?
+         if(error)
+            break;
+
+         //Display ASN.1 structure
+         error = asn1DumpObject(tag.value, tag.length, 0);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Read the contents of the PrivateKey structure
+         error = asn1ReadTag(tag.value, tag.length, &tag);
+         //Failed to decode ASN.1 tag?
+         if(error)
+            break;
+
+         //Enforce encoding, class and type
+         error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+         //The tag does not match the criteria?
+         if(error)
+            break;
+
+         //Point to the first field of the sequence
+         data = tag.value;
+         length = tag.length;
+
+         //Read the Version field
+         error = asn1ReadTag(data, length, &tag);
+         //Failed to decode ASN.1 tag?
+         if(error)
+            break;
+
+         //Enforce encoding, class and type
+         error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+         //The tag does not match the criteria?
+         if(error)
+            break;
+
+         //Skip the Version field
+         data += tag.totalLength;
+         length -= tag.totalLength;
+
+         //Read the PrivateKey field
+         error = asn1ReadTag(data, length, &tag);
+         //Failed to decode ASN.1 tag?
+         if(error)
+            break;
+
+         //Enforce encoding, class and type
+         error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OCTET_STRING);
+         //The tag does not match the criteria?
+         if(error)
+            break;
+
+         //Read the EC private key
+         error = mpiReadRaw(key, tag.value, tag.length);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Debug message
+         TRACE_DEBUG("EC private key:\r\n");
+         TRACE_DEBUG_MPI("  ", key);
+
+         //End of exception handling block
+      } while(0);
+   }
+
+   //Release previously allocated memory
+   cryptoFreeMem(buffer);
+
+   //Clean up side effects if necessary
+   if(error)
+      mpiFree(key);
+
+   //Return status code
+   return error;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Decode a PEM file containing a certificate
+ * @param[in,out] input Pointer to the PEM structure
+ * @param[in,out] inputLength Length of the PEM structure
+ * @param[in,out] output Pointer to the DER encoded certificate
+ * @param[in,out] outputSize Size of the memory block that holds the DER certificate
+ * @param[out] outputLength Length of the DER encoded certificate
+ * @return Error code
+ **/
+
+error_t pemReadCertificate(const char_t **input, size_t *inputLength,
+   uint8_t **output, size_t *outputSize, size_t *outputLength)
+{
+   error_t error;
+   size_t length;
+   size_t i;
+   size_t j;
+   int_t k;
+
+   //Check parameters
+   if(input == NULL || inputLength == NULL)
+      return ERROR_INVALID_PARAMETER;
+   if(output == NULL || outputSize == NULL || outputLength == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Search for the beginning tag
+   k = pemSearchTag(*input, *inputLength, "-----BEGIN CERTIFICATE-----", 27);
+   //Failed to find the specified tag?
+   if(k < 0)
+      return ERROR_END_OF_FILE;
+
+   //Advance the input pointer over the tag
+   *input += k + 27;
+   *inputLength -= k + 27;
+
+   //Search for the end tag
+   k = pemSearchTag(*input, *inputLength, "-----END CERTIFICATE-----", 25);
+   //Invalid PEM file?
+   if(k <= 0)
+      return ERROR_INVALID_SYNTAX;
+
+   //Length of the PEM structure
+   length = k;
+
+   //Increase buffer size?
+   if(length > *outputSize)
+   {
+      //Release previously allocated buffer if necessary
+      if(*output != NULL)
+      {
+         cryptoFreeMem(*output);
+         *output = NULL;
+         *outputSize = 0;
+      }
+
+      //Allocate a memory buffer to hold the decoded data
+      *output = cryptoAllocMem(length);
+      //Failed to allocate memory?
+      if(*output == NULL)
+         return ERROR_OUT_OF_MEMORY;
+
+      //Record the size of the buffer
+      *outputSize = length;
+   }
+
+   //Copy the contents of the PEM structure
+   memcpy(*output, *input, length);
+
+   //Advance the input pointer over the certificate
+   *input += length + 25;
+   *inputLength -= length + 25;
+
+   //Remove carriage returns and line feeds
+   for(i = 0, j = 0; i < length; i++)
+   {
+      if((*output)[i] != '\r' && (*output)[i] != '\n')
+         (*output)[j++] = (*output)[i];
+   }
+
+   //Start of exception handling block
+   do
+   {
+      //The PEM file is Base64 encoded...
+      error = base64Decode((char_t *) *output, j, *output, &length);
+      //Failed to decode the file?
+      if(error)
+         break;
+
+      //Display ASN.1 structure
+      error = asn1DumpObject(*output, length, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //End of exception handling block
+   } while(0);
+
+   //Clean up side effects
+   if(error)
+   {
+      //Release previously allocated memory
+      cryptoFreeMem(*output);
+      *output = NULL;
+      *outputSize = 0;
+   }
+
+   //Size of the decoded certificate
+   *outputLength = length;
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Search a string for a given tag
+ * @param[in] s String to search
+ * @param[in] sLen Length of the string to search
+ * @param[in] tag String containing the tag to search for
+ * @param[in] tagLen Length of the tag
+ * @return The index of the first occurrence of the tag in the string,
+ *   or -1 if the tag does not appear in the string
+ **/
+
+int_t pemSearchTag(const char_t *s, size_t sLen, const char_t *tag, size_t tagLen)
+{
+   size_t i;
+   size_t j;
+
+   //Loop through input string
+   for(i = 0; (i + tagLen) <= sLen; i++)
+   {
+      //Compare current substring with the given tag
+      for(j = 0; j < tagLen; j++)
+      {
+         if(s[i + j] != tag[j])
+            break;
+      }
+
+      //Check whether the tag has been found
+      if(j == tagLen)
+         return i;
+   }
+
+   //The tag does not appear in the string
+   return -1;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/pem.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,54 @@
+/**
+ * @file pem.h
+ * @brief PEM (Privacy-Enhanced Mail)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _PEM_H
+#define _PEM_H
+
+//Dependencies
+#include "crypto.h"
+#include "dh.h"
+#include "rsa.h"
+#include "dsa.h"
+#include "ec.h"
+
+//PEM format decoding functions
+error_t pemReadDhParameters(const char_t *input, size_t length, DhParameters *params);
+
+error_t pemReadRsaPrivateKey(const char_t *input, size_t length, RsaPrivateKey *key);
+error_t pemReadDsaPrivateKey(const char_t *input, size_t length, DsaPrivateKey *key);
+
+error_t pemReadEcParameters(const char_t *input, size_t length, EcDomainParameters *params);
+error_t pemReadEcPrivateKey(const char_t *input, size_t length, Mpi *key);
+
+error_t pemReadCertificate(const char_t **input, size_t *inputLength,
+   uint8_t **output, size_t *outputSize, size_t *outputLength);
+
+int_t pemSearchTag(const char_t *s, size_t sLen, const char_t *tag, size_t tagLen);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/pkcs5.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,220 @@
+/**
+ * @file pkcs5.c
+ * @brief PKCS #5 (Password-Based Cryptography Standard)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "pkcs5.h"
+#include "hmac.h"
+
+//Check crypto library configuration
+#if (PKCS5_SUPPORT == ENABLED)
+
+//PKCS #5 OID (1.2.840.113549.1.5)
+const uint8_t PKCS5_OID[8] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05};
+//PBKDF2 OID (1.2.840.113549.1.5.12)
+const uint8_t PBKDF2_OID[9] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x0C};
+
+
+/**
+ * @brief PBKDF1 key derivation function
+ *
+ * PBKDF1 applies a hash function, which shall be MD2, MD5 or SHA-1, to derive
+ * keys. The length of the derived key is bounded by the length of the hash
+ * function output, which is 16 octets for MD2 and MD5 and 20 octets for SHA-1
+ *
+ * @param[in] hash Underlying hash function (MD2, MD5 or SHA-1)
+ * @param[in] p Password, an octet string
+ * @param[in] pLen Length in octets of password
+ * @param[in] s Salt, an octet string
+ * @param[in] sLen Length in octets of salt
+ * @param[in] c Iteration count
+ * @param[out] dk Derived key
+ * @param[in] dkLen Intended length in octets of the derived key
+ * @return Error code
+ **/
+
+error_t pbkdf1(const HashAlgo *hash, const uint8_t *p, size_t pLen,
+   const uint8_t *s, size_t sLen, uint_t c, uint8_t *dk, size_t dkLen)
+{
+   uint_t i;
+   uint8_t *t;
+   HashContext *context;
+
+   //Check input parameters
+   if(c < 1 || dkLen > hash->digestSize)
+      return ERROR_INVALID_PARAMETER;
+
+   //Allocate a memory buffer to hold the hash context
+   context = cryptoAllocMem(hash->contextSize);
+   //Allocate a temporary buffer
+   t = cryptoAllocMem(hash->digestSize);
+
+   //Failed to allocate memory?
+   if(!context || !t)
+   {
+      //Free previously allocated memory
+      cryptoFreeMem(context);
+      cryptoFreeMem(t);
+
+      //Report an error
+      return ERROR_OUT_OF_MEMORY;
+   }
+
+   //Apply the hash function to the concatenation of P and S
+   hash->init(context);
+   hash->update(context, p, pLen);
+   hash->update(context, s, sLen);
+   hash->final(context, t);
+
+   //Iterate as many times as required
+   for(i = 1; i < c; i++)
+   {
+      //Apply the hash function to T(i - 1)
+      hash->init(context);
+      hash->update(context, t, hash->digestSize);
+      hash->final(context, t);
+   }
+
+   //Output the derived key DK
+   memcpy(dk, t, dkLen);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   cryptoFreeMem(t);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief PBKDF2 key derivation function
+ *
+ * PBKDF2 applies a pseudorandom function to derive keys. The
+ * length of the derived key is essentially unbounded
+ *
+ * @param[in] hash Hash algorithm used by the underlying PRF
+ * @param[in] p Password, an octet string
+ * @param[in] pLen Length in octets of password
+ * @param[in] s Salt, an octet string
+ * @param[in] sLen Length in octets of salt
+ * @param[in] c Iteration count
+ * @param[out] dk Derived key
+ * @param[in] dkLen Intended length in octets of the derived key
+ * @return Error code
+ **/
+
+error_t pbkdf2(const HashAlgo *hash, const uint8_t *p, size_t pLen,
+   const uint8_t *s, size_t sLen, uint_t c, uint8_t *dk, size_t dkLen)
+{
+   uint_t i;
+   uint_t j;
+   uint_t k;
+   uint8_t *u;
+   uint8_t *t;
+   HmacContext *context;
+   uint8_t a[4];
+
+   //Iteration count must be a positive integer
+   if(c < 1)
+      return ERROR_INVALID_PARAMETER;
+
+   //Allocate a memory buffer to hold the HMAC context
+   context = cryptoAllocMem(sizeof(HmacContext));
+   //Allocate temporary buffers
+   u = cryptoAllocMem(hash->digestSize);
+   t = cryptoAllocMem(hash->digestSize);
+
+   //Failed to allocate memory?
+   if(!context || !u || !t)
+   {
+      //Free previously allocated memory
+      cryptoFreeMem(context);
+      cryptoFreeMem(u);
+      cryptoFreeMem(t);
+
+      //Report an error
+      return ERROR_OUT_OF_MEMORY;
+   }
+
+   //For each block of the derived key apply the function F
+   for(i = 1; dkLen > 0; i++)
+   {
+      //Calculate the 4-octet encoding of the integer i (MSB first)
+      a[0] = (i >> 24) & 0xFF;
+      a[1] = (i >> 16) & 0xFF;
+      a[2] = (i >> 8) & 0xFF;
+      a[3] = i & 0xFF;
+
+      //Compute U1 = PRF(P, S || INT(i))
+      hmacInit(context, hash, p, pLen);
+      hmacUpdate(context, s, sLen);
+      hmacUpdate(context, a, 4);
+      hmacFinal(context, u);
+
+      //Save the resulting HMAC value
+      memcpy(t, u, hash->digestSize);
+
+      //Iterate as many times as required
+      for(j = 1; j < c; j++)
+      {
+         //Compute U(j) = PRF(P, U(j-1))
+         hmacInit(context, hash, p, pLen);
+         hmacUpdate(context, u, hash->digestSize);
+         hmacFinal(context, u);
+
+         //Compute T = U(1) xor U(2) xor ... xor U(c)
+         for(k = 0; k < hash->digestSize; k++)
+            t[k] ^= u[k];
+      }
+
+      //Number of octets in the current block
+      k = MIN(dkLen, hash->digestSize);
+      //Save the resulting block
+      memcpy(dk, t, k);
+
+      //Point to the next block
+      dk += k;
+      dkLen -= k;
+   }
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   cryptoFreeMem(u);
+   cryptoFreeMem(t);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/pkcs5.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,47 @@
+/**
+ * @file pkcs5.h
+ * @brief PKCS #5 (Password-Based Cryptography Standard)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _PKCS5_H
+#define _PKCS5_H
+
+//Dependencies
+#include "crypto.h"
+
+//PKCS #5 related constants
+extern const uint8_t PKCS5_OID[8];
+extern const uint8_t PBKDF2_OID[9];
+
+//PKCS #5 related functions
+error_t pbkdf1(const HashAlgo *hash, const uint8_t *p, size_t pLen,
+   const uint8_t *s, size_t sLen, uint_t c, uint8_t *dk, size_t dkLen);
+
+error_t pbkdf2(const HashAlgo *hash, const uint8_t *p, size_t pLen,
+   const uint8_t *s, size_t sLen, uint_t c, uint8_t *dk, size_t dkLen);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/poly1305.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,342 @@
+/**
+ * @file poly1305.c
+ * @brief Poly1305 message-authentication code
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "poly1305.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (POLY1305_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Initialize Poly1305 message-authentication code computation
+ * @param[in] context Pointer to the Poly1305 context to initialize
+ * @param[in] key Pointer to the 256-bit key
+ **/
+
+void poly1305Init(Poly1305Context *context, const uint8_t *key)
+{
+   //The 256-bit key is partitioned into two parts, called r and s
+   context->r[0] = LOAD32LE(key);
+   context->r[1] = LOAD32LE(key + 4);
+   context->r[2] = LOAD32LE(key + 8);
+   context->r[3] = LOAD32LE(key + 12);
+   context->s[0] = LOAD32LE(key + 16);
+   context->s[1] = LOAD32LE(key + 20);
+   context->s[2] = LOAD32LE(key + 24);
+   context->s[3] = LOAD32LE(key + 28);
+
+   //Certain bits of r are required to be 0
+   context->r[0] &= 0x0FFFFFFF;
+   context->r[1] &= 0x0FFFFFFC;
+   context->r[2] &= 0x0FFFFFFC;
+   context->r[3] &= 0x0FFFFFFC;
+
+   //The accumulator is set to zero
+   context->a[0] = 0;
+   context->a[1] = 0;
+   context->a[2] = 0;
+   context->a[3] = 0;
+   context->a[4] = 0;
+   context->a[5] = 0;
+   context->a[6] = 0;
+   context->a[7] = 0;
+
+   //Number of bytes in the buffer
+   context->size = 0;
+}
+
+
+/**
+ * @brief Update Poly1305 message-authentication code computation
+ * @param[in] context Pointer to the Poly1305 context
+ * @param[in] data Pointer to the input message
+ * @param[in] length Length of the input message
+ **/
+
+void poly1305Update(Poly1305Context *context, const void *data, size_t length)
+{
+   size_t n;
+
+   //Process the incoming data
+   while(length > 0)
+   {
+      //The buffer can hold at most 16 bytes
+      n = MIN(length, 16 - context->size);
+
+      //Copy the data to the buffer
+      memcpy(context->buffer + context->size, data, n);
+
+      //Update the Poly1305 context
+      context->size += n;
+      //Advance the data pointer
+      data = (uint8_t *) data + n;
+      //Remaining bytes to process
+      length -= n;
+
+      //Process message in 16-byte blocks
+      if(context->size == 16)
+      {
+         //Transform the 16-byte block
+         poly1305ProcessBlock(context);
+         //Empty the buffer
+         context->size = 0;
+      }
+   }
+}
+
+
+/**
+ * @brief Finalize Poly1305 message-authentication code computation
+ * @param[in] context Pointer to the Poly1305 context
+ * @param[out] tag Calculated message-authentication code
+ **/
+
+void poly1305Final(Poly1305Context *context, uint8_t *tag)
+{
+   uint32_t mask;
+   uint32_t b[4];
+
+   //Process the last block
+   if(context->size != 0)
+      poly1305ProcessBlock(context);
+
+   //Save the accumulator
+   b[0] = context->a[0] & 0xFFFFFFFF;
+   b[1] = context->a[1] & 0xFFFFFFFF;
+   b[2] = context->a[2] & 0xFFFFFFFF;
+   b[3] = context->a[3] & 0xFFFFFFFF;
+
+   //Compute a + 5
+   context->a[0] += 5;
+
+   //Propagate the carry
+   context->a[1] += context->a[0] >> 32;
+   context->a[2] += context->a[1] >> 32;
+   context->a[3] += context->a[2] >> 32;
+   context->a[4] += context->a[3] >> 32;
+
+   //If (a + 5) >= 2^130, form a mask with the value 0x00000000. Else, form
+   //a mask with the value 0xffffffff
+   mask = ((context->a[4] & 0x04) >> 2) - 1;
+
+   //Select between ((a - (2^130 - 5)) % 2^128) and (a % 2^128)
+   context->a[0] = (context->a[0] & ~mask) | (b[0] & mask);
+   context->a[1] = (context->a[1] & ~mask) | (b[1] & mask);
+   context->a[2] = (context->a[2] & ~mask) | (b[2] & mask);
+   context->a[3] = (context->a[3] & ~mask) | (b[3] & mask);
+
+   //Finally, the value of the secret key s is added to the accumulator
+   context->a[0] += context->s[0];
+   context->a[1] += context->s[1];
+   context->a[2] += context->s[2];
+   context->a[3] += context->s[3];
+
+   //Propagate the carry
+   context->a[1] += context->a[0] >> 32;
+   context->a[2] += context->a[1] >> 32;
+   context->a[3] += context->a[2] >> 32;
+   context->a[4] += context->a[3] >> 32;
+
+   //We only consider the least significant bits
+   b[0] = context->a[0] & 0xFFFFFFFF;
+   b[1] = context->a[1] & 0xFFFFFFFF;
+   b[2] = context->a[2] & 0xFFFFFFFF;
+   b[3] = context->a[3] & 0xFFFFFFFF;
+
+   //The result is serialized as a little-endian number, producing
+   //the 16 byte tag
+   STORE32LE(b[0], tag);
+   STORE32LE(b[1], tag + 4);
+   STORE32LE(b[2], tag + 8);
+   STORE32LE(b[3], tag + 12);
+
+   //Clear the accumulator
+   context->a[0] = 0;
+   context->a[1] = 0;
+   context->a[2] = 0;
+   context->a[3] = 0;
+   context->a[4] = 0;
+   context->a[5] = 0;
+   context->a[6] = 0;
+   context->a[7] = 0;
+
+   //Clear r and s
+   context->r[0] = 0;
+   context->r[1] = 0;
+   context->r[2] = 0;
+   context->r[3] = 0;
+   context->s[0] = 0;
+   context->s[1] = 0;
+   context->s[2] = 0;
+   context->s[3] = 0;
+}
+
+
+/**
+ * @brief Process message in 16-byte blocks
+ * @param[in] context Pointer to the Poly1305 context
+ **/
+
+void poly1305ProcessBlock(Poly1305Context *context)
+{
+   uint32_t a[8];
+   uint32_t r[4];
+   uint_t n;
+
+   //Retrieve the length of the last block
+   n = context->size;
+
+   //Add one bit beyond the number of octets. For a 16-byte block,
+   //this is equivalent to adding 2^128 to the number. For the shorter
+   //block, it can be 2^120, 2^112, or any power of two that is evenly
+   //divisible by 8, all the way down to 2^8
+   context->buffer[n++] = 0x01;
+
+   //If the resulting block is not 17 bytes long (the last block),
+   //pad it with zeros
+   while(n < 17)
+      context->buffer[n++] = 0x00;
+
+   //Read the block as a little-endian number
+   a[0] = LOAD32LE(context->buffer);
+   a[1] = LOAD32LE(context->buffer + 4);
+   a[2] = LOAD32LE(context->buffer + 8);
+   a[3] = LOAD32LE(context->buffer + 12);
+   a[4] = context->buffer[16];
+
+   //Add this number to the accumulator
+   context->a[0] += a[0];
+   context->a[1] += a[1];
+   context->a[2] += a[2];
+   context->a[3] += a[3];
+   context->a[4] += a[4];
+
+   //Propagate the carry
+   context->a[1] += context->a[0] >> 32;
+   context->a[2] += context->a[1] >> 32;
+   context->a[3] += context->a[2] >> 32;
+   context->a[4] += context->a[3] >> 32;
+
+   //We only consider the least significant bits
+   a[0] = context->a[0] & 0xFFFFFFFF;
+   a[1] = context->a[1] & 0xFFFFFFFF;
+   a[2] = context->a[2] & 0xFFFFFFFF;
+   a[3] = context->a[3] & 0xFFFFFFFF;
+   a[4] = context->a[4] & 0xFFFFFFFF;
+
+   //Copy r
+   r[0] = context->r[0];
+   r[1] = context->r[1];
+   r[2] = context->r[2];
+   r[3] = context->r[3];
+
+   //Multiply the accumulator by r
+   context->a[0] = (uint64_t) a[0] * r[0];
+   context->a[1] = (uint64_t) a[0] * r[1] + (uint64_t) a[1] * r[0];
+   context->a[2] = (uint64_t) a[0] * r[2] + (uint64_t) a[1] * r[1] + (uint64_t) a[2] * r[0];
+   context->a[3] = (uint64_t) a[0] * r[3] + (uint64_t) a[1] * r[2] + (uint64_t) a[2] * r[1] + (uint64_t) a[3] * r[0];
+   context->a[4] = (uint64_t) a[1] * r[3] + (uint64_t) a[2] * r[2] + (uint64_t) a[3] * r[1] + (uint64_t) a[4] * r[0];
+   context->a[5] = (uint64_t) a[2] * r[3] + (uint64_t) a[3] * r[2] + (uint64_t) a[4] * r[1];
+   context->a[6] = (uint64_t) a[3] * r[3] + (uint64_t) a[4] * r[2];
+   context->a[7] = (uint64_t) a[4] * r[3];
+
+   //Propagate the carry
+   context->a[1] += context->a[0] >> 32;
+   context->a[2] += context->a[1] >> 32;
+   context->a[3] += context->a[2] >> 32;
+   context->a[4] += context->a[3] >> 32;
+   context->a[5] += context->a[4] >> 32;
+   context->a[6] += context->a[5] >> 32;
+   context->a[7] += context->a[6] >> 32;
+
+   //Save the high part of the accumulator
+   a[0] = context->a[4] & 0xFFFFFFFC;
+   a[1] = context->a[5] & 0xFFFFFFFF;
+   a[2] = context->a[6] & 0xFFFFFFFF;
+   a[3] = context->a[7] & 0xFFFFFFFF;
+
+   //We only consider the least significant bits
+   context->a[0] &= 0xFFFFFFFF;
+   context->a[1] &= 0xFFFFFFFF;
+   context->a[2] &= 0xFFFFFFFF;
+   context->a[3] &= 0xFFFFFFFF;
+   context->a[4] &= 0x00000003;
+
+   //Perform fast modular reduction (first pass)
+   context->a[0] += a[0];
+   context->a[0] += (a[0] >> 2) | (a[1] << 30);
+   context->a[1] += a[1];
+   context->a[1] += (a[1] >> 2) | (a[2] << 30);
+   context->a[2] += a[2];
+   context->a[2] += (a[2] >> 2) | (a[3] << 30);
+   context->a[3] += a[3];
+   context->a[3] += (a[3] >> 2);
+
+   //Propagate the carry
+   context->a[1] += context->a[0] >> 32;
+   context->a[2] += context->a[1] >> 32;
+   context->a[3] += context->a[2] >> 32;
+   context->a[4] += context->a[3] >> 32;
+
+   //Save the high part of the accumulator
+   a[0] = context->a[4] & 0xFFFFFFFC;
+
+   //We only consider the least significant bits
+   context->a[0] &= 0xFFFFFFFF;
+   context->a[1] &= 0xFFFFFFFF;
+   context->a[2] &= 0xFFFFFFFF;
+   context->a[3] &= 0xFFFFFFFF;
+   context->a[4] &= 0x00000003;
+
+   //Perform fast modular reduction (second pass)
+   context->a[0] += a[0];
+   context->a[0] += a[0] >> 2;
+
+   //Propagate the carry
+   context->a[1] += context->a[0] >> 32;
+   context->a[2] += context->a[1] >> 32;
+   context->a[3] += context->a[2] >> 32;
+   context->a[4] += context->a[3] >> 32;
+
+   //We only consider the least significant bits
+   context->a[0] &= 0xFFFFFFFF;
+   context->a[1] &= 0xFFFFFFFF;
+   context->a[2] &= 0xFFFFFFFF;
+   context->a[3] &= 0xFFFFFFFF;
+   context->a[4] &= 0x00000003;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/poly1305.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,59 @@
+/**
+ * @file poly1305.h
+ * @brief Poly1305 message-authentication code
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _POLY1305_H
+#define _POLY1305_H
+
+//Dependencies
+#include "crypto.h"
+#include "mpi.h"
+
+
+/**
+ * @brief Poly1305 context
+ **/
+
+typedef struct
+{
+   uint32_t r[4];
+   uint32_t s[4];
+   uint64_t a[8];
+   uint8_t buffer[17];
+   size_t size;
+} Poly1305Context;
+
+
+//Poly1305 related functions
+void poly1305Init(Poly1305Context *context, const uint8_t *key);
+void poly1305Update(Poly1305Context *context, const void *data, size_t length);
+void poly1305Final(Poly1305Context *context, uint8_t *tag);
+
+void poly1305ProcessBlock(Poly1305Context *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/rc4.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,140 @@
+/**
+ * @file rc4.c
+ * @brief RC4 encryption algorithm
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "rc4.h"
+
+//Check crypto library configuration
+#if (RC4_SUPPORT == ENABLED)
+
+//Common interface for encryption algorithms
+const CipherAlgo rc4CipherAlgo =
+{
+   "RC4",
+   sizeof(Rc4Context),
+   CIPHER_ALGO_TYPE_STREAM,
+   0,
+   (CipherAlgoInit) rc4Init,
+   (CipherAlgoEncryptStream) rc4Cipher,
+   (CipherAlgoDecryptStream) rc4Cipher,
+   NULL,
+   NULL
+};
+
+
+/**
+ * @brief Initialize an RC4 context using the supplied key
+ * @param[in] context Pointer to the RC4 context to initialize
+ * @param[in] key Pointer to the key
+ * @param[in] length Length of the key
+ * @return Error code
+ **/
+
+error_t rc4Init(Rc4Context *context, const uint8_t *key, size_t length)
+{
+   uint_t i;
+   uint_t j;
+   uint8_t temp;
+
+   //Clear context
+   context->i = 0;
+   context->j = 0;
+
+   //Initialize the S array with identity permutation
+   for(i = 0; i < 256; i++)
+      context->s[i] = i;
+
+   //S is then processed for 256 iterations
+   for(i = 0, j = 0; i < 256; i++)
+   {
+      //Randomize the permutations using the supplied key
+      j = (j + context->s[i] + key[i % length]) % 256;
+
+      //Swap the values of S[i] and S[j]
+      temp = context->s[i];
+      context->s[i] = context->s[j];
+      context->s[j] = temp;
+   }
+
+   //RC4 context successfully initialized
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Encrypt/decrypt data with the RC4 algorithm
+ * @param[in] context Pointer to the RC4 context
+ * @param[in] input Pointer to the data to encrypt/decrypt
+ * @param[in] output Pointer to the resulting data
+ * @param[in] length Length of the input data
+ **/
+
+void rc4Cipher(Rc4Context *context, const uint8_t *input, uint8_t *output, size_t length)
+{
+   uint8_t temp;
+
+   //Restore context
+   uint_t i = context->i;
+   uint_t j = context->j;
+   uint8_t *s = context->s;
+
+   //Encryption loop
+   while(length > 0)
+   {
+      //Adjust indices
+      i = (i + 1) % 256;
+      j = (j + s[i]) % 256;
+
+      //Swap the values of S[i] and S[j]
+      temp = s[i];
+      s[i] = s[j];
+      s[j] = temp;
+
+      //XOR the input data with the RC4 stream
+      *output = *input ^ s[(s[i] + s[j]) % 256];
+
+      //Increment data pointers
+      input++;
+      output++;
+
+      //Remaining bytes to process
+      length--;
+   }
+
+   //Save context
+   context->i = i;
+   context->j = j;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/rc4.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,59 @@
+/**
+ * @file rc4.h
+ * @brief RC4 encryption algorithm
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _RC4_H
+#define _RC4_H
+
+//Dependencies
+#include "crypto.h"
+
+//Common interface for encryption algorithms
+#define RC4_CIPHER_ALGO (&rc4CipherAlgo)
+
+
+/**
+ * @brief RC4 algorithm context
+ **/
+
+typedef struct
+{
+   uint_t i;
+   uint_t j;
+   uint8_t s[256];
+} Rc4Context;
+
+
+//RC4 related constants
+extern const CipherAlgo rc4CipherAlgo;
+
+//RC4 related functions
+error_t rc4Init(Rc4Context *context, const uint8_t *key, size_t length);
+void rc4Cipher(Rc4Context *context, const uint8_t *input, uint8_t *output, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/rc6.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,244 @@
+/**
+ * @file rc6.c
+ * @brief RC6-32/20 block cipher
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * RC6 is a symmetric key block cipher derived from RC5
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "rc6.h"
+
+//Check crypto library configuration
+#if (RC6_SUPPORT == ENABLED)
+
+//RC6 magic constants
+#define P32 0xB7E15163
+#define Q32 0x9E3779B9
+
+//Common interface for encryption algorithms
+const CipherAlgo rc6CipherAlgo =
+{
+   "RC6",
+   sizeof(Rc6Context),
+   CIPHER_ALGO_TYPE_BLOCK,
+   RC6_BLOCK_SIZE,
+   (CipherAlgoInit) rc6Init,
+   NULL,
+   NULL,
+   (CipherAlgoEncryptBlock) rc6EncryptBlock,
+   (CipherAlgoDecryptBlock) rc6DecryptBlock
+};
+
+
+/**
+ * @brief Initialize a RC6 context using the supplied key
+ * @param[in] context Pointer to the RC6 context to initialize
+ * @param[in] key Pointer to the key
+ * @param[in] keyLength Length of the key
+ * @return Error code
+ **/
+
+error_t rc6Init(Rc6Context *context, const uint8_t *key, size_t keyLength)
+{
+   uint_t c;
+   uint_t i;
+   uint_t j;
+   uint_t s;
+   uint_t v;
+   uint32_t a;
+   uint32_t b;
+
+   //Invalid key length?
+   if(keyLength > RC6_MAX_KEY_SIZE)
+      return ERROR_INVALID_KEY_LENGTH;
+
+   //Convert the secret key from bytes to words
+   memset(context->l, 0, RC6_MAX_KEY_SIZE);
+   memcpy(context->l, key, keyLength);
+
+   //Calculate the length of the key in words
+   c = (keyLength > 0) ? (keyLength + 3) / 4 : 1;
+
+   //Initialize the first element of S
+   context->s[0] = P32;
+
+   //Initialize array S to a particular fixed pseudo random bit pattern
+   for(i = 1; i < (2 * RC6_NB_ROUNDS + 4); i++)
+      context->s[i] = context->s[i - 1] + Q32;
+
+   //Initialize variables
+   i = 0;
+   j = 0;
+   a = 0;
+   b = 0;
+
+   //Number of iterations
+   v = 3 * MAX(c, 2 * RC6_NB_ROUNDS + 4);
+
+   //Key expansion
+   for(s = 0; s < v; s++)
+   {
+      context->s[i] += a + b;
+      context->s[i] = ROL32(context->s[i], 3);
+      a = context->s[i];
+
+      context->l[j] += a + b;
+      context->l[j] = ROL32(context->l[j], (a + b) % 32);
+      b = context->l[j];
+
+      if(++i >= (2 * RC6_NB_ROUNDS + 4))
+         i = 0;
+      if(++j >= c)
+         j = 0;
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Encrypt a 16-byte block using RC6 algorithm
+ * @param[in] context Pointer to the RC6 context
+ * @param[in] input Plaintext block to encrypt
+ * @param[out] output Ciphertext block resulting from encryption
+ **/
+
+void rc6EncryptBlock(Rc6Context *context, const uint8_t *input, uint8_t *output)
+{
+   uint_t i;
+   uint32_t t;
+   uint32_t u;
+
+   //Load the 4 working registers with the plaintext
+   uint32_t a = LOAD32LE(input + 0);
+   uint32_t b = LOAD32LE(input + 4);
+   uint32_t c = LOAD32LE(input + 8);
+   uint32_t d = LOAD32LE(input + 12);
+
+   //First, update B and D
+   b += context->s[0];
+   d += context->s[1];
+
+   //Apply 20 rounds
+   for(i = 1; i <= RC6_NB_ROUNDS; i++)
+   {
+      t = (b * (2 * b + 1));
+      t = ROL32(t, 5);
+
+      u = (d * (2 * d + 1));
+      u = ROL32(u, 5);
+
+      a ^= t;
+      a = ROL32(a, u % 32) + context->s[2 * i];
+
+      c ^= u;
+      c = ROL32(c, t % 32) + context->s[2 * i + 1];
+
+      t = a;
+      a = b;
+      b = c;
+      c = d;
+      d = t;
+   }
+
+   //Update A and C
+   a += context->s[2 * RC6_NB_ROUNDS + 2];
+   c += context->s[2 * RC6_NB_ROUNDS + 3];
+
+   //The resulting value is the ciphertext
+   STORE32LE(a, output + 0);
+   STORE32LE(b, output + 4);
+   STORE32LE(c, output + 8);
+   STORE32LE(d, output + 12);
+}
+
+
+/**
+ * @brief Decrypt a 16-byte block using RC6 algorithm
+ * @param[in] context Pointer to the RC6 context
+ * @param[in] input Ciphertext block to decrypt
+ * @param[out] output Plaintext block resulting from decryption
+ **/
+
+void rc6DecryptBlock(Rc6Context *context, const uint8_t *input, uint8_t *output)
+{
+   uint_t i;
+   uint32_t t;
+   uint32_t u;
+
+   //Load the 4 working registers with the ciphertext
+   uint32_t a = LOAD32LE(input + 0);
+   uint32_t b = LOAD32LE(input + 4);
+   uint32_t c = LOAD32LE(input + 8);
+   uint32_t d = LOAD32LE(input + 12);
+
+   //First, update C and A
+   c -= context->s[2 * RC6_NB_ROUNDS + 3];
+   a -= context->s[2 * RC6_NB_ROUNDS + 2];
+
+   //Apply 20 rounds
+   for(i = RC6_NB_ROUNDS; i > 0; i--)
+   {
+      t = d;
+      d = c;
+      c = b;
+      b = a;
+      a = t;
+
+      u = (d * (2 * d + 1));
+      u = ROL32(u, 5);
+
+      t = (b * (2 * b + 1));
+      t = ROL32(t, 5);
+
+      c -= context->s[2 * i + 1];
+      c = ROR32(c, t % 32) ^ u;
+
+      a -= context->s[2 * i];
+      a = ROR32(a, u % 32) ^ t;
+   }
+
+   //Update D and B
+   d -= context->s[1];
+   b -= context->s[0];
+
+   //The resulting value is the plaintext
+   STORE32LE(a, output + 0);
+   STORE32LE(b, output + 4);
+   STORE32LE(c, output + 8);
+   STORE32LE(d, output + 12);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/rc6.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,66 @@
+/**
+ * @file rc6.h
+ * @brief RC6-32/20 block cipher
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _RC6_H
+#define _RC6_H
+
+//Dependencies
+#include "crypto.h"
+
+//RC6 block size
+#define RC6_BLOCK_SIZE 16
+//Maximum length of the encryption key in bytes
+#define RC6_MAX_KEY_SIZE 32
+//Number of rounds
+#define RC6_NB_ROUNDS 20
+
+//Common interface for encryption algorithms
+#define RC6_CIPHER_ALGO (&rc6CipherAlgo)
+
+
+/**
+ * @brief RC6 algorithm context
+ **/
+
+typedef struct
+{
+   uint32_t l[RC6_MAX_KEY_SIZE / 4];
+   uint32_t s[2 * RC6_NB_ROUNDS + 4];
+} Rc6Context;
+
+
+//RC6 related constants
+extern const CipherAlgo rc6CipherAlgo;
+
+//RC6 related functions
+error_t rc6Init(Rc6Context *context, const uint8_t *key, size_t keyLength);
+void rc6EncryptBlock(Rc6Context *context, const uint8_t *input, uint8_t *output);
+void rc6DecryptBlock(Rc6Context *context, const uint8_t *input, uint8_t *output);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/ripemd128.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,394 @@
+/**
+ * @file ripemd128.c
+ * @brief RIPEMD-128 hash function
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "ripemd128.h"
+
+//Check crypto library configuration
+#if (RIPEMD128_SUPPORT == ENABLED)
+
+//RIPEMD-128 auxiliary functions
+#define F(x, y, z) ((x) ^ (y) ^ (z))
+#define G(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define H(x, y, z) (((x) | ~(y)) ^ (z))
+#define I(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+
+#define FF(a, b, c, d, x, s) a += F(b, c, d) + (x), a = ROL32(a, s)
+#define GG(a, b, c, d, x, s) a += G(b, c, d) + (x) + 0x5A827999, a = ROL32(a, s)
+#define HH(a, b, c, d, x, s) a += H(b, c, d) + (x) + 0x6ED9EBA1, a = ROL32(a, s)
+#define II(a, b, c, d, x, s) a += I(b, c, d) + (x) + 0x8F1BBCDC, a = ROL32(a, s)
+
+#define FFF(a, b, c, d, x, s) a += F(b, c, d) + (x), a = ROL32(a, s)
+#define GGG(a, b, c, d, x, s) a += G(b, c, d) + (x) + 0x6D703EF3, a = ROL32(a, s)
+#define HHH(a, b, c, d, x, s) a += H(b, c, d) + (x) + 0x5C4DD124, a = ROL32(a, s)
+#define III(a, b, c, d, x, s) a += I(b, c, d) + (x) + 0x50A28BE6, a = ROL32(a, s)
+
+//RIPEMD-128 padding
+static const uint8_t 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
+};
+
+//RIPEMD-128 object identifier (1.3.36.3.2.2)
+static const uint8_t ripemd128Oid[] = {0x2B, 0x24, 0x03, 0x02, 0x02};
+
+//Common interface for hash algorithms
+const HashAlgo ripemd128HashAlgo =
+{
+   "RIPEMD-128",
+   ripemd128Oid,
+   sizeof(ripemd128Oid),
+   sizeof(Ripemd128Context),
+   RIPEMD128_BLOCK_SIZE,
+   RIPEMD128_DIGEST_SIZE,
+   (HashAlgoCompute) ripemd128Compute,
+   (HashAlgoInit) ripemd128Init,
+   (HashAlgoUpdate) ripemd128Update,
+   (HashAlgoFinal) ripemd128Final
+};
+
+
+/**
+ * @brief Digest a message using RIPEMD-128
+ * @param[in] data Pointer to the message being hashed
+ * @param[in] length Length of the message
+ * @param[out] digest Pointer to the calculated digest
+ * @return Error code
+ **/
+
+error_t ripemd128Compute(const void *data, size_t length, uint8_t *digest)
+{
+   //Allocate a memory buffer to hold the RIPEMD-128 context
+   Ripemd128Context *context = cryptoAllocMem(sizeof(Ripemd128Context));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the RIPEMD-128 context
+   ripemd128Init(context);
+   //Digest the message
+   ripemd128Update(context, data, length);
+   //Finalize the RIPEMD-128 message digest
+   ripemd128Final(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize RIPEMD-128 message digest context
+ * @param[in] context Pointer to the RIPEMD-128 context to initialize
+ **/
+
+void ripemd128Init(Ripemd128Context *context)
+{
+   //Set initial hash value
+   context->h[0] = 0x67452301;
+   context->h[1] = 0xEFCDAB89;
+   context->h[2] = 0x98BADCFE;
+   context->h[3] = 0x10325476;
+
+   //Number of bytes in the buffer
+   context->size = 0;
+   //Total length of the message
+   context->totalSize = 0;
+}
+
+
+/**
+ * @brief Update the RIPEMD-128 context with a portion of the message being hashed
+ * @param[in] context Pointer to the RIPEMD-128 context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void ripemd128Update(Ripemd128Context *context, const void *data, size_t length)
+{
+   size_t n;
+
+   //Process the incoming data
+   while(length > 0)
+   {
+      //The buffer can hold at most 64 bytes
+      n = MIN(length, 64 - context->size);
+
+      //Copy the data to the buffer
+      memcpy(context->buffer + context->size, data, n);
+
+      //Update the RIPEMD-128 context
+      context->size += n;
+      context->totalSize += n;
+      //Advance the data pointer
+      data = (uint8_t *) data + n;
+      //Remaining bytes to process
+      length -= n;
+
+      //Process message in 16-word blocks
+      if(context->size == 64)
+      {
+         //Transform the 16-word block
+         ripemd128ProcessBlock(context);
+         //Empty the buffer
+         context->size = 0;
+      }
+   }
+}
+
+
+/**
+ * @brief Finish the RIPEMD-128 message digest
+ * @param[in] context Pointer to the RIPEMD-128 context
+ * @param[out] digest Calculated digest (optional parameter)
+ **/
+
+void ripemd128Final(Ripemd128Context *context, uint8_t *digest)
+{
+   uint_t i;
+   size_t paddingSize;
+   uint64_t totalSize;
+
+   //Length of the original message (before padding)
+   totalSize = context->totalSize * 8;
+
+   //Pad the message so that its length is congruent to 56 modulo 64
+   if(context->size < 56)
+      paddingSize = 56 - context->size;
+   else
+      paddingSize = 64 + 56 - context->size;
+
+   //Append padding
+   ripemd128Update(context, padding, paddingSize);
+
+   //Append the length of the original message
+   context->x[14] = htole32((uint32_t) totalSize);
+   context->x[15] = htole32((uint32_t) (totalSize >> 32));
+
+   //Calculate the message digest
+   ripemd128ProcessBlock(context);
+
+   //Convert from host byte order to little-endian byte order
+   for(i = 0; i < 4; i++)
+      context->h[i] = htole32(context->h[i]);
+
+   //Copy the resulting digest
+   if(digest != NULL)
+      memcpy(digest, context->digest, RIPEMD128_DIGEST_SIZE);
+}
+
+
+/**
+ * @brief Process message in 16-word blocks
+ * @param[in] context Pointer to the RIPEMD-128 context
+ **/
+
+void ripemd128ProcessBlock(Ripemd128Context *context)
+{
+   uint_t i;
+
+   //Initialize the working registers
+   uint32_t aa= context->h[0];
+   uint32_t bb = context->h[1];
+   uint32_t cc = context->h[2];
+   uint32_t dd = context->h[3];
+   uint32_t aaa = context->h[0];
+   uint32_t bbb = context->h[1];
+   uint32_t ccc = context->h[2];
+   uint32_t ddd = context->h[3];
+
+   //Process message in 16-word blocks
+   uint32_t *x = context->x;
+
+   //Convert from little-endian byte order to host byte order
+   for(i = 0; i < 16; i++)
+      x[i] = letoh32(x[i]);
+
+   //Round 1
+   FF(aa, bb, cc, dd, x[0],  11);
+   FF(dd, aa, bb, cc, x[1],  14);
+   FF(cc, dd, aa, bb, x[2],  15);
+   FF(bb, cc, dd, aa, x[3],  12);
+   FF(aa, bb, cc, dd, x[4],  5);
+   FF(dd, aa, bb, cc, x[5],  8);
+   FF(cc, dd, aa, bb, x[6],  7);
+   FF(bb, cc, dd, aa, x[7],  9);
+   FF(aa, bb, cc, dd, x[8],  11);
+   FF(dd, aa, bb, cc, x[9],  13);
+   FF(cc, dd, aa, bb, x[10], 14);
+   FF(bb, cc, dd, aa, x[11], 15);
+   FF(aa, bb, cc, dd, x[12], 6);
+   FF(dd, aa, bb, cc, x[13], 7);
+   FF(cc, dd, aa, bb, x[14], 9);
+   FF(bb, cc, dd, aa, x[15], 8);
+
+   //Round 2
+   GG(aa, bb, cc, dd, x[7],  7);
+   GG(dd, aa, bb, cc, x[4],  6);
+   GG(cc, dd, aa, bb, x[13], 8);
+   GG(bb, cc, dd, aa, x[1],  13);
+   GG(aa, bb, cc, dd, x[10], 11);
+   GG(dd, aa, bb, cc, x[6],  9);
+   GG(cc, dd, aa, bb, x[15], 7);
+   GG(bb, cc, dd, aa, x[3],  15);
+   GG(aa, bb, cc, dd, x[12], 7);
+   GG(dd, aa, bb, cc, x[0],  12);
+   GG(cc, dd, aa, bb, x[9],  15);
+   GG(bb, cc, dd, aa, x[5],  9);
+   GG(aa, bb, cc, dd, x[2],  11);
+   GG(dd, aa, bb, cc, x[14], 7);
+   GG(cc, dd, aa, bb, x[11], 13);
+   GG(bb, cc, dd, aa, x[8],  12);
+
+   //Round 3
+   HH(aa, bb, cc, dd, x[3],  11);
+   HH(dd, aa, bb, cc, x[10], 13);
+   HH(cc, dd, aa, bb, x[14], 6);
+   HH(bb, cc, dd, aa, x[4],  7);
+   HH(aa, bb, cc, dd, x[9],  14);
+   HH(dd, aa, bb, cc, x[15], 9);
+   HH(cc, dd, aa, bb, x[8],  13);
+   HH(bb, cc, dd, aa, x[1],  15);
+   HH(aa, bb, cc, dd, x[2],  14);
+   HH(dd, aa, bb, cc, x[7],  8);
+   HH(cc, dd, aa, bb, x[0],  13);
+   HH(bb, cc, dd, aa, x[6],  6);
+   HH(aa, bb, cc, dd, x[13], 5);
+   HH(dd, aa, bb, cc, x[11], 12);
+   HH(cc, dd, aa, bb, x[5],  7);
+   HH(bb, cc, dd, aa, x[12], 5);
+
+   //Round 4
+   II(aa, bb, cc, dd, x[1],  11);
+   II(dd, aa, bb, cc, x[9],  12);
+   II(cc, dd, aa, bb, x[11], 14);
+   II(bb, cc, dd, aa, x[10], 15);
+   II(aa, bb, cc, dd, x[0],  14);
+   II(dd, aa, bb, cc, x[8],  15);
+   II(cc, dd, aa, bb, x[12], 9);
+   II(bb, cc, dd, aa, x[4],  8);
+   II(aa, bb, cc, dd, x[13], 9);
+   II(dd, aa, bb, cc, x[3],  14);
+   II(cc, dd, aa, bb, x[7],  5);
+   II(bb, cc, dd, aa, x[15], 6);
+   II(aa, bb, cc, dd, x[14], 8);
+   II(dd, aa, bb, cc, x[5],  6);
+   II(cc, dd, aa, bb, x[6],  5);
+   II(bb, cc, dd, aa, x[2],  12);
+
+   //Parallel round 1
+   III(aaa, bbb, ccc, ddd, x[5],  8);
+   III(ddd, aaa, bbb, ccc, x[14], 9);
+   III(ccc, ddd, aaa, bbb, x[7],  9);
+   III(bbb, ccc, ddd, aaa, x[0],  11);
+   III(aaa, bbb, ccc, ddd, x[9],  13);
+   III(ddd, aaa, bbb, ccc, x[2],  15);
+   III(ccc, ddd, aaa, bbb, x[11], 15);
+   III(bbb, ccc, ddd, aaa, x[4],  5);
+   III(aaa, bbb, ccc, ddd, x[13], 7);
+   III(ddd, aaa, bbb, ccc, x[6],  7);
+   III(ccc, ddd, aaa, bbb, x[15], 8);
+   III(bbb, ccc, ddd, aaa, x[8],  11);
+   III(aaa, bbb, ccc, ddd, x[1],  14);
+   III(ddd, aaa, bbb, ccc, x[10], 14);
+   III(ccc, ddd, aaa, bbb, x[3],  12);
+   III(bbb, ccc, ddd, aaa, x[12], 6);
+
+   //Parallel round 2
+   HHH(aaa, bbb, ccc, ddd, x[6],  9);
+   HHH(ddd, aaa, bbb, ccc, x[11], 13);
+   HHH(ccc, ddd, aaa, bbb, x[3],  15);
+   HHH(bbb, ccc, ddd, aaa, x[7],  7);
+   HHH(aaa, bbb, ccc, ddd, x[0],  12);
+   HHH(ddd, aaa, bbb, ccc, x[13], 8);
+   HHH(ccc, ddd, aaa, bbb, x[5],  9);
+   HHH(bbb, ccc, ddd, aaa, x[10], 11);
+   HHH(aaa, bbb, ccc, ddd, x[14], 7);
+   HHH(ddd, aaa, bbb, ccc, x[15], 7);
+   HHH(ccc, ddd, aaa, bbb, x[8],  12);
+   HHH(bbb, ccc, ddd, aaa, x[12], 7);
+   HHH(aaa, bbb, ccc, ddd, x[4],  6);
+   HHH(ddd, aaa, bbb, ccc, x[9],  15);
+   HHH(ccc, ddd, aaa, bbb, x[1],  13);
+   HHH(bbb, ccc, ddd, aaa, x[2],  11);
+
+   //Parallel round 3
+   GGG(aaa, bbb, ccc, ddd, x[15], 9);
+   GGG(ddd, aaa, bbb, ccc, x[5],  7);
+   GGG(ccc, ddd, aaa, bbb, x[1],  15);
+   GGG(bbb, ccc, ddd, aaa, x[3],  11);
+   GGG(aaa, bbb, ccc, ddd, x[7],  8);
+   GGG(ddd, aaa, bbb, ccc, x[14], 6);
+   GGG(ccc, ddd, aaa, bbb, x[6],  6);
+   GGG(bbb, ccc, ddd, aaa, x[9],  14);
+   GGG(aaa, bbb, ccc, ddd, x[11], 12);
+   GGG(ddd, aaa, bbb, ccc, x[8],  13);
+   GGG(ccc, ddd, aaa, bbb, x[12], 5);
+   GGG(bbb, ccc, ddd, aaa, x[2],  14);
+   GGG(aaa, bbb, ccc, ddd, x[10], 13);
+   GGG(ddd, aaa, bbb, ccc, x[0],  13);
+   GGG(ccc, ddd, aaa, bbb, x[4],  7);
+   GGG(bbb, ccc, ddd, aaa, x[13], 5);
+
+   //Parallel round 4
+   FFF(aaa, bbb, ccc, ddd, x[8],  15);
+   FFF(ddd, aaa, bbb, ccc, x[6],  5);
+   FFF(ccc, ddd, aaa, bbb, x[4],  8);
+   FFF(bbb, ccc, ddd, aaa, x[1],  11);
+   FFF(aaa, bbb, ccc, ddd, x[3],  14);
+   FFF(ddd, aaa, bbb, ccc, x[11], 14);
+   FFF(ccc, ddd, aaa, bbb, x[15], 6);
+   FFF(bbb, ccc, ddd, aaa, x[0],  14);
+   FFF(aaa, bbb, ccc, ddd, x[5],  6);
+   FFF(ddd, aaa, bbb, ccc, x[12], 9);
+   FFF(ccc, ddd, aaa, bbb, x[2],  12);
+   FFF(bbb, ccc, ddd, aaa, x[13], 9);
+   FFF(aaa, bbb, ccc, ddd, x[9],  12);
+   FFF(ddd, aaa, bbb, ccc, x[7],  5);
+   FFF(ccc, ddd, aaa, bbb, x[10], 15);
+   FFF(bbb, ccc, ddd, aaa, x[14], 8);
+
+   //Combine results
+   ddd = context->h[1] + cc + ddd;
+   context->h[1] = context->h[2] + dd + aaa;
+   context->h[2] = context->h[3] + aa + bbb;
+   context->h[3] = context->h[0] + bb + ccc;
+   context->h[0] = ddd;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/ripemd128.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,75 @@
+/**
+ * @file ripemd128.h
+ * @brief RIPEMD-128 hash function
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _RIPEMD128_H
+#define _RIPEMD128_H
+
+//Dependencies
+#include "crypto.h"
+
+//RIPEMD-128 block size
+#define RIPEMD128_BLOCK_SIZE 64
+//RIPEMD-128 digest size
+#define RIPEMD128_DIGEST_SIZE 16
+//Common interface for hash algorithms
+#define RIPEMD128_HASH_ALGO (&ripemd128HashAlgo)
+
+
+/**
+ * @brief RIPEMD-128 algorithm context
+ **/
+
+typedef struct
+{
+   union
+   {
+      uint32_t h[4];
+      uint8_t digest[16];
+   };
+   union
+   {
+      uint32_t x[16];
+      uint8_t buffer[64];
+   };
+   size_t size;
+   uint64_t totalSize;
+} Ripemd128Context;
+
+
+//RIPEMD-128 related constants
+extern const HashAlgo ripemd128HashAlgo;
+
+//RIPEMD-128 related functions
+error_t ripemd128Compute(const void *data, size_t length, uint8_t *digest);
+void ripemd128Init(Ripemd128Context *context);
+void ripemd128Update(Ripemd128Context *context, const void *data, size_t length);
+void ripemd128Final(Ripemd128Context *context, uint8_t *digest);
+void ripemd128ProcessBlock(Ripemd128Context *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/ripemd160.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,437 @@
+/**
+ * @file ripemd160.c
+ * @brief RIPEMD-160 hash function
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "ripemd160.h"
+
+//Check crypto library configuration
+#if (RIPEMD160_SUPPORT == ENABLED)
+
+//RIPEMD-160 auxiliary functions
+#define F(x, y, z) ((x) ^ (y) ^ (z))
+#define G(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define H(x, y, z) (((x) | ~(y)) ^ (z))
+#define I(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define J(x, y, z) ((x) ^ ((y) | ~(z)))
+
+#define FF(a, b, c, d, e, x, s) a += F(b, c, d) + (x), a = ROL32(a, s) + (e), c = ROL32(c, 10)
+#define GG(a, b, c, d, e, x, s) a += G(b, c, d) + (x) + 0x5A827999, a = ROL32(a, s) + (e), c = ROL32(c, 10)
+#define HH(a, b, c, d, e, x, s) a += H(b, c, d) + (x) + 0x6ED9EBA1, a = ROL32(a, s) + (e), c = ROL32(c, 10)
+#define II(a, b, c, d, e, x, s) a += I(b, c, d) + (x) + 0x8F1BBCDC, a = ROL32(a, s) + (e), c = ROL32(c, 10)
+#define JJ(a, b, c, d, e, x, s) a += J(b, c, d) + (x) + 0xA953FD4E, a = ROL32(a, s) + (e), c = ROL32(c, 10)
+
+#define FFF(a, b, c, d, e, x, s) a += F(b, c, d) + (x), a = ROL32(a, s) + (e), c = ROL32(c, 10)
+#define GGG(a, b, c, d, e, x, s) a += G(b, c, d) + (x) + 0x7A6D76E9, a = ROL32(a, s) + (e), c = ROL32(c, 10)
+#define HHH(a, b, c, d, e, x, s) a += H(b, c, d) + (x) + 0x6D703EF3, a = ROL32(a, s) + (e), c = ROL32(c, 10)
+#define III(a, b, c, d, e, x, s) a += I(b, c, d) + (x) + 0x5C4DD124, a = ROL32(a, s) + (e), c = ROL32(c, 10)
+#define JJJ(a, b, c, d, e, x, s) a += J(b, c, d) + (x) + 0x50A28BE6, a = ROL32(a, s) + (e), c = ROL32(c, 10)
+
+//RIPEMD-160 padding
+static const uint8_t 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
+};
+
+//RIPEMD-160 object identifier (1.3.36.3.2.1)
+static const uint8_t ripemd160Oid[] = {0x2B, 0x24, 0x03, 0x02, 0x01};
+
+//Common interface for hash algorithms
+const HashAlgo ripemd160HashAlgo =
+{
+   "RIPEMD-160",
+   ripemd160Oid,
+   sizeof(ripemd160Oid),
+   sizeof(Ripemd160Context),
+   RIPEMD160_BLOCK_SIZE,
+   RIPEMD160_DIGEST_SIZE,
+   (HashAlgoCompute) ripemd160Compute,
+   (HashAlgoInit) ripemd160Init,
+   (HashAlgoUpdate) ripemd160Update,
+   (HashAlgoFinal) ripemd160Final
+};
+
+
+/**
+ * @brief Digest a message using RIPEMD-160
+ * @param[in] data Pointer to the message being hashed
+ * @param[in] length Length of the message
+ * @param[out] digest Pointer to the calculated digest
+ * @return Error code
+ **/
+
+error_t ripemd160Compute(const void *data, size_t length, uint8_t *digest)
+{
+   //Allocate a memory buffer to hold the RIPEMD-160 context
+   Ripemd160Context *context = cryptoAllocMem(sizeof(Ripemd160Context));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the RIPEMD-160 context
+   ripemd160Init(context);
+   //Digest the message
+   ripemd160Update(context, data, length);
+   //Finalize the RIPEMD-160 message digest
+   ripemd160Final(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize RIPEMD-160 message digest context
+ * @param[in] context Pointer to the RIPEMD-160 context to initialize
+ **/
+
+void ripemd160Init(Ripemd160Context *context)
+{
+   //Set initial hash value
+   context->h[0] = 0x67452301;
+   context->h[1] = 0xEFCDAB89;
+   context->h[2] = 0x98BADCFE;
+   context->h[3] = 0x10325476;
+   context->h[4] = 0xC3D2E1F0;
+
+   //Number of bytes in the buffer
+   context->size = 0;
+   //Total length of the message
+   context->totalSize = 0;
+}
+
+
+/**
+ * @brief Update the RIPEMD-160 context with a portion of the message being hashed
+ * @param[in] context Pointer to the RIPEMD-160 context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void ripemd160Update(Ripemd160Context *context, const void *data, size_t length)
+{
+   size_t n;
+
+   //Process the incoming data
+   while(length > 0)
+   {
+      //The buffer can hold at most 64 bytes
+      n = MIN(length, 64 - context->size);
+
+      //Copy the data to the buffer
+      memcpy(context->buffer + context->size, data, n);
+
+      //Update the RIPEMD-160 context
+      context->size += n;
+      context->totalSize += n;
+      //Advance the data pointer
+      data = (uint8_t *) data + n;
+      //Remaining bytes to process
+      length -= n;
+
+      //Process message in 16-word blocks
+      if(context->size == 64)
+      {
+         //Transform the 16-word block
+         ripemd160ProcessBlock(context);
+         //Empty the buffer
+         context->size = 0;
+      }
+   }
+}
+
+
+/**
+ * @brief Finish the RIPEMD-160 message digest
+ * @param[in] context Pointer to the RIPEMD-160 context
+ * @param[out] digest Calculated digest (optional parameter)
+ **/
+
+void ripemd160Final(Ripemd160Context *context, uint8_t *digest)
+{
+   uint_t i;
+   size_t paddingSize;
+   uint64_t totalSize;
+
+   //Length of the original message (before padding)
+   totalSize = context->totalSize * 8;
+
+   //Pad the message so that its length is congruent to 56 modulo 64
+   if(context->size < 56)
+      paddingSize = 56 - context->size;
+   else
+      paddingSize = 64 + 56 - context->size;
+
+   //Append padding
+   ripemd160Update(context, padding, paddingSize);
+
+   //Append the length of the original message
+   context->x[14] = htole32((uint32_t) totalSize);
+   context->x[15] = htole32((uint32_t) (totalSize >> 32));
+
+   //Calculate the message digest
+   ripemd160ProcessBlock(context);
+
+   //Convert from host byte order to little-endian byte order
+   for(i = 0; i < 4; i++)
+      context->h[i] = htole32(context->h[i]);
+
+   //Copy the resulting digest
+   if(digest != NULL)
+      memcpy(digest, context->digest, RIPEMD160_DIGEST_SIZE);
+}
+
+
+/**
+ * @brief Process message in 16-word blocks
+ * @param[in] context Pointer to the RIPEMD-160 context
+ **/
+
+void ripemd160ProcessBlock(Ripemd160Context *context)
+{
+   uint_t i;
+
+   //Initialize the working registers
+   uint32_t aa= context->h[0];
+   uint32_t bb = context->h[1];
+   uint32_t cc = context->h[2];
+   uint32_t dd = context->h[3];
+   uint32_t ee = context->h[4];
+   uint32_t aaa = context->h[0];
+   uint32_t bbb = context->h[1];
+   uint32_t ccc = context->h[2];
+   uint32_t ddd = context->h[3];
+   uint32_t eee = context->h[4];
+
+   //Process message in 16-word blocks
+   uint32_t *x = context->x;
+
+   //Convert from little-endian byte order to host byte order
+   for(i = 0; i < 16; i++)
+      x[i] = letoh32(x[i]);
+
+   //Round 1
+   FF(aa, bb, cc, dd, ee, x[0],  11);
+   FF(ee, aa, bb, cc, dd, x[1],  14);
+   FF(dd, ee, aa, bb, cc, x[2],  15);
+   FF(cc, dd, ee, aa, bb, x[3],  12);
+   FF(bb, cc, dd, ee, aa, x[4],  5);
+   FF(aa, bb, cc, dd, ee, x[5],  8);
+   FF(ee, aa, bb, cc, dd, x[6],  7);
+   FF(dd, ee, aa, bb, cc, x[7],  9);
+   FF(cc, dd, ee, aa, bb, x[8],  11);
+   FF(bb, cc, dd, ee, aa, x[9],  13);
+   FF(aa, bb, cc, dd, ee, x[10], 14);
+   FF(ee, aa, bb, cc, dd, x[11], 15);
+   FF(dd, ee, aa, bb, cc, x[12], 6);
+   FF(cc, dd, ee, aa, bb, x[13], 7);
+   FF(bb, cc, dd, ee, aa, x[14], 9);
+   FF(aa, bb, cc, dd, ee, x[15], 8);
+
+   //Round 2
+   GG(ee, aa, bb, cc, dd, x[7],  7);
+   GG(dd, ee, aa, bb, cc, x[4],  6);
+   GG(cc, dd, ee, aa, bb, x[13], 8);
+   GG(bb, cc, dd, ee, aa, x[1],  13);
+   GG(aa, bb, cc, dd, ee, x[10], 11);
+   GG(ee, aa, bb, cc, dd, x[6],  9);
+   GG(dd, ee, aa, bb, cc, x[15], 7);
+   GG(cc, dd, ee, aa, bb, x[3],  15);
+   GG(bb, cc, dd, ee, aa, x[12], 7);
+   GG(aa, bb, cc, dd, ee, x[0],  12);
+   GG(ee, aa, bb, cc, dd, x[9],  15);
+   GG(dd, ee, aa, bb, cc, x[5],  9);
+   GG(cc, dd, ee, aa, bb, x[2],  11);
+   GG(bb, cc, dd, ee, aa, x[14], 7);
+   GG(aa, bb, cc, dd, ee, x[11], 13);
+   GG(ee, aa, bb, cc, dd, x[8],  12);
+
+   //Round 3
+   HH(dd, ee, aa, bb, cc, x[3],  11);
+   HH(cc, dd, ee, aa, bb, x[10], 13);
+   HH(bb, cc, dd, ee, aa, x[14], 6);
+   HH(aa, bb, cc, dd, ee, x[4],  7);
+   HH(ee, aa, bb, cc, dd, x[9],  14);
+   HH(dd, ee, aa, bb, cc, x[15], 9);
+   HH(cc, dd, ee, aa, bb, x[8],  13);
+   HH(bb, cc, dd, ee, aa, x[1],  15);
+   HH(aa, bb, cc, dd, ee, x[2],  14);
+   HH(ee, aa, bb, cc, dd, x[7],  8);
+   HH(dd, ee, aa, bb, cc, x[0],  13);
+   HH(cc, dd, ee, aa, bb, x[6],  6);
+   HH(bb, cc, dd, ee, aa, x[13], 5);
+   HH(aa, bb, cc, dd, ee, x[11], 12);
+   HH(ee, aa, bb, cc, dd, x[5],  7);
+   HH(dd, ee, aa, bb, cc, x[12], 5);
+
+   //Round 4
+   II(cc, dd, ee, aa, bb, x[1],  11);
+   II(bb, cc, dd, ee, aa, x[9],  12);
+   II(aa, bb, cc, dd, ee, x[11], 14);
+   II(ee, aa, bb, cc, dd, x[10], 15);
+   II(dd, ee, aa, bb, cc, x[0],  14);
+   II(cc, dd, ee, aa, bb, x[8],  15);
+   II(bb, cc, dd, ee, aa, x[12], 9);
+   II(aa, bb, cc, dd, ee, x[4],  8);
+   II(ee, aa, bb, cc, dd, x[13], 9);
+   II(dd, ee, aa, bb, cc, x[3],  14);
+   II(cc, dd, ee, aa, bb, x[7],  5);
+   II(bb, cc, dd, ee, aa, x[15], 6);
+   II(aa, bb, cc, dd, ee, x[14], 8);
+   II(ee, aa, bb, cc, dd, x[5],  6);
+   II(dd, ee, aa, bb, cc, x[6],  5);
+   II(cc, dd, ee, aa, bb, x[2],  12);
+
+   //Round 5
+   JJ(bb, cc, dd, ee, aa, x[4],  9);
+   JJ(aa, bb, cc, dd, ee, x[0],  15);
+   JJ(ee, aa, bb, cc, dd, x[5],  5);
+   JJ(dd, ee, aa, bb, cc, x[9],  11);
+   JJ(cc, dd, ee, aa, bb, x[7],  6);
+   JJ(bb, cc, dd, ee, aa, x[12], 8);
+   JJ(aa, bb, cc, dd, ee, x[2],  13);
+   JJ(ee, aa, bb, cc, dd, x[10], 12);
+   JJ(dd, ee, aa, bb, cc, x[14], 5);
+   JJ(cc, dd, ee, aa, bb, x[1],  12);
+   JJ(bb, cc, dd, ee, aa, x[3],  13);
+   JJ(aa, bb, cc, dd, ee, x[8],  14);
+   JJ(ee, aa, bb, cc, dd, x[11], 11);
+   JJ(dd, ee, aa, bb, cc, x[6],  8);
+   JJ(cc, dd, ee, aa, bb, x[15], 5);
+   JJ(bb, cc, dd, ee, aa, x[13], 6);
+
+   //Parallel round 1
+   JJJ(aaa, bbb, ccc, ddd, eee, x[5],  8);
+   JJJ(eee, aaa, bbb, ccc, ddd, x[14], 9);
+   JJJ(ddd, eee, aaa, bbb, ccc, x[7],  9);
+   JJJ(ccc, ddd, eee, aaa, bbb, x[0],  11);
+   JJJ(bbb, ccc, ddd, eee, aaa, x[9],  13);
+   JJJ(aaa, bbb, ccc, ddd, eee, x[2],  15);
+   JJJ(eee, aaa, bbb, ccc, ddd, x[11], 15);
+   JJJ(ddd, eee, aaa, bbb, ccc, x[4],  5);
+   JJJ(ccc, ddd, eee, aaa, bbb, x[13], 7);
+   JJJ(bbb, ccc, ddd, eee, aaa, x[6],  7);
+   JJJ(aaa, bbb, ccc, ddd, eee, x[15], 8);
+   JJJ(eee, aaa, bbb, ccc, ddd, x[8],  11);
+   JJJ(ddd, eee, aaa, bbb, ccc, x[1],  14);
+   JJJ(ccc, ddd, eee, aaa, bbb, x[10], 14);
+   JJJ(bbb, ccc, ddd, eee, aaa, x[3],  12);
+   JJJ(aaa, bbb, ccc, ddd, eee, x[12], 6);
+
+   //Parallel round 2
+   III(eee, aaa, bbb, ccc, ddd, x[6],   9);
+   III(ddd, eee, aaa, bbb, ccc, x[11], 13);
+   III(ccc, ddd, eee, aaa, bbb, x[3],  15);
+   III(bbb, ccc, ddd, eee, aaa, x[7],  7);
+   III(aaa, bbb, ccc, ddd, eee, x[0],  12);
+   III(eee, aaa, bbb, ccc, ddd, x[13], 8);
+   III(ddd, eee, aaa, bbb, ccc, x[5],  9);
+   III(ccc, ddd, eee, aaa, bbb, x[10], 11);
+   III(bbb, ccc, ddd, eee, aaa, x[14], 7);
+   III(aaa, bbb, ccc, ddd, eee, x[15], 7);
+   III(eee, aaa, bbb, ccc, ddd, x[8],  12);
+   III(ddd, eee, aaa, bbb, ccc, x[12], 7);
+   III(ccc, ddd, eee, aaa, bbb, x[4],  6);
+   III(bbb, ccc, ddd, eee, aaa, x[9],  15);
+   III(aaa, bbb, ccc, ddd, eee, x[1],  13);
+   III(eee, aaa, bbb, ccc, ddd, x[2],  11);
+
+   //Parallel round 3
+   HHH(ddd, eee, aaa, bbb, ccc, x[15], 9);
+   HHH(ccc, ddd, eee, aaa, bbb, x[5],  7);
+   HHH(bbb, ccc, ddd, eee, aaa, x[1],  15);
+   HHH(aaa, bbb, ccc, ddd, eee, x[3],  11);
+   HHH(eee, aaa, bbb, ccc, ddd, x[7],  8);
+   HHH(ddd, eee, aaa, bbb, ccc, x[14], 6);
+   HHH(ccc, ddd, eee, aaa, bbb, x[6],  6);
+   HHH(bbb, ccc, ddd, eee, aaa, x[9],  14);
+   HHH(aaa, bbb, ccc, ddd, eee, x[11], 12);
+   HHH(eee, aaa, bbb, ccc, ddd, x[8],  13);
+   HHH(ddd, eee, aaa, bbb, ccc, x[12], 5);
+   HHH(ccc, ddd, eee, aaa, bbb, x[2],  14);
+   HHH(bbb, ccc, ddd, eee, aaa, x[10], 13);
+   HHH(aaa, bbb, ccc, ddd, eee, x[0],  13);
+   HHH(eee, aaa, bbb, ccc, ddd, x[4],  7);
+   HHH(ddd, eee, aaa, bbb, ccc, x[13], 5);
+
+   //Parallel round 4
+   GGG(ccc, ddd, eee, aaa, bbb, x[8],  15);
+   GGG(bbb, ccc, ddd, eee, aaa, x[6],  5);
+   GGG(aaa, bbb, ccc, ddd, eee, x[4],  8);
+   GGG(eee, aaa, bbb, ccc, ddd, x[1],  11);
+   GGG(ddd, eee, aaa, bbb, ccc, x[3],  14);
+   GGG(ccc, ddd, eee, aaa, bbb, x[11], 14);
+   GGG(bbb, ccc, ddd, eee, aaa, x[15], 6);
+   GGG(aaa, bbb, ccc, ddd, eee, x[0],  14);
+   GGG(eee, aaa, bbb, ccc, ddd, x[5],  6);
+   GGG(ddd, eee, aaa, bbb, ccc, x[12], 9);
+   GGG(ccc, ddd, eee, aaa, bbb, x[2],  12);
+   GGG(bbb, ccc, ddd, eee, aaa, x[13], 9);
+   GGG(aaa, bbb, ccc, ddd, eee, x[9],  12);
+   GGG(eee, aaa, bbb, ccc, ddd, x[7],  5);
+   GGG(ddd, eee, aaa, bbb, ccc, x[10], 15);
+   GGG(ccc, ddd, eee, aaa, bbb, x[14], 8);
+
+   //Parallel round 5
+   FFF(bbb, ccc, ddd, eee, aaa, x[12], 8);
+   FFF(aaa, bbb, ccc, ddd, eee, x[15], 5);
+   FFF(eee, aaa, bbb, ccc, ddd, x[10], 12);
+   FFF(ddd, eee, aaa, bbb, ccc, x[4],  9);
+   FFF(ccc, ddd, eee, aaa, bbb, x[1],  12);
+   FFF(bbb, ccc, ddd, eee, aaa, x[5],  5);
+   FFF(aaa, bbb, ccc, ddd, eee, x[8],  14);
+   FFF(eee, aaa, bbb, ccc, ddd, x[7],  6);
+   FFF(ddd, eee, aaa, bbb, ccc, x[6],  8);
+   FFF(ccc, ddd, eee, aaa, bbb, x[2],  13);
+   FFF(bbb, ccc, ddd, eee, aaa, x[13], 6);
+   FFF(aaa, bbb, ccc, ddd, eee, x[14], 5);
+   FFF(eee, aaa, bbb, ccc, ddd, x[0],  15);
+   FFF(ddd, eee, aaa, bbb, ccc, x[3],  13);
+   FFF(ccc, ddd, eee, aaa, bbb, x[9],  11);
+   FFF(bbb, ccc, ddd, eee, aaa, x[11], 11);
+
+   //Combine results
+   ddd = context->h[1] + cc + ddd;
+   context->h[1] = context->h[2] + dd + eee;
+   context->h[2] = context->h[3] + ee + aaa;
+   context->h[3] = context->h[4] + aa + bbb;
+   context->h[4] = context->h[0] + bb + ccc;
+   context->h[0] = ddd;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/ripemd160.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,75 @@
+/**
+ * @file ripemd160.h
+ * @brief RIPEMD-160 hash function
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _RIPEMD160_H
+#define _RIPEMD160_H
+
+//Dependencies
+#include "crypto.h"
+
+//RIPEMD-160 block size
+#define RIPEMD160_BLOCK_SIZE 64
+//RIPEMD-160 digest size
+#define RIPEMD160_DIGEST_SIZE 20
+//Common interface for hash algorithms
+#define RIPEMD160_HASH_ALGO (&ripemd160HashAlgo)
+
+
+/**
+ * @brief RIPEMD-160 algorithm context
+ **/
+
+typedef struct
+{
+   union
+   {
+      uint32_t h[5];
+      uint8_t digest[20];
+   };
+   union
+   {
+      uint32_t x[16];
+      uint8_t buffer[64];
+   };
+   size_t size;
+   uint64_t totalSize;
+} Ripemd160Context;
+
+
+//RIPEMD-160 related constants
+extern const HashAlgo ripemd160HashAlgo;
+
+//RIPEMD-160 related functions
+error_t ripemd160Compute(const void *data, size_t length, uint8_t *digest);
+void ripemd160Init(Ripemd160Context *context);
+void ripemd160Update(Ripemd160Context *context, const void *data, size_t length);
+void ripemd160Final(Ripemd160Context *context, uint8_t *digest);
+void ripemd160ProcessBlock(Ripemd160Context *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/rsa.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,944 @@
+/**
+ * @file rsa.c
+ * @brief RSA public-key cryptography standard
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * RSA is an algorithm for public-key cryptography which is suitable for signing
+ * as well as encryption. Refer to PKCS #1 (RSA Cryptography Standard)
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include <string.h>
+#include "crypto.h"
+#include "rsa.h"
+#include "mpi.h"
+#include "asn1.h"
+#include "oid.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (RSA_SUPPORT == ENABLED)
+
+//PKCS #1 OID (1.2.840.113549.1.1)
+const uint8_t PKCS1_OID[8] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01};
+//RSA encryption OID (1.2.840.113549.1.1.1)
+const uint8_t RSA_ENCRYPTION_OID[9] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01};
+//MD5 with RSA encryption OID (1.2.840.113549.1.1.4)
+const uint8_t MD5_WITH_RSA_ENCRYPTION_OID[9] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04};
+//SHA-1 with RSA encryption OID (1.2.840.113549.1.1.5)
+const uint8_t SHA1_WITH_RSA_ENCRYPTION_OID[9] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05};
+//SHA-256 with RSA encryption OID (1.2.840.113549.1.1.11)
+const uint8_t SHA256_WITH_RSA_ENCRYPTION_OID[9] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B};
+//SHA-384 with RSA encryption OID (1.2.840.113549.1.1.12)
+const uint8_t SHA384_WITH_RSA_ENCRYPTION_OID[9] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C};
+//SHA-512 with RSA encryption OID (1.2.840.113549.1.1.13)
+const uint8_t SHA512_WITH_RSA_ENCRYPTION_OID[9] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D};
+//RSA PKCS #1 v1.5 signature with SHA-3-224 OID (2.16.840.1.101.3.4.3.13)
+const uint8_t RSASSA_PKCS1_v1_5_WITH_SHA3_224_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x0D};
+//RSA PKCS #1 v1.5 signature with SHA-3-256 OID (2.16.840.1.101.3.4.3.14)
+const uint8_t RSASSA_PKCS1_v1_5_WITH_SHA3_256_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x0E};
+//RSA PKCS #1 v1.5 signature with SHA-3-384 OID (2.16.840.1.101.3.4.3.15)
+const uint8_t RSASSA_PKCS1_v1_5_WITH_SHA3_384_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x0F};
+//RSA PKCS #1 v1.5 signature with SHA-3-512 OID (2.16.840.1.101.3.4.3.16)
+const uint8_t RSASSA_PKCS1_v1_5_WITH_SHA3_512_OID[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x10};
+
+
+/**
+ * @brief Initialize a RSA public key
+ * @param[in] key Pointer to the RSA public key to initialize
+ **/
+
+void rsaInitPublicKey(RsaPublicKey *key)
+{
+   //Initialize multiple precision integers
+   mpiInit(&key->n);
+   mpiInit(&key->e);
+}
+
+
+/**
+ * @brief Release a RSA public key
+ * @param[in] key Pointer to the RSA public key to free
+ **/
+
+void rsaFreePublicKey(RsaPublicKey *key)
+{
+   //Free multiple precision integers
+   mpiFree(&key->n);
+   mpiFree(&key->e);
+}
+
+
+/**
+ * @brief Initialize a RSA private key
+ * @param[in] key Pointer to the RSA private key to initialize
+ **/
+
+void rsaInitPrivateKey(RsaPrivateKey *key)
+{
+   //Initialize multiple precision integers
+   mpiInit(&key->n);
+   mpiInit(&key->e);
+   mpiInit(&key->d);
+   mpiInit(&key->p);
+   mpiInit(&key->q);
+   mpiInit(&key->dp);
+   mpiInit(&key->dq);
+   mpiInit(&key->qinv);
+}
+
+
+/**
+ * @brief Release a RSA private key
+ * @param[in] key Pointer to the RSA private key to free
+ **/
+
+void rsaFreePrivateKey(RsaPrivateKey *key)
+{
+   //Free multiple precision integers
+   mpiFree(&key->n);
+   mpiFree(&key->e);
+   mpiFree(&key->d);
+   mpiFree(&key->p);
+   mpiFree(&key->q);
+   mpiFree(&key->dp);
+   mpiFree(&key->dq);
+   mpiFree(&key->qinv);
+}
+
+
+/**
+ * @brief RSA encryption primitive
+ *
+ * The RSA encryption primitive produces a ciphertext representative from
+ * a message representative under the control of a public key
+ *
+ * @param[in] key RSA public key
+ * @param[in] m Message representative
+ * @param[out] c Ciphertext representative
+ * @return Error code
+ **/
+
+error_t rsaep(const RsaPublicKey *key, const Mpi *m, Mpi *c)
+{
+   //Ensure the RSA public key is valid
+   if(!key->n.size || !key->e.size)
+      return ERROR_INVALID_PARAMETER;
+
+   //The message representative m shall be between 0 and n - 1
+   if(mpiCompInt(m, 0) < 0 || mpiComp(m, &key->n) >= 0)
+      return ERROR_OUT_OF_RANGE;
+
+   //Perform modular exponentiation (c = m ^ e mod n)
+   return mpiExpMod(c, m, &key->e, &key->n);
+}
+
+
+/**
+ * @brief RSA decryption primitive
+ *
+ * The RSA decryption primitive recovers the message representative from
+ * the ciphertext representative under the control of a private key
+ *
+ * @param[in] key RSA private key
+ * @param[in] c Ciphertext representative
+ * @param[out] m Message representative
+ * @return Error code
+ **/
+
+error_t rsadp(const RsaPrivateKey *key, const Mpi *c, Mpi *m)
+{
+   error_t error;
+   Mpi m1;
+   Mpi m2;
+   Mpi h;
+
+   //The ciphertext representative c shall be between 0 and n - 1
+   if(mpiCompInt(c, 0) < 0 || mpiComp(c, &key->n) >= 0)
+      return ERROR_OUT_OF_RANGE;
+
+   //Initialize multiple-precision integers
+   mpiInit(&m1);
+   mpiInit(&m2);
+   mpiInit(&h);
+
+   //Use the Chinese remainder algorithm?
+   if(key->n.size && key->p.size && key->q.size &&
+      key->dp.size && key->dq.size && key->qinv.size)
+   {
+      //Compute m1 = c ^ dP mod p
+      MPI_CHECK(mpiExpMod(&m1, c, &key->dp, &key->p));
+      //Compute m2 = c ^ dQ mod q
+      MPI_CHECK(mpiExpMod(&m2, c, &key->dq, &key->q));
+      //Let h = (m1 - m2) * qInv mod p
+      MPI_CHECK(mpiSub(&h, &m1, &m2));
+      MPI_CHECK(mpiMulMod(&h, &h, &key->qinv, &key->p));
+      //Let m = m2 + q * h
+      MPI_CHECK(mpiMul(m, &key->q, &h));
+      MPI_CHECK(mpiAdd(m, m, &m2));
+   }
+   //Use modular exponentiation?
+   else if(key->n.size && key->d.size)
+   {
+      //Let m = c ^ d mod n
+      error = mpiExpMod(m, c, &key->d, &key->n);
+   }
+   //Invalid parameters?
+   else
+   {
+      //Report an error
+      error = ERROR_INVALID_PARAMETER;
+   }
+
+end:
+   //Free previously allocated memory
+   mpiFree(&m1);
+   mpiFree(&m2);
+   mpiFree(&h);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief RSA signature primitive
+ *
+ * The RSA signature primitive produces a signature representative from
+ * a message representative under the control of a private key
+ *
+ * @param[in] key RSA private key
+ * @param[in] m Message representative
+ * @param[out] s Signature representative
+ * @return Error code
+ **/
+
+error_t rsasp1(const RsaPrivateKey *key, const Mpi *m, Mpi *s)
+{
+   //RSASP1 primitive is the same as RSADP except for the names of its
+   //input and output arguments. They are distinguished as they are
+   //intended for different purposes
+   return rsadp(key, m, s);
+}
+
+
+/**
+ * @brief RSA verification primitive
+ *
+ * The RSA verification primitive recovers the message representative from
+ * the signature representative under the control of a public key
+ *
+ * @param[in] key RSA public key
+ * @param[in] s Signature representative
+ * @param[out] m Message representative
+ * @return Error code
+ **/
+
+error_t rsavp1(const RsaPublicKey *key, const Mpi *s, Mpi *m)
+{
+   //RSAVP1 primitive is the same as RSAEP except for the names of its
+   //input and output arguments. They are distinguished as they are
+   //intended for different purposes
+   return rsaep(key, s, m);
+}
+
+
+/**
+ * @brief PKCS #1 v1.5 encryption operation
+ * @param[in] prngAlgo PRNG algorithm
+ * @param[in] prngContext Pointer to the PRNG context
+ * @param[in] key Recipient's RSA public key
+ * @param[in] message Message to be encrypted
+ * @param[in] messageLength Length of the message to be encrypted
+ * @param[out] ciphertext Ciphertext resulting from the encryption operation
+ * @param[out] ciphertextLength Length of the resulting ciphertext
+ * @return Error code
+ **/
+
+error_t rsaesPkcs1v15Encrypt(const PrngAlgo *prngAlgo, void *prngContext, const RsaPublicKey *key,
+   const uint8_t *message, size_t messageLength, uint8_t *ciphertext, size_t *ciphertextLength)
+{
+   error_t error;
+   uint_t i;
+   uint_t j;
+   uint_t k;
+   uint_t n;
+   uint8_t *p;
+   Mpi m;
+   Mpi c;
+
+   //Check parameters
+   if(key == NULL || message == NULL)
+      return ERROR_INVALID_PARAMETER;
+   if(ciphertext == NULL || ciphertextLength == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_DEBUG("RSA PKCS #1 v1.5 encryption...\r\n");
+   TRACE_DEBUG("  Modulus:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->n);
+   TRACE_DEBUG("  Public exponent:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->e);
+   TRACE_DEBUG("  Message:\r\n");
+   TRACE_DEBUG_ARRAY("    ", message, messageLength);
+
+   //Initialize multiple-precision integers
+   mpiInit(&m);
+   mpiInit(&c);
+
+   //Get the length in octets of the modulus n
+   k = mpiGetByteLength(&key->n);
+
+   //Check the length of the message
+   if((messageLength + 11) > k)
+      return ERROR_INVALID_LENGTH;
+
+   //Point to the buffer where the encoded message EM will be formatted
+   p = ciphertext;
+
+   //The leading 0x00 octet ensures that the encoded message,
+   //converted to an integer, is less than the modulus
+   *(p++) = 0x00;
+   //For a public-key operation, the block type BT shall be 0x02
+   *(p++) = 0x02;
+
+   //Length of the padding string PS
+   n = k - messageLength - 3;
+
+   //Generate the padding string (pseudo-randomly generated non-zero octets)
+   while(n > 0)
+   {
+      //Generate random data
+      error = prngAlgo->read(prngContext, p, n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Parse the resulting octet string
+      for(i = 0, j = 0; j < n; j++)
+      {
+         //Strip any byte with a value of zero
+         if(p[j] != 0)
+            p[i++] = p[j];
+      }
+
+      //Advance data pointer
+      p += i;
+      n -= i;
+   }
+
+   //Append a 0x00 octet to the padding string
+   *(p++) = 0x00;
+   //Copy the message to be encrypted
+   memcpy(p, message, messageLength);
+
+   //Rewind to the beginning of the encoded message
+   p = ciphertext;
+
+   //Debug message
+   TRACE_DEBUG("  Encoded message\r\n");
+   TRACE_DEBUG_ARRAY("    ", p, k);
+
+   //Start of exception handling block
+   do
+   {
+      //Convert the encoded message EM to an integer message representative m
+      error = mpiReadRaw(&m, p, k);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Apply the RSAEP encryption primitive
+      error = rsaep(key, &m, &c);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Convert the ciphertext representative c to a ciphertext of length k octets
+      error = mpiWriteRaw(&c, ciphertext, k);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Length of the resulting ciphertext
+      *ciphertextLength = k;
+
+      //Debug message
+      TRACE_DEBUG("  Ciphertext:\r\n");
+      TRACE_DEBUG_ARRAY("    ", ciphertext, *ciphertextLength);
+
+      //End of exception handling block
+   } while(0);
+
+   //Free previously allocated memory
+   mpiFree(&m);
+   mpiFree(&c);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief PKCS #1 v1.5 decryption operation
+ * @param[in] key Recipient's RSA private key
+ * @param[in] ciphertext Ciphertext to be decrypted
+ * @param[in] ciphertextLength Length of the ciphertext to be decrypted
+ * @param[out] message Output buffer where to store the decrypted message
+ * @param[in] messageSize Size of the output buffer
+ * @param[out] messageLength Length of the decrypted message
+ * @return Error code
+ **/
+
+error_t rsaesPkcs1v15Decrypt(const RsaPrivateKey *key, const uint8_t *ciphertext,
+   size_t ciphertextLength, uint8_t *message, size_t messageSize, size_t *messageLength)
+{
+   error_t error;
+   uint_t i;
+   uint_t k;
+   uint8_t *em;
+   Mpi c;
+   Mpi m;
+
+   //Check parameters
+   if(key == NULL || ciphertext == NULL)
+      return ERROR_INVALID_PARAMETER;
+   if(message == NULL || messageLength == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_DEBUG("RSA PKCS #1 v1.5 decryption...\r\n");
+   TRACE_DEBUG("  Modulus:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->n);
+   TRACE_DEBUG("  Public exponent:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->e);
+   TRACE_DEBUG("  Private exponent:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->d);
+   TRACE_DEBUG("  Prime 1:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->p);
+   TRACE_DEBUG("  Prime 2:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->q);
+   TRACE_DEBUG("  Prime exponent 1:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->dp);
+   TRACE_DEBUG("  Prime exponent 2:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->dq);
+   TRACE_DEBUG("  Coefficient:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->qinv);
+   TRACE_DEBUG("  Ciphertext:\r\n");
+   TRACE_DEBUG_ARRAY("    ", ciphertext, ciphertextLength);
+
+   //Initialize multiple-precision integers
+   mpiInit(&c);
+   mpiInit(&m);
+
+   //Get the length in octets of the modulus n
+   k = mpiGetByteLength(&key->n);
+
+   //Check the length of the ciphertext
+   if(ciphertextLength != k || ciphertextLength < 11)
+      return ERROR_INVALID_LENGTH;
+
+   //Allocate a buffer to store the encoded message EM
+   em = cryptoAllocMem(k);
+   //Failed to allocate memory?
+   if(em == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Start of exception handling block
+   do
+   {
+      //Convert the ciphertext to an integer ciphertext representative c
+      error = mpiReadRaw(&c, ciphertext, ciphertextLength);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Apply the RSADP decryption primitive
+      error = rsadp(key, &c, &m);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Convert the message representative m to an encoded message EM of length k octets
+      error = mpiWriteRaw(&m, em, k);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Debug message
+      TRACE_DEBUG("  Encoded message\r\n");
+      TRACE_DEBUG_ARRAY("    ", em, k);
+
+      //The first octet of EM must have a value of 0x00
+      //and the block type BT shall be 0x02
+      if(em[0] != 0x00 || em[1] != 0x02)
+      {
+         //Report an error
+         error = ERROR_UNEXPECTED_VALUE;
+         break;
+      }
+
+      //An octet with hexadecimal value 0x00 is used to separate PS from M
+      for(i = 2; i < k && em[i] != 0x00; i++);
+
+      //Check whether the padding string is valid
+      if(i < 10 || i >= k)
+      {
+         //Report an error
+         error = ERROR_INVALID_PADDING;
+         break;
+      }
+
+      //Ensure that the output buffer is large enough
+      if(messageSize < (k - i - 1))
+      {
+         //Report an error
+         error = ERROR_INVALID_LENGTH;
+         break;
+      }
+
+      //Recover the length of the message
+      *messageLength = k - i - 1;
+      //Copy the message contents
+      memcpy(message, em + i + 1, *messageLength);
+
+      //Debug message
+      TRACE_DEBUG("  Message:\r\n");
+      TRACE_DEBUG_ARRAY("    ", message, *messageLength);
+
+      //End of exception handling block
+   } while(0);
+
+   //Release multiple precision integers
+   mpiFree(&c);
+   mpiFree(&m);
+   //Free previously allocated memory
+   cryptoFreeMem(em);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief PKCS #1 v1.5 signature generation operation
+ * @param[in] key Signer's RSA private key
+ * @param[in] hash Hash function used to digest the message
+ * @param[in] digest Digest of the message to be signed
+ * @param[out] signature Resulting signature
+ * @param[out] signatureLength Length of the resulting signature
+ * @return Error code
+ **/
+
+error_t rsassaPkcs1v15Sign(const RsaPrivateKey *key, const HashAlgo *hash,
+   const uint8_t *digest, uint8_t *signature, size_t *signatureLength)
+{
+   error_t error;
+   uint_t k;
+   uint8_t *em;
+   Mpi m;
+   Mpi s;
+
+   //Check parameters
+   if(key == NULL || hash == NULL || digest == NULL)
+      return ERROR_INVALID_PARAMETER;
+   if(signature == NULL || signatureLength == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_DEBUG("RSA PKCS #1 v1.5 signature generation...\r\n");
+   TRACE_DEBUG("  Modulus:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->n);
+   TRACE_DEBUG("  Public exponent:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->e);
+   TRACE_DEBUG("  Private exponent:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->d);
+   TRACE_DEBUG("  Prime 1:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->p);
+   TRACE_DEBUG("  Prime 2:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->q);
+   TRACE_DEBUG("  Prime exponent 1:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->dp);
+   TRACE_DEBUG("  Prime exponent 2:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->dq);
+   TRACE_DEBUG("  Coefficient:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->qinv);
+   TRACE_DEBUG("  Message digest:\r\n");
+   TRACE_DEBUG_ARRAY("    ", digest, hash->digestSize);
+
+   //Initialize multiple-precision integers
+   mpiInit(&m);
+   mpiInit(&s);
+
+   //Get the length in octets of the modulus n
+   k = mpiGetByteLength(&key->n);
+   //Point to the buffer where the encoded message EM will be generated
+   em = signature;
+
+   //Apply the EMSA-PKCS1-v1.5 encoding operation
+   error = emsaPkcs1v15Encode(hash, digest, em, k);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Debug message
+   TRACE_DEBUG("  Encoded message\r\n");
+   TRACE_DEBUG_ARRAY("    ", em, k);
+
+   //Start of exception handling block
+   do
+   {
+      //Convert the encoded message EM to an integer message representative m
+      error = mpiReadRaw(&m, em, k);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Apply the RSASP1 signature primitive
+      error = rsasp1(key, &m, &s);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Convert the signature representative s to a signature of length k octets
+      error = mpiWriteRaw(&s, signature, k);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Length of the resulting signature
+      *signatureLength = k;
+
+      //Debug message
+      TRACE_DEBUG("  Signature:\r\n");
+      TRACE_DEBUG_ARRAY("    ", signature, *signatureLength);
+
+      //End of exception handling block
+   } while(0);
+
+   //Free previously allocated memory
+   mpiFree(&m);
+   mpiFree(&s);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief PKCS #1 v1.5 signature verification operation
+ * @param[in] key Signer's RSA public key
+ * @param[in] hash Hash function used to digest the message
+ * @param[in] digest Digest of the message whose signature is to be verified
+ * @param[in] signature Signature to be verified
+ * @param[in] signatureLength Length of the signature to be verified
+ * @return Error code
+ **/
+
+error_t rsassaPkcs1v15Verify(const RsaPublicKey *key, const HashAlgo *hash,
+   const uint8_t *digest, const uint8_t *signature, size_t signatureLength)
+{
+   error_t error;
+   uint_t k;
+   uint8_t *em;
+   const uint8_t *oid;
+   size_t oidLength;
+   const uint8_t *d;
+   size_t dLength;
+   Mpi s;
+   Mpi m;
+
+   //Check parameters
+   if(key == NULL || hash == NULL || digest == NULL || signature == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_DEBUG("RSA PKCS #1 v1.5 signature verification...\r\n");
+   TRACE_DEBUG("  Modulus:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->n);
+   TRACE_DEBUG("  Public exponent:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->e);
+   TRACE_DEBUG("  Message digest:\r\n");
+   TRACE_DEBUG_ARRAY("    ", digest, hash->digestSize);
+   TRACE_DEBUG("  Signature:\r\n");
+   TRACE_DEBUG_ARRAY("    ", signature, signatureLength);
+
+   //Initialize multiple-precision integers
+   mpiInit(&s);
+   mpiInit(&m);
+
+   //Get the length in octets of the modulus n
+   k = mpiGetByteLength(&key->n);
+
+   //Check the length of the signature
+   if(signatureLength != k)
+      return ERROR_INVALID_LENGTH;
+
+   //Allocate a memory buffer to hold the encoded message
+   em = cryptoAllocMem(k);
+   //Failed to allocate memory?
+   if(em == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Start of exception handling block
+   do
+   {
+      //Convert the signature to an integer signature representative s
+      error = mpiReadRaw(&s, signature, signatureLength);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Apply the RSAVP1 verification primitive
+      error = rsavp1(key, &s, &m);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Convert the message representative m to an encoded message EM of length k octets
+      error = mpiWriteRaw(&m, em, k);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Debug message
+      TRACE_DEBUG("  Encoded message\r\n");
+      TRACE_DEBUG_ARRAY("    ", em, k);
+
+      //Parse the encoded message EM
+      error = emsaPkcs1v15Decode(em, k, &oid, &oidLength, &d, &dLength);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Assume an error...
+      error = ERROR_INVALID_SIGNATURE_ALGO;
+      //Ensure the hash algorithm identifier matches the OID
+      if(oidComp(oid, oidLength, hash->oid, hash->oidSize))
+         break;
+      //Check the length of the digest
+      if(dLength != hash->digestSize)
+         break;
+
+      //Compare the message digest
+      error = memcmp(digest, d, dLength) ? ERROR_INVALID_SIGNATURE : NO_ERROR;
+
+      //End of exception handling block
+   } while(0);
+
+   //Release multiple precision integers
+   mpiFree(&s);
+   mpiFree(&m);
+   //Free previously allocated memory
+   cryptoFreeMem(em);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief PKCS #1 v1.5 encoding method
+ * @param[in] hash Hash function used to digest the message
+ * @param[in] digest Digest of the message to be signed
+ * @param[out] em Encoded message
+ * @param[in] emLength Intended length of the encoded message
+ * @return Error code
+ **/
+
+error_t emsaPkcs1v15Encode(const HashAlgo *hash,
+   const uint8_t *digest, uint8_t *em, size_t emLength)
+{
+   uint_t i;
+   size_t paddingLength;
+
+   //Ensure the length of the digest is valid
+   if((hash->oidSize + hash->digestSize + 21) > emLength)
+      return ERROR_INVALID_LENGTH;
+
+   //The leading 0x00 octet ensures that the encoded message,
+   //converted to an integer, is less than the modulus
+   em[0] = 0x00;
+   //Block type 0x01 is used for private-key operations
+   em[1] = 0x01;
+
+   //Compute the length of the padding string PS
+   paddingLength = emLength - hash->oidSize - hash->digestSize - 13;
+   //Fill the padding string with 0xFF
+   memset(em + 2, 0xFF, paddingLength);
+
+   //Point to the byte that follows PS
+   i = paddingLength + 2;
+   //Append a 0x00 octet to PS
+   em[i++] = 0x00;
+
+   //Encode the DigestInfo using ASN.1
+   em[i++] = ASN1_ENCODING_CONSTRUCTED | ASN1_TYPE_SEQUENCE;
+   em[i++] = (uint8_t) (hash->oidSize + hash->digestSize + 8);
+   em[i++] = ASN1_ENCODING_CONSTRUCTED | ASN1_TYPE_SEQUENCE;
+   em[i++] = (uint8_t) (hash->oidSize + 4);
+   em[i++] = ASN1_TYPE_OBJECT_IDENTIFIER;
+   em[i++] = (uint8_t) hash->oidSize;
+
+   //Copy the hash algorithm OID
+   memcpy(em + i, hash->oid, hash->oidSize);
+   i += hash->oidSize;
+
+   //Encode the rest of the ASN.1 structure
+   em[i++] = ASN1_TYPE_NULL;
+   em[i++] = 0;
+   em[i++] = ASN1_TYPE_OCTET_STRING;
+   em[i++] = (uint8_t) hash->digestSize;
+
+   //Append the hash value
+   memcpy(em + i, digest, hash->digestSize);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief PKCS #1 v1.5 decoding method
+ * @param[in] em Encoded message
+ * @param[in] emLength Length of the encoded message
+ * @param[out] oid Hash algorithm OID
+ * @param[out] oidLength Length of the hash algorithm OID
+ * @param[out] digest Digest value
+ * @param[out] digestLength Length of the digest value
+
+ * @return Error code
+ **/
+
+error_t emsaPkcs1v15Decode(const uint8_t *em, size_t emLength, const uint8_t **oid,
+   size_t *oidLength, const uint8_t **digest, size_t *digestLength)
+{
+   error_t error;
+   uint_t i;
+   size_t length;
+   const uint8_t *data;
+   Asn1Tag tag;
+
+   //Check the length of the encoded message EM
+   if(emLength < 11)
+      return ERROR_INVALID_LENGTH;
+
+   //The first octet of EM must have a value of 0x00
+   if(em[0] != 0x00)
+      return ERROR_UNEXPECTED_VALUE;
+   //The block type BT shall be 0x01
+   if(em[1] != 0x01)
+      return ERROR_UNEXPECTED_VALUE;
+
+   //Check the padding string PS
+   for(i = 2; i < emLength; i++)
+   {
+      //A 0x00 octet indicates the end of the padding string
+      if(em[i] == 0x00)
+         break;
+      //Each byte of PS must be set to 0xFF when the block type is 0x01
+      if(em[i] != 0xFF)
+         return ERROR_INVALID_PADDING;
+   }
+
+   //Check whether the padding string is properly terminated
+   if(i >= emLength)
+      return ERROR_INVALID_PADDING;
+   //The length of PS cannot be less than 8 octets
+   if(i < 10)
+      return ERROR_INVALID_PADDING;
+
+   //Point to the DigestInfo structure
+   data = em + i + 1;
+   length = emLength - i - 1;
+
+   //Read the contents of the DigestInfo structure
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode the ASN.1 tag?
+   if(error)
+      return ERROR_INVALID_TAG;
+
+   //Enforce encoding, class and type
+   if(!tag.constructed || tag.objType != ASN1_TYPE_SEQUENCE)
+      return ERROR_INVALID_TAG;
+
+   //Point to the DigestAlgorithm structure
+   data = tag.value;
+   length = tag.length;
+
+   //Decode the DigestAlgorithm tag
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode the ASN.1 tag?
+   if(error)
+      return ERROR_INVALID_TAG;
+
+   //Enforce encoding, class and type
+   if(!tag.constructed || tag.objType != ASN1_TYPE_SEQUENCE)
+      return ERROR_INVALID_TAG;
+
+   //Save the location of the next tag
+   data += tag.totalLength;
+   length -= tag.totalLength;
+
+   //Decode the AlgorithmIdentifier tag
+   error = asn1ReadTag(tag.value, tag.length, &tag);
+   //Failed to decode the ASN.1 tag?
+   if(error)
+      return ERROR_INVALID_TAG;
+
+   //Enforce encoding, class and type
+   if(tag.constructed || tag.objType != ASN1_TYPE_OBJECT_IDENTIFIER)
+      return ERROR_INVALID_TAG;
+
+   //Save the hash algorithm OID
+   *oid = tag.value;
+   *oidLength = tag.length;
+
+   //Decode the DigestValue tag
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode the ASN.1 tag?
+   if(error)
+      return ERROR_INVALID_TAG;
+
+   //Enforce encoding, class and type
+   if(tag.constructed || tag.objType != ASN1_TYPE_OCTET_STRING)
+      return ERROR_INVALID_TAG;
+
+   //Save the hash value
+   *digest = tag.value;
+   *digestLength = tag.length;
+
+   //EM successfully decoded
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/rsa.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,109 @@
+/**
+ * @file rsa.h
+ * @brief RSA public-key cryptography standard
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _RSA_H
+#define _RSA_H
+
+//Dependencies
+#include "crypto.h"
+#include "mpi.h"
+
+
+/**
+ * @brief RSA public key
+ **/
+
+typedef struct
+{
+   Mpi n; ///<Modulus
+   Mpi e; ///<Public exponent
+} RsaPublicKey;
+
+
+/**
+ * @brief RSA private key
+ **/
+
+typedef struct
+{
+   Mpi n;    ///<Modulus
+   Mpi e;    ///<Public exponent
+   Mpi d;    ///<Private exponent
+   Mpi p;    ///<First factor
+   Mpi q;    ///<Second factor
+   Mpi dp;   ///<First factor's CRT exponent
+   Mpi dq;   ///<second factor's CRT exponent
+   Mpi qinv; ///<CRT coefficient
+} RsaPrivateKey;
+
+
+//RSA related constants
+extern const uint8_t PKCS1_OID[8];
+extern const uint8_t RSA_ENCRYPTION_OID[9];
+extern const uint8_t MD5_WITH_RSA_ENCRYPTION_OID[9];
+extern const uint8_t SHA1_WITH_RSA_ENCRYPTION_OID[9];
+extern const uint8_t SHA256_WITH_RSA_ENCRYPTION_OID[9];
+extern const uint8_t SHA384_WITH_RSA_ENCRYPTION_OID[9];
+extern const uint8_t SHA512_WITH_RSA_ENCRYPTION_OID[9];
+extern const uint8_t RSASSA_PKCS1_v1_5_WITH_SHA3_224_OID[9];
+extern const uint8_t RSASSA_PKCS1_v1_5_WITH_SHA3_256_OID[9];
+extern const uint8_t RSASSA_PKCS1_v1_5_WITH_SHA3_384_OID[9];
+extern const uint8_t RSASSA_PKCS1_v1_5_WITH_SHA3_512_OID[9];
+
+//RSA related functions
+void rsaInitPublicKey(RsaPublicKey *key);
+void rsaFreePublicKey(RsaPublicKey *key);
+void rsaInitPrivateKey(RsaPrivateKey *key);
+void rsaFreePrivateKey(RsaPrivateKey *key);
+
+error_t rsaep(const RsaPublicKey *key, const Mpi *m, Mpi *c);
+error_t rsadp(const RsaPrivateKey *key, const Mpi *c, Mpi *m);
+
+error_t rsasp1(const RsaPrivateKey *key, const Mpi *m, Mpi *s);
+error_t rsavp1(const RsaPublicKey *key, const Mpi *s, Mpi *m);
+
+error_t rsaesPkcs1v15Encrypt(const PrngAlgo *prngAlgo, void *prngContext, const RsaPublicKey *key,
+   const uint8_t *message, size_t messageLength, uint8_t *ciphertext, size_t *ciphertextLength);
+
+error_t rsaesPkcs1v15Decrypt(const RsaPrivateKey *key, const uint8_t *ciphertext,
+   size_t ciphertextLength, uint8_t *message, size_t messageSize, size_t *messageLength);
+
+error_t rsassaPkcs1v15Sign(const RsaPrivateKey *key, const HashAlgo *hash,
+   const uint8_t *digest, uint8_t *signature, size_t *signatureLength);
+
+error_t rsassaPkcs1v15Verify(const RsaPublicKey *key, const HashAlgo *hash,
+   const uint8_t *digest, const uint8_t *signature, size_t signatureLength);
+
+error_t emsaPkcs1v15Encode(const HashAlgo *hash,
+   const uint8_t *digest, uint8_t *em, size_t emLength);
+
+error_t emsaPkcs1v15Decode(const uint8_t *em, size_t emLength, const uint8_t **oid,
+   size_t *oidLength, const uint8_t **digest, size_t *digestLength);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/seed.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,384 @@
+/**
+ * @file seed.c
+ * @brief SEED encryption algorithm
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * SEED is a 128-bit symmetric key block cipher. Refer to RFC 4269
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "seed.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (SEED_SUPPORT == ENABLED)
+
+//Function G
+#define G(x) (ss0[(x) & 0xFF] ^ ss1[((x) >> 8) & 0xFF] ^ ss2[((x) >> 16) & 0xFF] ^ ss3[((x) >> 24) & 0xFF])
+
+//Round function F
+#define F(k0, k1, r0, r1, t0, t1) \
+{ \
+   t1 = (r0 ^ k0) ^ (r1 ^ k1); \
+   t1 = G(t1); \
+   t0 = t1 + (r0 ^ k0); \
+   t0 = G(t0); \
+   t1 += t0; \
+   t1 = G(t1); \
+   t0 += t1; \
+}
+
+//Key schedule constants
+static const uint32_t kc[16] =
+{
+   0x9E3779B9, 0x3C6EF373, 0x78DDE6E6, 0xF1BBCDCC,
+   0xE3779B99, 0xC6EF3733, 0x8DDE6E67, 0x1BBCDCCF,
+   0x3779B99E, 0x6EF3733C, 0xDDE6E678, 0xBBCDCCF1,
+   0x779B99E3, 0xEF3733C6, 0xDE6E678D, 0xBCDCCF1B
+};
+
+//S-Box SS0
+static const uint32_t ss0[256] =
+{
+   0x2989A1A8, 0x05858184, 0x16C6D2D4, 0x13C3D3D0, 0x14445054, 0x1D0D111C, 0x2C8CA0AC, 0x25052124,
+   0x1D4D515C, 0x03434340, 0x18081018, 0x1E0E121C, 0x11415150, 0x3CCCF0FC, 0x0ACAC2C8, 0x23436360,
+   0x28082028, 0x04444044, 0x20002020, 0x1D8D919C, 0x20C0E0E0, 0x22C2E2E0, 0x08C8C0C8, 0x17071314,
+   0x2585A1A4, 0x0F8F838C, 0x03030300, 0x3B4B7378, 0x3B8BB3B8, 0x13031310, 0x12C2D2D0, 0x2ECEE2EC,
+   0x30407070, 0x0C8C808C, 0x3F0F333C, 0x2888A0A8, 0x32023230, 0x1DCDD1DC, 0x36C6F2F4, 0x34447074,
+   0x2CCCE0EC, 0x15859194, 0x0B0B0308, 0x17475354, 0x1C4C505C, 0x1B4B5358, 0x3D8DB1BC, 0x01010100,
+   0x24042024, 0x1C0C101C, 0x33437370, 0x18889098, 0x10001010, 0x0CCCC0CC, 0x32C2F2F0, 0x19C9D1D8,
+   0x2C0C202C, 0x27C7E3E4, 0x32427270, 0x03838380, 0x1B8B9398, 0x11C1D1D0, 0x06868284, 0x09C9C1C8,
+   0x20406060, 0x10405050, 0x2383A3A0, 0x2BCBE3E8, 0x0D0D010C, 0x3686B2B4, 0x1E8E929C, 0x0F4F434C,
+   0x3787B3B4, 0x1A4A5258, 0x06C6C2C4, 0x38487078, 0x2686A2A4, 0x12021210, 0x2F8FA3AC, 0x15C5D1D4,
+   0x21416160, 0x03C3C3C0, 0x3484B0B4, 0x01414140, 0x12425250, 0x3D4D717C, 0x0D8D818C, 0x08080008,
+   0x1F0F131C, 0x19899198, 0x00000000, 0x19091118, 0x04040004, 0x13435350, 0x37C7F3F4, 0x21C1E1E0,
+   0x3DCDF1FC, 0x36467274, 0x2F0F232C, 0x27072324, 0x3080B0B0, 0x0B8B8388, 0x0E0E020C, 0x2B8BA3A8,
+   0x2282A2A0, 0x2E4E626C, 0x13839390, 0x0D4D414C, 0x29496168, 0x3C4C707C, 0x09090108, 0x0A0A0208,
+   0x3F8FB3BC, 0x2FCFE3EC, 0x33C3F3F0, 0x05C5C1C4, 0x07878384, 0x14041014, 0x3ECEF2FC, 0x24446064,
+   0x1ECED2DC, 0x2E0E222C, 0x0B4B4348, 0x1A0A1218, 0x06060204, 0x21012120, 0x2B4B6368, 0x26466264,
+   0x02020200, 0x35C5F1F4, 0x12829290, 0x0A8A8288, 0x0C0C000C, 0x3383B3B0, 0x3E4E727C, 0x10C0D0D0,
+   0x3A4A7278, 0x07474344, 0x16869294, 0x25C5E1E4, 0x26062224, 0x00808080, 0x2D8DA1AC, 0x1FCFD3DC,
+   0x2181A1A0, 0x30003030, 0x37073334, 0x2E8EA2AC, 0x36063234, 0x15051114, 0x22022220, 0x38083038,
+   0x34C4F0F4, 0x2787A3A4, 0x05454144, 0x0C4C404C, 0x01818180, 0x29C9E1E8, 0x04848084, 0x17879394,
+   0x35053134, 0x0BCBC3C8, 0x0ECEC2CC, 0x3C0C303C, 0x31417170, 0x11011110, 0x07C7C3C4, 0x09898188,
+   0x35457174, 0x3BCBF3F8, 0x1ACAD2D8, 0x38C8F0F8, 0x14849094, 0x19495158, 0x02828280, 0x04C4C0C4,
+   0x3FCFF3FC, 0x09494148, 0x39093138, 0x27476364, 0x00C0C0C0, 0x0FCFC3CC, 0x17C7D3D4, 0x3888B0B8,
+   0x0F0F030C, 0x0E8E828C, 0x02424240, 0x23032320, 0x11819190, 0x2C4C606C, 0x1BCBD3D8, 0x2484A0A4,
+   0x34043034, 0x31C1F1F0, 0x08484048, 0x02C2C2C0, 0x2F4F636C, 0x3D0D313C, 0x2D0D212C, 0x00404040,
+   0x3E8EB2BC, 0x3E0E323C, 0x3C8CB0BC, 0x01C1C1C0, 0x2A8AA2A8, 0x3A8AB2B8, 0x0E4E424C, 0x15455154,
+   0x3B0B3338, 0x1CCCD0DC, 0x28486068, 0x3F4F737C, 0x1C8C909C, 0x18C8D0D8, 0x0A4A4248, 0x16465254,
+   0x37477374, 0x2080A0A0, 0x2DCDE1EC, 0x06464244, 0x3585B1B4, 0x2B0B2328, 0x25456164, 0x3ACAF2F8,
+   0x23C3E3E0, 0x3989B1B8, 0x3181B1B0, 0x1F8F939C, 0x1E4E525C, 0x39C9F1F8, 0x26C6E2E4, 0x3282B2B0,
+   0x31013130, 0x2ACAE2E8, 0x2D4D616C, 0x1F4F535C, 0x24C4E0E4, 0x30C0F0F0, 0x0DCDC1CC, 0x08888088,
+   0x16061214, 0x3A0A3238, 0x18485058, 0x14C4D0D4, 0x22426260, 0x29092128, 0x07070304, 0x33033330,
+   0x28C8E0E8, 0x1B0B1318, 0x05050104, 0x39497178, 0x10809090, 0x2A4A6268, 0x2A0A2228, 0x1A8A9298
+};
+
+//S-Box SS1
+static const uint32_t ss1[256] =
+{
+   0x38380830, 0xE828C8E0, 0x2C2D0D21, 0xA42686A2, 0xCC0FCFC3, 0xDC1ECED2, 0xB03383B3, 0xB83888B0,
+   0xAC2F8FA3, 0x60204060, 0x54154551, 0xC407C7C3, 0x44044440, 0x6C2F4F63, 0x682B4B63, 0x581B4B53,
+   0xC003C3C3, 0x60224262, 0x30330333, 0xB43585B1, 0x28290921, 0xA02080A0, 0xE022C2E2, 0xA42787A3,
+   0xD013C3D3, 0x90118191, 0x10110111, 0x04060602, 0x1C1C0C10, 0xBC3C8CB0, 0x34360632, 0x480B4B43,
+   0xEC2FCFE3, 0x88088880, 0x6C2C4C60, 0xA82888A0, 0x14170713, 0xC404C4C0, 0x14160612, 0xF434C4F0,
+   0xC002C2C2, 0x44054541, 0xE021C1E1, 0xD416C6D2, 0x3C3F0F33, 0x3C3D0D31, 0x8C0E8E82, 0x98188890,
+   0x28280820, 0x4C0E4E42, 0xF436C6F2, 0x3C3E0E32, 0xA42585A1, 0xF839C9F1, 0x0C0D0D01, 0xDC1FCFD3,
+   0xD818C8D0, 0x282B0B23, 0x64264662, 0x783A4A72, 0x24270723, 0x2C2F0F23, 0xF031C1F1, 0x70324272,
+   0x40024242, 0xD414C4D0, 0x40014141, 0xC000C0C0, 0x70334373, 0x64274763, 0xAC2C8CA0, 0x880B8B83,
+   0xF437C7F3, 0xAC2D8DA1, 0x80008080, 0x1C1F0F13, 0xC80ACAC2, 0x2C2C0C20, 0xA82A8AA2, 0x34340430,
+   0xD012C2D2, 0x080B0B03, 0xEC2ECEE2, 0xE829C9E1, 0x5C1D4D51, 0x94148490, 0x18180810, 0xF838C8F0,
+   0x54174753, 0xAC2E8EA2, 0x08080800, 0xC405C5C1, 0x10130313, 0xCC0DCDC1, 0x84068682, 0xB83989B1,
+   0xFC3FCFF3, 0x7C3D4D71, 0xC001C1C1, 0x30310131, 0xF435C5F1, 0x880A8A82, 0x682A4A62, 0xB03181B1,
+   0xD011C1D1, 0x20200020, 0xD417C7D3, 0x00020202, 0x20220222, 0x04040400, 0x68284860, 0x70314171,
+   0x04070703, 0xD81BCBD3, 0x9C1D8D91, 0x98198991, 0x60214161, 0xBC3E8EB2, 0xE426C6E2, 0x58194951,
+   0xDC1DCDD1, 0x50114151, 0x90108090, 0xDC1CCCD0, 0x981A8A92, 0xA02383A3, 0xA82B8BA3, 0xD010C0D0,
+   0x80018181, 0x0C0F0F03, 0x44074743, 0x181A0A12, 0xE023C3E3, 0xEC2CCCE0, 0x8C0D8D81, 0xBC3F8FB3,
+   0x94168692, 0x783B4B73, 0x5C1C4C50, 0xA02282A2, 0xA02181A1, 0x60234363, 0x20230323, 0x4C0D4D41,
+   0xC808C8C0, 0x9C1E8E92, 0x9C1C8C90, 0x383A0A32, 0x0C0C0C00, 0x2C2E0E22, 0xB83A8AB2, 0x6C2E4E62,
+   0x9C1F8F93, 0x581A4A52, 0xF032C2F2, 0x90128292, 0xF033C3F3, 0x48094941, 0x78384870, 0xCC0CCCC0,
+   0x14150511, 0xF83BCBF3, 0x70304070, 0x74354571, 0x7C3F4F73, 0x34350531, 0x10100010, 0x00030303,
+   0x64244460, 0x6C2D4D61, 0xC406C6C2, 0x74344470, 0xD415C5D1, 0xB43484B0, 0xE82ACAE2, 0x08090901,
+   0x74364672, 0x18190911, 0xFC3ECEF2, 0x40004040, 0x10120212, 0xE020C0E0, 0xBC3D8DB1, 0x04050501,
+   0xF83ACAF2, 0x00010101, 0xF030C0F0, 0x282A0A22, 0x5C1E4E52, 0xA82989A1, 0x54164652, 0x40034343,
+   0x84058581, 0x14140410, 0x88098981, 0x981B8B93, 0xB03080B0, 0xE425C5E1, 0x48084840, 0x78394971,
+   0x94178793, 0xFC3CCCF0, 0x1C1E0E12, 0x80028282, 0x20210121, 0x8C0C8C80, 0x181B0B13, 0x5C1F4F53,
+   0x74374773, 0x54144450, 0xB03282B2, 0x1C1D0D11, 0x24250521, 0x4C0F4F43, 0x00000000, 0x44064642,
+   0xEC2DCDE1, 0x58184850, 0x50124252, 0xE82BCBE3, 0x7C3E4E72, 0xD81ACAD2, 0xC809C9C1, 0xFC3DCDF1,
+   0x30300030, 0x94158591, 0x64254561, 0x3C3C0C30, 0xB43686B2, 0xE424C4E0, 0xB83B8BB3, 0x7C3C4C70,
+   0x0C0E0E02, 0x50104050, 0x38390931, 0x24260622, 0x30320232, 0x84048480, 0x68294961, 0x90138393,
+   0x34370733, 0xE427C7E3, 0x24240420, 0xA42484A0, 0xC80BCBC3, 0x50134353, 0x080A0A02, 0x84078783,
+   0xD819C9D1, 0x4C0C4C40, 0x80038383, 0x8C0F8F83, 0xCC0ECEC2, 0x383B0B33, 0x480A4A42, 0xB43787B3
+};
+
+//S-Box SS2
+static const uint32_t ss2[256] =
+{
+   0xA1A82989, 0x81840585, 0xD2D416C6, 0xD3D013C3, 0x50541444, 0x111C1D0D, 0xA0AC2C8C, 0x21242505,
+   0x515C1D4D, 0x43400343, 0x10181808, 0x121C1E0E, 0x51501141, 0xF0FC3CCC, 0xC2C80ACA, 0x63602343,
+   0x20282808, 0x40440444, 0x20202000, 0x919C1D8D, 0xE0E020C0, 0xE2E022C2, 0xC0C808C8, 0x13141707,
+   0xA1A42585, 0x838C0F8F, 0x03000303, 0x73783B4B, 0xB3B83B8B, 0x13101303, 0xD2D012C2, 0xE2EC2ECE,
+   0x70703040, 0x808C0C8C, 0x333C3F0F, 0xA0A82888, 0x32303202, 0xD1DC1DCD, 0xF2F436C6, 0x70743444,
+   0xE0EC2CCC, 0x91941585, 0x03080B0B, 0x53541747, 0x505C1C4C, 0x53581B4B, 0xB1BC3D8D, 0x01000101,
+   0x20242404, 0x101C1C0C, 0x73703343, 0x90981888, 0x10101000, 0xC0CC0CCC, 0xF2F032C2, 0xD1D819C9,
+   0x202C2C0C, 0xE3E427C7, 0x72703242, 0x83800383, 0x93981B8B, 0xD1D011C1, 0x82840686, 0xC1C809C9,
+   0x60602040, 0x50501040, 0xA3A02383, 0xE3E82BCB, 0x010C0D0D, 0xB2B43686, 0x929C1E8E, 0x434C0F4F,
+   0xB3B43787, 0x52581A4A, 0xC2C406C6, 0x70783848, 0xA2A42686, 0x12101202, 0xA3AC2F8F, 0xD1D415C5,
+   0x61602141, 0xC3C003C3, 0xB0B43484, 0x41400141, 0x52501242, 0x717C3D4D, 0x818C0D8D, 0x00080808,
+   0x131C1F0F, 0x91981989, 0x00000000, 0x11181909, 0x00040404, 0x53501343, 0xF3F437C7, 0xE1E021C1,
+   0xF1FC3DCD, 0x72743646, 0x232C2F0F, 0x23242707, 0xB0B03080, 0x83880B8B, 0x020C0E0E, 0xA3A82B8B,
+   0xA2A02282, 0x626C2E4E, 0x93901383, 0x414C0D4D, 0x61682949, 0x707C3C4C, 0x01080909, 0x02080A0A,
+   0xB3BC3F8F, 0xE3EC2FCF, 0xF3F033C3, 0xC1C405C5, 0x83840787, 0x10141404, 0xF2FC3ECE, 0x60642444,
+   0xD2DC1ECE, 0x222C2E0E, 0x43480B4B, 0x12181A0A, 0x02040606, 0x21202101, 0x63682B4B, 0x62642646,
+   0x02000202, 0xF1F435C5, 0x92901282, 0x82880A8A, 0x000C0C0C, 0xB3B03383, 0x727C3E4E, 0xD0D010C0,
+   0x72783A4A, 0x43440747, 0x92941686, 0xE1E425C5, 0x22242606, 0x80800080, 0xA1AC2D8D, 0xD3DC1FCF,
+   0xA1A02181, 0x30303000, 0x33343707, 0xA2AC2E8E, 0x32343606, 0x11141505, 0x22202202, 0x30383808,
+   0xF0F434C4, 0xA3A42787, 0x41440545, 0x404C0C4C, 0x81800181, 0xE1E829C9, 0x80840484, 0x93941787,
+   0x31343505, 0xC3C80BCB, 0xC2CC0ECE, 0x303C3C0C, 0x71703141, 0x11101101, 0xC3C407C7, 0x81880989,
+   0x71743545, 0xF3F83BCB, 0xD2D81ACA, 0xF0F838C8, 0x90941484, 0x51581949, 0x82800282, 0xC0C404C4,
+   0xF3FC3FCF, 0x41480949, 0x31383909, 0x63642747, 0xC0C000C0, 0xC3CC0FCF, 0xD3D417C7, 0xB0B83888,
+   0x030C0F0F, 0x828C0E8E, 0x42400242, 0x23202303, 0x91901181, 0x606C2C4C, 0xD3D81BCB, 0xA0A42484,
+   0x30343404, 0xF1F031C1, 0x40480848, 0xC2C002C2, 0x636C2F4F, 0x313C3D0D, 0x212C2D0D, 0x40400040,
+   0xB2BC3E8E, 0x323C3E0E, 0xB0BC3C8C, 0xC1C001C1, 0xA2A82A8A, 0xB2B83A8A, 0x424C0E4E, 0x51541545,
+   0x33383B0B, 0xD0DC1CCC, 0x60682848, 0x737C3F4F, 0x909C1C8C, 0xD0D818C8, 0x42480A4A, 0x52541646,
+   0x73743747, 0xA0A02080, 0xE1EC2DCD, 0x42440646, 0xB1B43585, 0x23282B0B, 0x61642545, 0xF2F83ACA,
+   0xE3E023C3, 0xB1B83989, 0xB1B03181, 0x939C1F8F, 0x525C1E4E, 0xF1F839C9, 0xE2E426C6, 0xB2B03282,
+   0x31303101, 0xE2E82ACA, 0x616C2D4D, 0x535C1F4F, 0xE0E424C4, 0xF0F030C0, 0xC1CC0DCD, 0x80880888,
+   0x12141606, 0x32383A0A, 0x50581848, 0xD0D414C4, 0x62602242, 0x21282909, 0x03040707, 0x33303303,
+   0xE0E828C8, 0x13181B0B, 0x01040505, 0x71783949, 0x90901080, 0x62682A4A, 0x22282A0A, 0x92981A8A
+};
+
+//S-Box SS3
+static const uint32_t ss3[256] =
+{
+   0x08303838, 0xC8E0E828, 0x0D212C2D, 0x86A2A426, 0xCFC3CC0F, 0xCED2DC1E, 0x83B3B033, 0x88B0B838,
+   0x8FA3AC2F, 0x40606020, 0x45515415, 0xC7C3C407, 0x44404404, 0x4F636C2F, 0x4B63682B, 0x4B53581B,
+   0xC3C3C003, 0x42626022, 0x03333033, 0x85B1B435, 0x09212829, 0x80A0A020, 0xC2E2E022, 0x87A3A427,
+   0xC3D3D013, 0x81919011, 0x01111011, 0x06020406, 0x0C101C1C, 0x8CB0BC3C, 0x06323436, 0x4B43480B,
+   0xCFE3EC2F, 0x88808808, 0x4C606C2C, 0x88A0A828, 0x07131417, 0xC4C0C404, 0x06121416, 0xC4F0F434,
+   0xC2C2C002, 0x45414405, 0xC1E1E021, 0xC6D2D416, 0x0F333C3F, 0x0D313C3D, 0x8E828C0E, 0x88909818,
+   0x08202828, 0x4E424C0E, 0xC6F2F436, 0x0E323C3E, 0x85A1A425, 0xC9F1F839, 0x0D010C0D, 0xCFD3DC1F,
+   0xC8D0D818, 0x0B23282B, 0x46626426, 0x4A72783A, 0x07232427, 0x0F232C2F, 0xC1F1F031, 0x42727032,
+   0x42424002, 0xC4D0D414, 0x41414001, 0xC0C0C000, 0x43737033, 0x47636427, 0x8CA0AC2C, 0x8B83880B,
+   0xC7F3F437, 0x8DA1AC2D, 0x80808000, 0x0F131C1F, 0xCAC2C80A, 0x0C202C2C, 0x8AA2A82A, 0x04303434,
+   0xC2D2D012, 0x0B03080B, 0xCEE2EC2E, 0xC9E1E829, 0x4D515C1D, 0x84909414, 0x08101818, 0xC8F0F838,
+   0x47535417, 0x8EA2AC2E, 0x08000808, 0xC5C1C405, 0x03131013, 0xCDC1CC0D, 0x86828406, 0x89B1B839,
+   0xCFF3FC3F, 0x4D717C3D, 0xC1C1C001, 0x01313031, 0xC5F1F435, 0x8A82880A, 0x4A62682A, 0x81B1B031,
+   0xC1D1D011, 0x00202020, 0xC7D3D417, 0x02020002, 0x02222022, 0x04000404, 0x48606828, 0x41717031,
+   0x07030407, 0xCBD3D81B, 0x8D919C1D, 0x89919819, 0x41616021, 0x8EB2BC3E, 0xC6E2E426, 0x49515819,
+   0xCDD1DC1D, 0x41515011, 0x80909010, 0xCCD0DC1C, 0x8A92981A, 0x83A3A023, 0x8BA3A82B, 0xC0D0D010,
+   0x81818001, 0x0F030C0F, 0x47434407, 0x0A12181A, 0xC3E3E023, 0xCCE0EC2C, 0x8D818C0D, 0x8FB3BC3F,
+   0x86929416, 0x4B73783B, 0x4C505C1C, 0x82A2A022, 0x81A1A021, 0x43636023, 0x03232023, 0x4D414C0D,
+   0xC8C0C808, 0x8E929C1E, 0x8C909C1C, 0x0A32383A, 0x0C000C0C, 0x0E222C2E, 0x8AB2B83A, 0x4E626C2E,
+   0x8F939C1F, 0x4A52581A, 0xC2F2F032, 0x82929012, 0xC3F3F033, 0x49414809, 0x48707838, 0xCCC0CC0C,
+   0x05111415, 0xCBF3F83B, 0x40707030, 0x45717435, 0x4F737C3F, 0x05313435, 0x00101010, 0x03030003,
+   0x44606424, 0x4D616C2D, 0xC6C2C406, 0x44707434, 0xC5D1D415, 0x84B0B434, 0xCAE2E82A, 0x09010809,
+   0x46727436, 0x09111819, 0xCEF2FC3E, 0x40404000, 0x02121012, 0xC0E0E020, 0x8DB1BC3D, 0x05010405,
+   0xCAF2F83A, 0x01010001, 0xC0F0F030, 0x0A22282A, 0x4E525C1E, 0x89A1A829, 0x46525416, 0x43434003,
+   0x85818405, 0x04101414, 0x89818809, 0x8B93981B, 0x80B0B030, 0xC5E1E425, 0x48404808, 0x49717839,
+   0x87939417, 0xCCF0FC3C, 0x0E121C1E, 0x82828002, 0x01212021, 0x8C808C0C, 0x0B13181B, 0x4F535C1F,
+   0x47737437, 0x44505414, 0x82B2B032, 0x0D111C1D, 0x05212425, 0x4F434C0F, 0x00000000, 0x46424406,
+   0xCDE1EC2D, 0x48505818, 0x42525012, 0xCBE3E82B, 0x4E727C3E, 0xCAD2D81A, 0xC9C1C809, 0xCDF1FC3D,
+   0x00303030, 0x85919415, 0x45616425, 0x0C303C3C, 0x86B2B436, 0xC4E0E424, 0x8BB3B83B, 0x4C707C3C,
+   0x0E020C0E, 0x40505010, 0x09313839, 0x06222426, 0x02323032, 0x84808404, 0x49616829, 0x83939013,
+   0x07333437, 0xC7E3E427, 0x04202424, 0x84A0A424, 0xCBC3C80B, 0x43535013, 0x0A02080A, 0x87838407,
+   0xC9D1D819, 0x4C404C0C, 0x83838003, 0x8F838C0F, 0xCEC2CC0E, 0x0B33383B, 0x4A42480A, 0x87B3B437
+};
+
+//Common interface for encryption algorithms
+const CipherAlgo seedCipherAlgo =
+{
+   "SEED",
+   sizeof(SeedContext),
+   CIPHER_ALGO_TYPE_BLOCK,
+   SEED_BLOCK_SIZE,
+   (CipherAlgoInit) seedInit,
+   NULL,
+   NULL,
+   (CipherAlgoEncryptBlock) seedEncryptBlock,
+   (CipherAlgoDecryptBlock) seedDecryptBlock
+};
+
+
+/**
+ * @brief Initialize a SEED context using the supplied key
+ * @param[in] context Pointer to the SEED context to initialize
+ * @param[in] key Pointer to the key
+ * @param[in] keyLength Length of the key
+ * @return Error code
+ **/
+
+error_t seedInit(SeedContext *context, const uint8_t *key, size_t keyLength)
+{
+   uint_t i;
+   uint32_t t;
+   uint32_t key0;
+   uint32_t key1;
+   uint32_t key2;
+   uint32_t key3;
+
+   //Invalid key length?
+   if(keyLength != 16)
+      return ERROR_INVALID_KEY_LENGTH;
+
+   //The 128-bit input key is divided into four 32-bit blocks
+   key0 = LOAD32BE(key + 0);
+   key1 = LOAD32BE(key + 4);
+   key2 = LOAD32BE(key + 8);
+   key3 = LOAD32BE(key + 12);
+
+   //Apply 16 rounds
+   for(i = 0; i < 16; i++)
+   {
+      t = key0 + key2 - kc[i];
+      context->ks[2 * i] = G(t);
+      t = key1 - key3 + kc[i];
+      context->ks[2 * i + 1] = G(t);
+
+      //Odd round?
+      if(i % 2)
+      {
+         t = (key3 << 8) | (key2 >> 24);
+         key2 = (key2 << 8) | (key3 >> 24);
+         key3 = t;
+      }
+      //Even round?
+      else
+      {
+         t = (key1 >> 8) | (key0 << 24);
+         key0 = (key0 >> 8) | (key1 << 24);
+         key1 = t;
+      }
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Encrypt a 16-byte block using SEED algorithm
+ * @param[in] context Pointer to the SEED context
+ * @param[in] input Plaintext block to encrypt
+ * @param[out] output Ciphertext block resulting from encryption
+ **/
+
+void seedEncryptBlock(SeedContext *context, const uint8_t *input, uint8_t *output)
+{
+   uint_t i;
+   uint32_t temp0;
+   uint32_t temp1;
+   uint32_t *ks;
+
+   //The 128-bit input is divided into two 64-bit blocks (L and R)
+   uint32_t left0 = LOAD32BE(input + 0);
+   uint32_t left1 = LOAD32BE(input + 4);
+   uint32_t right0 = LOAD32BE(input + 8);
+   uint32_t right1 = LOAD32BE(input + 12);
+
+   //The key schedule must be applied in ascending order
+   ks = context->ks;
+
+   //Perform 16 rounds
+   for(i = 0; i < 16; i++)
+   {
+      //Apply function F
+      F(ks[0], ks[1], right0, right1, temp0, temp1);
+
+      temp0 ^= left0;
+      temp1 ^= left1;
+      left0 = right0;
+      left1 = right1;
+      right0 = temp0;
+      right1 = temp1;
+
+      //Advance current location in key schedule
+      ks += 2;
+   }
+
+   //The resulting value is the ciphertext
+   STORE32BE(right0, output + 0);
+   STORE32BE(right1, output + 4);
+   STORE32BE(left0, output + 8);
+   STORE32BE(left1, output + 12);
+}
+
+
+/**
+ * @brief Decrypt a 16-byte block using SEED algorithm
+ * @param[in] context Pointer to the SEED context
+ * @param[in] input Ciphertext block to decrypt
+ * @param[out] output Plaintext block resulting from decryption
+ **/
+
+void seedDecryptBlock(SeedContext *context, const uint8_t *input, uint8_t *output)
+{
+   uint_t i;
+   uint32_t temp0;
+   uint32_t temp1;
+   uint32_t *ks;
+
+   //The 128-bit input is divided into two 64-bit blocks (L and R)
+   uint32_t left0 = LOAD32BE(input + 0);
+   uint32_t left1 = LOAD32BE(input + 4);
+   uint32_t right0 = LOAD32BE(input + 8);
+   uint32_t right1 = LOAD32BE(input + 12);
+
+   //The key schedule must be applied in reverse order
+   ks = context->ks + 30;
+
+   //Perform 16 rounds
+   for(i = 0; i < 16; i++)
+   {
+      //Apply function F
+      F(ks[0], ks[1], right0, right1, temp0, temp1);
+
+      temp0 ^= left0;
+      temp1 ^= left1;
+      left0 = right0;
+      left1 = right1;
+      right0 = temp0;
+      right1 = temp1;
+
+      //Advance current location in key schedule
+      ks -= 2;
+   }
+
+   //The resulting value is the plaintext
+   STORE32BE(right0, output + 0);
+   STORE32BE(right1, output + 4);
+   STORE32BE(left0, output + 8);
+   STORE32BE(left1, output + 12);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/seed.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,60 @@
+/**
+ * @file seed.h
+ * @brief SEED encryption algorithm
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SEED_H
+#define _SEED_H
+
+//Dependencies
+#include "crypto.h"
+
+//SEED block size
+#define SEED_BLOCK_SIZE 16
+//Common interface for encryption algorithms
+#define SEED_CIPHER_ALGO (&seedCipherAlgo)
+
+
+/**
+ * @brief SEED algorithm context
+ **/
+
+typedef struct
+{
+   uint32_t ks[32];
+} SeedContext;
+
+
+//SEED related constants
+extern const CipherAlgo seedCipherAlgo;
+
+//SEED related functions
+error_t seedInit(SeedContext *context, const uint8_t *key, size_t keyLength);
+void seedEncryptBlock(SeedContext *context, const uint8_t *input, uint8_t *output);
+void seedDecryptBlock(SeedContext *context, const uint8_t *input, uint8_t *output);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha1.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,280 @@
+/**
+ * @file sha1.c
+ * @brief SHA-1 (Secure Hash Algorithm 1)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * SHA-1 is a secure hash algorithm for computing a condensed representation
+ * of an electronic message. Refer to FIPS 180-4 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "sha1.h"
+
+//Check crypto library configuration
+#if (SHA1_SUPPORT == ENABLED)
+
+//Macro to access the workspace as a circular buffer
+#define W(t) w[(t) & 0x0F]
+
+//SHA-1 auxiliary functions
+#define CH(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define PARITY(x, y, z) ((x) ^ (y) ^ (z))
+#define MAJ(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+
+//SHA-1 padding
+static const uint8_t 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
+};
+
+//SHA-1 constants
+static const uint32_t k[4] =
+{
+   0x5A827999,
+   0x6ED9EBA1,
+   0x8F1BBCDC,
+   0xCA62C1D6
+};
+
+//SHA-1 object identifier (1.3.14.3.2.26)
+static const uint8_t sha1Oid[] = {0x2B, 0x0E, 0x03, 0x02, 0x1A};
+
+//Common interface for hash algorithms
+const HashAlgo sha1HashAlgo =
+{
+   "SHA-1",
+   sha1Oid,
+   sizeof(sha1Oid),
+   sizeof(Sha1Context),
+   SHA1_BLOCK_SIZE,
+   SHA1_DIGEST_SIZE,
+   (HashAlgoCompute) sha1Compute,
+   (HashAlgoInit) sha1Init,
+   (HashAlgoUpdate) sha1Update,
+   (HashAlgoFinal) sha1Final
+};
+
+
+/**
+ * @brief Digest a message using SHA-1
+ * @param[in] data Pointer to the message being hashed
+ * @param[in] length Length of the message
+ * @param[out] digest Pointer to the calculated digest
+ * @return Error code
+ **/
+
+error_t sha1Compute(const void *data, size_t length, uint8_t *digest)
+{
+   //Allocate a memory buffer to hold the SHA-1 context
+   Sha1Context *context = cryptoAllocMem(sizeof(Sha1Context));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the SHA-1 context
+   sha1Init(context);
+   //Digest the message
+   sha1Update(context, data, length);
+   //Finalize the SHA-1 message digest
+   sha1Final(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize SHA-1 message digest context
+ * @param[in] context Pointer to the SHA-1 context to initialize
+ **/
+
+void sha1Init(Sha1Context *context)
+{
+   //Set initial hash value
+   context->h[0] = 0x67452301;
+   context->h[1] = 0xEFCDAB89;
+   context->h[2] = 0x98BADCFE;
+   context->h[3] = 0x10325476;
+   context->h[4] = 0xC3D2E1F0;
+
+   //Number of bytes in the buffer
+   context->size = 0;
+   //Total length of the message
+   context->totalSize = 0;
+}
+
+
+/**
+ * @brief Update the SHA-1 context with a portion of the message being hashed
+ * @param[in] context Pointer to the SHA-1 context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void sha1Update(Sha1Context *context, const void *data, size_t length)
+{
+   size_t n;
+
+   //Process the incoming data
+   while(length > 0)
+   {
+      //The buffer can hold at most 64 bytes
+      n = MIN(length, 64 - context->size);
+
+      //Copy the data to the buffer
+      memcpy(context->buffer + context->size, data, n);
+
+      //Update the SHA-1 context
+      context->size += n;
+      context->totalSize += n;
+      //Advance the data pointer
+      data = (uint8_t *) data + n;
+      //Remaining bytes to process
+      length -= n;
+
+      //Process message in 16-word blocks
+      if(context->size == 64)
+      {
+         //Transform the 16-word block
+         sha1ProcessBlock(context);
+         //Empty the buffer
+         context->size = 0;
+      }
+   }
+}
+
+
+/**
+ * @brief Finish the SHA-1 message digest
+ * @param[in] context Pointer to the SHA-1 context
+ * @param[out] digest Calculated digest (optional parameter)
+ **/
+
+void sha1Final(Sha1Context *context, uint8_t *digest)
+{
+   uint_t i;
+   size_t paddingSize;
+   uint64_t totalSize;
+
+   //Length of the original message (before padding)
+   totalSize = context->totalSize * 8;
+
+   //Pad the message so that its length is congruent to 56 modulo 64
+   if(context->size < 56)
+      paddingSize = 56 - context->size;
+   else
+      paddingSize = 64 + 56 - context->size;
+
+   //Append padding
+   sha1Update(context, padding, paddingSize);
+
+   //Append the length of the original message
+   context->w[14] = htobe32((uint32_t) (totalSize >> 32));
+   context->w[15] = htobe32((uint32_t) totalSize);
+
+   //Calculate the message digest
+   sha1ProcessBlock(context);
+
+   //Convert from host byte order to big-endian byte order
+   for(i = 0; i < 5; i++)
+      context->h[i] = htobe32(context->h[i]);
+
+   //Copy the resulting digest
+   if(digest != NULL)
+      memcpy(digest, context->digest, SHA1_DIGEST_SIZE);
+}
+
+
+/**
+ * @brief Process message in 16-word blocks
+ * @param[in] context Pointer to the SHA-1 context
+ **/
+
+void sha1ProcessBlock(Sha1Context *context)
+{
+   uint_t t;
+   uint32_t temp;
+
+   //Initialize the 5 working registers
+   uint32_t a = context->h[0];
+   uint32_t b = context->h[1];
+   uint32_t c = context->h[2];
+   uint32_t d = context->h[3];
+   uint32_t e = context->h[4];
+
+   //Process message in 16-word blocks
+   uint32_t *w = context->w;
+
+   //Convert from big-endian byte order to host byte order
+   for(t = 0; t < 16; t++)
+      w[t] = betoh32(w[t]);
+
+   //SHA-1 hash computation (alternate method)
+   for(t = 0; t < 80; t++)
+   {
+      //Prepare the message schedule
+      if(t >= 16)
+         W(t) = ROL32(W(t + 13) ^ W(t + 8) ^ W(t + 2) ^ W(t), 1);
+
+      //Calculate T
+      if(t < 20)
+         temp = ROL32(a, 5) + CH(b, c, d) + e + W(t) + k[0];
+      else if(t < 40)
+         temp = ROL32(a, 5) + PARITY(b, c, d) + e + W(t) + k[1];
+      else if(t < 60)
+         temp = ROL32(a, 5) + MAJ(b, c, d) + e + W(t) + k[2];
+      else
+         temp = ROL32(a, 5) + PARITY(b, c, d) + e + W(t) + k[3];
+
+      //Update the working registers
+      e = d;
+      d = c;
+      c = ROL32(b, 30);
+      b = a;
+      a = temp;
+   }
+
+   //Update the hash value
+   context->h[0] += a;
+   context->h[1] += b;
+   context->h[2] += c;
+   context->h[3] += d;
+   context->h[4] += e;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha1.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,75 @@
+/**
+ * @file sha1.h
+ * @brief SHA-1 (Secure Hash Algorithm 1)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SHA1_H
+#define _SHA1_H
+
+//Dependencies
+#include "crypto.h"
+
+//SHA-1 block size
+#define SHA1_BLOCK_SIZE 64
+//SHA-1 digest size
+#define SHA1_DIGEST_SIZE 20
+//Common interface for hash algorithms
+#define SHA1_HASH_ALGO (&sha1HashAlgo)
+
+
+/**
+ * @brief SHA-1 algorithm context
+ **/
+
+typedef struct
+{
+   union
+   {
+      uint32_t h[5];
+      uint8_t digest[20];
+   };
+   union
+   {
+      uint32_t w[16];
+      uint8_t buffer[64];
+   };
+   size_t size;
+   uint64_t totalSize;
+} Sha1Context;
+
+
+//SHA-1 related constants
+extern const HashAlgo sha1HashAlgo;
+
+//SHA-1 related functions
+error_t sha1Compute(const void *data, size_t length, uint8_t *digest);
+void sha1Init(Sha1Context *context);
+void sha1Update(Sha1Context *context, const void *data, size_t length);
+void sha1Final(Sha1Context *context, uint8_t *digest);
+void sha1ProcessBlock(Sha1Context *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha224.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,149 @@
+/**
+ * @file sha224.c
+ * @brief SHA-224 (Secure Hash Algorithm 224)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * SHA-224 is a secure hash algorithm for computing a condensed representation
+ * of an electronic message. Refer to FIPS 180-4 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "sha224.h"
+
+//Check crypto library configuration
+#if (SHA224_SUPPORT == ENABLED)
+
+//SHA-224 object identifier (2.16.840.1.101.3.4.2.4)
+static const uint8_t sha224Oid[] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04};
+
+//Common interface for hash algorithms
+const HashAlgo sha224HashAlgo =
+{
+   "SHA-224",
+   sha224Oid,
+   sizeof(sha224Oid),
+   sizeof(Sha224Context),
+   SHA224_BLOCK_SIZE,
+   SHA224_DIGEST_SIZE,
+   (HashAlgoCompute) sha224Compute,
+   (HashAlgoInit) sha224Init,
+   (HashAlgoUpdate) sha224Update,
+   (HashAlgoFinal) sha224Final
+};
+
+
+/**
+ * @brief Digest a message using SHA-224
+ * @param[in] data Pointer to the message being hashed
+ * @param[in] length Length of the message
+ * @param[out] digest Pointer to the calculated digest
+ * @return Error code
+ **/
+
+error_t sha224Compute(const void *data, size_t length, uint8_t *digest)
+{
+   //Allocate a memory buffer to hold the SHA-224 context
+   Sha224Context *context = cryptoAllocMem(sizeof(Sha224Context));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the SHA-224 context
+   sha224Init(context);
+   //Digest the message
+   sha224Update(context, data, length);
+   //Finalize the SHA-224 message digest
+   sha224Final(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize SHA-224 message digest context
+ * @param[in] context Pointer to the SHA-224 context to initialize
+ **/
+
+void sha224Init(Sha224Context *context)
+{
+   //Set initial hash value
+   context->h[0] = 0xC1059ED8;
+   context->h[1] = 0x367CD507;
+   context->h[2] = 0x3070DD17;
+   context->h[3] = 0xF70E5939;
+   context->h[4] = 0xFFC00B31;
+   context->h[5] = 0x68581511;
+   context->h[6] = 0x64F98FA7;
+   context->h[7] = 0xBEFA4FA4;
+
+   //Number of bytes in the buffer
+   context->size = 0;
+   //Total length of the message
+   context->totalSize = 0;
+}
+
+
+/**
+ * @brief Update the SHA-224 context with a portion of the message being hashed
+ * @param[in] context Pointer to the SHA-224 context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void sha224Update(Sha224Context *context, const void *data, size_t length)
+{
+   //The function is defined in the exact same manner as SHA-256
+   sha256Update(context, data, length);
+}
+
+
+/**
+ * @brief Finish the SHA-224 message digest
+ * @param[in] context Pointer to the SHA-224 context
+ * @param[out] digest Calculated digest (optional parameter)
+ **/
+
+void sha224Final(Sha224Context *context, uint8_t *digest)
+{
+   //The function is defined in the exact same manner as SHA-256
+   sha256Final(context, NULL);
+
+   //Copy the resulting digest
+   if(digest != NULL)
+      memcpy(digest, context->digest, SHA224_DIGEST_SIZE);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha224.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,61 @@
+/**
+ * @file sha224.h
+ * @brief SHA-224 (Secure Hash Algorithm 224)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SHA224_H
+#define _SHA224_H
+
+//Dependencies
+#include "crypto.h"
+#include "sha256.h"
+
+//SHA-224 block size
+#define SHA224_BLOCK_SIZE 64
+//SHA-224 digest size
+#define SHA224_DIGEST_SIZE 28
+//Common interface for hash algorithms
+#define SHA224_HASH_ALGO (&sha224HashAlgo)
+
+
+/**
+ * @brief SHA-224 algorithm context
+ **/
+
+typedef Sha256Context Sha224Context;
+
+
+//SHA-224 related constants
+extern const HashAlgo sha224HashAlgo;
+
+//SHA-224 related functions
+error_t sha224Compute(const void *data, size_t length, uint8_t *digest);
+void sha224Init(Sha224Context *context);
+void sha224Update(Sha224Context *context, const void *data, size_t length);
+void sha224Final(Sha224Context *context, uint8_t *digest);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha256.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,294 @@
+/**
+ * @file sha256.c
+ * @brief SHA-256 (Secure Hash Algorithm 256)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * SHA-256 is a secure hash algorithm for computing a condensed representation
+ * of an electronic message. Refer to FIPS 180-4 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "sha256.h"
+
+//Check crypto library configuration
+#if (SHA224_SUPPORT == ENABLED || SHA256_SUPPORT == ENABLED)
+
+//Macro to access the workspace as a circular buffer
+#define W(t) w[(t) & 0x0F]
+
+//SHA-256 auxiliary functions
+#define CH(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define MAJ(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+#define SIGMA1(x) (ROR32(x, 2) ^ ROR32(x, 13) ^ ROR32(x, 22))
+#define SIGMA2(x) (ROR32(x, 6) ^ ROR32(x, 11) ^ ROR32(x, 25))
+#define SIGMA3(x) (ROR32(x, 7) ^ ROR32(x, 18) ^ SHR32(x, 3))
+#define SIGMA4(x) (ROR32(x, 17) ^ ROR32(x, 19) ^ SHR32(x, 10))
+
+//SHA-256 padding
+static const uint8_t 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
+};
+
+//SHA-256 constants
+static const uint32_t k[64] =
+{
+   0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
+   0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
+   0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
+   0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
+   0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
+   0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
+   0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
+   0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2
+};
+
+//SHA-256 object identifier (2.16.840.1.101.3.4.2.1)
+static const uint8_t sha256Oid[] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01};
+
+//Common interface for hash algorithms
+const HashAlgo sha256HashAlgo =
+{
+   "SHA-256",
+   sha256Oid,
+   sizeof(sha256Oid),
+   sizeof(Sha256Context),
+   SHA256_BLOCK_SIZE,
+   SHA256_DIGEST_SIZE,
+   (HashAlgoCompute) sha256Compute,
+   (HashAlgoInit) sha256Init,
+   (HashAlgoUpdate) sha256Update,
+   (HashAlgoFinal) sha256Final
+};
+
+
+/**
+ * @brief Digest a message using SHA-256
+ * @param[in] data Pointer to the message being hashed
+ * @param[in] length Length of the message
+ * @param[out] digest Pointer to the calculated digest
+ * @return Error code
+ **/
+
+error_t sha256Compute(const void *data, size_t length, uint8_t *digest)
+{
+   //Allocate a memory buffer to hold the SHA-256 context
+   Sha256Context *context = cryptoAllocMem(sizeof(Sha256Context));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the SHA-256 context
+   sha256Init(context);
+   //Digest the message
+   sha256Update(context, data, length);
+   //Finalize the SHA-256 message digest
+   sha256Final(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize SHA-256 message digest context
+ * @param[in] context Pointer to the SHA-256 context to initialize
+ **/
+
+void sha256Init(Sha256Context *context)
+{
+   //Set initial hash value
+   context->h[0] = 0x6A09E667;
+   context->h[1] = 0xBB67AE85;
+   context->h[2] = 0x3C6EF372;
+   context->h[3] = 0xA54FF53A;
+   context->h[4] = 0x510E527F;
+   context->h[5] = 0x9B05688C;
+   context->h[6] = 0x1F83D9AB;
+   context->h[7] = 0x5BE0CD19;
+
+   //Number of bytes in the buffer
+   context->size = 0;
+   //Total length of the message
+   context->totalSize = 0;
+}
+
+
+/**
+ * @brief Update the SHA-256 context with a portion of the message being hashed
+ * @param[in] context Pointer to the SHA-256 context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void sha256Update(Sha256Context *context, const void *data, size_t length)
+{
+   size_t n;
+
+   //Process the incoming data
+   while(length > 0)
+   {
+      //The buffer can hold at most 64 bytes
+      n = MIN(length, 64 - context->size);
+
+      //Copy the data to the buffer
+      memcpy(context->buffer + context->size, data, n);
+
+      //Update the SHA-256 context
+      context->size += n;
+      context->totalSize += n;
+      //Advance the data pointer
+      data = (uint8_t *) data + n;
+      //Remaining bytes to process
+      length -= n;
+
+      //Process message in 16-word blocks
+      if(context->size == 64)
+      {
+         //Transform the 16-word block
+         sha256ProcessBlock(context);
+         //Empty the buffer
+         context->size = 0;
+      }
+   }
+}
+
+
+/**
+ * @brief Finish the SHA-256 message digest
+ * @param[in] context Pointer to the SHA-256 context
+ * @param[out] digest Calculated digest (optional parameter)
+ **/
+
+void sha256Final(Sha256Context *context, uint8_t *digest)
+{
+   uint_t i;
+   size_t paddingSize;
+   uint64_t totalSize;
+
+   //Length of the original message (before padding)
+   totalSize = context->totalSize * 8;
+
+   //Pad the message so that its length is congruent to 56 modulo 64
+   if(context->size < 56)
+      paddingSize = 56 - context->size;
+   else
+      paddingSize = 64 + 56 - context->size;
+
+   //Append padding
+   sha256Update(context, padding, paddingSize);
+
+   //Append the length of the original message
+   context->w[14] = htobe32((uint32_t) (totalSize >> 32));
+   context->w[15] = htobe32((uint32_t) totalSize);
+
+   //Calculate the message digest
+   sha256ProcessBlock(context);
+
+   //Convert from host byte order to big-endian byte order
+   for(i = 0; i < 8; i++)
+      context->h[i] = htobe32(context->h[i]);
+
+   //Copy the resulting digest
+   if(digest != NULL)
+      memcpy(digest, context->digest, SHA256_DIGEST_SIZE);
+}
+
+
+/**
+ * @brief Process message in 16-word blocks
+ * @param[in] context Pointer to the SHA-256 context
+ **/
+
+void sha256ProcessBlock(Sha256Context *context)
+{
+   uint_t t;
+   uint32_t temp1;
+   uint32_t temp2;
+
+   //Initialize the 8 working registers
+   uint32_t a = context->h[0];
+   uint32_t b = context->h[1];
+   uint32_t c = context->h[2];
+   uint32_t d = context->h[3];
+   uint32_t e = context->h[4];
+   uint32_t f = context->h[5];
+   uint32_t g = context->h[6];
+   uint32_t h = context->h[7];
+
+   //Process message in 16-word blocks
+   uint32_t *w = context->w;
+
+   //Convert from big-endian byte order to host byte order
+   for(t = 0; t < 16; t++)
+      w[t] = betoh32(w[t]);
+
+   //SHA-256 hash computation (alternate method)
+   for(t = 0; t < 64; t++)
+   {
+      //Prepare the message schedule
+      if(t >= 16)
+         W(t) += SIGMA4(W(t + 14)) + W(t + 9) + SIGMA3(W(t + 1));
+
+      //Calculate T1 and T2
+      temp1 = h + SIGMA2(e) + CH(e, f, g) + k[t] + W(t);
+      temp2 = SIGMA1(a) + MAJ(a, b, c);
+
+      //Update the working registers
+      h = g;
+      g = f;
+      f = e;
+      e = d + temp1;
+      d = c;
+      c = b;
+      b = a;
+      a = temp1 + temp2;
+   }
+
+   //Update the hash value
+   context->h[0] += a;
+   context->h[1] += b;
+   context->h[2] += c;
+   context->h[3] += d;
+   context->h[4] += e;
+   context->h[5] += f;
+   context->h[6] += g;
+   context->h[7] += h;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha256.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,75 @@
+/**
+ * @file sha256.h
+ * @brief SHA-256 (Secure Hash Algorithm 256)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SHA256_H
+#define _SHA256_H
+
+//Dependencies
+#include "crypto.h"
+
+//SHA-256 block size
+#define SHA256_BLOCK_SIZE 64
+//SHA-256 digest size
+#define SHA256_DIGEST_SIZE 32
+//Common interface for hash algorithms
+#define SHA256_HASH_ALGO (&sha256HashAlgo)
+
+
+/**
+ * @brief SHA-256 algorithm context
+ **/
+
+typedef struct
+{
+   union
+   {
+      uint32_t h[8];
+      uint8_t digest[32];
+   };
+   union
+   {
+      uint32_t w[16];
+      uint8_t buffer[64];
+   };
+   size_t size;
+   uint64_t totalSize;
+} Sha256Context;
+
+
+//SHA-256 related constants
+extern const HashAlgo sha256HashAlgo;
+
+//SHA-256 related functions
+error_t sha256Compute(const void *data, size_t length, uint8_t *digest);
+void sha256Init(Sha256Context *context);
+void sha256Update(Sha256Context *context, const void *data, size_t length);
+void sha256Final(Sha256Context *context, uint8_t *digest);
+void sha256ProcessBlock(Sha256Context *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha384.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,149 @@
+/**
+ * @file sha384.c
+ * @brief SHA-384 (Secure Hash Algorithm 384)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * SHA-384 is a secure hash algorithm for computing a condensed representation
+ * of an electronic message. Refer to FIPS 180-4 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "sha384.h"
+
+//Check crypto library configuration
+#if (SHA384_SUPPORT == ENABLED)
+
+//SHA-384 object identifier (2.16.840.1.101.3.4.2.2)
+static const uint8_t sha384Oid[] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02};
+
+//Common interface for hash algorithms
+const HashAlgo sha384HashAlgo =
+{
+   "SHA-384",
+   sha384Oid,
+   sizeof(sha384Oid),
+   sizeof(Sha384Context),
+   SHA384_BLOCK_SIZE,
+   SHA384_DIGEST_SIZE,
+   (HashAlgoCompute) sha384Compute,
+   (HashAlgoInit) sha384Init,
+   (HashAlgoUpdate) sha384Update,
+   (HashAlgoFinal) sha384Final
+};
+
+
+/**
+ * @brief Digest a message using SHA-384
+ * @param[in] data Pointer to the message being hashed
+ * @param[in] length Length of the message
+ * @param[out] digest Pointer to the calculated digest
+ * @return Error code
+ **/
+
+error_t sha384Compute(const void *data, size_t length, uint8_t *digest)
+{
+   //Allocate a memory buffer to hold the SHA-384 context
+   Sha384Context *context = cryptoAllocMem(sizeof(Sha384Context));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the SHA-384 context
+   sha384Init(context);
+   //Digest the message
+   sha384Update(context, data, length);
+   //Finalize the SHA-384 message digest
+   sha384Final(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize SHA-384 message digest context
+ * @param[in] context Pointer to the SHA-384 context to initialize
+ **/
+
+void sha384Init(Sha384Context *context)
+{
+   //Set initial hash value
+   context->h[0] = 0xCBBB9D5DC1059ED8;
+   context->h[1] = 0x629A292A367CD507;
+   context->h[2] = 0x9159015A3070DD17;
+   context->h[3] = 0x152FECD8F70E5939;
+   context->h[4] = 0x67332667FFC00B31;
+   context->h[5] = 0x8EB44A8768581511;
+   context->h[6] = 0xDB0C2E0D64F98FA7;
+   context->h[7] = 0x47B5481DBEFA4FA4;
+
+   //Number of bytes in the buffer
+   context->size = 0;
+   //Total length of the message
+   context->totalSize = 0;
+}
+
+
+/**
+ * @brief Update the SHA-384 context with a portion of the message being hashed
+ * @param[in] context Pointer to the SHA-384 context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void sha384Update(Sha384Context *context, const void *data, size_t length)
+{
+   //The function is defined in the exact same manner as SHA-512
+   sha512Update(context, data, length);
+}
+
+
+/**
+ * @brief Finish the SHA-384 message digest
+ * @param[in] context Pointer to the SHA-384 context
+ * @param[out] digest Calculated digest (optional parameter)
+ **/
+
+void sha384Final(Sha384Context *context, uint8_t *digest)
+{
+   //The function is defined in the exact same manner as SHA-512
+   sha512Final(context, NULL);
+
+   //Copy the resulting digest
+   if(digest != NULL)
+      memcpy(digest, context->digest, SHA384_DIGEST_SIZE);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha384.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,61 @@
+/**
+ * @file sha384.h
+ * @brief SHA-384 (Secure Hash Algorithm 384)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SHA384_H
+#define _SHA384_H
+
+//Dependencies
+#include "crypto.h"
+#include "sha512.h"
+
+//SHA-384 block size
+#define SHA384_BLOCK_SIZE 128
+//SHA-384 digest size
+#define SHA384_DIGEST_SIZE 48
+//Common interface for hash algorithms
+#define SHA384_HASH_ALGO (&sha384HashAlgo)
+
+
+/**
+ * @brief SHA-384 algorithm context
+ **/
+
+typedef Sha512Context Sha384Context;
+
+
+//SHA-384 related constants
+extern const HashAlgo sha384HashAlgo;
+
+//SHA-384 related functions
+error_t sha384Compute(const void *data, size_t length, uint8_t *digest);
+void sha384Init(Sha384Context *context);
+void sha384Update(Sha384Context *context, const void *data, size_t length);
+void sha384Final(Sha384Context *context, uint8_t *digest);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha3_224.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,137 @@
+/**
+ * @file sha3_224.c
+ * @brief SHA3-224 hash function (SHA-3 with 224-bit output)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * SHA-3 is a secure hash algorithm for computing a condensed representation
+ * of an electronic message. Refer to FIPS 202 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "sha3_224.h"
+
+//Check crypto library configuration
+#if (SHA3_224_SUPPORT == ENABLED)
+
+//SHA3-224 object identifier (2.16.840.1.101.3.4.2.7)
+static const uint8_t sha3_224Oid[] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x07};
+
+//Common interface for hash algorithms
+const HashAlgo sha3_224HashAlgo =
+{
+   "SHA3-224",
+   sha3_224Oid,
+   sizeof(sha3_224Oid),
+   sizeof(Sha3_224Context),
+   SHA3_224_BLOCK_SIZE,
+   SHA3_224_DIGEST_SIZE,
+   (HashAlgoCompute) sha3_224Compute,
+   (HashAlgoInit) sha3_224Init,
+   (HashAlgoUpdate) sha3_224Update,
+   (HashAlgoFinal) sha3_224Final
+};
+
+
+/**
+ * @brief Digest a message using SHA3-224
+ * @param[in] data Pointer to the message being hashed
+ * @param[in] length Length of the message
+ * @param[out] digest Pointer to the calculated digest
+ * @return Error code
+ **/
+
+error_t sha3_224Compute(const void *data, size_t length, uint8_t *digest)
+{
+   Sha3_224Context *context;
+
+   //Allocate a memory buffer to hold the SHA3-224 context
+   context = cryptoAllocMem(sizeof(Sha3_224Context));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the SHA3-224 context
+   sha3_224Init(context);
+   //Digest the message
+   sha3_224Update(context, data, length);
+   //Finalize the SHA3-224 message digest
+   sha3_224Final(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize SHA3-224 message digest context
+ * @param[in] context Pointer to the SHA3-224 context to initialize
+ **/
+
+void sha3_224Init(Sha3_224Context *context)
+{
+   //The capacity of the sponge is twice the digest length
+   keccakInit(context, 2 * 224);
+}
+
+
+/**
+ * @brief Update the SHA3-224 context with a portion of the message being hashed
+ * @param[in] context Pointer to the SHA3-224 context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void sha3_224Update(Sha3_224Context *context, const void *data, size_t length)
+{
+   //Absorb the input data
+   keccakAbsorb(context, data, length);
+}
+
+
+/**
+ * @brief Finish the SHA3-224 message digest
+ * @param[in] context Pointer to the SHA3-224 context
+ * @param[out] digest Calculated digest (optional parameter)
+ **/
+
+void sha3_224Final(Sha3_224Context *context, uint8_t *digest)
+{
+   //Finish absorbing phase (SHA-3 padding differs from Keccak)
+   keccakFinal(context, 0x06);
+   //Extract data from the squeezing phase
+   keccakSqueeze(context, digest, SHA3_224_DIGEST_SIZE);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha3_224.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,61 @@
+/**
+ * @file sha3_224.h
+ * @brief SHA3-224 hash function (SHA-3 with 224-bit output)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SHA3_224_H
+#define _SHA3_224_H
+
+//Dependencies
+#include "crypto.h"
+#include "keccak.h"
+
+//SHA3-224 block size
+#define SHA3_224_BLOCK_SIZE 144
+//SHA3-224 digest size
+#define SHA3_224_DIGEST_SIZE 28
+//Common interface for hash algorithms
+#define SHA3_224_HASH_ALGO (&sha3_224HashAlgo)
+
+
+/**
+ * @brief SHA3-224 algorithm context
+ **/
+
+typedef KeccakContext Sha3_224Context;
+
+
+//SHA3-224 related constants
+extern const HashAlgo sha3_224HashAlgo;
+
+//SHA3-224 related functions
+error_t sha3_224Compute(const void *data, size_t length, uint8_t *digest);
+void sha3_224Init(Sha3_224Context *context);
+void sha3_224Update(Sha3_224Context *context, const void *data, size_t length);
+void sha3_224Final(Sha3_224Context *context, uint8_t *digest);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha3_256.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,137 @@
+/**
+ * @file sha3_256.c
+ * @brief SHA3-256 hash function (SHA-3 with 256-bit output)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * SHA-3 is a secure hash algorithm for computing a condensed representation
+ * of an electronic message. Refer to FIPS 202 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "sha3_256.h"
+
+//Check crypto library configuration
+#if (SHA3_256_SUPPORT == ENABLED)
+
+//SHA3-256 object identifier (2.16.840.1.101.3.4.2.8)
+static const uint8_t sha3_256Oid[] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x08};
+
+//Common interface for hash algorithms
+const HashAlgo sha3_256HashAlgo =
+{
+   "SHA3-256",
+   sha3_256Oid,
+   sizeof(sha3_256Oid),
+   sizeof(Sha3_256Context),
+   SHA3_256_BLOCK_SIZE,
+   SHA3_256_DIGEST_SIZE,
+   (HashAlgoCompute) sha3_256Compute,
+   (HashAlgoInit) sha3_256Init,
+   (HashAlgoUpdate) sha3_256Update,
+   (HashAlgoFinal) sha3_256Final
+};
+
+
+/**
+ * @brief Digest a message using SHA3-256
+ * @param[in] data Pointer to the message being hashed
+ * @param[in] length Length of the message
+ * @param[out] digest Pointer to the calculated digest
+ * @return Error code
+ **/
+
+error_t sha3_256Compute(const void *data, size_t length, uint8_t *digest)
+{
+   Sha3_256Context *context;
+
+   //Allocate a memory buffer to hold the SHA3-256 context
+   context = cryptoAllocMem(sizeof(Sha3_256Context));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the SHA3-256 context
+   sha3_256Init(context);
+   //Digest the message
+   sha3_256Update(context, data, length);
+   //Finalize the SHA3-256 message digest
+   sha3_256Final(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize SHA3-256 message digest context
+ * @param[in] context Pointer to the SHA3-256 context to initialize
+ **/
+
+void sha3_256Init(Sha3_256Context *context)
+{
+   //The capacity of the sponge is twice the digest length
+   keccakInit(context, 2 * 256);
+}
+
+
+/**
+ * @brief Update the SHA3-256 context with a portion of the message being hashed
+ * @param[in] context Pointer to the SHA3-256 context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void sha3_256Update(Sha3_256Context *context, const void *data, size_t length)
+{
+   //Absorb the input data
+   keccakAbsorb(context, data, length);
+}
+
+
+/**
+ * @brief Finish the SHA3-256 message digest
+ * @param[in] context Pointer to the SHA3-256 context
+ * @param[out] digest Calculated digest (optional parameter)
+ **/
+
+void sha3_256Final(Sha3_256Context *context, uint8_t *digest)
+{
+   //Finish absorbing phase (SHA-3 padding differs from Keccak)
+   keccakFinal(context, 0x06);
+   //Extract data from the squeezing phase
+   keccakSqueeze(context, digest, SHA3_256_DIGEST_SIZE);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha3_256.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,61 @@
+/**
+ * @file sha3_256.h
+ * @brief SHA3-256 hash function (SHA-3 with 256-bit output)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SHA3_256_H
+#define _SHA3_256_H
+
+//Dependencies
+#include "crypto.h"
+#include "keccak.h"
+
+//SHA3-256 block size
+#define SHA3_256_BLOCK_SIZE 136
+//SHA3-256 digest size
+#define SHA3_256_DIGEST_SIZE 32
+//Common interface for hash algorithms
+#define SHA3_256_HASH_ALGO (&sha3_256HashAlgo)
+
+
+/**
+ * @brief SHA3-256 algorithm context
+ **/
+
+typedef KeccakContext Sha3_256Context;
+
+
+//SHA3-256 related constants
+extern const HashAlgo sha3_256HashAlgo;
+
+//SHA3-256 related functions
+error_t sha3_256Compute(const void *data, size_t length, uint8_t *digest);
+void sha3_256Init(Sha3_256Context *context);
+void sha3_256Update(Sha3_256Context *context, const void *data, size_t length);
+void sha3_256Final(Sha3_256Context *context, uint8_t *digest);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha3_384.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,137 @@
+/**
+ * @file sha3_384.c
+ * @brief SHA3-384 hash function (SHA-3 with 384-bit output)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * SHA-3 is a secure hash algorithm for computing a condensed representation
+ * of an electronic message. Refer to FIPS 202 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "sha3_384.h"
+
+//Check crypto library configuration
+#if (SHA3_384_SUPPORT == ENABLED)
+
+//SHA3-384 object identifier (2.16.840.1.101.3.4.2.9)
+static const uint8_t sha3_384Oid[] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x09};
+
+//Common interface for hash algorithms
+const HashAlgo sha3_384HashAlgo =
+{
+   "SHA3-384",
+   sha3_384Oid,
+   sizeof(sha3_384Oid),
+   sizeof(Sha3_384Context),
+   SHA3_384_BLOCK_SIZE,
+   SHA3_384_DIGEST_SIZE,
+   (HashAlgoCompute) sha3_384Compute,
+   (HashAlgoInit) sha3_384Init,
+   (HashAlgoUpdate) sha3_384Update,
+   (HashAlgoFinal) sha3_384Final
+};
+
+
+/**
+ * @brief Digest a message using SHA3-384
+ * @param[in] data Pointer to the message being hashed
+ * @param[in] length Length of the message
+ * @param[out] digest Pointer to the calculated digest
+ * @return Error code
+ **/
+
+error_t sha3_384Compute(const void *data, size_t length, uint8_t *digest)
+{
+   Sha3_384Context *context;
+
+   //Allocate a memory buffer to hold the SHA3-384 context
+   context = cryptoAllocMem(sizeof(Sha3_384Context));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the SHA3-384 context
+   sha3_384Init(context);
+   //Digest the message
+   sha3_384Update(context, data, length);
+   //Finalize the SHA3-384 message digest
+   sha3_384Final(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize SHA3-384 message digest context
+ * @param[in] context Pointer to the SHA3-384 context to initialize
+ **/
+
+void sha3_384Init(Sha3_384Context *context)
+{
+   //The capacity of the sponge is twice the digest length
+   keccakInit(context, 2 * 384);
+}
+
+
+/**
+ * @brief Update the SHA3-384 context with a portion of the message being hashed
+ * @param[in] context Pointer to the SHA3-384 context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void sha3_384Update(Sha3_384Context *context, const void *data, size_t length)
+{
+   //Absorb the input data
+   keccakAbsorb(context, data, length);
+}
+
+
+/**
+ * @brief Finish the SHA3-384 message digest
+ * @param[in] context Pointer to the SHA3-384 context
+ * @param[out] digest Calculated digest (optional parameter)
+ **/
+
+void sha3_384Final(Sha3_384Context *context, uint8_t *digest)
+{
+   //Finish absorbing phase (SHA-3 padding differs from Keccak)
+   keccakFinal(context, 0x06);
+   //Extract data from the squeezing phase
+   keccakSqueeze(context, digest, SHA3_384_DIGEST_SIZE);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha3_384.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,61 @@
+/**
+ * @file sha3_384.h
+ * @brief SHA3-384 hash function (SHA-3 with 384-bit output)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SHA3_384_H
+#define _SHA3_384_H
+
+//Dependencies
+#include "crypto.h"
+#include "keccak.h"
+
+//SHA3-384 block size
+#define SHA3_384_BLOCK_SIZE 104
+//SHA3-384 digest size
+#define SHA3_384_DIGEST_SIZE 48
+//Common interface for hash algorithms
+#define SHA3_384_HASH_ALGO (&sha3_384HashAlgo)
+
+
+/**
+ * @brief SHA3-384 algorithm context
+ **/
+
+typedef KeccakContext Sha3_384Context;
+
+
+//SHA3-384 related constants
+extern const HashAlgo sha3_384HashAlgo;
+
+//SHA3-384 related functions
+error_t sha3_384Compute(const void *data, size_t length, uint8_t *digest);
+void sha3_384Init(Sha3_384Context *context);
+void sha3_384Update(Sha3_384Context *context, const void *data, size_t length);
+void sha3_384Final(Sha3_384Context *context, uint8_t *digest);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha3_512.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,137 @@
+/**
+ * @file sha3_512.c
+ * @brief SHA3-512 hash function (SHA-3 with 512-bit output)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * SHA-3 is a secure hash algorithm for computing a condensed representation
+ * of an electronic message. Refer to FIPS 202 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "sha3_512.h"
+
+//Check crypto library configuration
+#if (SHA3_512_SUPPORT == ENABLED)
+
+//SHA3-512 object identifier (2.16.840.1.101.3.4.2.10)
+static const uint8_t sha3_512Oid[] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0A};
+
+//Common interface for hash algorithms
+const HashAlgo sha3_512HashAlgo =
+{
+   "SHA3-512",
+   sha3_512Oid,
+   sizeof(sha3_512Oid),
+   sizeof(Sha3_512Context),
+   SHA3_512_BLOCK_SIZE,
+   SHA3_512_DIGEST_SIZE,
+   (HashAlgoCompute) sha3_512Compute,
+   (HashAlgoInit) sha3_512Init,
+   (HashAlgoUpdate) sha3_512Update,
+   (HashAlgoFinal) sha3_512Final
+};
+
+
+/**
+ * @brief Digest a message using SHA3-512
+ * @param[in] data Pointer to the message being hashed
+ * @param[in] length Length of the message
+ * @param[out] digest Pointer to the calculated digest
+ * @return Error code
+ **/
+
+error_t sha3_512Compute(const void *data, size_t length, uint8_t *digest)
+{
+   Sha3_512Context *context;
+
+   //Allocate a memory buffer to hold the SHA3-512 context
+   context = cryptoAllocMem(sizeof(Sha3_512Context));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the SHA3-512 context
+   sha3_512Init(context);
+   //Digest the message
+   sha3_512Update(context, data, length);
+   //Finalize the SHA3-512 message digest
+   sha3_512Final(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize SHA3-512 message digest context
+ * @param[in] context Pointer to the SHA3-512 context to initialize
+ **/
+
+void sha3_512Init(Sha3_512Context *context)
+{
+   //The capacity of the sponge is twice the digest length
+   keccakInit(context, 2 * 512);
+}
+
+
+/**
+ * @brief Update the SHA3-512 context with a portion of the message being hashed
+ * @param[in] context Pointer to the SHA3-512 context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void sha3_512Update(Sha3_512Context *context, const void *data, size_t length)
+{
+   //Absorb the input data
+   keccakAbsorb(context, data, length);
+}
+
+
+/**
+ * @brief Finish the SHA3-512 message digest
+ * @param[in] context Pointer to the SHA3-512 context
+ * @param[out] digest Calculated digest (optional parameter)
+ **/
+
+void sha3_512Final(Sha3_512Context *context, uint8_t *digest)
+{
+   //Finish absorbing phase (SHA-3 padding differs from Keccak)
+   keccakFinal(context, 0x06);
+   //Extract data from the squeezing phase
+   keccakSqueeze(context, digest, SHA3_512_DIGEST_SIZE);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha3_512.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,61 @@
+/**
+ * @file sha3_512.h
+ * @brief SHA3-512 hash function (SHA-3 with 512-bit output)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SHA3_512_H
+#define _SHA3_512_H
+
+//Dependencies
+#include "crypto.h"
+#include "keccak.h"
+
+//SHA3-512 block size
+#define SHA3_512_BLOCK_SIZE 72
+//SHA3-512 digest size
+#define SHA3_512_DIGEST_SIZE 64
+//Common interface for hash algorithms
+#define SHA3_512_HASH_ALGO (&sha3_512HashAlgo)
+
+
+/**
+ * @brief SHA3-512 algorithm context
+ **/
+
+typedef KeccakContext Sha3_512Context;
+
+
+//SHA3-512 related constants
+extern const HashAlgo sha3_512HashAlgo;
+
+//SHA3-512 related functions
+error_t sha3_512Compute(const void *data, size_t length, uint8_t *digest);
+void sha3_512Init(Sha3_512Context *context);
+void sha3_512Update(Sha3_512Context *context, const void *data, size_t length);
+void sha3_512Final(Sha3_512Context *context, uint8_t *digest);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha512.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,311 @@
+/**
+ * @file sha512.c
+ * @brief SHA-512 (Secure Hash Algorithm 512)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * SHA-512 is a secure hash algorithm for computing a condensed representation
+ * of an electronic message. Refer to FIPS 180-4 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "sha512.h"
+
+//Check crypto library configuration
+#if (SHA384_SUPPORT == ENABLED || SHA512_SUPPORT == ENABLED || \
+   SHA512_224_SUPPORT == ENABLED || SHA512_256_SUPPORT == ENABLED)
+
+//Macro to access the workspace as a circular buffer
+#define W(t) w[(t) & 0x0F]
+
+//SHA-512 auxiliary functions
+#define CH(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define MAJ(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+#define SIGMA1(x) (ROR64(x, 28) ^ ROR64(x, 34) ^ ROR64(x, 39))
+#define SIGMA2(x) (ROR64(x, 14) ^ ROR64(x, 18) ^ ROR64(x, 41))
+#define SIGMA3(x) (ROR64(x, 1) ^ ROR64(x, 8) ^ SHR64(x, 7))
+#define SIGMA4(x) (ROR64(x, 19) ^ ROR64(x, 61) ^ SHR64(x, 6))
+
+//SHA-512 padding
+static const uint8_t padding[128] =
+{
+   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,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+//SHA-512 constants
+static const uint64_t k[80] =
+{
+   0x428A2F98D728AE22, 0x7137449123EF65CD, 0xB5C0FBCFEC4D3B2F, 0xE9B5DBA58189DBBC,
+   0x3956C25BF348B538, 0x59F111F1B605D019, 0x923F82A4AF194F9B, 0xAB1C5ED5DA6D8118,
+   0xD807AA98A3030242, 0x12835B0145706FBE, 0x243185BE4EE4B28C, 0x550C7DC3D5FFB4E2,
+   0x72BE5D74F27B896F, 0x80DEB1FE3B1696B1, 0x9BDC06A725C71235, 0xC19BF174CF692694,
+   0xE49B69C19EF14AD2, 0xEFBE4786384F25E3, 0x0FC19DC68B8CD5B5, 0x240CA1CC77AC9C65,
+   0x2DE92C6F592B0275, 0x4A7484AA6EA6E483, 0x5CB0A9DCBD41FBD4, 0x76F988DA831153B5,
+   0x983E5152EE66DFAB, 0xA831C66D2DB43210, 0xB00327C898FB213F, 0xBF597FC7BEEF0EE4,
+   0xC6E00BF33DA88FC2, 0xD5A79147930AA725, 0x06CA6351E003826F, 0x142929670A0E6E70,
+   0x27B70A8546D22FFC, 0x2E1B21385C26C926, 0x4D2C6DFC5AC42AED, 0x53380D139D95B3DF,
+   0x650A73548BAF63DE, 0x766A0ABB3C77B2A8, 0x81C2C92E47EDAEE6, 0x92722C851482353B,
+   0xA2BFE8A14CF10364, 0xA81A664BBC423001, 0xC24B8B70D0F89791, 0xC76C51A30654BE30,
+   0xD192E819D6EF5218, 0xD69906245565A910, 0xF40E35855771202A, 0x106AA07032BBD1B8,
+   0x19A4C116B8D2D0C8, 0x1E376C085141AB53, 0x2748774CDF8EEB99, 0x34B0BCB5E19B48A8,
+   0x391C0CB3C5C95A63, 0x4ED8AA4AE3418ACB, 0x5B9CCA4F7763E373, 0x682E6FF3D6B2B8A3,
+   0x748F82EE5DEFB2FC, 0x78A5636F43172F60, 0x84C87814A1F0AB72, 0x8CC702081A6439EC,
+   0x90BEFFFA23631E28, 0xA4506CEBDE82BDE9, 0xBEF9A3F7B2C67915, 0xC67178F2E372532B,
+   0xCA273ECEEA26619C, 0xD186B8C721C0C207, 0xEADA7DD6CDE0EB1E, 0xF57D4F7FEE6ED178,
+   0x06F067AA72176FBA, 0x0A637DC5A2C898A6, 0x113F9804BEF90DAE, 0x1B710B35131C471B,
+   0x28DB77F523047D84, 0x32CAAB7B40C72493, 0x3C9EBE0A15C9BEBC, 0x431D67C49C100D4C,
+   0x4CC5D4BECB3E42B6, 0x597F299CFC657E2A, 0x5FCB6FAB3AD6FAEC, 0x6C44198C4A475817
+};
+
+//SHA-512 object identifier (2.16.840.1.101.3.4.2.3)
+static const uint8_t sha512Oid[] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03};
+
+//Common interface for hash algorithms
+const HashAlgo sha512HashAlgo =
+{
+   "SHA-512",
+   sha512Oid,
+   sizeof(sha512Oid),
+   sizeof(Sha512Context),
+   SHA512_BLOCK_SIZE,
+   SHA512_DIGEST_SIZE,
+   (HashAlgoCompute) sha512Compute,
+   (HashAlgoInit) sha512Init,
+   (HashAlgoUpdate) sha512Update,
+   (HashAlgoFinal) sha512Final
+};
+
+
+/**
+ * @brief Digest a message using SHA-512
+ * @param[in] data Pointer to the message being hashed
+ * @param[in] length Length of the message
+ * @param[out] digest Pointer to the calculated digest
+ * @return Error code
+ **/
+
+error_t sha512Compute(const void *data, size_t length, uint8_t *digest)
+{
+   //Allocate a memory buffer to hold the SHA-512 context
+   Sha512Context *context = cryptoAllocMem(sizeof(Sha512Context));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the SHA-512 context
+   sha512Init(context);
+   //Digest the message
+   sha512Update(context, data, length);
+   //Finalize the SHA-512 message digest
+   sha512Final(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize SHA-512 message digest context
+ * @param[in] context Pointer to the SHA-512 context to initialize
+ **/
+
+void sha512Init(Sha512Context *context)
+{
+   //Set initial hash value
+   context->h[0] = 0x6A09E667F3BCC908;
+   context->h[1] = 0xBB67AE8584CAA73B;
+   context->h[2] = 0x3C6EF372FE94F82B;
+   context->h[3] = 0xA54FF53A5F1D36F1;
+   context->h[4] = 0x510E527FADE682D1;
+   context->h[5] = 0x9B05688C2B3E6C1F;
+   context->h[6] = 0x1F83D9ABFB41BD6B;
+   context->h[7] = 0x5BE0CD19137E2179;
+
+   //Number of bytes in the buffer
+   context->size = 0;
+   //Total length of the message
+   context->totalSize = 0;
+}
+
+
+/**
+ * @brief Update the SHA-512 context with a portion of the message being hashed
+ * @param[in] context Pointer to the SHA-512 context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void sha512Update(Sha512Context *context, const void *data, size_t length)
+{
+   size_t n;
+
+   //Process the incoming data
+   while(length > 0)
+   {
+      //The buffer can hold at most 128 bytes
+      n = MIN(length, 128 - context->size);
+
+      //Copy the data to the buffer
+      memcpy(context->buffer + context->size, data, n);
+
+      //Update the SHA-512 context
+      context->size += n;
+      context->totalSize += n;
+      //Advance the data pointer
+      data = (uint8_t *) data + n;
+      //Remaining bytes to process
+      length -= n;
+
+      //Process message in 16-word blocks
+      if(context->size == 128)
+      {
+         //Transform the 16-word block
+         sha512ProcessBlock(context);
+         //Empty the buffer
+         context->size = 0;
+      }
+   }
+}
+
+
+/**
+ * @brief Finish the SHA-512 message digest
+ * @param[in] context Pointer to the SHA-512 context
+ * @param[out] digest Calculated digest (optional parameter)
+ **/
+
+void sha512Final(Sha512Context *context, uint8_t *digest)
+{
+   uint_t i;
+   size_t paddingSize;
+   uint64_t totalSize;
+
+   //Length of the original message (before padding)
+   totalSize = context->totalSize * 8;
+
+   //Pad the message so that its length is congruent to 112 modulo 128
+   if(context->size < 112)
+      paddingSize = 112 - context->size;
+   else
+      paddingSize = 128 + 112 - context->size;
+
+   //Append padding
+   sha512Update(context, padding, paddingSize);
+
+   //Append the length of the original message
+   context->w[14] = 0;
+   context->w[15] = htobe64(totalSize);
+
+   //Calculate the message digest
+   sha512ProcessBlock(context);
+
+   //Convert from host byte order to big-endian byte order
+   for(i = 0; i < 8; i++)
+      context->h[i] = htobe64(context->h[i]);
+
+   //Copy the resulting digest
+   if(digest != NULL)
+      memcpy(digest, context->digest, SHA512_DIGEST_SIZE);
+}
+
+
+/**
+ * @brief Process message in 16-word blocks
+ * @param[in] context Pointer to the SHA-512 context
+ **/
+
+void sha512ProcessBlock(Sha512Context *context)
+{
+   uint_t t;
+   uint64_t temp1;
+   uint64_t temp2;
+
+   //Initialize the 8 working registers
+   uint64_t a = context->h[0];
+   uint64_t b = context->h[1];
+   uint64_t c = context->h[2];
+   uint64_t d = context->h[3];
+   uint64_t e = context->h[4];
+   uint64_t f = context->h[5];
+   uint64_t g = context->h[6];
+   uint64_t h = context->h[7];
+
+   //Process message in 16-word blocks
+   uint64_t *w = context->w;
+
+   //Convert from big-endian byte order to host byte order
+   for(t = 0; t < 16; t++)
+      w[t] = betoh64(w[t]);
+
+   //SHA-512 hash computation (alternate method)
+   for(t = 0; t < 80; t++)
+   {
+      //Prepare the message schedule
+      if(t >= 16)
+         W(t) += SIGMA4(W(t + 14)) + W(t + 9) + SIGMA3(W(t + 1));
+
+      //Calculate T1 and T2
+      temp1 = h + SIGMA2(e) + CH(e, f, g) + k[t] + W(t);
+      temp2 = SIGMA1(a) + MAJ(a, b, c);
+
+      //Update the working registers
+      h = g;
+      g = f;
+      f = e;
+      e = d + temp1;
+      d = c;
+      c = b;
+      b = a;
+      a = temp1 + temp2;
+   }
+
+   //Update the hash value
+   context->h[0] += a;
+   context->h[1] += b;
+   context->h[2] += c;
+   context->h[3] += d;
+   context->h[4] += e;
+   context->h[5] += f;
+   context->h[6] += g;
+   context->h[7] += h;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha512.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,75 @@
+/**
+ * @file sha512.h
+ * @brief SHA-512 (Secure Hash Algorithm 512)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SHA512_H
+#define _SHA512_H
+
+//Dependencies
+#include "crypto.h"
+
+//SHA-512 block size
+#define SHA512_BLOCK_SIZE 128
+//SHA-512 digest size
+#define SHA512_DIGEST_SIZE 64
+//Common interface for hash algorithms
+#define SHA512_HASH_ALGO (&sha512HashAlgo)
+
+
+/**
+ * @brief SHA-512 algorithm context
+ **/
+
+typedef struct
+{
+   union
+   {
+      uint64_t h[8];
+      uint8_t digest[64];
+   };
+   union
+   {
+      uint64_t w[16];
+      uint8_t buffer[128];
+   };
+   size_t size;
+   uint64_t totalSize;
+} Sha512Context;
+
+
+//SHA-512 related constants
+extern const HashAlgo sha512HashAlgo;
+
+//SHA-512 related functions
+error_t sha512Compute(const void *data, size_t length, uint8_t *digest);
+void sha512Init(Sha512Context *context);
+void sha512Update(Sha512Context *context, const void *data, size_t length);
+void sha512Final(Sha512Context *context, uint8_t *digest);
+void sha512ProcessBlock(Sha512Context *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha512_224.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,149 @@
+/**
+ * @file sha512_224.c
+ * @brief SHA-512/224 (Secure Hash Algorithm)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * SHA-512/224 is a secure hash algorithm for computing a condensed representation
+ * of an electronic message. Refer to FIPS 180-4 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "sha512_224.h"
+
+//Check crypto library configuration
+#if (SHA512_224_SUPPORT == ENABLED)
+
+//SHA-512/224 object identifier (2.16.840.1.101.3.4.2.5)
+static const uint8_t sha512_224Oid[] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x05};
+
+//Common interface for hash algorithms
+const HashAlgo sha512_224HashAlgo =
+{
+   "SHA-512/224",
+   sha512_224Oid,
+   sizeof(sha512_224Oid),
+   sizeof(Sha512_224Context),
+   SHA512_224_BLOCK_SIZE,
+   SHA512_224_DIGEST_SIZE,
+   (HashAlgoCompute) sha512_224Compute,
+   (HashAlgoInit) sha512_224Init,
+   (HashAlgoUpdate) sha512_224Update,
+   (HashAlgoFinal) sha512_224Final
+};
+
+
+/**
+ * @brief Digest a message using SHA-512/224
+ * @param[in] data Pointer to the message being hashed
+ * @param[in] length Length of the message
+ * @param[out] digest Pointer to the calculated digest
+ * @return Error code
+ **/
+
+error_t sha512_224Compute(const void *data, size_t length, uint8_t *digest)
+{
+   //Allocate a memory buffer to hold the SHA-512/224 context
+   Sha512_224Context *context = cryptoAllocMem(sizeof(Sha512_224Context));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the SHA-512/224 context
+   sha512_224Init(context);
+   //Digest the message
+   sha512_224Update(context, data, length);
+   //Finalize the SHA-512/224 message digest
+   sha512_224Final(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize SHA-512/224 message digest context
+ * @param[in] context Pointer to the SHA-512/224 context to initialize
+ **/
+
+void sha512_224Init(Sha512_224Context *context)
+{
+   //Set initial hash value
+   context->h[0] = 0x8C3D37C819544DA2;
+   context->h[1] = 0x73E1996689DCD4D6;
+   context->h[2] = 0x1DFAB7AE32FF9C82;
+   context->h[3] = 0x679DD514582F9FCF;
+   context->h[4] = 0x0F6D2B697BD44DA8;
+   context->h[5] = 0x77E36F7304C48942;
+   context->h[6] = 0x3F9D85A86A1D36C8;
+   context->h[7] = 0x1112E6AD91D692A1;
+
+   //Number of bytes in the buffer
+   context->size = 0;
+   //Total length of the message
+   context->totalSize = 0;
+}
+
+
+/**
+ * @brief Update the SHA-512/224 context with a portion of the message being hashed
+ * @param[in] context Pointer to the SHA-512/224 context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void sha512_224Update(Sha512_224Context *context, const void *data, size_t length)
+{
+   //The function is defined in the exact same manner as SHA-512
+   sha512Update(context, data, length);
+}
+
+
+/**
+ * @brief Finish the SHA-512/224 message digest
+ * @param[in] context Pointer to the SHA-512/224 context
+ * @param[out] digest Calculated digest (optional parameter)
+ **/
+
+void sha512_224Final(Sha512_224Context *context, uint8_t *digest)
+{
+   //The function is defined in the exact same manner as SHA-512
+   sha512Final(context, NULL);
+
+   //Copy the resulting digest
+   if(digest != NULL)
+      memcpy(digest, context->digest, SHA512_224_DIGEST_SIZE);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha512_224.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,61 @@
+/**
+ * @file sha512_224.h
+ * @brief SHA-512/224 (Secure Hash Algorithm)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SHA512_224_H
+#define _SHA512_224_H
+
+//Dependencies
+#include "crypto.h"
+#include "sha512.h"
+
+//SHA-512/224 block size
+#define SHA512_224_BLOCK_SIZE 128
+//SHA-512/224 digest size
+#define SHA512_224_DIGEST_SIZE 28
+//Common interface for hash algorithms
+#define SHA512_224_HASH_ALGO (&sha512_224HashAlgo)
+
+
+/**
+ * @brief SHA-512/224 algorithm context
+ **/
+
+typedef Sha512Context Sha512_224Context;
+
+
+//SHA-512/224 related constants
+extern const HashAlgo sha512_224HashAlgo;
+
+//SHA-512/224 related functions
+error_t sha512_224Compute(const void *data, size_t length, uint8_t *digest);
+void sha512_224Init(Sha512_224Context *context);
+void sha512_224Update(Sha512_224Context *context, const void *data, size_t length);
+void sha512_224Final(Sha512_224Context *context, uint8_t *digest);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha512_256.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,149 @@
+/**
+ * @file sha512_256.c
+ * @brief SHA-512/256 (Secure Hash Algorithm)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * SHA-512/256 is a secure hash algorithm for computing a condensed representation
+ * of an electronic message. Refer to FIPS 180-4 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "sha512_256.h"
+
+//Check crypto library configuration
+#if (SHA512_256_SUPPORT == ENABLED)
+
+//SHA-512/256 object identifier (2.16.840.1.101.3.4.2.6)
+static const uint8_t sha512_256Oid[] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x06};
+
+//Common interface for hash algorithms
+const HashAlgo sha512_256HashAlgo =
+{
+   "SHA-512/256",
+   sha512_256Oid,
+   sizeof(sha512_256Oid),
+   sizeof(Sha512_256Context),
+   SHA512_256_BLOCK_SIZE,
+   SHA512_256_DIGEST_SIZE,
+   (HashAlgoCompute) sha512_256Compute,
+   (HashAlgoInit) sha512_256Init,
+   (HashAlgoUpdate) sha512_256Update,
+   (HashAlgoFinal) sha512_256Final
+};
+
+
+/**
+ * @brief Digest a message using SHA-512/256
+ * @param[in] data Pointer to the message being hashed
+ * @param[in] length Length of the message
+ * @param[out] digest Pointer to the calculated digest
+ * @return Error code
+ **/
+
+error_t sha512_256Compute(const void *data, size_t length, uint8_t *digest)
+{
+   //Allocate a memory buffer to hold the SHA-512/256 context
+   Sha512_256Context *context = cryptoAllocMem(sizeof(Sha512_256Context));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the SHA-512/256 context
+   sha512_256Init(context);
+   //Digest the message
+   sha512_256Update(context, data, length);
+   //Finalize the SHA-512/256 message digest
+   sha512_256Final(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize SHA-512/256 message digest context
+ * @param[in] context Pointer to the SHA-512/256 context to initialize
+ **/
+
+void sha512_256Init(Sha512_256Context *context)
+{
+   //Set initial hash value
+   context->h[0] = 0x22312194FC2BF72C;
+   context->h[1] = 0x9F555FA3C84C64C2;
+   context->h[2] = 0x2393B86B6F53B151;
+   context->h[3] = 0x963877195940EABD;
+   context->h[4] = 0x96283EE2A88EFFE3;
+   context->h[5] = 0xBE5E1E2553863992;
+   context->h[6] = 0x2B0199FC2C85B8AA;
+   context->h[7] = 0x0EB72DDC81C52CA2;
+
+   //Number of bytes in the buffer
+   context->size = 0;
+   //Total length of the message
+   context->totalSize = 0;
+}
+
+
+/**
+ * @brief Update the SHA-512/256 context with a portion of the message being hashed
+ * @param[in] context Pointer to the SHA-512/256 context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void sha512_256Update(Sha512_256Context *context, const void *data, size_t length)
+{
+   //The function is defined in the exact same manner as SHA-512
+   sha512Update(context, data, length);
+}
+
+
+/**
+ * @brief Finish the SHA-512/256 message digest
+ * @param[in] context Pointer to the SHA-512/256 context
+ * @param[out] digest Calculated digest (optional parameter)
+ **/
+
+void sha512_256Final(Sha512_256Context *context, uint8_t *digest)
+{
+   //The function is defined in the exact same manner as SHA-512
+   sha512Final(context, NULL);
+
+   //Copy the resulting digest
+   if(digest != NULL)
+      memcpy(digest, context->digest, SHA512_256_DIGEST_SIZE);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/sha512_256.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,61 @@
+/**
+ * @file sha512_256.h
+ * @brief SHA-512/256 (Secure Hash Algorithm)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SHA512_256_H
+#define _SHA512_256_H
+
+//Dependencies
+#include "crypto.h"
+#include "sha512.h"
+
+//SHA-512/256 block size
+#define SHA512_256_BLOCK_SIZE 128
+//SHA-512/256 digest size
+#define SHA512_256_DIGEST_SIZE 32
+//Common interface for hash algorithms
+#define SHA512_256_HASH_ALGO (&sha512_256HashAlgo)
+
+
+/**
+ * @brief SHA-512/256 algorithm context
+ **/
+
+typedef Sha512Context Sha512_256Context;
+
+
+//SHA-512/256 related constants
+extern const HashAlgo sha512_256HashAlgo;
+
+//SHA-512/256 related functions
+error_t sha512_256Compute(const void *data, size_t length, uint8_t *digest);
+void sha512_256Init(Sha512_256Context *context);
+void sha512_256Update(Sha512_256Context *context, const void *data, size_t length);
+void sha512_256Final(Sha512_256Context *context, uint8_t *digest);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/tiger.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,559 @@
+/**
+ * @file tiger.c
+ * @brief Tiger hash function
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "tiger.h"
+
+//Check crypto library configuration
+#if (TIGER_SUPPORT == ENABLED)
+
+//Round function
+#define ROUND(a, b, c, x, mul) \
+{ \
+      c ^= x; \
+      a -= t1[c & 0xFF] ^ t2[(c >> 16) & 0xFF] ^ t3[(c >> 32) & 0xFF] ^ t4[(c >> 48) & 0xFF]; \
+      b += t4[(c >> 8) & 0xFF] ^ t3[(c >> 24) & 0xFF] ^ t2[(c >> 40) & 0xFF] ^ t1[(c >> 56) & 0xFF]; \
+      b *= mul; \
+}
+
+//Key schedule
+#define KEY_SCHEDULE(x) \
+{ \
+   x[0] -= x[7] ^ 0xA5A5A5A5A5A5A5A5; \
+   x[1] ^= x[0]; \
+   x[2] += x[1]; \
+   x[3] -= x[2] ^ ((~x[1]) << 19); \
+   x[4] ^= x[3]; \
+   x[5] += x[4]; \
+   x[6] -= x[5] ^ ((~x[4]) >> 23); \
+   x[7] ^= x[6]; \
+   x[0] += x[7]; \
+   x[1] -= x[0] ^ ((~x[7]) << 19); \
+   x[2] ^= x[1]; \
+   x[3] += x[2]; \
+   x[4] -= x[3] ^ ((~x[2]) >> 23); \
+   x[5] ^= x[4]; \
+   x[6] += x[5]; \
+   x[7] -= x[6] ^ 0x0123456789ABCDEF; \
+}
+
+//Tiger padding
+static const uint8_t padding[64] =
+{
+   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+//S-box 1
+static const uint64_t t1[256] =
+{
+   0x02AAB17CF7E90C5E, 0xAC424B03E243A8EC, 0x72CD5BE30DD5FCD3, 0x6D019B93F6F97F3A,
+   0xCD9978FFD21F9193, 0x7573A1C9708029E2, 0xB164326B922A83C3, 0x46883EEE04915870,
+   0xEAACE3057103ECE6, 0xC54169B808A3535C, 0x4CE754918DDEC47C, 0x0AA2F4DFDC0DF40C,
+   0x10B76F18A74DBEFA, 0xC6CCB6235AD1AB6A, 0x13726121572FE2FF, 0x1A488C6F199D921E,
+   0x4BC9F9F4DA0007CA, 0x26F5E6F6E85241C7, 0x859079DBEA5947B6, 0x4F1885C5C99E8C92,
+   0xD78E761EA96F864B, 0x8E36428C52B5C17D, 0x69CF6827373063C1, 0xB607C93D9BB4C56E,
+   0x7D820E760E76B5EA, 0x645C9CC6F07FDC42, 0xBF38A078243342E0, 0x5F6B343C9D2E7D04,
+   0xF2C28AEB600B0EC6, 0x6C0ED85F7254BCAC, 0x71592281A4DB4FE5, 0x1967FA69CE0FED9F,
+   0xFD5293F8B96545DB, 0xC879E9D7F2A7600B, 0x860248920193194E, 0xA4F9533B2D9CC0B3,
+   0x9053836C15957613, 0xDB6DCF8AFC357BF1, 0x18BEEA7A7A370F57, 0x037117CA50B99066,
+   0x6AB30A9774424A35, 0xF4E92F02E325249B, 0x7739DB07061CCAE1, 0xD8F3B49CECA42A05,
+   0xBD56BE3F51382F73, 0x45FAED5843B0BB28, 0x1C813D5C11BF1F83, 0x8AF0E4B6D75FA169,
+   0x33EE18A487AD9999, 0x3C26E8EAB1C94410, 0xB510102BC0A822F9, 0x141EEF310CE6123B,
+   0xFC65B90059DDB154, 0xE0158640C5E0E607, 0x884E079826C3A3CF, 0x930D0D9523C535FD,
+   0x35638D754E9A2B00, 0x4085FCCF40469DD5, 0xC4B17AD28BE23A4C, 0xCAB2F0FC6A3E6A2E,
+   0x2860971A6B943FCD, 0x3DDE6EE212E30446, 0x6222F32AE01765AE, 0x5D550BB5478308FE,
+   0xA9EFA98DA0EDA22A, 0xC351A71686C40DA7, 0x1105586D9C867C84, 0xDCFFEE85FDA22853,
+   0xCCFBD0262C5EEF76, 0xBAF294CB8990D201, 0xE69464F52AFAD975, 0x94B013AFDF133E14,
+   0x06A7D1A32823C958, 0x6F95FE5130F61119, 0xD92AB34E462C06C0, 0xED7BDE33887C71D2,
+   0x79746D6E6518393E, 0x5BA419385D713329, 0x7C1BA6B948A97564, 0x31987C197BFDAC67,
+   0xDE6C23C44B053D02, 0x581C49FED002D64D, 0xDD474D6338261571, 0xAA4546C3E473D062,
+   0x928FCE349455F860, 0x48161BBACAAB94D9, 0x63912430770E6F68, 0x6EC8A5E602C6641C,
+   0x87282515337DDD2B, 0x2CDA6B42034B701B, 0xB03D37C181CB096D, 0xE108438266C71C6F,
+   0x2B3180C7EB51B255, 0xDF92B82F96C08BBC, 0x5C68C8C0A632F3BA, 0x5504CC861C3D0556,
+   0xABBFA4E55FB26B8F, 0x41848B0AB3BACEB4, 0xB334A273AA445D32, 0xBCA696F0A85AD881,
+   0x24F6EC65B528D56C, 0x0CE1512E90F4524A, 0x4E9DD79D5506D35A, 0x258905FAC6CE9779,
+   0x2019295B3E109B33, 0xF8A9478B73A054CC, 0x2924F2F934417EB0, 0x3993357D536D1BC4,
+   0x38A81AC21DB6FF8B, 0x47C4FBF17D6016BF, 0x1E0FAADD7667E3F5, 0x7ABCFF62938BEB96,
+   0xA78DAD948FC179C9, 0x8F1F98B72911E50D, 0x61E48EAE27121A91, 0x4D62F7AD31859808,
+   0xECEBA345EF5CEAEB, 0xF5CEB25EBC9684CE, 0xF633E20CB7F76221, 0xA32CDF06AB8293E4,
+   0x985A202CA5EE2CA4, 0xCF0B8447CC8A8FB1, 0x9F765244979859A3, 0xA8D516B1A1240017,
+   0x0BD7BA3EBB5DC726, 0xE54BCA55B86ADB39, 0x1D7A3AFD6C478063, 0x519EC608E7669EDD,
+   0x0E5715A2D149AA23, 0x177D4571848FF194, 0xEEB55F3241014C22, 0x0F5E5CA13A6E2EC2,
+   0x8029927B75F5C361, 0xAD139FABC3D6E436, 0x0D5DF1A94CCF402F, 0x3E8BD948BEA5DFC8,
+   0xA5A0D357BD3FF77E, 0xA2D12E251F74F645, 0x66FD9E525E81A082, 0x2E0C90CE7F687A49,
+   0xC2E8BCBEBA973BC5, 0x000001BCE509745F, 0x423777BBE6DAB3D6, 0xD1661C7EAEF06EB5,
+   0xA1781F354DAACFD8, 0x2D11284A2B16AFFC, 0xF1FC4F67FA891D1F, 0x73ECC25DCB920ADA,
+   0xAE610C22C2A12651, 0x96E0A810D356B78A, 0x5A9A381F2FE7870F, 0xD5AD62EDE94E5530,
+   0xD225E5E8368D1427, 0x65977B70C7AF4631, 0x99F889B2DE39D74F, 0x233F30BF54E1D143,
+   0x9A9675D3D9A63C97, 0x5470554FF334F9A8, 0x166ACB744A4F5688, 0x70C74CAAB2E4AEAD,
+   0xF0D091646F294D12, 0x57B82A89684031D1, 0xEFD95A5A61BE0B6B, 0x2FBD12E969F2F29A,
+   0x9BD37013FEFF9FE8, 0x3F9B0404D6085A06, 0x4940C1F3166CFE15, 0x09542C4DCDF3DEFB,
+   0xB4C5218385CD5CE3, 0xC935B7DC4462A641, 0x3417F8A68ED3B63F, 0xB80959295B215B40,
+   0xF99CDAEF3B8C8572, 0x018C0614F8FCB95D, 0x1B14ACCD1A3ACDF3, 0x84D471F200BB732D,
+   0xC1A3110E95E8DA16, 0x430A7220BF1A82B8, 0xB77E090D39DF210E, 0x5EF4BD9F3CD05E9D,
+   0x9D4FF6DA7E57A444, 0xDA1D60E183D4A5F8, 0xB287C38417998E47, 0xFE3EDC121BB31886,
+   0xC7FE3CCC980CCBEF, 0xE46FB590189BFD03, 0x3732FD469A4C57DC, 0x7EF700A07CF1AD65,
+   0x59C64468A31D8859, 0x762FB0B4D45B61F6, 0x155BAED099047718, 0x68755E4C3D50BAA6,
+   0xE9214E7F22D8B4DF, 0x2ADDBF532EAC95F4, 0x32AE3909B4BD0109, 0x834DF537B08E3450,
+   0xFA209DA84220728D, 0x9E691D9B9EFE23F7, 0x0446D288C4AE8D7F, 0x7B4CC524E169785B,
+   0x21D87F0135CA1385, 0xCEBB400F137B8AA5, 0x272E2B66580796BE, 0x3612264125C2B0DE,
+   0x057702BDAD1EFBB2, 0xD4BABB8EACF84BE9, 0x91583139641BC67B, 0x8BDC2DE08036E024,
+   0x603C8156F49F68ED, 0xF7D236F7DBEF5111, 0x9727C4598AD21E80, 0xA08A0896670A5FD7,
+   0xCB4A8F4309EBA9CB, 0x81AF564B0F7036A1, 0xC0B99AA778199ABD, 0x959F1EC83FC8E952,
+   0x8C505077794A81B9, 0x3ACAAF8F056338F0, 0x07B43F50627A6778, 0x4A44AB49F5ECCC77,
+   0x3BC3D6E4B679EE98, 0x9CC0D4D1CF14108C, 0x4406C00B206BC8A0, 0x82A18854C8D72D89,
+   0x67E366B35C3C432C, 0xB923DD61102B37F2, 0x56AB2779D884271D, 0xBE83E1B0FF1525AF,
+   0xFB7C65D4217E49A9, 0x6BDBE0E76D48E7D4, 0x08DF828745D9179E, 0x22EA6A9ADD53BD34,
+   0xE36E141C5622200A, 0x7F805D1B8CB750EE, 0xAFE5C7A59F58E837, 0xE27F996A4FB1C23C,
+   0xD3867DFB0775F0D0, 0xD0E673DE6E88891A, 0x123AEB9EAFB86C25, 0x30F1D5D5C145B895,
+   0xBB434A2DEE7269E7, 0x78CB67ECF931FA38, 0xF33B0372323BBF9C, 0x52D66336FB279C74,
+   0x505F33AC0AFB4EAA, 0xE8A5CD99A2CCE187, 0x534974801E2D30BB, 0x8D2D5711D5876D90,
+   0x1F1A412891BC038E, 0xD6E2E71D82E56648, 0x74036C3A497732B7, 0x89B67ED96361F5AB,
+   0xFFED95D8F1EA02A2, 0xE72B3BD61464D43D, 0xA6300F170BDC4820, 0xEBC18760ED78A77A
+};
+
+//S-box 2
+static const uint64_t t2[256] =
+{
+   0xE6A6BE5A05A12138, 0xB5A122A5B4F87C98, 0x563C6089140B6990, 0x4C46CB2E391F5DD5,
+   0xD932ADDBC9B79434, 0x08EA70E42015AFF5, 0xD765A6673E478CF1, 0xC4FB757EAB278D99,
+   0xDF11C6862D6E0692, 0xDDEB84F10D7F3B16, 0x6F2EF604A665EA04, 0x4A8E0F0FF0E0DFB3,
+   0xA5EDEEF83DBCBA51, 0xFC4F0A2A0EA4371E, 0xE83E1DA85CB38429, 0xDC8FF882BA1B1CE2,
+   0xCD45505E8353E80D, 0x18D19A00D4DB0717, 0x34A0CFEDA5F38101, 0x0BE77E518887CAF2,
+   0x1E341438B3C45136, 0xE05797F49089CCF9, 0xFFD23F9DF2591D14, 0x543DDA228595C5CD,
+   0x661F81FD99052A33, 0x8736E641DB0F7B76, 0x15227725418E5307, 0xE25F7F46162EB2FA,
+   0x48A8B2126C13D9FE, 0xAFDC541792E76EEA, 0x03D912BFC6D1898F, 0x31B1AAFA1B83F51B,
+   0xF1AC2796E42AB7D9, 0x40A3A7D7FCD2EBAC, 0x1056136D0AFBBCC5, 0x7889E1DD9A6D0C85,
+   0xD33525782A7974AA, 0xA7E25D09078AC09B, 0xBD4138B3EAC6EDD0, 0x920ABFBE71EB9E70,
+   0xA2A5D0F54FC2625C, 0xC054E36B0B1290A3, 0xF6DD59FF62FE932B, 0x3537354511A8AC7D,
+   0xCA845E9172FADCD4, 0x84F82B60329D20DC, 0x79C62CE1CD672F18, 0x8B09A2ADD124642C,
+   0xD0C1E96A19D9E726, 0x5A786A9B4BA9500C, 0x0E020336634C43F3, 0xC17B474AEB66D822,
+   0x6A731AE3EC9BAAC2, 0x8226667AE0840258, 0x67D4567691CAECA5, 0x1D94155C4875ADB5,
+   0x6D00FD985B813FDF, 0x51286EFCB774CD06, 0x5E8834471FA744AF, 0xF72CA0AEE761AE2E,
+   0xBE40E4CDAEE8E09A, 0xE9970BBB5118F665, 0x726E4BEB33DF1964, 0x703B000729199762,
+   0x4631D816F5EF30A7, 0xB880B5B51504A6BE, 0x641793C37ED84B6C, 0x7B21ED77F6E97D96,
+   0x776306312EF96B73, 0xAE528948E86FF3F4, 0x53DBD7F286A3F8F8, 0x16CADCE74CFC1063,
+   0x005C19BDFA52C6DD, 0x68868F5D64D46AD3, 0x3A9D512CCF1E186A, 0x367E62C2385660AE,
+   0xE359E7EA77DCB1D7, 0x526C0773749ABE6E, 0x735AE5F9D09F734B, 0x493FC7CC8A558BA8,
+   0xB0B9C1533041AB45, 0x321958BA470A59BD, 0x852DB00B5F46C393, 0x91209B2BD336B0E5,
+   0x6E604F7D659EF19F, 0xB99A8AE2782CCB24, 0xCCF52AB6C814C4C7, 0x4727D9AFBE11727B,
+   0x7E950D0C0121B34D, 0x756F435670AD471F, 0xF5ADD442615A6849, 0x4E87E09980B9957A,
+   0x2ACFA1DF50AEE355, 0xD898263AFD2FD556, 0xC8F4924DD80C8FD6, 0xCF99CA3D754A173A,
+   0xFE477BACAF91BF3C, 0xED5371F6D690C12D, 0x831A5C285E687094, 0xC5D3C90A3708A0A4,
+   0x0F7F903717D06580, 0x19F9BB13B8FDF27F, 0xB1BD6F1B4D502843, 0x1C761BA38FFF4012,
+   0x0D1530C4E2E21F3B, 0x8943CE69A7372C8A, 0xE5184E11FEB5CE66, 0x618BDB80BD736621,
+   0x7D29BAD68B574D0B, 0x81BB613E25E6FE5B, 0x071C9C10BC07913F, 0xC7BEEB7909AC2D97,
+   0xC3E58D353BC5D757, 0xEB017892F38F61E8, 0xD4EFFB9C9B1CC21A, 0x99727D26F494F7AB,
+   0xA3E063A2956B3E03, 0x9D4A8B9A4AA09C30, 0x3F6AB7D500090FB4, 0x9CC0F2A057268AC0,
+   0x3DEE9D2DEDBF42D1, 0x330F49C87960A972, 0xC6B2720287421B41, 0x0AC59EC07C00369C,
+   0xEF4EAC49CB353425, 0xF450244EEF0129D8, 0x8ACC46E5CAF4DEB6, 0x2FFEAB63989263F7,
+   0x8F7CB9FE5D7A4578, 0x5BD8F7644E634635, 0x427A7315BF2DC900, 0x17D0C4AA2125261C,
+   0x3992486C93518E50, 0xB4CBFEE0A2D7D4C3, 0x7C75D6202C5DDD8D, 0xDBC295D8E35B6C61,
+   0x60B369D302032B19, 0xCE42685FDCE44132, 0x06F3DDB9DDF65610, 0x8EA4D21DB5E148F0,
+   0x20B0FCE62FCD496F, 0x2C1B912358B0EE31, 0xB28317B818F5A308, 0xA89C1E189CA6D2CF,
+   0x0C6B18576AAADBC8, 0xB65DEAA91299FAE3, 0xFB2B794B7F1027E7, 0x04E4317F443B5BEB,
+   0x4B852D325939D0A6, 0xD5AE6BEEFB207FFC, 0x309682B281C7D374, 0xBAE309A194C3B475,
+   0x8CC3F97B13B49F05, 0x98A9422FF8293967, 0x244B16B01076FF7C, 0xF8BF571C663D67EE,
+   0x1F0D6758EEE30DA1, 0xC9B611D97ADEB9B7, 0xB7AFD5887B6C57A2, 0x6290AE846B984FE1,
+   0x94DF4CDEACC1A5FD, 0x058A5BD1C5483AFF, 0x63166CC142BA3C37, 0x8DB8526EB2F76F40,
+   0xE10880036F0D6D4E, 0x9E0523C9971D311D, 0x45EC2824CC7CD691, 0x575B8359E62382C9,
+   0xFA9E400DC4889995, 0xD1823ECB45721568, 0xDAFD983B8206082F, 0xAA7D29082386A8CB,
+   0x269FCD4403B87588, 0x1B91F5F728BDD1E0, 0xE4669F39040201F6, 0x7A1D7C218CF04ADE,
+   0x65623C29D79CE5CE, 0x2368449096C00BB1, 0xAB9BF1879DA503BA, 0xBC23ECB1A458058E,
+   0x9A58DF01BB401ECC, 0xA070E868A85F143D, 0x4FF188307DF2239E, 0x14D565B41A641183,
+   0xEE13337452701602, 0x950E3DCF3F285E09, 0x59930254B9C80953, 0x3BF299408930DA6D,
+   0xA955943F53691387, 0xA15EDECAA9CB8784, 0x29142127352BE9A0, 0x76F0371FFF4E7AFB,
+   0x0239F450274F2228, 0xBB073AF01D5E868B, 0xBFC80571C10E96C1, 0xD267088568222E23,
+   0x9671A3D48E80B5B0, 0x55B5D38AE193BB81, 0x693AE2D0A18B04B8, 0x5C48B4ECADD5335F,
+   0xFD743B194916A1CA, 0x2577018134BE98C4, 0xE77987E83C54A4AD, 0x28E11014DA33E1B9,
+   0x270CC59E226AA213, 0x71495F756D1A5F60, 0x9BE853FB60AFEF77, 0xADC786A7F7443DBF,
+   0x0904456173B29A82, 0x58BC7A66C232BD5E, 0xF306558C673AC8B2, 0x41F639C6B6C9772A,
+   0x216DEFE99FDA35DA, 0x11640CC71C7BE615, 0x93C43694565C5527, 0xEA038E6246777839,
+   0xF9ABF3CE5A3E2469, 0x741E768D0FD312D2, 0x0144B883CED652C6, 0xC20B5A5BA33F8552,
+   0x1AE69633C3435A9D, 0x97A28CA4088CFDEC, 0x8824A43C1E96F420, 0x37612FA66EEEA746,
+   0x6B4CB165F9CF0E5A, 0x43AA1C06A0ABFB4A, 0x7F4DC26FF162796B, 0x6CBACC8E54ED9B0F,
+   0xA6B7FFEFD2BB253E, 0x2E25BC95B0A29D4F, 0x86D6A58BDEF1388C, 0xDED74AC576B6F054,
+   0x8030BDBC2B45805D, 0x3C81AF70E94D9289, 0x3EFF6DDA9E3100DB, 0xB38DC39FDFCC8847,
+   0x123885528D17B87E, 0xF2DA0ED240B1B642, 0x44CEFADCD54BF9A9, 0x1312200E433C7EE6,
+   0x9FFCC84F3A78C748, 0xF0CD1F72248576BB, 0xEC6974053638CFE4, 0x2BA7B67C0CEC4E4C,
+   0xAC2F4DF3E5CE32ED, 0xCB33D14326EA4C11, 0xA4E9044CC77E58BC, 0x5F513293D934FCEF,
+   0x5DC9645506E55444, 0x50DE418F317DE40A, 0x388CB31A69DDE259, 0x2DB4A83455820A86,
+   0x9010A91E84711AE9, 0x4DF7F0B7B1498371, 0xD62A2EABC0977179, 0x22FAC097AA8D5C0E
+};
+
+//S-box 3
+static const uint64_t t3[256] =
+{
+   0xF49FCC2FF1DAF39B, 0x487FD5C66FF29281, 0xE8A30667FCDCA83F, 0x2C9B4BE3D2FCCE63,
+   0xDA3FF74B93FBBBC2, 0x2FA165D2FE70BA66, 0xA103E279970E93D4, 0xBECDEC77B0E45E71,
+   0xCFB41E723985E497, 0xB70AAA025EF75017, 0xD42309F03840B8E0, 0x8EFC1AD035898579,
+   0x96C6920BE2B2ABC5, 0x66AF4163375A9172, 0x2174ABDCCA7127FB, 0xB33CCEA64A72FF41,
+   0xF04A4933083066A5, 0x8D970ACDD7289AF5, 0x8F96E8E031C8C25E, 0xF3FEC02276875D47,
+   0xEC7BF310056190DD, 0xF5ADB0AEBB0F1491, 0x9B50F8850FD58892, 0x4975488358B74DE8,
+   0xA3354FF691531C61, 0x0702BBE481D2C6EE, 0x89FB24057DEDED98, 0xAC3075138596E902,
+   0x1D2D3580172772ED, 0xEB738FC28E6BC30D, 0x5854EF8F63044326, 0x9E5C52325ADD3BBE,
+   0x90AA53CF325C4623, 0xC1D24D51349DD067, 0x2051CFEEA69EA624, 0x13220F0A862E7E4F,
+   0xCE39399404E04864, 0xD9C42CA47086FCB7, 0x685AD2238A03E7CC, 0x066484B2AB2FF1DB,
+   0xFE9D5D70EFBF79EC, 0x5B13B9DD9C481854, 0x15F0D475ED1509AD, 0x0BEBCD060EC79851,
+   0xD58C6791183AB7F8, 0xD1187C5052F3EEE4, 0xC95D1192E54E82FF, 0x86EEA14CB9AC6CA2,
+   0x3485BEB153677D5D, 0xDD191D781F8C492A, 0xF60866BAA784EBF9, 0x518F643BA2D08C74,
+   0x8852E956E1087C22, 0xA768CB8DC410AE8D, 0x38047726BFEC8E1A, 0xA67738B4CD3B45AA,
+   0xAD16691CEC0DDE19, 0xC6D4319380462E07, 0xC5A5876D0BA61938, 0x16B9FA1FA58FD840,
+   0x188AB1173CA74F18, 0xABDA2F98C99C021F, 0x3E0580AB134AE816, 0x5F3B05B773645ABB,
+   0x2501A2BE5575F2F6, 0x1B2F74004E7E8BA9, 0x1CD7580371E8D953, 0x7F6ED89562764E30,
+   0xB15926FF596F003D, 0x9F65293DA8C5D6B9, 0x6ECEF04DD690F84C, 0x4782275FFF33AF88,
+   0xE41433083F820801, 0xFD0DFE409A1AF9B5, 0x4325A3342CDB396B, 0x8AE77E62B301B252,
+   0xC36F9E9F6655615A, 0x85455A2D92D32C09, 0xF2C7DEA949477485, 0x63CFB4C133A39EBA,
+   0x83B040CC6EBC5462, 0x3B9454C8FDB326B0, 0x56F56A9E87FFD78C, 0x2DC2940D99F42BC6,
+   0x98F7DF096B096E2D, 0x19A6E01E3AD852BF, 0x42A99CCBDBD4B40B, 0xA59998AF45E9C559,
+   0x366295E807D93186, 0x6B48181BFAA1F773, 0x1FEC57E2157A0A1D, 0x4667446AF6201AD5,
+   0xE615EBCACFB0F075, 0xB8F31F4F68290778, 0x22713ED6CE22D11E, 0x3057C1A72EC3C93B,
+   0xCB46ACC37C3F1F2F, 0xDBB893FD02AAF50E, 0x331FD92E600B9FCF, 0xA498F96148EA3AD6,
+   0xA8D8426E8B6A83EA, 0xA089B274B7735CDC, 0x87F6B3731E524A11, 0x118808E5CBC96749,
+   0x9906E4C7B19BD394, 0xAFED7F7E9B24A20C, 0x6509EADEEB3644A7, 0x6C1EF1D3E8EF0EDE,
+   0xB9C97D43E9798FB4, 0xA2F2D784740C28A3, 0x7B8496476197566F, 0x7A5BE3E6B65F069D,
+   0xF96330ED78BE6F10, 0xEEE60DE77A076A15, 0x2B4BEE4AA08B9BD0, 0x6A56A63EC7B8894E,
+   0x02121359BA34FEF4, 0x4CBF99F8283703FC, 0x398071350CAF30C8, 0xD0A77A89F017687A,
+   0xF1C1A9EB9E423569, 0x8C7976282DEE8199, 0x5D1737A5DD1F7ABD, 0x4F53433C09A9FA80,
+   0xFA8B0C53DF7CA1D9, 0x3FD9DCBC886CCB77, 0xC040917CA91B4720, 0x7DD00142F9D1DCDF,
+   0x8476FC1D4F387B58, 0x23F8E7C5F3316503, 0x032A2244E7E37339, 0x5C87A5D750F5A74B,
+   0x082B4CC43698992E, 0xDF917BECB858F63C, 0x3270B8FC5BF86DDA, 0x10AE72BB29B5DD76,
+   0x576AC94E7700362B, 0x1AD112DAC61EFB8F, 0x691BC30EC5FAA427, 0xFF246311CC327143,
+   0x3142368E30E53206, 0x71380E31E02CA396, 0x958D5C960AAD76F1, 0xF8D6F430C16DA536,
+   0xC8FFD13F1BE7E1D2, 0x7578AE66004DDBE1, 0x05833F01067BE646, 0xBB34B5AD3BFE586D,
+   0x095F34C9A12B97F0, 0x247AB64525D60CA8, 0xDCDBC6F3017477D1, 0x4A2E14D4DECAD24D,
+   0xBDB5E6D9BE0A1EEB, 0x2A7E70F7794301AB, 0xDEF42D8A270540FD, 0x01078EC0A34C22C1,
+   0xE5DE511AF4C16387, 0x7EBB3A52BD9A330A, 0x77697857AA7D6435, 0x004E831603AE4C32,
+   0xE7A21020AD78E312, 0x9D41A70C6AB420F2, 0x28E06C18EA1141E6, 0xD2B28CBD984F6B28,
+   0x26B75F6C446E9D83, 0xBA47568C4D418D7F, 0xD80BADBFE6183D8E, 0x0E206D7F5F166044,
+   0xE258A43911CBCA3E, 0x723A1746B21DC0BC, 0xC7CAA854F5D7CDD3, 0x7CAC32883D261D9C,
+   0x7690C26423BA942C, 0x17E55524478042B8, 0xE0BE477656A2389F, 0x4D289B5E67AB2DA0,
+   0x44862B9C8FBBFD31, 0xB47CC8049D141365, 0x822C1B362B91C793, 0x4EB14655FB13DFD8,
+   0x1ECBBA0714E2A97B, 0x6143459D5CDE5F14, 0x53A8FBF1D5F0AC89, 0x97EA04D81C5E5B00,
+   0x622181A8D4FDB3F3, 0xE9BCD341572A1208, 0x1411258643CCE58A, 0x9144C5FEA4C6E0A4,
+   0x0D33D06565CF620F, 0x54A48D489F219CA1, 0xC43E5EAC6D63C821, 0xA9728B3A72770DAF,
+   0xD7934E7B20DF87EF, 0xE35503B61A3E86E5, 0xCAE321FBC819D504, 0x129A50B3AC60BFA6,
+   0xCD5E68EA7E9FB6C3, 0xB01C90199483B1C7, 0x3DE93CD5C295376C, 0xAED52EDF2AB9AD13,
+   0x2E60F512C0A07884, 0xBC3D86A3E36210C9, 0x35269D9B163951CE, 0x0C7D6E2AD0CDB5FA,
+   0x59E86297D87F5733, 0x298EF221898DB0E7, 0x55000029D1A5AA7E, 0x8BC08AE1B5061B45,
+   0xC2C31C2B6C92703A, 0x94CC596BAF25EF42, 0x0A1D73DB22540456, 0x04B6A0F9D9C4179A,
+   0xEFFDAFA2AE3D3C60, 0xF7C8075BB49496C4, 0x9CC5C7141D1CD4E3, 0x78BD1638218E5534,
+   0xB2F11568F850246A, 0xEDFABCFA9502BC29, 0x796CE5F2DA23051B, 0xAAE128B0DC93537C,
+   0x3A493DA0EE4B29AE, 0xB5DF6B2C416895D7, 0xFCABBD25122D7F37, 0x70810B58105DC4B1,
+   0xE10FDD37F7882A90, 0x524DCAB5518A3F5C, 0x3C9E85878451255B, 0x4029828119BD34E2,
+   0x74A05B6F5D3CECCB, 0xB610021542E13ECA, 0x0FF979D12F59E2AC, 0x6037DA27E4F9CC50,
+   0x5E92975A0DF1847D, 0xD66DE190D3E623FE, 0x5032D6B87B568048, 0x9A36B7CE8235216E,
+   0x80272A7A24F64B4A, 0x93EFED8B8C6916F7, 0x37DDBFF44CCE1555, 0x4B95DB5D4B99BD25,
+   0x92D3FDA169812FC0, 0xFB1A4A9A90660BB6, 0x730C196946A4B9B2, 0x81E289AA7F49DA68,
+   0x64669A0F83B1A05F, 0x27B3FF7D9644F48B, 0xCC6B615C8DB675B3, 0x674F20B9BCEBBE95,
+   0x6F31238275655982, 0x5AE488713E45CF05, 0xBF619F9954C21157, 0xEABAC46040A8EAE9,
+   0x454C6FE9F2C0C1CD, 0x419CF6496412691C, 0xD3DC3BEF265B0F70, 0x6D0E60F5C3578A9E
+};
+
+//S-box 4
+static const uint64_t t4[256] =
+{
+   0x5B0E608526323C55, 0x1A46C1A9FA1B59F5, 0xA9E245A17C4C8FFA, 0x65CA5159DB2955D7,
+   0x05DB0A76CE35AFC2, 0x81EAC77EA9113D45, 0x528EF88AB6AC0A0D, 0xA09EA253597BE3FF,
+   0x430DDFB3AC48CD56, 0xC4B3A67AF45CE46F, 0x4ECECFD8FBE2D05E, 0x3EF56F10B39935F0,
+   0x0B22D6829CD619C6, 0x17FD460A74DF2069, 0x6CF8CC8E8510ED40, 0xD6C824BF3A6ECAA7,
+   0x61243D581A817049, 0x048BACB6BBC163A2, 0xD9A38AC27D44CC32, 0x7FDDFF5BAAF410AB,
+   0xAD6D495AA804824B, 0xE1A6A74F2D8C9F94, 0xD4F7851235DEE8E3, 0xFD4B7F886540D893,
+   0x247C20042AA4BFDA, 0x096EA1C517D1327C, 0xD56966B4361A6685, 0x277DA5C31221057D,
+   0x94D59893A43ACFF7, 0x64F0C51CCDC02281, 0x3D33BCC4FF6189DB, 0xE005CB184CE66AF1,
+   0xFF5CCD1D1DB99BEA, 0xB0B854A7FE42980F, 0x7BD46A6A718D4B9F, 0xD10FA8CC22A5FD8C,
+   0xD31484952BE4BD31, 0xC7FA975FCB243847, 0x4886ED1E5846C407, 0x28CDDB791EB70B04,
+   0xC2B00BE2F573417F, 0x5C9590452180F877, 0x7A6BDDFFF370EB00, 0xCE509E38D6D9D6A4,
+   0xEBEB0F00647FA702, 0x1DCC06CF76606F06, 0xE4D9F28BA286FF0A, 0xD85A305DC918C262,
+   0x475B1D8732225F54, 0x2D4FB51668CCB5FE, 0xA679B9D9D72BBA20, 0x53841C0D912D43A5,
+   0x3B7EAA48BF12A4E8, 0x781E0E47F22F1DDF, 0xEFF20CE60AB50973, 0x20D261D19DFFB742,
+   0x16A12B03062A2E39, 0x1960EB2239650495, 0x251C16FED50EB8B8, 0x9AC0C330F826016E,
+   0xED152665953E7671, 0x02D63194A6369570, 0x5074F08394B1C987, 0x70BA598C90B25CE1,
+   0x794A15810B9742F6, 0x0D5925E9FCAF8C6C, 0x3067716CD868744E, 0x910AB077E8D7731B,
+   0x6A61BBDB5AC42F61, 0x93513EFBF0851567, 0xF494724B9E83E9D5, 0xE887E1985C09648D,
+   0x34B1D3C675370CFD, 0xDC35E433BC0D255D, 0xD0AAB84234131BE0, 0x08042A50B48B7EAF,
+   0x9997C4EE44A3AB35, 0x829A7B49201799D0, 0x263B8307B7C54441, 0x752F95F4FD6A6CA6,
+   0x927217402C08C6E5, 0x2A8AB754A795D9EE, 0xA442F7552F72943D, 0x2C31334E19781208,
+   0x4FA98D7CEAEE6291, 0x55C3862F665DB309, 0xBD0610175D53B1F3, 0x46FE6CB840413F27,
+   0x3FE03792DF0CFA59, 0xCFE700372EB85E8F, 0xA7BE29E7ADBCE118, 0xE544EE5CDE8431DD,
+   0x8A781B1B41F1873E, 0xA5C94C78A0D2F0E7, 0x39412E2877B60728, 0xA1265EF3AFC9A62C,
+   0xBCC2770C6A2506C5, 0x3AB66DD5DCE1CE12, 0xE65499D04A675B37, 0x7D8F523481BFD216,
+   0x0F6F64FCEC15F389, 0x74EFBE618B5B13C8, 0xACDC82B714273E1D, 0xDD40BFE003199D17,
+   0x37E99257E7E061F8, 0xFA52626904775AAA, 0x8BBBF63A463D56F9, 0xF0013F1543A26E64,
+   0xA8307E9F879EC898, 0xCC4C27A4150177CC, 0x1B432F2CCA1D3348, 0xDE1D1F8F9F6FA013,
+   0x606602A047A7DDD6, 0xD237AB64CC1CB2C7, 0x9B938E7225FCD1D3, 0xEC4E03708E0FF476,
+   0xFEB2FBDA3D03C12D, 0xAE0BCED2EE43889A, 0x22CB8923EBFB4F43, 0x69360D013CF7396D,
+   0x855E3602D2D4E022, 0x073805BAD01F784C, 0x33E17A133852F546, 0xDF4874058AC7B638,
+   0xBA92B29C678AA14A, 0x0CE89FC76CFAADCD, 0x5F9D4E0908339E34, 0xF1AFE9291F5923B9,
+   0x6E3480F60F4A265F, 0xEEBF3A2AB29B841C, 0xE21938A88F91B4AD, 0x57DFEFF845C6D3C3,
+   0x2F006B0BF62CAAF2, 0x62F479EF6F75EE78, 0x11A55AD41C8916A9, 0xF229D29084FED453,
+   0x42F1C27B16B000E6, 0x2B1F76749823C074, 0x4B76ECA3C2745360, 0x8C98F463B91691BD,
+   0x14BCC93CF1ADE66A, 0x8885213E6D458397, 0x8E177DF0274D4711, 0xB49B73B5503F2951,
+   0x10168168C3F96B6B, 0x0E3D963B63CAB0AE, 0x8DFC4B5655A1DB14, 0xF789F1356E14DE5C,
+   0x683E68AF4E51DAC1, 0xC9A84F9D8D4B0FD9, 0x3691E03F52A0F9D1, 0x5ED86E46E1878E80,
+   0x3C711A0E99D07150, 0x5A0865B20C4E9310, 0x56FBFC1FE4F0682E, 0xEA8D5DE3105EDF9B,
+   0x71ABFDB12379187A, 0x2EB99DE1BEE77B9C, 0x21ECC0EA33CF4523, 0x59A4D7521805C7A1,
+   0x3896F5EB56AE7C72, 0xAA638F3DB18F75DC, 0x9F39358DABE9808E, 0xB7DEFA91C00B72AC,
+   0x6B5541FD62492D92, 0x6DC6DEE8F92E4D5B, 0x353F57ABC4BEEA7E, 0x735769D6DA5690CE,
+   0x0A234AA642391484, 0xF6F9508028F80D9D, 0xB8E319A27AB3F215, 0x31AD9C1151341A4D,
+   0x773C22A57BEF5805, 0x45C7561A07968633, 0xF913DA9E249DBE36, 0xDA652D9B78A64C68,
+   0x4C27A97F3BC334EF, 0x76621220E66B17F4, 0x967743899ACD7D0B, 0xF3EE5BCAE0ED6782,
+   0x409F753600C879FC, 0x06D09A39B5926DB6, 0x6F83AEB0317AC588, 0x01E6CA4A86381F21,
+   0x66FF3462D19F3025, 0x72207C24DDFD3BFB, 0x4AF6B6D3E2ECE2EB, 0x9C994DBEC7EA08DE,
+   0x49ACE597B09A8BC4, 0xB38C4766CF0797BA, 0x131B9373C57C2A75, 0xB1822CCE61931E58,
+   0x9D7555B909BA1C0C, 0x127FAFDD937D11D2, 0x29DA3BADC66D92E4, 0xA2C1D57154C2ECBC,
+   0x58C5134D82F6FE24, 0x1C3AE3515B62274F, 0xE907C82E01CB8126, 0xF8ED091913E37FCB,
+   0x3249D8F9C80046C9, 0x80CF9BEDE388FB63, 0x1881539A116CF19E, 0x5103F3F76BD52457,
+   0x15B7E6F5AE47F7A8, 0xDBD7C6DED47E9CCF, 0x44E55C410228BB1A, 0xB647D4255EDB4E99,
+   0x5D11882BB8AAFC30, 0xF5098BBB29D3212A, 0x8FB5EA14E90296B3, 0x677B942157DD025A,
+   0xFB58E7C0A390ACB5, 0x89D3674C83BD4A01, 0x9E2DA4DF4BF3B93B, 0xFCC41E328CAB4829,
+   0x03F38C96BA582C52, 0xCAD1BDBD7FD85DB2, 0xBBB442C16082AE83, 0xB95FE86BA5DA9AB0,
+   0xB22E04673771A93F, 0x845358C9493152D8, 0xBE2A488697B4541E, 0x95A2DC2DD38E6966,
+   0xC02C11AC923C852B, 0x2388B1990DF2A87B, 0x7C8008FA1B4F37BE, 0x1F70D0C84D54E503,
+   0x5490ADEC7ECE57D4, 0x002B3C27D9063A3A, 0x7EAEA3848030A2BF, 0xC602326DED2003C0,
+   0x83A7287D69A94086, 0xC57A5FCB30F57A8A, 0xB56844E479EBE779, 0xA373B40F05DCBCE9,
+   0xD71A786E88570EE2, 0x879CBACDBDE8F6A0, 0x976AD1BCC164A32F, 0xAB21E25E9666D78B,
+   0x901063AAE5E5C33C, 0x9818B34448698D90, 0xE36487AE3E1E8ABB, 0xAFBDF931893BDCB4,
+   0x6345A0DC5FBBD519, 0x8628FE269B9465CA, 0x1E5D01603F9C51EC, 0x4DE44006A15049B7,
+   0xBF6C70E5F776CBB1, 0x411218F2EF552BED, 0xCB0C0708705A36A3, 0xE74D14754F986044,
+   0xCD56D9430EA8280E, 0xC12591D7535F5065, 0xC83223F1720AEF96, 0xC3A0396F7363A51F
+};
+
+//Tiger object identifier (1.3.6.1.4.1.11591.12.2)
+static const uint8_t tigerOid[] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0C, 0x02};
+
+//Common interface for hash algorithms
+const HashAlgo tigerHashAlgo =
+{
+   "TIGER",
+   tigerOid,
+   sizeof(tigerOid),
+   sizeof(TigerContext),
+   TIGER_BLOCK_SIZE,
+   TIGER_DIGEST_SIZE,
+   (HashAlgoCompute) tigerCompute,
+   (HashAlgoInit) tigerInit,
+   (HashAlgoUpdate) tigerUpdate,
+   (HashAlgoFinal) tigerFinal
+};
+
+
+/**
+ * @brief Digest a message using Tiger
+ * @param[in] data Pointer to the message being hashed
+ * @param[in] length Length of the message
+ * @param[out] digest Pointer to the calculated digest
+ * @return Error code
+ **/
+
+error_t tigerCompute(const void *data, size_t length, uint8_t *digest)
+{
+   //Allocate a memory buffer to hold the Tiger context
+   TigerContext *context = cryptoAllocMem(sizeof(TigerContext));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the Tiger context
+   tigerInit(context);
+   //Digest the message
+   tigerUpdate(context, data, length);
+   //Finalize the Tiger message digest
+   tigerFinal(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize Tiger message digest context
+ * @param[in] context Pointer to the Tiger context to initialize
+ **/
+
+void tigerInit(TigerContext *context)
+{
+   //Set initial hash value
+   context->h[0] = 0x0123456789ABCDEF;
+   context->h[1] = 0xFEDCBA9876543210;
+   context->h[2] = 0xF096A5B4C3B2E187;
+
+   //Number of bytes in the buffer
+   context->size = 0;
+   //Total length of the message
+   context->totalSize = 0;
+}
+
+
+/**
+ * @brief Update the Tiger context with a portion of the message being hashed
+ * @param[in] context Pointer to the Tiger context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void tigerUpdate(TigerContext *context, const void *data, size_t length)
+{
+   size_t n;
+
+   //Process the incoming data
+   while(length > 0)
+   {
+      //The buffer can hold at most 64 bytes
+      n = MIN(length, 64 - context->size);
+
+      //Copy the data to the buffer
+      memcpy(context->buffer + context->size, data, n);
+
+      //Update the Tiger context
+      context->size += n;
+      context->totalSize += n;
+      //Advance the data pointer
+      data = (uint8_t *) data + n;
+      //Remaining bytes to process
+      length -= n;
+
+      //Process message in 16-word blocks
+      if(context->size == 64)
+      {
+         //Transform the 16-word block
+         tigerProcessBlock(context);
+         //Empty the buffer
+         context->size = 0;
+      }
+   }
+}
+
+
+/**
+ * @brief Finish the Tiger message digest
+ * @param[in] context Pointer to the Tiger context
+ * @param[out] digest Calculated digest (optional parameter)
+ **/
+
+void tigerFinal(TigerContext *context, uint8_t *digest)
+{
+   uint_t i;
+   size_t paddingSize;
+   uint64_t totalSize;
+
+   //Length of the original message (before padding)
+   totalSize = context->totalSize * 8;
+
+   //Pad the message so that its length is congruent to 56 modulo 64
+   if(context->size < 56)
+      paddingSize = 56 - context->size;
+   else
+      paddingSize = 64 + 56 - context->size;
+
+   //Append padding
+   tigerUpdate(context, padding, paddingSize);
+
+   //Append the length of the original message
+   context->x[7] = htole64(totalSize);
+
+   //Calculate the message digest
+   tigerProcessBlock(context);
+
+   //Convert from host byte order to little-endian byte order
+   for(i = 0; i < 3; i++)
+      context->h[i] = htole64(context->h[i]);
+
+   //Copy the resulting digest
+   if(digest != NULL)
+      memcpy(digest, context->digest, TIGER_DIGEST_SIZE);
+}
+
+
+/**
+ * @brief Process message in 16-word blocks
+ * @param[in] context Pointer to the Tiger context
+ **/
+
+void tigerProcessBlock(TigerContext *context)
+{
+   uint_t i;
+
+   //Initialize the 4 working registers
+   uint64_t a = context->h[0];
+   uint64_t b = context->h[1];
+   uint64_t c = context->h[2];
+
+   //Each 512-bit message block is divided into eight 64-bit words
+   uint64_t *x = context->x;
+
+   //Convert from little-endian byte order to host byte order
+   for(i = 0; i < 8; i++)
+      x[i] = letoh64(x[i]);
+
+   //The computation consists of three passes
+   ROUND(a, b, c, x[0], 5);
+   ROUND(b, c, a, x[1], 5);
+   ROUND(c, a, b, x[2], 5);
+   ROUND(a, b, c, x[3], 5);
+   ROUND(b, c, a, x[4], 5);
+   ROUND(c, a, b, x[5], 5);
+   ROUND(a, b, c, x[6], 5);
+   ROUND(b, c, a, x[7], 5);
+   KEY_SCHEDULE(x);
+   ROUND(c, a, b, x[0], 7);
+   ROUND(a, b, c, x[1], 7);
+   ROUND(b, c, a, x[2], 7);
+   ROUND(c, a, b, x[3], 7);
+   ROUND(a, b, c, x[4], 7);
+   ROUND(b, c, a, x[5], 7);
+   ROUND(c, a, b, x[6], 7);
+   ROUND(a, b, c, x[7], 7);
+   KEY_SCHEDULE(x);
+   ROUND(b, c, a, x[0], 9);
+   ROUND(c, a, b, x[1], 9);
+   ROUND(a, b, c, x[2], 9);
+   ROUND(b, c, a, x[3], 9);
+   ROUND(c, a, b, x[4], 9);
+   ROUND(a, b, c, x[5], 9);
+   ROUND(b, c, a, x[6], 9);
+   ROUND(c, a, b, x[7], 9);
+
+   //Update the hash value
+   context->h[0] = a ^ context->h[0];
+   context->h[1] = b - context->h[1];
+   context->h[2] = c + context->h[2];
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/tiger.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,75 @@
+/**
+ * @file tiger.h
+ * @brief Tiger hash function
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TIGER_H
+#define _TIGER_H
+
+//Dependencies
+#include "crypto.h"
+
+//Tiger block size
+#define TIGER_BLOCK_SIZE 64
+//Tiger digest size
+#define TIGER_DIGEST_SIZE 24
+//Common interface for hash algorithms
+#define TIGER_HASH_ALGO (&tigerHashAlgo)
+
+
+/**
+ * @brief Tiger algorithm context
+ **/
+
+typedef struct
+{
+   union
+   {
+      uint64_t h[3];
+      uint8_t digest[24];
+   };
+   union
+   {
+      uint64_t x[8];
+      uint8_t buffer[64];
+   };
+   size_t size;
+   uint64_t totalSize;
+} TigerContext;
+
+
+//Tiger related constants
+extern const HashAlgo tigerHashAlgo;
+
+//Tiger related functions
+error_t tigerCompute(const void *data, size_t length, uint8_t *digest);
+void tigerInit(TigerContext *context);
+void tigerUpdate(TigerContext *context, const void *data, size_t length);
+void tigerFinal(TigerContext *context, uint8_t *digest);
+void tigerProcessBlock(TigerContext *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/whirlpool.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,394 @@
+/**
+ * @file whirlpool.c
+ * @brief Whirlpool hash function
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * Whirlpool is a hash function that operates on messages less than 2^256 bits
+ * in length, and produces a message digest of 512 bits
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "whirlpool.h"
+
+//Check crypto library configuration
+#if (WHIRLPOOL_SUPPORT == ENABLED)
+
+//Round function
+#define RHO(b, a, n, c) \
+{ \
+   b = t[(a[n] >> 56) & 0xFF]; \
+   b ^= ROR64(t[(a[(n + 7) % 8] >> 48) & 0xFF], 8); \
+   b ^= ROR64(t[(a[(n + 6) % 8] >> 40) & 0xFF], 16); \
+   b ^= ROR64(t[(a[(n + 5) % 8] >> 32) & 0xFF], 24); \
+   b ^= ROR64(t[(a[(n + 4) % 8] >> 24) & 0xFF], 32); \
+   b ^= ROR64(t[(a[(n + 3) % 8] >> 16) & 0xFF], 40); \
+   b ^= ROR64(t[(a[(n + 2) % 8] >> 8) & 0xFF], 48); \
+   b ^= ROR64(t[a[(n + 1) % 8] & 0xFF], 56); \
+   b ^= c; \
+}
+
+//Whirlpool padding
+static const uint8_t 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
+};
+
+//Whirlpool constants
+static const uint64_t rc[10] =
+{
+   0x1823C6E887B8014F,
+   0x36A6D2F5796F9152,
+   0x60BC9B8EA30C7B35,
+   0x1DE0D7C22E4BFE57,
+   0x157737E59FF04ADA,
+   0x58C9290AB1A06B85,
+   0xBD5D10F4CB3E0567,
+   0xE427418BA77D95D8,
+   0xFBEE7C66DD17479E,
+   0xCA2DBF07AD5A8333
+};
+
+//Whirlpool look-up table
+static const uint64_t t[256] =
+{
+   0x18186018C07830D8, 0x23238C2305AF4626, 0xC6C63FC67EF991B8, 0xE8E887E8136FCDFB,
+   0x878726874CA113CB, 0xB8B8DAB8A9626D11, 0x0101040108050209, 0x4F4F214F426E9E0D,
+   0x3636D836ADEE6C9B, 0xA6A6A2A6590451FF, 0xD2D26FD2DEBDB90C, 0xF5F5F3F5FB06F70E,
+   0x7979F979EF80F296, 0x6F6FA16F5FCEDE30, 0x91917E91FCEF3F6D, 0x52525552AA07A4F8,
+   0x60609D6027FDC047, 0xBCBCCABC89766535, 0x9B9B569BACCD2B37, 0x8E8E028E048C018A,
+   0xA3A3B6A371155BD2, 0x0C0C300C603C186C, 0x7B7BF17BFF8AF684, 0x3535D435B5E16A80,
+   0x1D1D741DE8693AF5, 0xE0E0A7E05347DDB3, 0xD7D77BD7F6ACB321, 0xC2C22FC25EED999C,
+   0x2E2EB82E6D965C43, 0x4B4B314B627A9629, 0xFEFEDFFEA321E15D, 0x575741578216AED5,
+   0x15155415A8412ABD, 0x7777C1779FB6EEE8, 0x3737DC37A5EB6E92, 0xE5E5B3E57B56D79E,
+   0x9F9F469F8CD92313, 0xF0F0E7F0D317FD23, 0x4A4A354A6A7F9420, 0xDADA4FDA9E95A944,
+   0x58587D58FA25B0A2, 0xC9C903C906CA8FCF, 0x2929A429558D527C, 0x0A0A280A5022145A,
+   0xB1B1FEB1E14F7F50, 0xA0A0BAA0691A5DC9, 0x6B6BB16B7FDAD614, 0x85852E855CAB17D9,
+   0xBDBDCEBD8173673C, 0x5D5D695DD234BA8F, 0x1010401080502090, 0xF4F4F7F4F303F507,
+   0xCBCB0BCB16C08BDD, 0x3E3EF83EEDC67CD3, 0x0505140528110A2D, 0x676781671FE6CE78,
+   0xE4E4B7E47353D597, 0x27279C2725BB4E02, 0x4141194132588273, 0x8B8B168B2C9D0BA7,
+   0xA7A7A6A7510153F6, 0x7D7DE97DCF94FAB2, 0x95956E95DCFB3749, 0xD8D847D88E9FAD56,
+   0xFBFBCBFB8B30EB70, 0xEEEE9FEE2371C1CD, 0x7C7CED7CC791F8BB, 0x6666856617E3CC71,
+   0xDDDD53DDA68EA77B, 0x17175C17B84B2EAF, 0x4747014702468E45, 0x9E9E429E84DC211A,
+   0xCACA0FCA1EC589D4, 0x2D2DB42D75995A58, 0xBFBFC6BF9179632E, 0x07071C07381B0E3F,
+   0xADAD8EAD012347AC, 0x5A5A755AEA2FB4B0, 0x838336836CB51BEF, 0x3333CC3385FF66B6,
+   0x636391633FF2C65C, 0x02020802100A0412, 0xAAAA92AA39384993, 0x7171D971AFA8E2DE,
+   0xC8C807C80ECF8DC6, 0x19196419C87D32D1, 0x494939497270923B, 0xD9D943D9869AAF5F,
+   0xF2F2EFF2C31DF931, 0xE3E3ABE34B48DBA8, 0x5B5B715BE22AB6B9, 0x88881A8834920DBC,
+   0x9A9A529AA4C8293E, 0x262698262DBE4C0B, 0x3232C8328DFA64BF, 0xB0B0FAB0E94A7D59,
+   0xE9E983E91B6ACFF2, 0x0F0F3C0F78331E77, 0xD5D573D5E6A6B733, 0x80803A8074BA1DF4,
+   0xBEBEC2BE997C6127, 0xCDCD13CD26DE87EB, 0x3434D034BDE46889, 0x48483D487A759032,
+   0xFFFFDBFFAB24E354, 0x7A7AF57AF78FF48D, 0x90907A90F4EA3D64, 0x5F5F615FC23EBE9D,
+   0x202080201DA0403D, 0x6868BD6867D5D00F, 0x1A1A681AD07234CA, 0xAEAE82AE192C41B7,
+   0xB4B4EAB4C95E757D, 0x54544D549A19A8CE, 0x93937693ECE53B7F, 0x222288220DAA442F,
+   0x64648D6407E9C863, 0xF1F1E3F1DB12FF2A, 0x7373D173BFA2E6CC, 0x12124812905A2482,
+   0x40401D403A5D807A, 0x0808200840281048, 0xC3C32BC356E89B95, 0xECEC97EC337BC5DF,
+   0xDBDB4BDB9690AB4D, 0xA1A1BEA1611F5FC0, 0x8D8D0E8D1C830791, 0x3D3DF43DF5C97AC8,
+   0x97976697CCF1335B, 0x0000000000000000, 0xCFCF1BCF36D483F9, 0x2B2BAC2B4587566E,
+   0x7676C57697B3ECE1, 0x8282328264B019E6, 0xD6D67FD6FEA9B128, 0x1B1B6C1BD87736C3,
+   0xB5B5EEB5C15B7774, 0xAFAF86AF112943BE, 0x6A6AB56A77DFD41D, 0x50505D50BA0DA0EA,
+   0x45450945124C8A57, 0xF3F3EBF3CB18FB38, 0x3030C0309DF060AD, 0xEFEF9BEF2B74C3C4,
+   0x3F3FFC3FE5C37EDA, 0x55554955921CAAC7, 0xA2A2B2A2791059DB, 0xEAEA8FEA0365C9E9,
+   0x656589650FECCA6A, 0xBABAD2BAB9686903, 0x2F2FBC2F65935E4A, 0xC0C027C04EE79D8E,
+   0xDEDE5FDEBE81A160, 0x1C1C701CE06C38FC, 0xFDFDD3FDBB2EE746, 0x4D4D294D52649A1F,
+   0x92927292E4E03976, 0x7575C9758FBCEAFA, 0x06061806301E0C36, 0x8A8A128A249809AE,
+   0xB2B2F2B2F940794B, 0xE6E6BFE66359D185, 0x0E0E380E70361C7E, 0x1F1F7C1FF8633EE7,
+   0x6262956237F7C455, 0xD4D477D4EEA3B53A, 0xA8A89AA829324D81, 0x96966296C4F43152,
+   0xF9F9C3F99B3AEF62, 0xC5C533C566F697A3, 0x2525942535B14A10, 0x59597959F220B2AB,
+   0x84842A8454AE15D0, 0x7272D572B7A7E4C5, 0x3939E439D5DD72EC, 0x4C4C2D4C5A619816,
+   0x5E5E655ECA3BBC94, 0x7878FD78E785F09F, 0x3838E038DDD870E5, 0x8C8C0A8C14860598,
+   0xD1D163D1C6B2BF17, 0xA5A5AEA5410B57E4, 0xE2E2AFE2434DD9A1, 0x616199612FF8C24E,
+   0xB3B3F6B3F1457B42, 0x2121842115A54234, 0x9C9C4A9C94D62508, 0x1E1E781EF0663CEE,
+   0x4343114322528661, 0xC7C73BC776FC93B1, 0xFCFCD7FCB32BE54F, 0x0404100420140824,
+   0x51515951B208A2E3, 0x99995E99BCC72F25, 0x6D6DA96D4FC4DA22, 0x0D0D340D68391A65,
+   0xFAFACFFA8335E979, 0xDFDF5BDFB684A369, 0x7E7EE57ED79BFCA9, 0x242490243DB44819,
+   0x3B3BEC3BC5D776FE, 0xABAB96AB313D4B9A, 0xCECE1FCE3ED181F0, 0x1111441188552299,
+   0x8F8F068F0C890383, 0x4E4E254E4A6B9C04, 0xB7B7E6B7D1517366, 0xEBEB8BEB0B60CBE0,
+   0x3C3CF03CFDCC78C1, 0x81813E817CBF1FFD, 0x94946A94D4FE3540, 0xF7F7FBF7EB0CF31C,
+   0xB9B9DEB9A1676F18, 0x13134C13985F268B, 0x2C2CB02C7D9C5851, 0xD3D36BD3D6B8BB05,
+   0xE7E7BBE76B5CD38C, 0x6E6EA56E57CBDC39, 0xC4C437C46EF395AA, 0x03030C03180F061B,
+   0x565645568A13ACDC, 0x44440D441A49885E, 0x7F7FE17FDF9EFEA0, 0xA9A99EA921374F88,
+   0x2A2AA82A4D825467, 0xBBBBD6BBB16D6B0A, 0xC1C123C146E29F87, 0x53535153A202A6F1,
+   0xDCDC57DCAE8BA572, 0x0B0B2C0B58271653, 0x9D9D4E9D9CD32701, 0x6C6CAD6C47C1D82B,
+   0x3131C43195F562A4, 0x7474CD7487B9E8F3, 0xF6F6FFF6E309F115, 0x464605460A438C4C,
+   0xACAC8AAC092645A5, 0x89891E893C970FB5, 0x14145014A04428B4, 0xE1E1A3E15B42DFBA,
+   0x16165816B04E2CA6, 0x3A3AE83ACDD274F7, 0x6969B9696FD0D206, 0x09092409482D1241,
+   0x7070DD70A7ADE0D7, 0xB6B6E2B6D954716F, 0xD0D067D0CEB7BD1E, 0xEDED93ED3B7EC7D6,
+   0xCCCC17CC2EDB85E2, 0x424215422A578468, 0x98985A98B4C22D2C, 0xA4A4AAA4490E55ED,
+   0x2828A0285D885075, 0x5C5C6D5CDA31B886, 0xF8F8C7F8933FED6B, 0x8686228644A411C2
+};
+
+//Whirlpool object identifier (1.0.10118.3.0.55)
+static const uint8_t whirlpoolOid[] = {0x28, 0xCF, 0x06, 0x03, 0x00, 0x37};
+
+//Common interface for hash algorithms
+const HashAlgo whirlpoolHashAlgo =
+{
+   "WHIRLPOOL",
+   whirlpoolOid,
+   sizeof(whirlpoolOid),
+   sizeof(WhirlpoolContext),
+   WHIRLPOOL_BLOCK_SIZE,
+   WHIRLPOOL_DIGEST_SIZE,
+   (HashAlgoCompute) whirlpoolCompute,
+   (HashAlgoInit) whirlpoolInit,
+   (HashAlgoUpdate) whirlpoolUpdate,
+   (HashAlgoFinal) whirlpoolFinal
+};
+
+
+/**
+ * @brief Digest a message using Whirlpool
+ * @param[in] data Pointer to the message being hashed
+ * @param[in] length Length of the message
+ * @param[out] digest Pointer to the calculated digest
+ * @return Error code
+ **/
+
+error_t whirlpoolCompute(const void *data, size_t length, uint8_t *digest)
+{
+   //Allocate a memory buffer to hold the Whirlpool context
+   WhirlpoolContext *context = cryptoAllocMem(sizeof(WhirlpoolContext));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize the Whirlpool context
+   whirlpoolInit(context);
+   //Digest the message
+   whirlpoolUpdate(context, data, length);
+   //Finalize the Whirlpool message digest
+   whirlpoolFinal(context, digest);
+
+   //Free previously allocated memory
+   cryptoFreeMem(context);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize Whirlpool message digest context
+ * @param[in] context Pointer to the Whirlpool context to initialize
+ **/
+
+void whirlpoolInit(WhirlpoolContext *context)
+{
+   uint_t i;
+
+   //Set initial hash value
+   for(i = 0; i < 8; i++)
+      context->h[i] = 0;
+
+   //Number of bytes in the buffer
+   context->size = 0;
+   //Total length of the message
+   context->totalSize = 0;
+}
+
+
+/**
+ * @brief Update the Whirlpool context with a portion of the message being hashed
+ * @param[in] context Pointer to the Whirlpool context
+ * @param[in] data Pointer to the buffer being hashed
+ * @param[in] length Length of the buffer
+ **/
+
+void whirlpoolUpdate(WhirlpoolContext *context, const void *data, size_t length)
+{
+   size_t n;
+
+   //Process the incoming data
+   while(length > 0)
+   {
+      //The buffer can hold at most 64 bytes
+      n = MIN(length, 64 - context->size);
+
+      //Copy the data to the buffer
+      memcpy(context->buffer + context->size, data, n);
+
+      //Update the Whirlpool context
+      context->size += n;
+      context->totalSize += n;
+      //Advance the data pointer
+      data = (uint8_t *) data + n;
+      //Remaining bytes to process
+      length -= n;
+
+      //Process message in 8-word blocks
+      if(context->size == 64)
+      {
+         //Transform the 8-word block
+         whirlpoolProcessBlock(context);
+         //Empty the buffer
+         context->size = 0;
+      }
+   }
+}
+
+
+/**
+ * @brief Finish the Whirlpool message digest
+ * @param[in] context Pointer to the Whirlpool context
+ * @param[out] digest Calculated digest (optional parameter)
+ **/
+
+void whirlpoolFinal(WhirlpoolContext *context, uint8_t *digest)
+{
+   uint_t i;
+   size_t paddingSize;
+   uint64_t totalSize;
+
+   //Length of the original message (before padding)
+   totalSize = context->totalSize * 8;
+
+   //Pad the message so that its length is congruent to 32 modulo 64
+   if(context->size < 32)
+      paddingSize = 32 - context->size;
+   else
+      paddingSize = 64 + 32 - context->size;
+
+   //Append padding
+   whirlpoolUpdate(context, padding, paddingSize);
+
+   //Append the length of the original message
+   context->x[4] = 0;
+   context->x[5] = 0;
+   context->x[6] = 0;
+   context->x[7] = htobe64(totalSize);
+
+   //Calculate the message digest
+   whirlpoolProcessBlock(context);
+
+   //Convert from host byte order to big-endian byte order
+   for(i = 0; i < 8; i++)
+      context->h[i] = htobe64(context->h[i]);
+
+   //Copy the resulting digest
+   if(digest != NULL)
+      memcpy(digest, context->digest, WHIRLPOOL_DIGEST_SIZE);
+}
+
+
+/**
+ * @brief Process message in 16-word blocks
+ * @param[in] context Pointer to the Whirlpool context
+ **/
+
+void whirlpoolProcessBlock(WhirlpoolContext *context)
+{
+   uint_t i;
+
+   uint64_t *x = context->x;
+   uint64_t *k = context->k;
+   uint64_t *l = context->l;
+   uint64_t *state = context->state;
+
+   //Convert from big-endian byte order to host byte order
+   for(i = 0; i < 8; i++)
+      x[i] = betoh64(x[i]);
+
+   k[0] = context->h[0];
+   k[1] = context->h[1];
+   k[2] = context->h[2];
+   k[3] = context->h[3];
+   k[4] = context->h[4];
+   k[5] = context->h[5];
+   k[6] = context->h[6];
+   k[7] = context->h[7];
+
+   state[0] = x[0] ^ k[0];
+   state[1] = x[1] ^ k[1];
+   state[2] = x[2] ^ k[2];
+   state[3] = x[3] ^ k[3];
+   state[4] = x[4] ^ k[4];
+   state[5] = x[5] ^ k[5];
+   state[6] = x[6] ^ k[6];
+   state[7] = x[7] ^ k[7];
+
+   //Iterate over all rounds
+   for(i = 0; i < 10; i++)
+   {
+      //Key schedule
+      RHO(l[0], k, 0, rc[i]);
+      RHO(l[1], k, 1, 0);
+      RHO(l[2], k, 2, 0);
+      RHO(l[3], k, 3, 0);
+      RHO(l[4], k, 4, 0);
+      RHO(l[5], k, 5, 0);
+      RHO(l[6], k, 6, 0);
+      RHO(l[7], k, 7, 0);
+
+      k[0] = l[0];
+      k[1] = l[1];
+      k[2] = l[2];
+      k[3] = l[3];
+      k[4] = l[4];
+      k[5] = l[5];
+      k[6] = l[6];
+      k[7] = l[7];
+
+      //Apply the round function
+      RHO(l[0], state, 0, k[0]);
+      RHO(l[1], state, 1, k[1]);
+      RHO(l[2], state, 2, k[2]);
+      RHO(l[3], state, 3, k[3]);
+      RHO(l[4], state, 4, k[4]);
+      RHO(l[5], state, 5, k[5]);
+      RHO(l[6], state, 6, k[6]);
+      RHO(l[7], state, 7, k[7]);
+
+      state[0] = l[0];
+      state[1] = l[1];
+      state[2] = l[2];
+      state[3] = l[3];
+      state[4] = l[4];
+      state[5] = l[5];
+      state[6] = l[6];
+      state[7] = l[7];
+   }
+
+   //Update the hash value
+   context->h[0] ^= state[0] ^ x[0];
+   context->h[1] ^= state[1] ^ x[1];
+   context->h[2] ^= state[2] ^ x[2];
+   context->h[3] ^= state[3] ^ x[3];
+   context->h[4] ^= state[4] ^ x[4];
+   context->h[5] ^= state[5] ^ x[5];
+   context->h[6] ^= state[6] ^ x[6];
+   context->h[7] ^= state[7] ^ x[7];
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/whirlpool.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,80 @@
+/**
+ * @file whirlpool.h
+ * @brief Whirlpool hash function
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _WHIRLPOOL_H
+#define _WHIRLPOOL_H
+
+//Dependencies
+#include "crypto.h"
+
+//Whirlpool block size
+#define WHIRLPOOL_BLOCK_SIZE 64
+//Whirlpool digest size
+#define WHIRLPOOL_DIGEST_SIZE 64
+//Common interface for hash algorithms
+#define WHIRLPOOL_HASH_ALGO (&whirlpoolHashAlgo)
+
+
+/**
+ * @brief Whirlpool algorithm context
+ **/
+
+typedef struct
+{
+   union
+   {
+      uint64_t h[8];
+      uint8_t digest[64];
+   };
+   union
+   {
+      uint64_t x[8];
+      uint8_t buffer[64];
+   };
+
+   uint64_t k[8];
+   uint64_t l[8];
+   uint64_t state[8];
+
+   size_t size;
+   uint64_t totalSize;
+} WhirlpoolContext;
+
+
+//Whirlpool related constants
+extern const HashAlgo whirlpoolHashAlgo;
+
+//Whirlpool related functions
+error_t whirlpoolCompute(const void *data, size_t length, uint8_t *digest);
+void whirlpoolInit(WhirlpoolContext *context);
+void whirlpoolUpdate(WhirlpoolContext *context, const void *data, size_t length);
+void whirlpoolFinal(WhirlpoolContext *context, uint8_t *digest);
+void whirlpoolProcessBlock(WhirlpoolContext *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/x509.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,2325 @@
+/**
+ * @file x509.c
+ * @brief X.509 certificate parsing and verification
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include <ctype.h>
+#include "crypto.h"
+#include "x509.h"
+#include "asn1.h"
+#include "oid.h"
+#include "rsa.h"
+#include "dsa.h"
+#include "ecdsa.h"
+#include "md5.h"
+#include "sha1.h"
+#include "sha224.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "sha512.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (X509_SUPPORT == ENABLED)
+
+//Common Name OID (2.5.4.3)
+const uint8_t X509_COMMON_NAME_OID[3] = {0x55, 0x04, 0x03};
+//Surname OID (2.5.4.4)
+const uint8_t X509_SURNAME_OID[3] = {0x55, 0x04, 0x04};
+//Serial Number OID (2.5.4.5)
+const uint8_t X509_SERIAL_NUMBER_OID[3] = {0x55, 0x04, 0x05};
+//Country Name OID (2.5.4.6)
+const uint8_t X509_COUNTRY_NAME_OID[3] = {0x55, 0x04, 0x06};
+//Locality Name OID (2.5.4.7)
+const uint8_t X509_LOCALITY_NAME_OID[3] = {0x55, 0x04, 0x07};
+//State Or Province Name OID (2.5.4.8)
+const uint8_t X509_STATE_OR_PROVINCE_NAME_OID[] = {0x55, 0x04, 0x08};
+//Organization Name OID (2.5.4.10)
+const uint8_t X509_ORGANIZATION_NAME_OID[3] = {0x55, 0x04, 0x0A};
+//Organizational Unit Name OID (2.5.4.11)
+const uint8_t X509_ORGANIZATIONAL_UNIT_NAME_OID[3] = {0x55, 0x04, 0x0B};
+//Title OID (2.5.4.12)
+const uint8_t X509_TITLE_OID[3] = {0x55, 0x04, 0x0C};
+//Name OID (2.5.4.41)
+const uint8_t X509_NAME_OID[3] = {0x55, 0x04, 0x29};
+//Given Name OID (2.5.4.42)
+const uint8_t X509_GIVEN_NAME_OID[3] = {0x55, 0x04, 0x2A};
+//Initials OID (2.5.4.43)
+const uint8_t X509_INITIALS_OID[3] = {0x55, 0x04, 0x2B};
+//Generation Qualifier OID (2.5.4.44)
+const uint8_t X509_GENERATION_QUALIFIER_OID[3] = {0x55, 0x04, 0x2C};
+//DN Qualifier OID (2.5.4.46)
+const uint8_t X509_DN_QUALIFIER_OID[3] = {0x55, 0x04, 0x2E};
+//Pseudonym OID (2.5.4.65)
+const uint8_t X509_PSEUDONYM_OID[3] = {0x55, 0x04, 0x41};
+
+//Subject Directory Attributes OID (2.5.29.9)
+const uint8_t X509_SUBJECT_DIRECTORY_ATTR_OID[3] = {0x55, 0x1D, 0x09};
+//Subject Key Identifier OID (2.5.29.14)
+const uint8_t X509_SUBJECT_KEY_ID_OID[3] = {0x55, 0x1D, 0x0E};
+//Key Usage OID (2.5.29.15)
+const uint8_t X509_KEY_USAGE_OID[3] = {0x55, 0x1D, 0x0F};
+//Subject Alternative Name OID (2.5.29.17)
+const uint8_t X509_SUBJECT_ALT_NAME_OID[3] = {0x55, 0x1D, 0x11};
+//Issuer Alternative Name OID (2.5.29.18)
+const uint8_t X509_ISSUER_ALT_NAME_OID[3] = {0x55, 0x1D, 0x12};
+//Basic Constraints OID (2.5.29.19)
+const uint8_t X509_BASIC_CONSTRAINTS_OID[3] = {0x55, 0x1D, 0x13};
+//Name Constraints OID (2.5.29.30)
+const uint8_t X509_NAME_CONSTRAINTS_OID[3] = {0x55, 0x1D, 0x1E};
+//CRL Distribution Points OID (2.5.29.31)
+const uint8_t X509_CRL_DISTR_POINTS_OID[3] = {0x55, 0x1D, 0x1F};
+//Certificate Policies OID (2.5.29.32)
+const uint8_t X509_CERTIFICATE_POLICIES_OID[3] = {0x55, 0x1D, 0x20};
+//Policy Mappings OID (2.5.29.33)
+const uint8_t X509_POLICY_MAPPINGS_OID[3] = {0x55, 0x1D, 0x21};
+//Authority Key Identifier OID (2.5.29.35)
+const uint8_t X509_AUTHORITY_KEY_ID_OID[3] = {0x55, 0x1D, 0x23};
+//Policy Constraints OID (2.5.29.36)
+const uint8_t X509_POLICY_CONSTRAINTS_OID[3] = {0x55, 0x1D, 0x24};
+//Extended Key Usage OID (2.5.29.37)
+const uint8_t X509_EXTENDED_KEY_USAGE_OID[3] = {0x55, 0x1D, 0x25};
+//Freshest CRL OID (2.5.29.46)
+const uint8_t X509_FRESHEST_CRL_OID[3] = {0x55, 0x1D, 0x2E};
+//Inhibit Any-Policy OID (2.5.29.54)
+const uint8_t X509_INHIBIT_ANY_POLICY_OID[3] = {0x55, 0x1D, 0x36};
+
+
+/**
+ * @brief Parse a X.509 certificate
+ * @param[in] data Pointer to the X.509 certificate to parse
+ * @param[in] length Length of the X.509 certificate
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseCertificate(const uint8_t *data, size_t length,
+   X509CertificateInfo *certInfo)
+{
+   error_t error;
+   size_t totalLength;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("Parsing X.509 certificate...\r\n");
+   //Clear the certificate information structure
+   memset(certInfo, 0, sizeof(X509CertificateInfo));
+
+   //Read the contents of the certificate
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return ERROR_BAD_CERTIFICATE;
+
+   //Point to the very first field
+   data = tag.value;
+   length = tag.length;
+
+   //Parse TBSCertificate structure
+   error = x509ParseTbsCertificate(data, length, &totalLength, certInfo);
+   //Any error to report?
+   if(error)
+      return ERROR_BAD_CERTIFICATE;
+
+   //Point to the next field
+   data += totalLength;
+   length -= totalLength;
+
+   //Parse SignatureAlgorithm structure
+   error = x509ParseSignatureAlgo(data, length, &totalLength, certInfo);
+   //Any error to report?
+   if(error)
+      return ERROR_BAD_CERTIFICATE;
+
+   //Point to the next field
+   data += totalLength;
+   length -= totalLength;
+
+   //Parse SignatureValue structure
+   error = x509ParseSignatureValue(data, length, &totalLength, certInfo);
+   //Any error to report?
+   if(error)
+      return ERROR_BAD_CERTIFICATE;
+
+   //Certificate successfully parsed
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse TBSCertificate structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseTbsCertificate(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo)
+{
+   error_t error;
+   size_t n;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("  Parsing TBSCertificate...\r\n");
+
+   //Read the contents of the TBSCertificate structure
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Save the total length of the field
+   *totalLength = tag.totalLength;
+
+   //The ASN.1 DER encoded tbsCertificate is used as the input to the signature function
+   certInfo->tbsCertificate = data;
+   certInfo->tbsCertificateLen = tag.totalLength;
+
+   //Point to the very first field of the TBSCertificate
+   data = tag.value;
+   length = tag.length;
+
+   //Parse Version field
+   error = x509ParseVersion(data, length, &n, certInfo);
+   //Failed to parse Version field?
+   if(error)
+      return error;
+
+   //Point to the next field
+   data += n;
+   length -= n;
+
+   //Read SerialNumber field
+   error = x509ParseSerialNumber(data, length, &n, certInfo);
+   //Failed to parse SerialNumber field?
+   if(error)
+      return error;
+
+   //Point to the next field
+   data += n;
+   length -= n;
+
+   //Read Signature field
+   error = x509ParseSignature(data, length, &n, certInfo);
+   //Failed to parse Signature field?
+   if(error)
+      return error;
+
+   //Point to the next field
+   data += n;
+   length -= n;
+
+   //Read Issuer field
+   error = x509ParseName(data, length, &n, &certInfo->issuer);
+   //Failed to parse Issuer field?
+   if(error)
+      return error;
+
+   //Point to the next field
+   data += n;
+   length -= n;
+
+   //Read Validity field
+   error = x509ParseValidity(data, length, &n, certInfo);
+   //Failed to parse Validity field?
+   if(error)
+      return error;
+
+   //Point to the next field
+   data += n;
+   length -= n;
+
+   //Read Subject field
+   error = x509ParseName(data, length, &n, &certInfo->subject);
+   //Failed to parse Subject field?
+   if(error)
+      return error;
+
+   //Point to the next field
+   data += n;
+   length -= n;
+
+   //Read SubjectPublicKeyInfo field
+   error = x509ParseSubjectPublicKeyInfo(data, length, &n, certInfo);
+   //Failed to parse Version field?
+   if(error)
+      return error;
+
+   //Point to the next field
+   data += n;
+   length -= n;
+
+   //Read IssuerUniqueID field (optional)
+   error = x509ParseIssuerUniqueId(data, length, &n, certInfo);
+   //Failed to parse Version field?
+   if(error)
+      return error;
+
+   //Point to the next field
+   data += n;
+   length -= n;
+
+   //Read SubjectUniqueID field (optional)
+   error = x509ParseSubjectUniqueId(data, length, &n, certInfo);
+   //Failed to parse Version field?
+   if(error)
+      return error;
+
+   //Point to the next field
+   data += n;
+   length -= n;
+
+   //Read SubjectUniqueID field (optional)
+   error = x509ParseExtensions(data, length, &n, certInfo);
+   //Failed to parse Version field?
+   if(error)
+      return error;
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse Version field
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseVersion(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo)
+{
+   error_t error;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("    Parsing Version...\r\n");
+
+   //Explicit tagging shall be used to encode version
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_CONTEXT_SPECIFIC, 0);
+
+   //The tag does not match the criteria?
+   if(error)
+   {
+      //Assume X.509v1 format
+      certInfo->version = X509_VERSION_1;
+      //Skip the current field
+      *totalLength = 0;
+      //Exit immediately
+      return NO_ERROR;
+   }
+
+   //Save the total length of the field
+   *totalLength = tag.totalLength;
+
+   //Read the inner tag
+   error = asn1ReadTag(tag.value, tag.length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Check length field
+   if(tag.length != 1)
+      return ERROR_INVALID_LENGTH;
+   //Check version field
+   if(tag.value[0] > X509_VERSION_3)
+      return ERROR_INVALID_VERSION;
+
+   //Save certificate version
+   certInfo->version = tag.value[0];
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse SerialNumber field
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseSerialNumber(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo)
+{
+   error_t error;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("    Parsing SerialNumber...\r\n");
+
+   //Read the contents of the SerialNumber structure
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Save the total length of the field
+   *totalLength = tag.totalLength;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Get the signature value
+   certInfo->serialNumber = tag.value;
+   certInfo->serialNumberLen = tag.length;
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse Signature field
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseSignature(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo)
+{
+   error_t error;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("    Parsing Signature...\r\n");
+
+   //Read the contents of the Signature structure
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Save the total length of the field
+   *totalLength = tag.totalLength;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Read the inner tag
+   error = asn1ReadTag(tag.value, tag.length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OBJECT_IDENTIFIER);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Get the signature algorithm identifier
+   certInfo->signatureAlgo = tag.value;
+   certInfo->signatureAlgoLen = tag.length;
+
+   //Validity field successfully parsed
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse Name structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] name Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseName(const uint8_t *data, size_t length,
+   size_t *totalLength, X509Name *name)
+{
+   error_t error;
+   Asn1Tag tag;
+   Asn1Tag attrType;
+   Asn1Tag attrValue;
+
+   //Debug message
+   TRACE_DEBUG("    Parsing Name...\r\n");
+
+   //Read the contents of the Name structure
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Save the total length of the field
+   *totalLength = tag.totalLength;
+
+   //Raw ASN.1 sequence
+   name->rawData = data;
+   name->rawDataLen = tag.totalLength;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //The Name describes a hierarchical name composed of attributes
+   data = tag.value;
+   length = tag.length;
+
+   //Loop through all the attributes
+   while(length > 0)
+   {
+      //Read current attribute
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SET);
+      //The tag does not match the criteria?
+      if(error)
+         return error;
+
+      //Point to the next attribute
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the inner tag
+      error = asn1ReadTag(tag.value, tag.length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+      //The tag does not match the criteria?
+      if(error)
+         return error;
+
+      //Read attribute type
+      error = asn1ReadTag(tag.value, tag.length, &attrType);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&attrType, FALSE,
+         ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OBJECT_IDENTIFIER);
+      //The tag does not match the criteria?
+      if(error)
+         return error;
+
+      //Read attribute value
+      error = asn1ReadTag(tag.value + attrType.totalLength,
+         tag.length - attrType.totalLength, &attrValue);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //Check the length of the OID
+      if(attrType.length == 3)
+      {
+         //Common Name attribute found?
+         if(!memcmp(attrType.value, X509_COMMON_NAME_OID, 3))
+         {
+            name->commonName = (const char_t *) attrValue.value;
+            name->commonNameLen = attrValue.length;
+         }
+         //Surname attribute found?
+         else if(!memcmp(attrType.value, X509_SURNAME_OID, 3))
+         {
+            name->surname = (const char_t *) attrValue.value;
+            name->surnameLen = attrValue.length;
+         }
+         //Serial Number attribute found?
+         else if(!memcmp(attrType.value, X509_SERIAL_NUMBER_OID, 3))
+         {
+            name->serialNumber = (const char_t *) attrValue.value;
+            name->serialNumberLen = attrValue.length;
+         }
+         //Country Name attribute found?
+         else if(!memcmp(attrType.value, X509_COUNTRY_NAME_OID, 3))
+         {
+            name->countryName = (const char_t *) attrValue.value;
+            name->countryNameLen = attrValue.length;
+         }
+         //Locality Name attribute found?
+         else if(!memcmp(attrType.value, X509_LOCALITY_NAME_OID, 3))
+         {
+            name->localityName = (const char_t *) attrValue.value;
+            name->localityNameLen = attrValue.length;
+         }
+         //State Or Province Name attribute found?
+         else if(!memcmp(attrType.value, X509_STATE_OR_PROVINCE_NAME_OID, 3))
+         {
+            name->stateOrProvinceName = (const char_t *) attrValue.value;
+            name->stateOrProvinceNameLen = attrValue.length;
+         }
+         //Organization Name attribute found?
+         else if(!memcmp(attrType.value, X509_ORGANIZATION_NAME_OID, 3))
+         {
+            name->organizationName = (const char_t *) attrValue.value;
+            name->organizationNameLen = attrValue.length;
+         }
+         //Organizational Unit Name attribute found?
+         else if(!memcmp(attrType.value, X509_ORGANIZATIONAL_UNIT_NAME_OID, 3))
+         {
+            name->organizationalUnitName = (const char_t *) attrValue.value;
+            name->organizationalUnitNameLen = attrValue.length;
+         }
+         //Title attribute found?
+         else if(!memcmp(attrType.value, X509_TITLE_OID, 3))
+         {
+            name->title = (const char_t *) attrValue.value;
+            name->titleLen = attrValue.length;
+         }
+         //Name attribute found?
+         else if(!memcmp(attrType.value, X509_NAME_OID, 3))
+         {
+            name->name = (const char_t *) attrValue.value;
+            name->nameLen = attrValue.length;
+         }
+         //Given Name attribute found?
+         else if(!memcmp(attrType.value, X509_GIVEN_NAME_OID, 3))
+         {
+            name->givenName = (const char_t *) attrValue.value;
+            name->givenNameLen = attrValue.length;
+         }
+         //Initials attribute OID (2.5.4.43)
+         else if(!memcmp(attrType.value, X509_INITIALS_OID, 3))
+         {
+            name->initials = (const char_t *) attrValue.value;
+            name->initialsLen = attrValue.length;
+         }
+         //Generation Qualifier attribute found?
+         else if(!memcmp(attrType.value, X509_GENERATION_QUALIFIER_OID, 3))
+         {
+            name->generationQualifier = (const char_t *) attrValue.value;
+            name->generationQualifierLen = attrValue.length;
+         }
+         //DN Qualifier attribute found?
+         else if(!memcmp(attrType.value, X509_DN_QUALIFIER_OID, 3))
+         {
+            name->dnQualifier = (const char_t *) attrValue.value;
+            name->dnQualifierLen = attrValue.length;
+         }
+         //Pseudonym attribute found?
+         else if(!memcmp(attrType.value, X509_PSEUDONYM_OID, 3))
+         {
+            name->pseudonym = (const char_t *) attrValue.value;
+            name->pseudonymLen = attrValue.length;
+         }
+      }
+   }
+
+   //Name field successfully parsed
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse Validity field
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseValidity(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo)
+{
+   error_t error;
+   size_t n;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("    Parsing Validity...\r\n");
+
+   //Read current ASN.1 tag
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Save the total length of the field
+   *totalLength = tag.totalLength;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Point to the very first field of the sequence
+   data = tag.value;
+   length = tag.length;
+
+   //NotBefore field may be encoded as UTCTime or GeneralizedTime
+   error = x509ParseTime(data, length, &n, &certInfo->validity.notBefore);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Point to the next field
+   data += n;
+   length -= n;
+
+   //NotAfter field may be encoded as UTCTime or GeneralizedTime
+   error = x509ParseTime(data, length, &n, &certInfo->validity.notAfter);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Validity field successfully parsed
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse UTCTime or GeneralizedTime structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] dateTime date resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseTime(const uint8_t *data, size_t length,
+   size_t *totalLength, DateTime *dateTime)
+{
+   error_t error;
+   uint_t value;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("      Parsing Time...\r\n");
+
+   //Read current ASN.1 tag
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Save the total length of the field
+   *totalLength = tag.totalLength;
+
+   //The date may be encoded as UTCTime or GeneralizedTime
+   if(!asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_UTC_TIME))
+   {
+      //Check the length of the UTCTime field
+      if(tag.length < 12)
+         return ERROR_INVALID_SYNTAX;
+
+      //The UTCTime uses a 2-digit representation of the year
+      error = x509ParseInt(tag.value, 2, &value);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //If YY is greater than or equal to 50, the year shall be interpreted
+      //as 19YY. If YY is less than 50, the year shall be interpreted as 20YY
+      if(value >= 50)
+         dateTime->year = 1900 + value;
+      else
+         dateTime->year = 2000 + value;
+
+      //Point to the next field
+      data = tag.value + 2;
+   }
+   else if(!asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_GENERALIZED_TIME))
+   {
+      //Check the length of the GeneralizedTime field
+      if(tag.length < 14)
+         return ERROR_INVALID_SYNTAX;
+
+      //The GeneralizedTime uses a 4-digit representation of the year
+      error = x509ParseInt(tag.value, 4, &value);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Point to the next field
+      data = tag.value + 4;
+   }
+   else
+   {
+      //The tag does not contain a valid date
+      return ERROR_FAILURE;
+   }
+
+   //Month
+   error = x509ParseInt(data, 2, &value);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Save the resulting value
+   dateTime->month = value;
+
+   //Day
+   error = x509ParseInt(data + 2, 2, &value);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Save the resulting value
+   dateTime->day = value;
+
+   //Hours
+   error = x509ParseInt(data + 4, 2, &value);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Save the resulting value
+   dateTime->hours = value;
+
+   //Minutes
+   error = x509ParseInt(data + 6, 2, &value);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Save the resulting value
+   dateTime->minutes = value;
+
+   //Seconds
+   error = x509ParseInt(data + 8, 2, &value);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Save the resulting value
+   dateTime->seconds = value;
+
+   //Milliseconds
+   dateTime->milliseconds = 0;
+
+   //UTCTime or GeneralizedTime field successfully parsed
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse SubjectPublicKeyInfo structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseSubjectPublicKeyInfo(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo)
+{
+   error_t error;
+   size_t n;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("    Parsing SubjectPublicKeyInfo...\r\n");
+
+   //Read SubjectPublicKeyInfo field
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Save the total length of the field
+   *totalLength = tag.totalLength;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Point to the first field
+   data = tag.value;
+   length = tag.length;
+
+   //Read AlgorithmIdentifier field
+   error = x509ParseAlgorithmIdentifier(data, length, &n, certInfo);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Point to the next field
+   data += n;
+   length -= n;
+
+   //The SubjectPublicKey structure is encapsulated within a bit string
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_BIT_STRING);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //The bit string shall contain an initial octet which encodes
+   //the number of unused bits in the final subsequent octet
+   if(tag.length < 1 || tag.value[0] != 0x00)
+      return ERROR_FAILURE;
+
+#if (RSA_SUPPORT == ENABLED)
+   //RSA algorithm identifier?
+   if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen,
+      RSA_ENCRYPTION_OID, sizeof(RSA_ENCRYPTION_OID)))
+   {
+      //Read RSAPublicKey structure
+      error = x509ParseRsaPublicKey(tag.value + 1, tag.length - 1, certInfo);
+   }
+   else
+#endif
+#if (DSA_SUPPORT == ENABLED)
+   //DSA algorithm identifier?
+   if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen,
+      DSA_OID, sizeof(DSA_OID)))
+   {
+      //Read DSAPublicKey structure
+      error = x509ParseDsaPublicKey(tag.value + 1, tag.length - 1, certInfo);
+   }
+   else
+#endif
+#if (EC_SUPPORT == ENABLED)
+   //EC public key identifier?
+   if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen,
+      EC_PUBLIC_KEY_OID, sizeof(EC_PUBLIC_KEY_OID)))
+   {
+      //Read ECPublicKey structure
+      error = x509ParseEcPublicKey(tag.value + 1, tag.length - 1, certInfo);
+   }
+   else
+#endif
+   //The certificate does not contain any valid public key...
+   {
+      //Report an error
+      error = ERROR_BAD_CERTIFICATE;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse AlgorithmIdentifier structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseAlgorithmIdentifier(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo)
+{
+   error_t error;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("      Parsing AlgorithmIdentifier...\r\n");
+
+   //Read AlgorithmIdentifier field
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Save the total length of the field
+   *totalLength = tag.totalLength;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Point to the first field
+   data = tag.value;
+   length = tag.length;
+
+   //Read algorithm identifier (OID)
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OBJECT_IDENTIFIER);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Save the algorithm identifier
+   certInfo->subjectPublicKeyInfo.oid = tag.value;
+   certInfo->subjectPublicKeyInfo.oidLen = tag.length;
+
+   //Point to the next field (if any)
+   data += tag.totalLength;
+   length -= tag.totalLength;
+
+#if (RSA_SUPPORT == ENABLED)
+   //RSA algorithm identifier?
+   if(!asn1CheckOid(&tag, RSA_ENCRYPTION_OID, sizeof(RSA_ENCRYPTION_OID)))
+   {
+      //RSA does not require any additional parameters
+      error = NO_ERROR;
+   }
+   else
+#endif
+#if (DSA_SUPPORT == ENABLED)
+   //DSA algorithm identifier?
+   if(!asn1CheckOid(&tag, DSA_OID, sizeof(DSA_OID)))
+   {
+      //Read DsaParameters structure
+      error = x509ParseDsaParameters(data, length, certInfo);
+   }
+   else
+#endif
+#if (EC_SUPPORT == ENABLED)
+   //EC public key identifier?
+   if(!asn1CheckOid(&tag, EC_PUBLIC_KEY_OID, sizeof(EC_PUBLIC_KEY_OID)))
+   {
+      //Read ECParameters structure
+      error = x509ParseEcParameters(data, length, certInfo);
+   }
+   else
+#endif
+   //The certificate does not contain any valid public key...
+   {
+      //Report an error
+      error = ERROR_BAD_CERTIFICATE;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse RSAPublicKey structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseRsaPublicKey(const uint8_t *data,
+   size_t length, X509CertificateInfo *certInfo)
+{
+#if (RSA_SUPPORT == ENABLED)
+   error_t error;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("      Parsing RSAPublicKey...\r\n");
+
+   //Read RSAPublicKey structure
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Point to the first field
+   data = tag.value;
+   length = tag.length;
+
+   //Read Modulus field
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Get the modulus
+   certInfo->subjectPublicKeyInfo.rsaPublicKey.n = tag.value;
+   certInfo->subjectPublicKeyInfo.rsaPublicKey.nLen = tag.length;
+
+   //Point to the next field
+   data += tag.totalLength;
+   length -= tag.totalLength;
+
+   //Read PublicExponent field
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Get the public exponent
+   certInfo->subjectPublicKeyInfo.rsaPublicKey.e = tag.value;
+   certInfo->subjectPublicKeyInfo.rsaPublicKey.eLen = tag.length;
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Parse DSA domain parameters
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseDsaParameters(const uint8_t *data,
+   size_t length, X509CertificateInfo *certInfo)
+{
+#if (DSA_SUPPORT == ENABLED)
+   error_t error;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("        Parsing DSAParameters...\r\n");
+
+   //Read DSAParameters structure
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Point to the first field
+   data = tag.value;
+   length = tag.length;
+
+   //Read the parameter p
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Save the parameter p
+   certInfo->subjectPublicKeyInfo.dsaParams.p = tag.value;
+   certInfo->subjectPublicKeyInfo.dsaParams.pLen = tag.length;
+
+   //Point to the next field
+   data += tag.totalLength;
+   length -= tag.totalLength;
+
+   //Read the parameter q
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Save the parameter q
+   certInfo->subjectPublicKeyInfo.dsaParams.q = tag.value;
+   certInfo->subjectPublicKeyInfo.dsaParams.qLen = tag.length;
+
+   //Point to the next field
+   data += tag.totalLength;
+   length -= tag.totalLength;
+
+   //Read the parameter g
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Save the parameter g
+   certInfo->subjectPublicKeyInfo.dsaParams.g = tag.value;
+   certInfo->subjectPublicKeyInfo.dsaParams.gLen = tag.length;
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Parse DSAPublicKey structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseDsaPublicKey(const uint8_t *data,
+   size_t length, X509CertificateInfo *certInfo)
+{
+#if (DSA_SUPPORT == ENABLED)
+   error_t error;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("      Parsing DSAPublicKey...\r\n");
+
+   //Read DSAPublicKey structure
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Save the DSA public value
+   certInfo->subjectPublicKeyInfo.dsaPublicKey.y = tag.value;
+   certInfo->subjectPublicKeyInfo.dsaPublicKey.yLen = tag.length;
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Parse ECParameters structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseEcParameters(const uint8_t *data,
+   size_t length, X509CertificateInfo *certInfo)
+{
+#if (EC_SUPPORT == ENABLED)
+   error_t error;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("        Parsing ECParameters...\r\n");
+
+   //Read namedCurve field
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OBJECT_IDENTIFIER);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //The namedCurve field identifies all the required values for a particular
+   //set of elliptic curve domain parameters to be represented by an object
+   //identifier
+   certInfo->subjectPublicKeyInfo.ecParams.namedCurve = tag.value;
+   certInfo->subjectPublicKeyInfo.ecParams.namedCurveLen = tag.length;
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Parse ECPublicKey structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseEcPublicKey(const uint8_t *data,
+   size_t length, X509CertificateInfo *certInfo)
+{
+#if (EC_SUPPORT == ENABLED)
+   //Debug message
+   TRACE_DEBUG("      Parsing ECPublicKey...\r\n");
+
+   //Make sure the EC public key is valid
+   if(!length)
+      return ERROR_BAD_CERTIFICATE;
+
+   //Save the EC public value
+   certInfo->subjectPublicKeyInfo.ecPublicKey.q = data;
+   certInfo->subjectPublicKeyInfo.ecPublicKey.qLen = length;
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Parse IssuerUniqueID structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseIssuerUniqueId(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo)
+{
+   error_t error;
+   Asn1Tag tag;
+
+   //No more data to process?
+   if(!length)
+   {
+      //The IssuerUniqueID field is optional
+      *totalLength = 0;
+      //Exit immediately
+      return NO_ERROR;
+   }
+
+   //Explicit tagging is used to encode the IssuerUniqueID field
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_CONTEXT_SPECIFIC, 1);
+   //The tag does not match the criteria?
+   if(error)
+   {
+      //The IssuerUniqueID field is optional
+      *totalLength = 0;
+      //Exit immediately
+      return NO_ERROR;
+   }
+
+   //Save the total length of the field
+   *totalLength = tag.totalLength;
+
+   //Debug message
+   TRACE_DEBUG("    Parsing IssuerUniqueID...\r\n");
+
+   //This field must only appear if the version is 2 or 3
+   if(certInfo->version < X509_VERSION_2)
+      return ERROR_INVALID_VERSION;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse SubjectUniqueID structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseSubjectUniqueId(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo)
+{
+   error_t error;
+   Asn1Tag tag;
+
+   //No more data to process?
+   if(!length)
+   {
+      //The SubjectUniqueID field is optional
+      *totalLength = 0;
+      //Exit immediately
+      return NO_ERROR;
+   }
+
+   //Explicit tagging is used to encode the SubjectUniqueID field
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_CONTEXT_SPECIFIC, 2);
+   //The tag does not match the criteria?
+   if(error)
+   {
+      //The SubjectUniqueID field is optional
+      *totalLength = 0;
+      //Exit immediately
+      return NO_ERROR;
+   }
+
+   //Save the total length of the field
+   *totalLength = tag.totalLength;
+
+   //Debug message
+   TRACE_DEBUG("    Parsing SubjectUniqueID...\r\n");
+
+   //This field must only appear if the version is 2 or 3
+   if(certInfo->version < X509_VERSION_2)
+      return ERROR_INVALID_VERSION;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse Extensions structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseExtensions(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo)
+{
+   error_t error;
+   bool_t critical;
+   const uint8_t *extensionData;
+   size_t extensionLength;
+   Asn1Tag tag;
+   Asn1Tag oidTag;
+
+   //No more data to process?
+   if(!length)
+   {
+      //The Extensions field is optional
+      *totalLength = 0;
+      //Exit immediately
+      return NO_ERROR;
+   }
+
+   //Explicit tagging is used to encode the Extensions field
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_CONTEXT_SPECIFIC, 3);
+   //The tag does not match the criteria?
+   if(error)
+   {
+      //The Extensions field is optional
+      *totalLength = 0;
+      //Exit immediately
+      return NO_ERROR;
+   }
+
+   //Save the total length of the field
+   *totalLength = tag.totalLength;
+
+   //Debug message
+   TRACE_DEBUG("    Parsing Extensions...\r\n");
+
+   //This field must only appear if the version is 3
+   if(certInfo->version < X509_VERSION_3)
+      return ERROR_INVALID_VERSION;
+
+   //Read inner tag
+   error = asn1ReadTag(tag.value, tag.length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //This field is a sequence of one or more certificate extensions
+   data = tag.value;
+   length = tag.length;
+
+   //Loop through the extensions
+   while(length > 0)
+   {
+      //Read current extension
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+      //The tag does not match the criteria?
+      if(error)
+         return error;
+
+      //Point to the next extension
+      data += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Contents of the current extension
+      extensionData = tag.value;
+      extensionLength = tag.length;
+
+      //Read the object identifier
+      error = asn1ReadTag(extensionData, extensionLength, &oidTag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&oidTag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OBJECT_IDENTIFIER);
+      //The tag does not match the criteria?
+      if(error)
+         return error;
+
+      //Next item
+      extensionData += oidTag.totalLength;
+      extensionLength -= oidTag.totalLength;
+
+      //Read the Critical flag (if present)
+      error = asn1ReadTag(extensionData, extensionLength, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_BOOLEAN);
+
+      //Check whether the Critical field is present
+      if(!error)
+      {
+         //Make sure the length of the boolean is valid
+         if(tag.length != 1)
+            return ERROR_INVALID_LENGTH;
+
+         //Each extension in a certificate is designated as either
+         //critical or non-critical
+         critical = tag.value[0] ? TRUE : FALSE;
+
+         //Next item
+         extensionData += tag.totalLength;
+         extensionLength -= tag.totalLength;
+      }
+      else
+      {
+         //The extension is considered as non-critical
+         critical = FALSE;
+      }
+
+      //The extension itself is encapsulated in an octet string
+      error = asn1ReadTag(extensionData, extensionLength, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OCTET_STRING);
+      //The tag does not match the criteria?
+      if(error)
+         return error;
+
+      //The current extension matches the BasicConstraints OID?
+      if(!oidComp(oidTag.value, oidTag.length, X509_BASIC_CONSTRAINTS_OID, 3))
+      {
+         //Parse BasicConstraints extension
+         error = x509ParseBasicConstraints(tag.value, tag.length, certInfo);
+         //Any error to report?
+         if(error)
+            return error;
+      }
+      //The current extension matches the KeyUsage OID?
+      else if(!oidComp(oidTag.value, oidTag.length, X509_KEY_USAGE_OID, 3))
+      {
+         //Parse KeyUsage extension
+      }
+      //The current extension matches the ExtendedKeyUsage OID?
+      else if(!oidComp(oidTag.value, oidTag.length, X509_EXTENDED_KEY_USAGE_OID, 3))
+      {
+         //Parse ExtendedKeyUsage extension
+      }
+      //The current extension is marked as critical?
+      else if(critical)
+      {
+         //A certificate-using system must reject the certificate if it encounters
+         //a critical extension it does not recognize or a critical extension that
+         //contains information that it cannot process
+         return ERROR_UNSUPPORTED_EXTENSION;
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse BasicConstraints structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseBasicConstraints(const uint8_t *data,
+   size_t length, X509CertificateInfo *certInfo)
+{
+   error_t error;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("      Parsing BasicConstraints...\r\n");
+
+   //The BasicConstraints structure shall contain a valid sequence
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Point to the first item of the sequence
+   data = tag.value;
+   length = tag.length;
+
+   //The cA boolean is optional...
+   if(length > 0)
+   {
+      //The cA boolean indicates whether the certified public key
+      //may be used to verify certificate signatures
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_BOOLEAN);
+
+      //The cA field is present?
+      if(!error)
+      {
+         //The tag should be 1-byte long
+         if(tag.length != 1)
+            return ERROR_INVALID_LENGTH;
+
+         //Get boolean value
+         certInfo->basicConstraints.ca = tag.value ? TRUE : FALSE;
+
+         //Point to the next item
+         data += tag.totalLength;
+         length -= tag.totalLength;
+      }
+   }
+
+   //The pathLenConstraint field is optional...
+   if(length > 0)
+   {
+      //The pathLenConstraint field gives the maximum number of non-self-issued
+      //intermediate certificates that may follow this certificate in a valid
+      //certification path
+      error = asn1ReadTag(data, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_INTEGER);
+      //The tag does not match the criteria?
+      if(error)
+         return error;
+
+      //The pathLenConstraint field is not supported
+      certInfo->basicConstraints.pathLenConstraint = 0;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse SignatureAlgorithm structure
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseSignatureAlgo(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo)
+{
+   error_t error;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("  Parsing SignatureAlgorithm...\r\n");
+
+   //Read the contents of the SignatureAlgorithm field
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Save the total length of the field
+   *totalLength = tag.totalLength;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Read the inner tag
+   error = asn1ReadTag(tag.value, tag.length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //This field must contain the same algorithm identifier
+   //as the signature field in the TBSCertificate sequence
+   error = asn1CheckOid(&tag, certInfo->signatureAlgo, certInfo->signatureAlgoLen);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse SignatureValue field
+ * @param[in] data Pointer to the ASN.1 structure to parse
+ * @param[in] length Length of the ASN.1 structure
+ * @param[out] totalLength Number of bytes that have been parsed
+ * @param[out] certInfo Information resulting from the parsing process
+ * @return Error code
+ **/
+
+error_t x509ParseSignatureValue(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo)
+{
+   error_t error;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("  Parsing SignatureValue...\r\n");
+
+   //Read the contents of the SignatureValue structure
+   error = asn1ReadTag(data, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Save the total length of the field
+   *totalLength = tag.totalLength;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_BIT_STRING);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //The bit string shall contain an initial octet which encodes
+   //the number of unused bits in the final subsequent octet
+   if(tag.length < 1 || tag.value[0] != 0x00)
+      return ERROR_FAILURE;
+
+   //Get the signature value
+   certInfo->signatureValue = tag.value + 1;
+   certInfo->signatureValueLen = tag.length - 1;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Convert string to integer
+ * @param[in] data String containing the representation of an integral number
+ * @param[in] length Length of the string
+ * @param[out] value On success, the function returns the converted integral number
+ * @return Error code
+ **/
+
+error_t x509ParseInt(const uint8_t *data, size_t length, uint_t *value)
+{
+   //Initialize integer value
+   *value = 0;
+
+   //Parse the string
+   while(length > 0)
+   {
+      //Check whether the character is decimal digit
+      if(!isdigit(*data))
+         return ERROR_FAILURE;
+
+      //Convert the string to integer
+      *value = *value * 10 + (*data - '0');
+
+      //Next character
+      data++;
+      length--;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Read a RSA public key
+ * @param[in] certInfo X.509 certificate
+ * @param[out] key RSA public key
+ * @return Error code
+ **/
+
+error_t x509ReadRsaPublicKey(const X509CertificateInfo *certInfo, RsaPublicKey *key)
+{
+#if (RSA_SUPPORT == ENABLED)
+   error_t error;
+
+   //The certificate shall contain a valid RSA public key
+   if(!certInfo->subjectPublicKeyInfo.rsaPublicKey.n ||
+      !certInfo->subjectPublicKeyInfo.rsaPublicKey.e)
+   {
+      //Report an error
+      return ERROR_INVALID_KEY;
+   }
+
+   //Convert the modulus to a big number
+   error = mpiReadRaw(&key->n, certInfo->subjectPublicKeyInfo.rsaPublicKey.n,
+      certInfo->subjectPublicKeyInfo.rsaPublicKey.nLen);
+   //Convertion failed?
+   if(error)
+      return error;
+
+   //Convert the public exponent to a big number
+   error = mpiReadRaw(&key->e, certInfo->subjectPublicKeyInfo.rsaPublicKey.e,
+      certInfo->subjectPublicKeyInfo.rsaPublicKey.eLen);
+   //Convertion failed?
+   if(error)
+      return error;
+
+   //Debug message
+   TRACE_DEBUG("RSA public key:\r\n");
+   TRACE_DEBUG("  Modulus:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->n);
+   TRACE_DEBUG("  Public exponent:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->e);
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Read a DSA public key
+ * @param[in] certInfo X.509 certificate
+ * @param[out] key DSA public key
+ * @return Error code
+ **/
+
+error_t x509ReadDsaPublicKey(const X509CertificateInfo *certInfo, DsaPublicKey *key)
+{
+#if (DSA_SUPPORT == ENABLED)
+   error_t error;
+
+   //The certificate shall contain a valid DSA public key
+   if(!certInfo->subjectPublicKeyInfo.dsaParams.p ||
+      !certInfo->subjectPublicKeyInfo.dsaParams.q ||
+      !certInfo->subjectPublicKeyInfo.dsaParams.g ||
+      !certInfo->subjectPublicKeyInfo.dsaPublicKey.y)
+   {
+      //Report an error
+      return ERROR_INVALID_KEY;
+   }
+
+   //Convert the parameter p to a big number
+   error = mpiReadRaw(&key->p, certInfo->subjectPublicKeyInfo.dsaParams.p,
+      certInfo->subjectPublicKeyInfo.dsaParams.pLen);
+   //Convertion failed?
+   if(error)
+      return error;
+
+   //Convert the parameter q to a big number
+   error = mpiReadRaw(&key->q, certInfo->subjectPublicKeyInfo.dsaParams.q,
+      certInfo->subjectPublicKeyInfo.dsaParams.qLen);
+   //Convertion failed?
+   if(error)
+      return error;
+
+   //Convert the parameter g to a big number
+   error = mpiReadRaw(&key->g, certInfo->subjectPublicKeyInfo.dsaParams.g,
+      certInfo->subjectPublicKeyInfo.dsaParams.gLen);
+   //Convertion failed?
+   if(error)
+      return error;
+
+   //Convert the public value to a big number
+   error = mpiReadRaw(&key->y, certInfo->subjectPublicKeyInfo.dsaPublicKey.y,
+      certInfo->subjectPublicKeyInfo.dsaPublicKey.yLen);
+   //Convertion failed?
+   if(error)
+      return error;
+
+   //Debug message
+   TRACE_DEBUG("DSA public key:\r\n");
+   TRACE_DEBUG("  Parameter p:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->p);
+   TRACE_DEBUG("  Parameter q:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->q);
+   TRACE_DEBUG("  Parameter g:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->g);
+   TRACE_DEBUG("  Public value y:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->y);
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief X.509 certificate validation
+ * @param[in] certInfo X.509 certificate to be verified
+ * @param[in] issuerCertInfo Issuer certificate
+ * @return Error code
+ **/
+
+error_t x509ValidateCertificate(const X509CertificateInfo *certInfo,
+   const X509CertificateInfo *issuerCertInfo)
+{
+   error_t error;
+   time_t currentTime;
+   time_t notBefore;
+   time_t notAfter;
+   const HashAlgo *hashAlgo;
+   HashContext *hashContext;
+
+   //Use RSA, DSA or ECDSA signature algorithm?
+#if (RSA_SUPPORT == ENABLED)
+   bool_t rsaSignAlgo = FALSE;
+#endif
+#if (DSA_SUPPORT == ENABLED)
+   bool_t dsaSignAlgo = FALSE;
+#endif
+#if (ECDSA_SUPPORT == ENABLED)
+   bool_t ecdsaSignAlgo = FALSE;
+#endif
+
+   //Retrieve current time
+   currentTime = getCurrentUnixTime();
+
+   //Any real-time clock implemented?
+   if(currentTime != 0)
+   {
+      //Convert NotBefore and NotAfter to Unix timestamps
+      notBefore = convertDateToUnixTime(&certInfo->validity.notBefore);
+      notAfter = convertDateToUnixTime(&certInfo->validity.notAfter);
+
+      //Check the certificate validity period
+      if(currentTime < notBefore || currentTime > notAfter)
+         return ERROR_CERTIFICATE_EXPIRED;
+   }
+
+   //Make sure that the subject and issuer names chain correctly
+   if(certInfo->issuer.rawDataLen != issuerCertInfo->subject.rawDataLen)
+      return ERROR_BAD_CERTIFICATE;
+   if(memcmp(certInfo->issuer.rawData, issuerCertInfo->subject.rawData, certInfo->issuer.rawDataLen))
+      return ERROR_BAD_CERTIFICATE;
+
+   //Ensure that the issuer certificate is a CA certificate
+   if(issuerCertInfo->version >= X509_VERSION_3 && !issuerCertInfo->basicConstraints.ca)
+      return ERROR_BAD_CERTIFICATE;
+
+   //Retrieve the signature algorithm that has been used to sign the certificate
+#if (RSA_SUPPORT == ENABLED && MD5_SUPPORT == ENABLED)
+   if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      MD5_WITH_RSA_ENCRYPTION_OID, sizeof(MD5_WITH_RSA_ENCRYPTION_OID)))
+   {
+      //MD5 with RSA signature algorithm
+      rsaSignAlgo = TRUE;
+      hashAlgo = MD5_HASH_ALGO;
+   }
+   else
+#endif
+#if (RSA_SUPPORT == ENABLED && SHA1_SUPPORT == ENABLED)
+   if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      SHA1_WITH_RSA_ENCRYPTION_OID, sizeof(SHA1_WITH_RSA_ENCRYPTION_OID)))
+   {
+      //SHA-1 with RSA signature algorithm
+      rsaSignAlgo = TRUE;
+      hashAlgo = SHA1_HASH_ALGO;
+   }
+   else
+#endif
+#if (RSA_SUPPORT == ENABLED && SHA256_SUPPORT == ENABLED)
+   if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      SHA256_WITH_RSA_ENCRYPTION_OID, sizeof(SHA256_WITH_RSA_ENCRYPTION_OID)))
+   {
+      //SHA-256 with RSA signature algorithm
+      rsaSignAlgo = TRUE;
+      hashAlgo = SHA256_HASH_ALGO;
+   }
+   else
+#endif
+#if (RSA_SUPPORT == ENABLED && SHA384_SUPPORT == ENABLED)
+   if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      SHA384_WITH_RSA_ENCRYPTION_OID, sizeof(SHA384_WITH_RSA_ENCRYPTION_OID)))
+   {
+      //SHA-384 with RSA signature algorithm
+      rsaSignAlgo = TRUE;
+      hashAlgo = SHA384_HASH_ALGO;
+   }
+   else
+#endif
+#if (RSA_SUPPORT == ENABLED && SHA512_SUPPORT == ENABLED)
+   if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      SHA512_WITH_RSA_ENCRYPTION_OID, sizeof(SHA512_WITH_RSA_ENCRYPTION_OID)))
+   {
+      //SHA-512 with RSA signature algorithm
+      rsaSignAlgo = TRUE;
+      hashAlgo = SHA512_HASH_ALGO;
+   }
+   else
+#endif
+#if (DSA_SUPPORT == ENABLED && SHA1_SUPPORT == ENABLED)
+   if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      DSA_WITH_SHA1_OID, sizeof(DSA_WITH_SHA1_OID)))
+   {
+      //DSA with SHA-1 signature algorithm
+      dsaSignAlgo = TRUE;
+      hashAlgo = SHA1_HASH_ALGO;
+   }
+   else
+#endif
+#if (DSA_SUPPORT == ENABLED && SHA224_SUPPORT == ENABLED)
+   if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      DSA_WITH_SHA224_OID, sizeof(DSA_WITH_SHA224_OID)))
+   {
+      //DSA with SHA-224 signature algorithm
+      dsaSignAlgo = TRUE;
+      hashAlgo = SHA224_HASH_ALGO;
+   }
+   else
+#endif
+#if (DSA_SUPPORT == ENABLED && SHA256_SUPPORT == ENABLED)
+   if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      DSA_WITH_SHA256_OID, sizeof(DSA_WITH_SHA256_OID)))
+   {
+      //DSA with SHA-256 signature algorithm
+      dsaSignAlgo = TRUE;
+      hashAlgo = SHA256_HASH_ALGO;
+   }
+   else
+#endif
+#if (ECDSA_SUPPORT == ENABLED && SHA1_SUPPORT == ENABLED)
+   if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      ECDSA_WITH_SHA1_OID, sizeof(ECDSA_WITH_SHA1_OID)))
+   {
+      //ECDSA with SHA-1 signature algorithm
+      ecdsaSignAlgo = TRUE;
+      hashAlgo = SHA1_HASH_ALGO;
+   }
+   else
+#endif
+#if (ECDSA_SUPPORT == ENABLED && SHA224_SUPPORT == ENABLED)
+   if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      ECDSA_WITH_SHA224_OID, sizeof(ECDSA_WITH_SHA224_OID)))
+   {
+      //ECDSA with SHA-224 signature algorithm
+      ecdsaSignAlgo = TRUE;
+      hashAlgo = SHA224_HASH_ALGO;
+   }
+   else
+#endif
+#if (ECDSA_SUPPORT == ENABLED && SHA256_SUPPORT == ENABLED)
+   if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      ECDSA_WITH_SHA256_OID, sizeof(ECDSA_WITH_SHA256_OID)))
+   {
+      //ECDSA with SHA-256 signature algorithm
+      ecdsaSignAlgo = TRUE;
+      hashAlgo = SHA256_HASH_ALGO;
+   }
+   else
+#endif
+#if (ECDSA_SUPPORT == ENABLED && SHA384_SUPPORT == ENABLED)
+   if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      ECDSA_WITH_SHA384_OID, sizeof(ECDSA_WITH_SHA384_OID)))
+   {
+      //ECDSA with SHA-384 signature algorithm
+      ecdsaSignAlgo = TRUE;
+      hashAlgo = SHA384_HASH_ALGO;
+   }
+   else
+#endif
+#if (ECDSA_SUPPORT == ENABLED && SHA512_SUPPORT == ENABLED)
+   if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      ECDSA_WITH_SHA512_OID, sizeof(ECDSA_WITH_SHA512_OID)))
+   {
+      //ECDSA with SHA-512 signature algorithm
+      ecdsaSignAlgo = TRUE;
+      hashAlgo = SHA512_HASH_ALGO;
+   }
+   else
+#endif
+   {
+      //The specified signature algorithm is not supported
+      return ERROR_UNSUPPORTED_SIGNATURE_ALGO;
+   }
+
+   //Allocate a memory buffer to hold the hash context
+   hashContext = cryptoAllocMem(hashAlgo->contextSize);
+   //Failed to allocate memory?
+   if(hashContext == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Digest the TBSCertificate structure using the specified hash algorithm
+   hashAlgo->init(hashContext);
+   hashAlgo->update(hashContext, certInfo->tbsCertificate, certInfo->tbsCertificateLen);
+   hashAlgo->final(hashContext, NULL);
+
+   //Check signature algorithm
+#if (RSA_SUPPORT == ENABLED)
+   if(rsaSignAlgo)
+   {
+      RsaPublicKey publicKey;
+
+      //Initialize RSA public key
+      rsaInitPublicKey(&publicKey);
+
+      //Get the RSA public key
+      error = x509ReadRsaPublicKey(issuerCertInfo, &publicKey);
+
+      //Check status code
+      if(!error)
+      {
+         //Verify RSA signature
+         error = rsassaPkcs1v15Verify(&publicKey, hashAlgo, hashContext->digest,
+            certInfo->signatureValue, certInfo->signatureValueLen);
+      }
+
+      //Release previously allocated resources
+      rsaFreePublicKey(&publicKey);
+   }
+   else
+#endif
+#if (DSA_SUPPORT == ENABLED)
+   if(dsaSignAlgo)
+   {
+      DsaPublicKey publicKey;
+      DsaSignature signature;
+
+      //Initialize DSA public key
+      dsaInitPublicKey(&publicKey);
+      //Initialize DSA signature
+      dsaInitSignature(&signature);
+
+      //Get the DSA public key
+      error = x509ReadDsaPublicKey(issuerCertInfo, &publicKey);
+
+      //Check status code
+      if(!error)
+      {
+         //Read the ASN.1 encoded signature
+         error = dsaReadSignature(certInfo->signatureValue,
+            certInfo->signatureValueLen, &signature);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Verify DSA signature
+         error = dsaVerifySignature(&publicKey, hashContext->digest,
+            hashAlgo->digestSize, &signature);
+      }
+
+      //Release previously allocated resources
+      dsaFreePublicKey(&publicKey);
+      dsaFreeSignature(&signature);
+   }
+   else
+#endif
+#if (ECDSA_SUPPORT == ENABLED)
+   if(ecdsaSignAlgo)
+   {
+      const EcCurveInfo *curveInfo;
+      EcDomainParameters params;
+      EcPoint publicKey;
+      EcdsaSignature signature;
+
+      //Initialize EC domain parameters
+      ecInitDomainParameters(&params);
+      //Initialize ECDSA public key
+      ecInit(&publicKey);
+      //Initialize ECDSA signature
+      ecdsaInitSignature(&signature);
+
+      //Retrieve EC domain parameters
+      curveInfo = ecGetCurveInfo(issuerCertInfo->subjectPublicKeyInfo.ecParams.namedCurve,
+         issuerCertInfo->subjectPublicKeyInfo.ecParams.namedCurveLen);
+
+      //Make sure the specified elliptic curve is supported
+      error = (curveInfo == NULL) ? ERROR_BAD_CERTIFICATE : NO_ERROR;
+
+      //Check status code
+      if(!error)
+      {
+         //Load EC domain parameters
+         error = ecLoadDomainParameters(&params, curveInfo);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Retrieve the EC public key
+         error = ecImport(&params, &publicKey, issuerCertInfo->subjectPublicKeyInfo.ecPublicKey.q,
+            issuerCertInfo->subjectPublicKeyInfo.ecPublicKey.qLen);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Read the ASN.1 encoded signature
+         error = ecdsaReadSignature(certInfo->signatureValue,
+            certInfo->signatureValueLen, &signature);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Verify ECDSA signature
+         error = ecdsaVerifySignature(&params, &publicKey,
+            hashContext->digest, hashAlgo->digestSize, &signature);
+      }
+
+      //Release previously allocated resources
+      ecFreeDomainParameters(&params);
+      ecFree(&publicKey);
+      ecdsaFreeSignature(&signature);
+   }
+   else
+#endif
+   {
+      //The signature algorithm is not supported...
+      error = ERROR_UNSUPPORTED_SIGNATURE_ALGO;
+   }
+
+   //Release hash algorithm context
+   cryptoFreeMem(hashContext);
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/x509.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,327 @@
+/**
+ * @file x509.h
+ * @brief X.509 certificate parsing and verification
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _X509_H
+#define _X509_H
+
+//Dependencies
+#include "crypto.h"
+#include "date_time.h"
+#include "rsa.h"
+#include "dsa.h"
+#include "ec.h"
+
+
+/**
+ * @brief X.509 versions
+ **/
+
+typedef enum
+{
+   X509_VERSION_1 = 0x00,
+   X509_VERSION_2 = 0x01,
+   X509_VERSION_3 = 0x02,
+} X509Version;
+
+
+/**
+ * @brief Issuer or subject name
+ **/
+
+typedef struct
+{
+   const uint8_t *rawData;
+   size_t rawDataLen;
+   const char_t *commonName;
+   size_t commonNameLen;
+   const char_t *surname;
+   size_t surnameLen;
+   const char_t *serialNumber;
+   size_t serialNumberLen;
+   const char_t *countryName;
+   size_t countryNameLen;
+   const char_t *localityName;
+   size_t localityNameLen;
+   const char_t *stateOrProvinceName;
+   size_t stateOrProvinceNameLen;
+   const char_t *organizationName;
+   size_t organizationNameLen;
+   const char_t *organizationalUnitName;
+   size_t organizationalUnitNameLen;
+   const char_t *title;
+   size_t titleLen;
+   const char_t *name;
+   size_t nameLen;
+   const char_t *givenName;
+   size_t givenNameLen;
+   const char_t *initials;
+   size_t initialsLen;
+   const char_t *generationQualifier;
+   size_t generationQualifierLen;
+   const char_t *dnQualifier;
+   size_t dnQualifierLen;
+   const char_t *pseudonym;
+   size_t pseudonymLen;
+} X509Name;
+
+
+/**
+ * @brief Validity
+ **/
+
+typedef struct
+{
+   DateTime notBefore;
+   DateTime notAfter;
+} X509Validity;
+
+
+/**
+ * @brief RSA public key
+ **/
+
+typedef struct
+{
+   const uint8_t *n;
+   size_t nLen;
+   const uint8_t *e;
+   size_t eLen;
+} X509RsaPublicKey;
+
+
+/**
+ * @brief DSA domain parameters
+ **/
+
+typedef struct
+{
+   const uint8_t *p;
+   size_t pLen;
+   const uint8_t *q;
+   size_t qLen;
+   const uint8_t *g;
+   size_t gLen;
+} X509DsaParameters;
+
+
+/**
+ * @brief DSA public key
+ **/
+
+typedef struct
+{
+   const uint8_t *y;
+   size_t yLen;
+} X509DsaPublicKey;
+
+
+/**
+ * @brief EC parameters
+ **/
+
+typedef struct
+{
+   const uint8_t *namedCurve;
+   size_t namedCurveLen;
+} X509EcParameters;
+
+
+/**
+ * @brief EC public key
+ **/
+
+typedef struct
+{
+   const uint8_t *q;
+   size_t qLen;
+} X509EcPublicKey;
+
+
+/**
+ * @brief Subject public key info
+ **/
+
+typedef struct
+{
+   const uint8_t *oid;
+   size_t oidLen;
+#if (RSA_SUPPORT == ENABLED)
+   X509RsaPublicKey rsaPublicKey;
+#endif
+#if (DSA_SUPPORT == ENABLED)
+   X509DsaParameters dsaParams;
+   X509DsaPublicKey dsaPublicKey;
+#endif
+#if (EC_SUPPORT == ENABLED)
+   X509EcParameters ecParams;
+   X509EcPublicKey ecPublicKey;
+#endif
+} X509SubjectPublicKeyInfo;
+
+
+/**
+ * @brief Basic constraints
+ **/
+
+typedef struct
+{
+   bool_t ca;
+   uint_t pathLenConstraint;
+} X509BasicContraints;
+
+
+/**
+ * @brief X.509 certificate
+ **/
+
+typedef struct
+{
+   const uint8_t *tbsCertificate;
+   size_t tbsCertificateLen;
+   uint8_t version;
+   const uint8_t *serialNumber;
+   size_t serialNumberLen;
+   X509Name issuer;
+   X509Validity validity;
+   X509Name subject;
+   X509SubjectPublicKeyInfo subjectPublicKeyInfo;
+   X509BasicContraints basicConstraints;
+   const uint8_t *signatureAlgo;
+   size_t signatureAlgoLen;
+   const uint8_t *signatureValue;
+   size_t signatureValueLen;
+} X509CertificateInfo;
+
+
+//X.509 related constants
+extern const uint8_t X509_COMMON_NAME_OID[3];
+extern const uint8_t X509_SURNAME_OID[3];
+extern const uint8_t X509_SERIAL_NUMBER_OID[3];
+extern const uint8_t X509_COUNTRY_NAME_OID[3];
+extern const uint8_t X509_LOCALITY_NAME_OID[3];
+extern const uint8_t X509_STATE_OR_PROVINCE_NAME_OID[3];
+extern const uint8_t X509_ORGANIZATION_NAME_OID[3];
+extern const uint8_t X509_ORGANIZATIONAL_UNIT_NAME_OID[3];
+extern const uint8_t X509_TITLE_OID[3];
+extern const uint8_t X509_NAME_OID[3];
+extern const uint8_t X509_GIVEN_NAME_OID[3];
+extern const uint8_t X509_INITIALS_OID[3];
+extern const uint8_t X509_GENERATION_QUALIFIER_OID[3];
+extern const uint8_t X509_DN_QUALIFIER_OID[3];
+extern const uint8_t X509_PSEUDONYM_OID[3];
+
+extern const uint8_t X509_SUBJECT_DIRECTORY_ATTR_OID[3];
+extern const uint8_t X509_SUBJECT_KEY_ID_OID[3];
+extern const uint8_t X509_KEY_USAGE_OID[3];
+extern const uint8_t X509_SUBJECT_ALT_NAME_OID[3];
+extern const uint8_t X509_ISSUER_ALT_NAME_OID[3];
+extern const uint8_t X509_BASIC_CONSTRAINTS_OID[3];
+extern const uint8_t X509_NAME_CONSTRAINTS_OID[3];
+extern const uint8_t X509_CRL_DISTR_POINTS_OID[3];
+extern const uint8_t X509_CERTIFICATE_POLICIES_OID[3];
+extern const uint8_t X509_POLICY_MAPPINGS_OID[3];
+extern const uint8_t X509_AUTHORITY_KEY_ID_OID[3];
+extern const uint8_t X509_POLICY_CONSTRAINTS_OID[3];
+extern const uint8_t X509_EXTENDED_KEY_USAGE_OID[3];
+extern const uint8_t X509_FRESHEST_CRL_OID[3];
+extern const uint8_t X509_INHIBIT_ANY_POLICY_OID[3];
+
+//X.509 related functions
+error_t x509ParseCertificate(const uint8_t *data, size_t length,
+   X509CertificateInfo *certInfo);
+
+error_t x509ParseTbsCertificate(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo);
+
+error_t x509ParseVersion(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo);
+
+error_t x509ParseSerialNumber(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo);
+
+error_t x509ParseSignature(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo);
+
+error_t x509ParseName(const uint8_t *data, size_t length,
+   size_t *totalLength, X509Name *name);
+
+error_t x509ParseValidity(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo);
+
+error_t x509ParseTime(const uint8_t *data, size_t length,
+   size_t *totalLength, DateTime *dateTime);
+
+error_t x509ParseSubjectPublicKeyInfo(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo);
+
+error_t x509ParseAlgorithmIdentifier(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo);
+
+error_t x509ParseRsaPublicKey(const uint8_t *data,
+   size_t length, X509CertificateInfo *certInfo);
+
+error_t x509ParseDsaParameters(const uint8_t *data,
+   size_t length, X509CertificateInfo *certInfo);
+
+error_t x509ParseDsaPublicKey(const uint8_t *data,
+   size_t length, X509CertificateInfo *certInfo);
+
+error_t x509ParseEcParameters(const uint8_t *data,
+   size_t length, X509CertificateInfo *certInfo);
+
+error_t x509ParseEcPublicKey(const uint8_t *data,
+   size_t length, X509CertificateInfo *certInfo);
+
+error_t x509ParseIssuerUniqueId(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo);
+
+error_t x509ParseSubjectUniqueId(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo);
+
+error_t x509ParseExtensions(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo);
+
+error_t x509ParseBasicConstraints(const uint8_t *data,
+   size_t length, X509CertificateInfo *certInfo);
+
+error_t x509ParseSignatureAlgo(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo);
+
+error_t x509ParseSignatureValue(const uint8_t *data, size_t length,
+   size_t *totalLength, X509CertificateInfo *certInfo);
+
+error_t x509ParseInt(const uint8_t *data, size_t length, uint_t *value);
+
+error_t x509ReadRsaPublicKey(const X509CertificateInfo *certInfo, RsaPublicKey *key);
+error_t x509ReadDsaPublicKey(const X509CertificateInfo *certInfo, DsaPublicKey *key);
+
+error_t x509ValidateCertificate(const X509CertificateInfo *certInfo,
+   const X509CertificateInfo *issuerCertInfo);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/yarrow.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,353 @@
+/**
+ * @file yarrow.c
+ * @brief Yarrow PRNG
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "yarrow.h"
+#include "debug.h"
+
+//Check crypto library configuration
+#if (YARROW_SUPPORT == ENABLED)
+
+//Common interface for PRNG algorithms
+const PrngAlgo yarrowPrngAlgo =
+{
+   "Yarrow",
+   sizeof(YarrowContext),
+   (PrngAlgoInit) yarrowInit,
+   (PrngAlgoRelease) yarrowRelease,
+   (PrngAlgoSeed) yarrowSeed,
+   (PrngAlgoAddEntropy) yarrowAddEntropy,
+   (PrngAlgoRead) yarrowRead
+};
+
+
+/**
+ * @brief Initialize PRNG context
+ * @param[in] context Pointer to the PRNG context to initialize
+ * @return Error code
+ **/
+
+error_t yarrowInit(YarrowContext *context)
+{
+   //Clear PRNG state
+   memset(context, 0, sizeof(YarrowContext));
+
+   //Create a mutex to prevent simultaneous access to the PRNG state
+   if(!osCreateMutex(&context->mutex))
+   {
+      //Failed to create mutex
+      return ERROR_OUT_OF_RESOURCES;
+   }
+
+   //Initialize hash contexts
+   sha256Init(&context->fastPool);
+   sha256Init(&context->slowPool);
+
+   //The PRNG is not ready to generate random data
+   context->ready = FALSE;
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Release PRNG context
+ * @param[in] context Pointer to the PRNG context
+ **/
+
+void yarrowRelease(YarrowContext *context)
+{
+   //Release previously allocated resources
+   osDeleteMutex(&context->mutex);
+
+   //Clear PRNG state
+   memset(context, 0, sizeof(YarrowContext));
+}
+
+
+/**
+ * @brief Seed the PRNG state
+ * @param[in] context Pointer to the PRNG context
+ * @param[in] input Pointer to the input data
+ * @param[in] length Length of the input data
+ * @return Error code
+ **/
+
+error_t yarrowSeed(YarrowContext *context, const uint8_t *input, size_t length)
+{
+   //Check parameters
+   if(length < sizeof(context->key))
+      return ERROR_INVALID_PARAMETER;
+
+   //Add entropy to the fast pool
+   sha256Update(&context->fastPool, input, length);
+   //Reseed from the fast pool
+   yarrowFastReseed(context);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Add entropy to the PRNG state
+ * @param[in] context Pointer to the PRNG context
+ * @param[in] source Entropy source identifier
+ * @param[in] input Pointer to the input data
+ * @param[in] length Length of the input data
+ * @param[in] entropy Actual number of bits of entropy
+ * @return Error code
+ **/
+
+error_t yarrowAddEntropy(YarrowContext *context, uint_t source,
+   const uint8_t *input, size_t length, size_t entropy)
+{
+   uint_t i;
+   uint_t k;
+
+   //Check parameters
+   if(source >= YARROW_N)
+      return ERROR_INVALID_PARAMETER;
+
+   //Acquire exclusive access to the PRNG state
+   osAcquireMutex(&context->mutex);
+
+   //Entropy from samples are collected into two pools
+   if(context->currentPool[source] == YARROW_FAST_POOL_ID)
+   {
+      //Each pool contains running hash of all inputs since last reseed
+      sha256Update(&context->fastPool, input, length);
+      //Estimate the amount of entropy we have collected thus far
+      context->fastPoolEntropy[source] += entropy;
+
+      //Reseed when any source estimate reaches 100 bits
+      if(context->fastPoolEntropy[source] >= YARROW_FAST_THRESHOLD)
+         yarrowFastReseed(context);
+
+      //The samples from each source alternate between the two pools
+      context->currentPool[source] = YARROW_SLOW_POOL_ID;
+   }
+   else
+   {
+      //Each pool contains running hash of all inputs since last reseed
+      sha256Update(&context->slowPool, input, length);
+      //Estimate the amount of entropy we have collected thus far
+      context->slowPoolEntropy[source] += entropy;
+
+      //Prevent overflows while adding up the entropy estimate
+      if(context->slowPoolEntropy[source] >= YARROW_SLOW_THRESHOLD)
+         context->slowPoolEntropy[source] = YARROW_SLOW_THRESHOLD;
+
+      //At least two different sources must be over 160 bits in the slow
+      //pool before the slow pool reseeds
+      for(k = 0, i = 0; i < YARROW_N; i++)
+      {
+         //Check whether the current source has hit the threshold
+         if(context->slowPoolEntropy[i] >= YARROW_SLOW_THRESHOLD)
+            k++;
+      }
+
+      //Reseed from the slow pool?
+      if(k >= YARROW_K)
+         yarrowSlowReseed(context);
+
+      //The samples from each source alternate between the two pools
+      context->currentPool[source] = YARROW_FAST_POOL_ID;
+   }
+
+   //Release exclusive access to the PRNG state
+   osReleaseMutex(&context->mutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Read random data
+ * @param[in] context Pointer to the PRNG context
+ * @param[out] output Buffer where to store the output data
+ * @param[in] length Desired length in bytes
+ * @return Error code
+ **/
+
+error_t yarrowRead(YarrowContext *context, uint8_t *output, size_t length)
+{
+   size_t n;
+   uint8_t buffer[AES_BLOCK_SIZE];
+
+   //Make sure that the PRNG has been properly seeded
+   if(!context->ready)
+      return ERROR_PRNG_NOT_READY;
+
+   //Acquire exclusive access to the PRNG state
+   osAcquireMutex(&context->mutex);
+
+   //Generate random data in a block-by-block fashion
+   while(length > 0)
+   {
+      //Number of bytes to process at a time
+      n = MIN(length, AES_BLOCK_SIZE);
+
+      //Generate a random block
+      yarrowGenerateBlock(context, buffer);
+      //Copy data to the output buffer
+      memcpy(output, buffer, n);
+
+      //We keep track of how many blocks we have output
+      context->blockCount++;
+
+      //Next block
+      output += n;
+      length -= n;
+   }
+
+   //Apply generator gate?
+   if(context->blockCount >= YARROW_PG)
+   {
+      //Generate some random bytes
+      yarrowGenerateBlock(context, context->key);
+      //Use them as the new key
+      aesInit(&context->cipherContext, context->key, sizeof(context->key));
+
+      //Reset block counter
+      context->blockCount = 0;
+   }
+
+   //Release exclusive access to the PRNG state
+   osReleaseMutex(&context->mutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Generate a random block of data
+ * @param[in] context Pointer to the PRNG context
+ * @param[out] output Buffer where to store the output block
+ **/
+
+void yarrowGenerateBlock(YarrowContext *context, uint8_t *output)
+{
+   int_t i;
+
+   //Encrypt counter block
+   aesEncryptBlock(&context->cipherContext, context->counter, output);
+
+   //Increment counter value
+   for(i = AES_BLOCK_SIZE - 1; i >= 0; i--)
+   {
+      //Increment the current byte and propagate the carry if necessary
+      if(++(context->counter[i]) != 0)
+         break;
+   }
+}
+
+
+/**
+ * @brief Reseed from the fast pool
+ * @param[in] context Pointer to the PRNG context
+ **/
+
+void yarrowFastReseed(YarrowContext *context)
+{
+   size_t i;
+
+   //Reseeding from the fast pool use the current key and the hash of all
+   //inputs to the fast pool since the last reseed, to generate a new key
+   sha256Update(&context->fastPool, context->key, sizeof(context->key));
+   sha256Final(&context->fastPool, context->key);
+
+   //Set the new key
+   aesInit(&context->cipherContext, context->key, sizeof(context->key));
+
+   //Define the new value of the counter
+   memset(context->counter, 0, sizeof(context->counter));
+   aesEncryptBlock(&context->cipherContext, context->counter, context->counter);
+
+   //Reset the hash context
+   sha256Init(&context->fastPool);
+
+   //The entropy estimates for the fast pool are all reset to zero
+   for(i = 0; i < YARROW_N; i++)
+      context->fastPoolEntropy[i] = 0;
+
+   //The PRNG is ready to generate random data
+   context->ready = TRUE;
+}
+
+
+/**
+ * @brief Reseed from the slow pool
+ * @param[in] context Pointer to the PRNG context
+ **/
+
+void yarrowSlowReseed(YarrowContext *context)
+{
+   size_t i;
+
+   //Compute the hash of all inputs to the fast pool
+   sha256Final(&context->fastPool, NULL);
+
+   //Reseeding from the slow pool use the current key, the hash of all inputs to the
+   //fast pool and the hash of all inputs to the slow pool, to generate a new key
+   sha256Update(&context->slowPool, context->key, sizeof(context->key));
+   sha256Update(&context->slowPool, context->fastPool.digest, SHA256_DIGEST_SIZE);
+   sha256Final(&context->slowPool, context->key);
+
+   //Set the new key
+   aesInit(&context->cipherContext, context->key, sizeof(context->key));
+
+   //Define the new value of the counter
+   memset(context->counter, 0, sizeof(context->counter));
+   aesEncryptBlock(&context->cipherContext, context->counter, context->counter);
+
+   //Reset the hash contexts
+   sha256Init(&context->fastPool);
+   sha256Init(&context->slowPool);
+
+   //The entropy estimates for both pools are reset to zero
+   for(i = 0; i < YARROW_N; i++)
+   {
+      context->fastPoolEntropy[i] = 0;
+      context->slowPoolEntropy[i] = 0;
+   }
+
+   //The PRNG is ready to generate random data
+   context->ready = TRUE;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_crypto/yarrow.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,91 @@
+/**
+ * @file yarrow.h
+ * @brief Yarrow PRNG
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _YARROW_H
+#define _YARROW_H
+
+//Dependencies
+#include "crypto.h"
+#include "aes.h"
+#include "sha256.h"
+
+//Common interface for PRNG algorithms
+#define YARROW_PRNG_ALGO (&yarrowPrngAlgo)
+
+//Pool identifiers
+#define YARROW_FAST_POOL_ID 0
+#define YARROW_SLOW_POOL_ID 1
+
+//Yarrow PRNG parameters
+#define YARROW_N 3
+#define YARROW_K 2
+#define YARROW_PG 10
+#define YARROW_FAST_THRESHOLD 100
+#define YARROW_SLOW_THRESHOLD 160
+
+
+/**
+ * @brief Yarrow PRNG context
+ **/
+
+typedef struct
+{
+   OsMutex mutex;                    //Mutex to prevent simultaneous access to the PRNG state
+   bool_t ready;                     //This flag tells whether the PRNG has been properly seeded
+   uint_t currentPool[YARROW_N];     //Current pool identifier
+   Sha256Context fastPool;           //Fast pool
+   size_t fastPoolEntropy[YARROW_N]; //Entropy estimation (fast pool)
+   Sha256Context slowPool;           //Slow pool
+   size_t slowPoolEntropy[YARROW_N]; //Entropy estimation (slow pool)
+   AesContext cipherContext;         //Cipher context
+   uint8_t key[32];                  //Current key
+   uint8_t counter[16];              //Counter block
+   size_t blockCount;                //Number of blocks that have been generated
+} YarrowContext;
+
+
+//Yarrow related constants
+extern const PrngAlgo yarrowPrngAlgo;
+
+//Yarrow related functions
+error_t yarrowInit(YarrowContext *context);
+void yarrowRelease(YarrowContext *context);
+
+error_t yarrowSeed(YarrowContext *context, const uint8_t *input, size_t length);
+
+error_t yarrowAddEntropy(YarrowContext *context, uint_t source,
+   const uint8_t *input, size_t length, size_t entropy);
+
+error_t yarrowRead(YarrowContext *context, uint8_t *output, size_t length);
+
+void yarrowGenerateBlock(YarrowContext *context, uint8_t *output);
+void yarrowFastReseed(YarrowContext *context);
+void yarrowSlowReseed(YarrowContext *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/ssl_common.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,201 @@
+/**
+ * @file ssl_common.c
+ * @brief Functions common to SSL 3.0 client and server
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TLS_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "crypto.h"
+#include "tls.h"
+#include "ssl_common.h"
+#include "debug.h"
+
+//Check SSL library configuration
+#if (TLS_SUPPORT == ENABLED)
+
+//pad1 pattern
+const uint8_t sslPad1[48] =
+{
+   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+   0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36
+};
+
+//pad2 pattern
+const uint8_t sslPad2[48] =
+{
+   0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+   0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+   0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C
+};
+
+
+/**
+ * @brief Key expansion function (SSL 3.0)
+ * @param[in] secret Pointer to the secret
+ * @param[in] secretLength Length of the secret
+ * @param[in] random Pointer to the random bytes
+ * @param[in] randomLength Length of the random bytes
+ * @param[out] output Pointer to the output
+ * @param[in] outputLength Desired output length
+ * @return Error code
+ **/
+
+error_t sslExpandKey(const uint8_t *secret, size_t secretLength,
+   const uint8_t *random, size_t randomLength, uint8_t *output, size_t outputLength)
+{
+   uint_t i;
+   size_t n;
+   char_t pad[16];
+   Md5Context *md5Context;
+   Sha1Context *sha1Context;
+
+   //Output length cannot exceed 256 bytes
+   if(outputLength > (sizeof(pad) * MD5_DIGEST_SIZE))
+      return ERROR_INVALID_LENGTH;
+
+   //Allocate a memory buffer to hold the MD5 context
+   md5Context = tlsAllocMem(sizeof(Md5Context));
+   //Allocate a memory buffer to hold the SHA-1 context
+   sha1Context = tlsAllocMem(sizeof(Sha1Context));
+
+   //Failed to allocate memory?
+   if(md5Context == NULL || sha1Context == NULL)
+   {
+      //Release previously allocated resources
+      tlsFreeMem(md5Context);
+      tlsFreeMem(sha1Context);
+
+      //Report an error
+      return ERROR_OUT_OF_MEMORY;
+   }
+
+   //Loop until enough output has been generated
+   for(i = 0; outputLength > 0; i++)
+   {
+      //Generate pad
+      memset(pad, 'A' + i, i + 1);
+
+      //Compute SHA(pad + secret + random)
+      sha1Init(sha1Context);
+      sha1Update(sha1Context, pad, i + 1);
+      sha1Update(sha1Context, secret, secretLength);
+      sha1Update(sha1Context, random, randomLength);
+      sha1Final(sha1Context, NULL);
+
+      //Then compute MD5(secret + SHA(pad + secret + random))
+      md5Init(md5Context);
+      md5Update(md5Context, secret, secretLength);
+      md5Update(md5Context, sha1Context->digest, SHA1_DIGEST_SIZE);
+      md5Final(md5Context, NULL);
+
+      //Calculate the number of bytes to copy
+      n = MIN(outputLength, MD5_DIGEST_SIZE);
+      //Copy the resulting hash value
+      memcpy(output, md5Context->digest, n);
+
+      //Advance data pointer
+      output += n;
+      //Decrement byte counter
+      outputLength -= n;
+   }
+
+   //Release previously allocated resources
+   tlsFreeMem(md5Context);
+   tlsFreeMem(sha1Context);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Compute message authentication code (SSL 3.0)
+ * @param[in] context Pointer to the TLS context
+ * @param[in] secret MAC secret
+ * @param[in] seqNum 64-bit sequence number
+ * @param[in] record Pointer to the TLS record
+ * @param[in] data Pointer to the record data
+ * @param[in] length Length of the data
+ * @param[out] mac The computed MAC value
+ * @return Error code
+ **/
+
+error_t sslComputeMac(TlsContext *context, const void *secret, TlsSequenceNumber seqNum,
+   const TlsRecord *record, const uint8_t *data, size_t length, uint8_t *mac)
+{
+   size_t padLength;
+   HashContext *hashContext;
+   const HashAlgo *hash;
+
+   //Hash function that will be used to compute MAC
+   hash = context->hashAlgo;
+   //Point to the hash context
+   hashContext = (HashContext *) context->hmacContext.hashContext;
+
+   //The length of pad1 and pad2 depends on hash algorithm
+   if(hash == MD5_HASH_ALGO)
+   {
+      //48-byte long patterns are used with MD5
+      padLength = 48;
+   }
+   else if(hash == SHA1_HASH_ALGO)
+   {
+      //40-byte long patterns are used with SHA-1
+      padLength = 40;
+   }
+   else
+   {
+      //SSL 3.0 supports only MD5 and SHA-1 hash functions
+      return ERROR_INVALID_PARAMETER;
+   }
+
+   //Compute hash(secret + pad1 + seqNum + type + length + data)
+   hash->init(hashContext);
+   hash->update(hashContext, secret, context->macKeyLen);
+   hash->update(hashContext, sslPad1, padLength);
+   hash->update(hashContext, seqNum, sizeof(TlsSequenceNumber));
+   hash->update(hashContext, &record->type, sizeof(record->type));
+   hash->update(hashContext, (void *) &record->length, sizeof(record->length));
+   hash->update(hashContext, data, length);
+   hash->final(hashContext, mac);
+
+   //Then compute hash(secret + pad2 + hash(secret + pad1 + seqNum + type + length + data))
+   hash->init(hashContext);
+   hash->update(hashContext, secret, context->macKeyLen);
+   hash->update(hashContext, sslPad2, padLength);
+   hash->update(hashContext, mac, hash->digestSize);
+   hash->final(hashContext, mac);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/ssl_common.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,48 @@
+/**
+ * @file ssl_common.h
+ * @brief Functions common to SSL 3.0 client and server
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SSL_COMMON_H
+#define _SSL_COMMON_H
+
+//Dependencies
+#include "crypto.h"
+#include "tls.h"
+
+//SSL 3.0 related constants
+extern const uint8_t sslPad1[48];
+extern const uint8_t sslPad2[48];
+
+//SSL 3.0 related functions
+error_t sslExpandKey(const uint8_t *secret, size_t secretLength,
+   const uint8_t *random, size_t randomLength, uint8_t *output, size_t outputLength);
+
+error_t sslComputeMac(TlsContext *context, const void *secret, TlsSequenceNumber seqNum,
+   const TlsRecord *record, const uint8_t *data, size_t length, uint8_t *mac);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1430 @@
+/**
+ * @file tls.c
+ * @brief TLS (Transport Layer Security)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The TLS protocol provides communications security over the Internet. The
+ * protocol allows client/server applications to communicate in a way that
+ * is designed to prevent eavesdropping, tampering, or message forgery
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TLS_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include <ctype.h>
+#include "tls.h"
+#include "tls_client.h"
+#include "tls_server.h"
+#include "tls_common.h"
+#include "tls_record.h"
+#include "tls_misc.h"
+#include "x509.h"
+#include "pem.h"
+#include "debug.h"
+
+//Check SSL library configuration
+#if (TLS_SUPPORT == ENABLED)
+
+
+/**
+ * @brief TLS context initialization
+ * @return Handle referencing the fully initialized TLS context
+ **/
+
+TlsContext *tlsInit(void)
+{
+   TlsContext *context;
+
+   //Allocate a memory buffer to hold the TLS context
+   context = tlsAllocMem(sizeof(TlsContext));
+
+   //Successful memory allocation?
+   if(context != NULL)
+   {
+      //Clear TLS context
+      memset(context, 0, sizeof(TlsContext));
+
+      //Default state
+      context->state = TLS_STATE_INIT;
+      //Default operation mode
+      context->entity = TLS_CONNECTION_END_CLIENT;
+      //Default TLS version
+      context->version = TLS_MIN_VERSION;
+      //Default client authentication mode
+      context->clientAuthMode = TLS_CLIENT_AUTH_NONE;
+
+#if (TLS_DH_ANON_SUPPORT == ENABLED || TLS_DHE_RSA_SUPPORT == ENABLED || \
+   TLS_DHE_DSS_SUPPORT == ENABLED || TLS_DHE_PSK_SUPPORT == ENABLED)
+      //Initialize Diffie-Hellman context
+      dhInit(&context->dhContext);
+#endif
+
+#if (TLS_ECDH_ANON_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED || \
+   TLS_ECDHE_ECDSA_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+      //Initialize ECDH context
+      ecdhInit(&context->ecdhContext);
+#endif
+
+#if (TLS_RSA_SIGN_SUPPORT == ENABLED || TLS_RSA_SUPPORT == ENABLED || \
+   TLS_DHE_RSA_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED)
+      //Initialize peer's RSA public key
+      rsaInitPublicKey(&context->peerRsaPublicKey);
+#endif
+
+#if (TLS_DSA_SIGN_SUPPORT == ENABLED || TLS_DHE_DSS_SUPPORT == ENABLED)
+      //Initialize peer's DSA public key
+      dsaInitPublicKey(&context->peerDsaPublicKey);
+#endif
+
+#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED || TLS_ECDHE_ECDSA_SUPPORT == ENABLED)
+      //Initialize peer's EC domain parameters
+      ecInitDomainParameters(&context->peerEcParams);
+      //Initialize peer's EC public key
+      ecInit(&context->peerEcPublicKey);
+#endif
+
+      //Set the maximum fragment length for outgoing TLS records
+      context->txRecordMaxLen = TLS_MAX_RECORD_LENGTH;
+
+      //Compute the corresponding buffer size
+      context->txBufferSize = TLS_MAX_RECORD_LENGTH +
+         sizeof(TlsRecord) + TLS_MAX_RECORD_OVERHEAD;
+
+      //Save the maximum fragment length for incoming TLS records
+      context->rxRecordMaxLen = TLS_MAX_RECORD_LENGTH;
+
+      //Compute the corresponding buffer size
+      context->rxBufferSize = TLS_MAX_RECORD_LENGTH +
+         sizeof(TlsRecord) + TLS_MAX_RECORD_OVERHEAD;
+   }
+
+   //Return a pointer to the freshly created TLS context
+   return context;
+}
+
+
+/**
+ * @brief Set send and receive callbacks (I/O abstraction layer)
+ * @param[in] context Pointer to the TLS context
+ * @param[in] handle Handle for I/O operations
+ * @param[in] sendCallback Send callback function
+ * @param[in] receiveCallback Receive callback function
+ * @return Error code
+ **/
+
+error_t tlsSetIoCallbacks(TlsContext *context, TlsIoHandle handle,
+   TlsIoSendCallback sendCallback, TlsIoReceiveCallback receiveCallback)
+{
+   //Check parameters
+   if(context == NULL || sendCallback == NULL || receiveCallback == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save I/O handle
+   context->handle = handle;
+
+   //Save send and receive callback functions
+   context->sendCallback = sendCallback;
+   context->receiveCallback = receiveCallback;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set operation mode (client or server)
+ * @param[in] context Pointer to the TLS context
+ * @param[in] entity Specifies whether this entity is considered a client or a server
+ * @return Error code
+ **/
+
+error_t tlsSetConnectionEnd(TlsContext *context, TlsConnectionEnd entity)
+{
+   //Invalid TLS context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Invalid parameter?
+   if(entity != TLS_CONNECTION_END_CLIENT && entity != TLS_CONNECTION_END_SERVER)
+      return ERROR_INVALID_PARAMETER;
+
+   //Check whether TLS operates as a client or a server
+   context->entity = entity;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set the pseudo-random number generator to be used
+ * @param[in] context Pointer to the TLS context
+ * @param[in] prngAlgo PRNG algorithm
+ * @param[in] prngContext Pointer to the PRNG context
+ * @return Error code
+ **/
+
+error_t tlsSetPrng(TlsContext *context, const PrngAlgo *prngAlgo, void *prngContext)
+{
+   //Invalid TLS context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Invalid parameters?
+   if(prngAlgo == NULL || prngContext == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //PRNG algorithm that will be used to generate random numbers
+   context->prngAlgo = prngAlgo;
+   //PRNG context
+   context->prngContext = prngContext;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set the name of the remote server
+ * @param[in] context Pointer to the TLS context
+ * @param[in] serverName Fully qualified domain name of the server
+ * @return Error code
+ **/
+
+error_t tlsSetServerName(TlsContext *context, const char_t *serverName)
+{
+   size_t i;
+   size_t length;
+
+   //Invalid parameters?
+   if(context == NULL || serverName == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Retrieve the length of the server name
+   length = strlen(serverName);
+
+   //Check whether the server name has already been configured
+   if(context->serverName != NULL)
+   {
+      //Release memory
+      tlsFreeMem(context->serverName);
+      context->serverName = NULL;
+   }
+
+   //Valid server name?
+   if(length > 0)
+   {
+      //Allocate a memory block to hold the hostname
+      context->serverName = tlsAllocMem(length + 1);
+      //Failed to allocate memory?
+      if(context->serverName == NULL)
+         return ERROR_OUT_OF_MEMORY;
+
+      //Convert the hostname into lowercase
+      for(i = 0; i < length; i++)
+         context->serverName[i] = tolower((uint8_t) serverName[i]);
+
+      //Properly terminate the string with a NULL character
+      context->serverName[length] = '\0';
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set session cache
+ * @param[in] context Pointer to the TLS context
+ * @param[in] cache Session cache that will be used to save/resume TLS sessions
+ * @return Error code
+ **/
+
+error_t tlsSetCache(TlsContext *context, TlsCache *cache)
+{
+   //Check parameters
+   if(context == NULL || cache == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //The cache will be used to save/resume TLS sessions
+   context->cache = cache;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set client authentication mode
+ * @param[in] context Pointer to the TLS context
+ * @param[in] mode Client authentication mode
+ * @return Error code
+ **/
+
+error_t tlsSetClientAuthMode(TlsContext *context, TlsClientAuthMode mode)
+{
+   //Invalid TLS context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save client authentication mode
+   context->clientAuthMode = mode;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set TLS buffer size
+ * @param[in] context Pointer to the TLS context
+ * @param[in] txBufferSize TX buffer size
+ * @param[in] rxBufferSize RX buffer size
+ * @return Error code
+ **/
+
+error_t tlsSetBufferSize(TlsContext *context,
+   size_t txBufferSize, size_t rxBufferSize)
+{
+   //Invalid TLS context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Check parameters
+   if(txBufferSize < 512 || rxBufferSize < 512)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save the maximum fragment length for outgoing TLS records
+   context->txRecordMaxLen = txBufferSize;
+
+   //Compute the corresponding buffer size
+   context->txBufferSize = txBufferSize +
+      sizeof(TlsRecord) + TLS_MAX_RECORD_OVERHEAD;
+
+   //Save the maximum fragment length for incoming TLS records
+   context->rxRecordMaxLen = rxBufferSize;
+
+   //Compute the corresponding buffer size
+   context->rxBufferSize = rxBufferSize +
+      sizeof(TlsRecord) + TLS_MAX_RECORD_OVERHEAD;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Specify the list of allowed cipher suites
+ * @param[in] context Pointer to the TLS context
+ * @param[in] cipherSuites Pointer to the cipher suite list
+ * @param[in] length Number of cipher suites in the list
+ * @return Error code
+ **/
+
+error_t tlsSetCipherSuites(TlsContext *context,
+   const uint16_t *cipherSuites, uint_t length)
+{
+   //Invalid TLS context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Check parameters
+   if(cipherSuites == NULL && length != 0)
+      return ERROR_INVALID_PARAMETER;
+
+   //Restrict the cipher suites that can be used
+   context->cipherSuites = cipherSuites;
+   context->numCipherSuites = length;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Import Diffie-Hellman parameters
+ * @param[in] context Pointer to the TLS context
+ * @param[in] params PEM structure that holds Diffie-Hellman parameters
+ * @param[in] length Total length of the DER structure
+ * @return Error code
+ **/
+
+error_t tlsSetDhParameters(TlsContext *context,
+   const char_t *params, size_t length)
+{
+#if (TLS_DH_ANON_SUPPORT == ENABLED || TLS_DHE_RSA_SUPPORT == ENABLED || \
+   TLS_DHE_DSS_SUPPORT == ENABLED || TLS_DHE_PSK_SUPPORT == ENABLED)
+   //Invalid TLS context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Check parameters
+   if(params == NULL && length != 0)
+      return ERROR_INVALID_PARAMETER;
+
+   //Decode the PEM structure that holds Diffie-Hellman parameters
+   return pemReadDhParameters(params, length, &context->dhContext.params);
+#else
+   //Diffie-Hellman is not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Set the list of supported ALPN protocols
+ * @param[in] context Pointer to the TLS context
+ * @param[in] protocolList Comma-delimited list of supported protocols
+ * @return Error code
+ **/
+
+error_t tlsSetAlpnProtocolList(TlsContext *context, const char_t *protocolList)
+{
+#if (TLS_ALPN_SUPPORT == ENABLED)
+   size_t length;
+
+   //Invalid parameters?
+   if(context == NULL || protocolList == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Retrieve the length of the list
+   length = strlen(protocolList);
+
+   //Check whether the list of supported protocols has already been configured
+   if(context->protocolList != NULL)
+   {
+      //Release memory
+      tlsFreeMem(context->protocolList);
+      context->protocolList = NULL;
+   }
+
+   //Check whether the list of protocols is valid
+   if(length > 0)
+   {
+      //Allocate a memory block to hold the list
+      context->protocolList = tlsAllocMem(length + 1);
+      //Failed to allocate memory?
+      if(context->protocolList == NULL)
+         return ERROR_OUT_OF_MEMORY;
+
+      //Save the list of supported protocols
+      strcpy(context->protocolList, protocolList);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //ALPN is not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Get the name of the negotiated ALPN protocol
+ * @param[in] context Pointer to the TLS context
+ * @return Pointer to the protocol name
+ **/
+
+const char_t *tlsGetAlpnProtocol(TlsContext *context)
+{
+   //Not implemented
+   return NULL;
+}
+
+
+/**
+ * @brief Set the pre-shared key to be used
+ * @param[in] context Pointer to the TLS context
+ * @param[in] psk Pointer to the pre-shared key
+ * @param[in] pskLength Length of the pre-shared key, in bytes
+ * @return Error code
+ **/
+
+error_t tlsSetPsk(TlsContext *context, const uint8_t *psk, size_t pskLength)
+{
+#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
+   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   //Invalid TLS context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Check parameters
+   if(psk == NULL && pskLength != 0)
+      return ERROR_INVALID_PARAMETER;
+
+   //Check whether the pre-shared key has already been configured
+   if(context->psk != NULL)
+   {
+      //Release memory
+      memset(context->psk, 0, context->pskLen);
+      tlsFreeMem(context->psk);
+      //Re-initialize length
+      context->pskLen = 0;
+   }
+
+   //Valid PSK?
+   if(pskLength > 0)
+   {
+      //Allocate a memory block to hold the pre-shared key
+      context->psk = tlsAllocMem(pskLength);
+      //Failed to allocate memory?
+      if(context->psk == NULL)
+         return ERROR_OUT_OF_MEMORY;
+
+      //Save the pre-shared key
+      memcpy(context->psk, psk, pskLength);
+      //Save the length of the key
+      context->pskLen = pskLength;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //PSK key exchange is not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Set the PSK identity to be used by the client
+ * @param[in] context Pointer to the TLS context
+ * @param[in] pskIdentity NULL-terminated string that contains the PSK identity
+ * @return Error code
+ **/
+
+error_t tlsSetPskIdentity(TlsContext *context, const char_t *pskIdentity)
+{
+#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
+   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   size_t length;
+
+   //Invalid parameters?
+   if(context == NULL || pskIdentity == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Retrieve the length of the PSK identity
+   length = strlen(pskIdentity);
+
+   //Check whether the PSK identity has already been configured
+   if(context->pskIdentity != NULL)
+   {
+      //Release memory
+      tlsFreeMem(context->pskIdentity);
+      context->pskIdentity = NULL;
+   }
+
+   //Valid PSK identity?
+   if(length > 0)
+   {
+      //Allocate a memory block to hold the PSK identity
+      context->pskIdentity = tlsAllocMem(length + 1);
+      //Failed to allocate memory?
+      if(context->pskIdentity == NULL)
+         return ERROR_OUT_OF_MEMORY;
+
+      //Save the PSK identity
+      strcpy(context->pskIdentity, pskIdentity);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //PSK key exchange is not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Set the PSK identity hint to be used by the server
+ * @param[in] context Pointer to the TLS context
+ * @param[in] pskIdentityHint NULL-terminated string that contains the PSK identity hint
+ * @return Error code
+ **/
+
+error_t tlsSetPskIdentityHint(TlsContext *context, const char_t *pskIdentityHint)
+{
+#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
+   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   size_t length;
+
+   //Invalid parameters?
+   if(context == NULL || pskIdentityHint == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Retrieve the length of the PSK identity hint
+   length = strlen(pskIdentityHint);
+
+   //Check whether the PSK identity hint has already been configured
+   if(context->pskIdentityHint != NULL)
+   {
+      //Release memory
+      tlsFreeMem(context->pskIdentityHint);
+      context->pskIdentityHint = NULL;
+   }
+
+   //Valid PSK identity hint?
+   if(length > 0)
+   {
+      //Allocate a memory block to hold the PSK identity hint
+      context->pskIdentityHint = tlsAllocMem(length + 1);
+      //Failed to allocate memory?
+      if(context->pskIdentityHint == NULL)
+         return ERROR_OUT_OF_MEMORY;
+
+      //Save the PSK identity hint
+      strcpy(context->pskIdentityHint, pskIdentityHint);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //PSK key exchange is not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Register the PSK callback function
+ * @param[in] context Pointer to the TLS context
+ * @param[in] pskCallback PSK callback function
+ * @return Error code
+ **/
+
+error_t tlsSetPskCallback(TlsContext *context, TlsPskCallback pskCallback)
+{
+#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
+   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   //Invalid parameters?
+   if(context == NULL || pskCallback == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save the PSK callback function
+   context->pskCallback = pskCallback;
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //PSK key exchange is not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Import a trusted CA list
+ * @param[in] context Pointer to the TLS context
+ * @param[in] trustedCaList List of trusted CA (PEM format)
+ * @param[in] length Total length of the list
+ * @return Error code
+ **/
+
+error_t tlsSetTrustedCaList(TlsContext *context,
+   const char_t *trustedCaList, size_t length)
+{
+   //Invalid TLS context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Check parameters
+   if(trustedCaList == NULL && length != 0)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save the certificate chain
+   context->trustedCaList = trustedCaList;
+   context->trustedCaListLen = length;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Import a certificate and the corresponding private key
+ * @param[in] context Pointer to the TLS context
+ * @param[in] certChain Certificate chain (PEM format)
+ * @param[in] certChainLength Total length of the certificate chain
+ * @param[in] privateKey Private key (PEM format)
+ * @param[in] privateKeyLength Total length of the private key
+ * @return Error code
+ **/
+
+error_t tlsAddCertificate(TlsContext *context, const char_t *certChain,
+   size_t certChainLength, const char_t *privateKey, size_t privateKeyLength)
+{
+   error_t error;
+   const char_t *p;
+   size_t n;
+   uint8_t *derCert;
+   size_t derCertSize;
+   size_t derCertLength;
+   X509CertificateInfo *certInfo;
+   TlsCertificateType certType;
+   TlsSignatureAlgo certSignAlgo;
+   TlsHashAlgo certHashAlgo;
+   TlsEcNamedCurve namedCurve;
+
+   //Invalid TLS context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Check parameters
+   if(certChain == NULL || certChainLength == 0)
+      return ERROR_INVALID_PARAMETER;
+   if(privateKey == NULL || privateKeyLength == 0)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure there is enough room to add the certificate
+   if(context->numCerts >= TLS_MAX_CERTIFICATES)
+      return ERROR_OUT_OF_RESOURCES;
+
+   //Allocate a memory buffer to store X.509 certificate info
+   certInfo = tlsAllocMem(sizeof(X509CertificateInfo));
+   //Failed to allocate memory?
+   if(certInfo == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the certificate chain
+   p = certChain;
+   n = certChainLength;
+
+   //DER encoded certificate
+   derCert = NULL;
+   derCertSize = 0;
+   derCertLength = 0;
+
+   //Start of exception handling block
+   do
+   {
+      //Decode end entity certificate
+      error = pemReadCertificate(&p, &n, &derCert, &derCertSize, &derCertLength);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Parse X.509 certificate
+      error = x509ParseCertificate(derCert, derCertLength, certInfo);
+      //Failed to parse the X.509 certificate?
+      if(error)
+         break;
+
+      //Retrieve the signature algorithm that has been used to sign the certificate
+      error = tlsGetCertificateType(certInfo, &certType,
+         &certSignAlgo, &certHashAlgo, &namedCurve);
+      //The specified signature algorithm is not supported?
+      if(error)
+         break;
+
+      //End of exception handling block
+   } while(0);
+
+   //Check whether the certificate is acceptable
+   if(!error)
+   {
+      //Point to the structure that describes the certificate
+      TlsCertDesc *cert = &context->certs[context->numCerts];
+
+      //Save the certificate chain and the corresponding private key
+      cert->certChain = certChain;
+      cert->certChainLength = certChainLength;
+      cert->privateKey = privateKey;
+      cert->privateKeyLength = privateKeyLength;
+      cert->type = certType;
+      cert->signAlgo = certSignAlgo;
+      cert->hashAlgo = certHashAlgo;
+      cert->namedCurve = namedCurve;
+
+      //Update the number of certificates
+      context->numCerts++;
+   }
+
+   //Release previously allocated memory
+   tlsFreeMem(derCert);
+   tlsFreeMem(certInfo);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Initiate the TLS handshake
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsConnect(TlsContext *context)
+{
+   error_t error;
+
+   //Invalid TLS context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Ensure the I/O callback functions are properly registered
+   if(context->sendCallback == NULL || context->receiveCallback == NULL)
+      return ERROR_NOT_CONFIGURED;
+
+   //Verify that the PRNG is properly set
+   if(context->prngAlgo == NULL || context->prngContext == NULL)
+      return ERROR_NOT_CONFIGURED;
+
+   //Check current state
+   if(context->state == TLS_STATE_INIT)
+   {
+      //Allocate send buffer if necessary
+      if(context->txBuffer == NULL)
+      {
+         //Allocate TX buffer
+         context->txBuffer = tlsAllocMem(context->txBufferSize);
+
+         //Failed to allocate memory?
+         if(context->txBuffer == NULL)
+            return ERROR_OUT_OF_MEMORY;
+
+         //Clear TX buffer
+         memset(context->txBuffer, 0, context->txBufferSize);
+      }
+
+      //Allocate receive buffer if necessary
+      if(context->rxBuffer == NULL)
+      {
+         //Allocate RX buffer
+         context->rxBuffer = tlsAllocMem(context->rxBufferSize);
+
+         //Failed to allocate memory?
+         if(context->rxBuffer == NULL)
+         {
+            //Clean up side effects
+            tlsFreeMem(context->txBuffer);
+            context->txBuffer = NULL;
+            //Report an error
+            return ERROR_OUT_OF_MEMORY;
+         }
+
+         //Clear RX buffer
+         memset(context->rxBuffer, 0, context->rxBufferSize);
+      }
+   }
+
+   //Perform TLS handshake
+   error = tlsHandshake(context);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send application data to the remote host using TLS
+ * @param[in] context Pointer to the TLS context
+ * @param[in] data Pointer to a buffer containing the data to be transmitted
+ * @param[in] length Number of bytes to be transmitted
+ * @param[out] written Actual number of bytes written (optional parameter)
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t tlsWrite(TlsContext *context, const void *data,
+   size_t length, size_t *written, uint_t flags)
+{
+   error_t error;
+   size_t n;
+   size_t totalLength;
+
+   //Invalid TLS context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Check parameters
+   if(data == NULL && length != 0)
+      return ERROR_INVALID_PARAMETER;
+
+   //Ensure the I/O callback functions are properly registered
+   if(context->sendCallback == NULL || context->receiveCallback == NULL)
+      return ERROR_NOT_CONFIGURED;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Actual number of bytes written
+   totalLength = 0;
+
+   //Send as much data as possible
+   while(totalLength < length)
+   {
+      //Check current state
+      if(context->state == TLS_STATE_APPLICATION_DATA)
+      {
+         //Calculate the number of bytes to write at a time
+         n = MIN(length - totalLength, context->txRecordMaxLen);
+         //The record length must not exceed 16384 bytes
+         n = MIN(n, TLS_MAX_RECORD_LENGTH);
+
+         //Send application data
+         error = tlsWriteProtocolData(context, data, n, TLS_TYPE_APPLICATION_DATA);
+
+         //Check status code
+         if(!error)
+         {
+            //Advance data pointer
+            data = (uint8_t *) data + n;
+            //Update byte counter
+            totalLength += n;
+         }
+         else
+         {
+            //Send an alert message to the peer, if applicable
+            tlsProcessError(context, error);
+         }
+      }
+      else
+      {
+         //The connection has not yet been established
+         error = ERROR_NOT_CONNECTED;
+      }
+
+      //Any error to report?
+      if(error)
+         break;
+   }
+
+   //Total number of data that have been written
+   if(written != NULL)
+      *written = totalLength;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Receive application data from a the remote host using TLS
+ * @param[in] context Pointer to the TLS context
+ * @param[out] data Buffer into which received data will be placed
+ * @param[in] size Maximum number of bytes that can be received
+ * @param[out] received Number of bytes that have been received
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t tlsRead(TlsContext *context, void *data,
+   size_t size, size_t *received, uint_t flags)
+{
+   error_t error;
+   size_t i;
+   size_t n;
+   uint8_t *p;
+   TlsContentType contentType;
+
+   //Invalid TLS context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Check parameters
+   if(data == NULL && received == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Ensure the I/O callback functions are properly registered
+   if(context->sendCallback == NULL || context->receiveCallback == NULL)
+      return ERROR_NOT_CONFIGURED;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //No data has been read yet
+   *received = 0;
+
+   //Read as much data as possible
+   while(*received < size)
+   {
+      //Check current state
+      if(context->state == TLS_STATE_APPLICATION_DATA)
+      {
+         //The TLS record layer receives uninterpreted data from higher layers
+         error = tlsReadProtocolData(context, (void **) &p, &n, &contentType);
+
+         //Check status code
+         if(!error)
+         {
+            //Application data received?
+            if(contentType == TLS_TYPE_APPLICATION_DATA)
+            {
+               //Limit the number of bytes to read at a time
+               n = MIN(n, size - *received);
+
+               //The TLS_FLAG_BREAK_CHAR flag causes the function to stop reading
+               //data as soon as the specified break character is encountered
+               if(flags & TLS_FLAG_BREAK_CHAR)
+               {
+                  //Retrieve the break character code
+                  char_t c = LSB(flags);
+
+                  //Search for the specified break character
+                  for(i = 0; i < n && p[i] != c; i++);
+                  //Adjust the number of data to read
+                  n = MIN(n, i + 1);
+
+                  //Copy data to user buffer
+                  memcpy(data, p, n);
+                  //Total number of data that have been read
+                  *received += n;
+
+                  //Advance data pointer
+                  context->rxBufferPos += n;
+                  //Number of bytes still pending in the receive buffer
+                  context->rxBufferLen -= n;
+
+                  //Check whether a break character has been found
+                  if(n > 0 && p[n - 1] == c)
+                     break;
+               }
+               else
+               {
+                  //Copy data to user buffer
+                  memcpy(data, p, n);
+                  //Total number of data that have been read
+                  *received += n;
+
+                  //Advance data pointer
+                  context->rxBufferPos += n;
+                  //Number of bytes still pending in the receive buffer
+                  context->rxBufferLen -= n;
+
+                  //The TLS_FLAG_WAIT_ALL flag causes the function to return
+                  //only when the requested number of bytes have been read
+                  if(!(flags & TLS_FLAG_WAIT_ALL))
+                     break;
+               }
+
+               //Advance data pointer
+               data = (uint8_t *) data + n;
+            }
+            //Alert message received?
+            else if(contentType == TLS_TYPE_ALERT)
+            {
+               //Parse Alert message
+               error = tlsParseAlert(context, (TlsAlert *) p, n);
+
+               //Advance data pointer
+               context->rxBufferPos += n;
+               //Number of bytes still pending in the receive buffer
+               context->rxBufferLen -= n;
+            }
+            //An inappropriate message was received?
+            else
+            {
+               //Report an error
+               error = ERROR_UNEXPECTED_MESSAGE;
+            }
+         }
+
+         //Any error to report?
+         if(error)
+         {
+            //Send an alert message to the peer, if applicable
+            tlsProcessError(context, error);
+         }
+      }
+      else if(context->state == TLS_STATE_CLOSING ||
+         context->state == TLS_STATE_CLOSED)
+      {
+         //Check whether a fatal alert message has been sent or received
+         if(context->fatalAlertSent || context->fatalAlertReceived)
+         {
+            //Alert messages with a level of fatal result in the immediate
+            //termination of the connection
+            error = ERROR_FAILURE;
+         }
+         else
+         {
+            //The user must be satisfied with data already on hand
+            if(*received > 0)
+            {
+               //Some data are pending in the receive buffer
+               error = NO_ERROR;
+               break;
+            }
+            else
+            {
+               //The receive buffer is empty
+               error = ERROR_END_OF_STREAM;
+            }
+         }
+      }
+      else
+      {
+         //The connection has not yet been established
+         error = ERROR_NOT_CONNECTED;
+      }
+
+      //Any error to report?
+      if(error)
+         break;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Gracefully close TLS session
+ * @param[in] context Pointer to the TLS context
+ **/
+
+error_t tlsShutdown(TlsContext *context)
+{
+   //Either party may initiate a close by sending a close_notify alert
+   return tlsShutdownEx(context, FALSE);
+}
+
+
+/**
+ * @brief Gracefully close TLS session
+ * @param[in] context Pointer to the TLS context
+ * @param[in] waitForCloseNotify Wait for the close notify alert from the peer
+ **/
+
+error_t tlsShutdownEx(TlsContext *context, bool_t waitForCloseNotify)
+{
+   error_t error;
+   size_t n;
+   uint8_t *p;
+   TlsContentType contentType;
+
+   //Invalid TLS context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Ensure the I/O callback functions are properly registered
+   if(context->sendCallback == NULL || context->receiveCallback == NULL)
+      return ERROR_NOT_CONFIGURED;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Wait for the TLS session to be closed
+   while(context->state != TLS_STATE_CLOSED)
+   {
+      //Check current state
+      if(context->state == TLS_STATE_APPLICATION_DATA)
+      {
+         //Flush send buffer
+         error = tlsWriteProtocolData(context, NULL, 0, TLS_TYPE_NONE);
+
+         //Check status code
+         if(!error)
+         {
+            //Either party may initiate a close by sending a close_notify alert
+            context->state = TLS_STATE_CLOSING;
+         }
+      }
+      if(context->state == TLS_STATE_CLOSING)
+      {
+         //Flush send buffer
+         error = tlsWriteProtocolData(context, NULL, 0, TLS_TYPE_NONE);
+
+         //Check status code
+         if(!error)
+         {
+            //Unless some other fatal alert has been transmitted, each party
+            //is required to send a close_notify alert before closing the
+            //write side of the connection
+            if(context->fatalAlertSent || context->fatalAlertReceived)
+            {
+               //Close the connection immediately
+               context->state = TLS_STATE_CLOSED;
+            }
+            else if(!context->closeNotifySent)
+            {
+               //Notifies the recipient that the sender will not send any
+               //more messages on this connection
+               error = tlsSendAlert(context, TLS_ALERT_LEVEL_WARNING,
+                  TLS_ALERT_CLOSE_NOTIFY);
+            }
+            else if(!context->closeNotifyReceived && waitForCloseNotify)
+            {
+               //Wait for the responding close_notify alert
+               error = tlsReadProtocolData(context, (void **) &p, &n, &contentType);
+
+               //Check status code
+               if(!error)
+               {
+                  //Application data received?
+                  if(contentType == TLS_TYPE_APPLICATION_DATA)
+                  {
+                     //Advance data pointer
+                     context->rxBufferPos += n;
+                     //Number of bytes still pending in the receive buffer
+                     context->rxBufferLen -= n;
+                  }
+                  //Alert message received?
+                  else if(contentType == TLS_TYPE_ALERT)
+                  {
+                     //Parse Alert message
+                     error = tlsParseAlert(context, (TlsAlert *) p, n);
+
+                     //Advance data pointer
+                     context->rxBufferPos += n;
+                     //Number of bytes still pending in the receive buffer
+                     context->rxBufferLen -= n;
+                  }
+                  //An inappropriate message was received?
+                  else
+                  {
+                     //Report an error
+                     error = ERROR_UNEXPECTED_MESSAGE;
+                  }
+               }
+            }
+            else
+            {
+               //The connection is closed
+               context->state = TLS_STATE_CLOSED;
+            }
+         }
+      }
+      else
+      {
+         //Report an error
+         error = ERROR_NOT_CONNECTED;
+      }
+
+      //Any error to report?
+      if(error)
+         break;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Release TLS context
+ * @param[in] context Pointer to the TLS context
+ **/
+
+void tlsFree(TlsContext *context)
+{
+   //Valid TLS context?
+   if(context != NULL)
+   {
+      //Release server name
+      if(context->serverName != NULL)
+         tlsFreeMem(context->serverName);
+
+#if (TLS_ALPN_SUPPORT == ENABLED)
+      //Release the list of supported protocols
+      if(context->protocolList != NULL)
+         tlsFreeMem(context->protocolList);
+#endif
+
+#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
+   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+      //Release the pre-shared key
+      if(context->psk != NULL)
+      {
+         memset(context->psk, 0, context->pskLen);
+         tlsFreeMem(context->psk);
+      }
+
+      //Release the PSK identity
+      if(context->pskIdentity != NULL)
+         tlsFreeMem(context->pskIdentity);
+
+      //Release the PSK identity hint
+      if(context->pskIdentityHint != NULL)
+         tlsFreeMem(context->pskIdentityHint);
+#endif
+
+#if (TLS_DH_ANON_SUPPORT == ENABLED || TLS_DHE_RSA_SUPPORT == ENABLED || \
+   TLS_DHE_DSS_SUPPORT == ENABLED || TLS_DHE_PSK_SUPPORT == ENABLED)
+      //Release Diffie-Hellman context
+      dhFree(&context->dhContext);
+#endif
+
+#if (TLS_ECDH_ANON_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED || \
+   TLS_ECDHE_ECDSA_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+      //Release ECDH context
+      ecdhFree(&context->ecdhContext);
+#endif
+
+#if (TLS_RSA_SIGN_SUPPORT == ENABLED || TLS_RSA_SUPPORT == ENABLED || \
+   TLS_DHE_RSA_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED)
+      //Release peer's RSA public key
+      rsaFreePublicKey(&context->peerRsaPublicKey);
+#endif
+
+#if (TLS_DSA_SIGN_SUPPORT == ENABLED || TLS_DHE_DSS_SUPPORT == ENABLED)
+      //Release peer's DSA public key
+      dsaFreePublicKey(&context->peerDsaPublicKey);
+#endif
+
+#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED || TLS_ECDHE_ECDSA_SUPPORT == ENABLED)
+      //Release peer's EC domain parameters
+      ecFreeDomainParameters(&context->peerEcParams);
+      //Release peer's EC public key
+      ecFree(&context->peerEcPublicKey);
+#endif
+
+      //Release send buffer
+      if(context->txBuffer != NULL)
+      {
+         memset(context->txBuffer, 0, context->txBufferSize);
+         tlsFreeMem(context->txBuffer);
+      }
+
+      //Release receive buffer
+      if(context->rxBuffer != NULL)
+      {
+         memset(context->rxBuffer, 0, context->rxBufferSize);
+         tlsFreeMem(context->rxBuffer);
+      }
+
+      //Release the MD5 context used to compute verify data
+      if(context->handshakeMd5Context != NULL)
+      {
+         memset(context->handshakeMd5Context, 0, sizeof(Md5Context));
+         tlsFreeMem(context->handshakeMd5Context);
+      }
+
+      //Release the SHA-1 context used to compute verify data
+      if(context->handshakeSha1Context != NULL)
+      {
+         memset(context->handshakeSha1Context, 0, sizeof(Sha1Context));
+         tlsFreeMem(context->handshakeSha1Context);
+      }
+
+      //Release the hash context used to compute verify data (TLS 1.2)
+      if(context->handshakeHashContext != NULL)
+      {
+         memset(context->handshakeHashContext, 0, context->prfHashAlgo->contextSize);
+         tlsFreeMem(context->handshakeHashContext);
+      }
+
+      //Release the encryption context (TX path)
+      if(context->writeCipherContext != NULL)
+      {
+         memset(context->writeCipherContext, 0, context->cipherAlgo->contextSize);
+         tlsFreeMem(context->writeCipherContext);
+      }
+
+      //Release the encryption context (RX path)
+      if(context->readCipherContext != NULL)
+      {
+         memset(context->readCipherContext, 0, context->cipherAlgo->contextSize);
+         tlsFreeMem(context->readCipherContext);
+      }
+
+#if (TLS_GCM_CIPHER_SUPPORT == ENABLED)
+      //Release the GCM context (TX path)
+      if(context->writeGcmContext != NULL)
+      {
+         memset(context->writeGcmContext, 0, sizeof(GcmContext));
+         tlsFreeMem(context->writeGcmContext);
+      }
+
+      //Release the GCM context (RX path)
+      if(context->readGcmContext != NULL)
+      {
+         memset(context->readGcmContext, 0, sizeof(GcmContext));
+         tlsFreeMem(context->readGcmContext);
+      }
+#endif
+
+      //Clear the TLS context before freeing memory
+      memset(context, 0, sizeof(TlsContext));
+      tlsFreeMem(context);
+   }
+}
+
+
+/**
+ * @brief Save TLS session
+ * @param[in] context Pointer to the TLS context
+ * @param[out] session Buffer where to store the current session parameters
+ * @return Error code
+ **/
+
+error_t tlsSaveSession(const TlsContext *context, TlsSession *session)
+{
+   //Check parameters
+   if(context == NULL || session == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Invalid session parameters?
+   if(!context->sessionIdLen || !context->cipherSuite)
+      return ERROR_FAILURE;
+
+   //Save session identifier
+   memcpy(session->id, context->sessionId, context->sessionIdLen);
+   session->idLength = context->sessionIdLen;
+
+   //Get current time
+   session->timestamp = osGetSystemTime();
+
+   //Negotiated cipher suite and compression method
+   session->cipherSuite = context->cipherSuite;
+   session->compressionMethod = context->compressionMethod;
+
+   //Save master secret
+   memcpy(session->masterSecret, context->masterSecret, 48);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Restore TLS session
+ * @param[in] context Pointer to the TLS context
+ * @param[in] session Pointer to the session to be restored
+ * @return Error code
+ **/
+
+error_t tlsRestoreSession(TlsContext *context, const TlsSession *session)
+{
+   //Check parameters
+   if(context == NULL || session == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Restore session identifier
+   memcpy(context->sessionId, session->id, session->idLength);
+   context->sessionIdLen = session->idLength;
+
+   //Negotiated cipher suite and compression method
+   context->cipherSuite = session->cipherSuite;
+   context->compressionMethod = session->compressionMethod;
+
+   //Restore master secret
+   memcpy(context->masterSecret, session->masterSecret, 48);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1600 @@
+/**
+ * @file tls.h
+ * @brief TLS (Transport Layer Security)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TLS_H
+#define _TLS_H
+
+//Dependencies
+#include "os_port.h"
+#include "crypto.h"
+#include "tls_config.h"
+#include "hmac.h"
+#include "rsa.h"
+#include "dsa.h"
+#include "ecdsa.h"
+#include "dh.h"
+#include "ecdh.h"
+#include "cipher_mode_gcm.h"
+
+//TLS version numbers
+#define SSL_VERSION_3_0 0x0300
+#define TLS_VERSION_1_0 0x0301
+#define TLS_VERSION_1_1 0x0302
+#define TLS_VERSION_1_2 0x0303
+
+//Enable SSL/TLS support
+#ifndef TLS_SUPPORT
+   #define TLS_SUPPORT ENABLED
+#elif (TLS_SUPPORT != ENABLED && TLS_SUPPORT != DISABLED)
+   #error TLS_SUPPORT parameter is not valid
+#endif
+
+//Client mode of operation
+#ifndef TLS_CLIENT_SUPPORT
+   #define TLS_CLIENT_SUPPORT ENABLED
+#elif (TLS_CLIENT_SUPPORT != ENABLED && TLS_CLIENT_SUPPORT != DISABLED)
+   #error TLS_CLIENT_SUPPORT parameter is not valid
+#endif
+
+//Server mode of operation
+#ifndef TLS_SERVER_SUPPORT
+   #define TLS_SERVER_SUPPORT ENABLED
+#elif (TLS_SERVER_SUPPORT != ENABLED && TLS_SERVER_SUPPORT != DISABLED)
+   #error TLS_SERVER_SUPPORT parameter is not valid
+#endif
+
+//Minimum version that can be negotiated
+#ifndef TLS_MIN_VERSION
+   #define TLS_MIN_VERSION TLS_VERSION_1_0
+#elif (TLS_MIN_VERSION < SSL_VERSION_3_0)
+   #error TLS_MIN_VERSION parameter is not valid
+#endif
+
+//Maximum version that can be negotiated
+#ifndef TLS_MAX_VERSION
+   #define TLS_MAX_VERSION TLS_VERSION_1_2
+#elif (TLS_MAX_VERSION > TLS_VERSION_1_2 || TLS_MAX_VERSION < TLS_MIN_VERSION)
+   #error TLS_MAX_VERSION parameter is not valid
+#endif
+
+//Session resumption mechanism
+#ifndef TLS_SESSION_RESUME_SUPPORT
+   #define TLS_SESSION_RESUME_SUPPORT ENABLED
+#elif (TLS_SESSION_RESUME_SUPPORT != ENABLED && TLS_SESSION_RESUME_SUPPORT != DISABLED)
+   #error TLS_SESSION_RESUME_SUPPORT parameter is not valid
+#endif
+
+//Lifetime of session cache entries
+#ifndef TLS_SESSION_CACHE_LIFETIME
+   #define TLS_SESSION_CACHE_LIFETIME 3600000
+#elif (TLS_SESSION_CACHE_LIFETIME < 1000)
+   #error TLS_SESSION_CACHE_LIFETIME parameter is not valid
+#endif
+
+//SNI (Server Name Indication) extension
+#ifndef TLS_SNI_SUPPORT
+   #define TLS_SNI_SUPPORT ENABLED
+#elif (TLS_SNI_SUPPORT != ENABLED && TLS_SNI_SUPPORT != DISABLED)
+   #error TLS_SNI_SUPPORT parameter is not valid
+#endif
+
+//ALPN (Application-Layer Protocol Negotiation) extension
+#ifndef TLS_ALPN_SUPPORT
+   #define TLS_ALPN_SUPPORT DISABLED
+#elif (TLS_ALPN_SUPPORT != ENABLED && TLS_ALPN_SUPPORT != DISABLED)
+   #error TLS_ALPN_SUPPORT parameter is not valid
+#endif
+
+//Maximum number of certificates the end entity can load
+#ifndef TLS_MAX_CERTIFICATES
+   #define TLS_MAX_CERTIFICATES 3
+#elif (TLS_MAX_CERTIFICATES < 1)
+   #error TLS_MAX_CERTIFICATES parameter is not valid
+#endif
+
+//RSA key exchange support
+#ifndef TLS_RSA_SUPPORT
+   #define TLS_RSA_SUPPORT ENABLED
+#elif (TLS_RSA_SUPPORT != ENABLED && TLS_RSA_SUPPORT != DISABLED)
+   #error TLS_RSA_SUPPORT parameter is not valid
+#endif
+
+//DHE_RSA key exchange support
+#ifndef TLS_DHE_RSA_SUPPORT
+   #define TLS_DHE_RSA_SUPPORT ENABLED
+#elif (TLS_DHE_RSA_SUPPORT != ENABLED && TLS_DHE_RSA_SUPPORT != DISABLED)
+   #error TLS_DHE_RSA_SUPPORT parameter is not valid
+#endif
+
+//DHE_DSS key exchange support
+#ifndef TLS_DHE_DSS_SUPPORT
+   #define TLS_DHE_DSS_SUPPORT ENABLED
+#elif (TLS_DHE_DSS_SUPPORT != ENABLED && TLS_DHE_DSS_SUPPORT != DISABLED)
+   #error TLS_DHE_DSS_SUPPORT parameter is not valid
+#endif
+
+//DH_anon key exchange support
+#ifndef TLS_DH_ANON_SUPPORT
+   #define TLS_DH_ANON_SUPPORT DISABLED
+#elif (TLS_DH_ANON_SUPPORT != ENABLED && TLS_DH_ANON_SUPPORT != DISABLED)
+   #error TLS_DH_ANON_SUPPORT parameter is not valid
+#endif
+
+//ECDHE_RSA key exchange support
+#ifndef TLS_ECDHE_RSA_SUPPORT
+   #define TLS_ECDHE_RSA_SUPPORT ENABLED
+#elif (TLS_ECDHE_RSA_SUPPORT != ENABLED && TLS_ECDHE_RSA_SUPPORT != DISABLED)
+   #error TLS_ECDHE_RSA_SUPPORT parameter is not valid
+#endif
+
+//ECDHE_ECDSA key exchange support
+#ifndef TLS_ECDHE_ECDSA_SUPPORT
+   #define TLS_ECDHE_ECDSA_SUPPORT ENABLED
+#elif (TLS_ECDHE_ECDSA_SUPPORT != ENABLED && TLS_ECDHE_ECDSA_SUPPORT != DISABLED)
+   #error TLS_ECDHE_ECDSA_SUPPORT parameter is not valid
+#endif
+
+//ECDH_anon key exchange support
+#ifndef TLS_ECDH_ANON_SUPPORT
+   #define TLS_ECDH_ANON_SUPPORT DISABLED
+#elif (TLS_ECDH_ANON_SUPPORT != ENABLED && TLS_ECDH_ANON_SUPPORT != DISABLED)
+   #error TLS_ECDH_ANON_SUPPORT parameter is not valid
+#endif
+
+//PSK key exchange support
+#ifndef TLS_PSK_SUPPORT
+   #define TLS_PSK_SUPPORT DISABLED
+#elif (TLS_PSK_SUPPORT != ENABLED && TLS_PSK_SUPPORT != DISABLED)
+   #error TLS_PSK_SUPPORT parameter is not valid
+#endif
+
+//RSA_PSK key exchange support
+#ifndef TLS_RSA_PSK_SUPPORT
+   #define TLS_RSA_PSK_SUPPORT DISABLED
+#elif (TLS_RSA_PSK_SUPPORT != ENABLED && TLS_RSA_PSK_SUPPORT != DISABLED)
+   #error TLS_RSA_PSK_SUPPORT parameter is not valid
+#endif
+
+//DHE_PSK key exchange support
+#ifndef TLS_DHE_PSK_SUPPORT
+   #define TLS_DHE_PSK_SUPPORT DISABLED
+#elif (TLS_DHE_PSK_SUPPORT != ENABLED && TLS_DHE_PSK_SUPPORT != DISABLED)
+   #error TLS_DHE_PSK_SUPPORT parameter is not valid
+#endif
+
+//ECDHE_PSK key exchange support
+#ifndef TLS_ECDHE_PSK_SUPPORT
+   #define TLS_ECDHE_PSK_SUPPORT DISABLED
+#elif (TLS_ECDHE_PSK_SUPPORT != ENABLED && TLS_ECDHE_PSK_SUPPORT != DISABLED)
+   #error TLS_ECDHE_PSK_SUPPORT parameter is not valid
+#endif
+
+//RSA signature capability
+#ifndef TLS_RSA_SIGN_SUPPORT
+   #define TLS_RSA_SIGN_SUPPORT ENABLED
+#elif (TLS_RSA_SIGN_SUPPORT != ENABLED && TLS_RSA_SIGN_SUPPORT != DISABLED)
+   #error TLS_RSA_SIGN_SUPPORT parameter is not valid
+#endif
+
+//DSA signature capability
+#ifndef TLS_DSA_SIGN_SUPPORT
+   #define TLS_DSA_SIGN_SUPPORT ENABLED
+#elif (TLS_DSA_SIGN_SUPPORT != ENABLED && TLS_DSA_SIGN_SUPPORT != DISABLED)
+   #error TLS_DSA_SIGN_SUPPORT parameter is not valid
+#endif
+
+//ECDSA signature capability
+#ifndef TLS_ECDSA_SIGN_SUPPORT
+   #define TLS_ECDSA_SIGN_SUPPORT ENABLED
+#elif (TLS_ECDSA_SIGN_SUPPORT != ENABLED && TLS_ECDSA_SIGN_SUPPORT != DISABLED)
+   #error TLS_ECDSA_SIGN_SUPPORT parameter is not valid
+#endif
+
+//Stream cipher support
+#ifndef TLS_STREAM_CIPHER_SUPPORT
+   #define TLS_STREAM_CIPHER_SUPPORT ENABLED
+#elif (TLS_STREAM_CIPHER_SUPPORT != ENABLED && TLS_STREAM_CIPHER_SUPPORT != DISABLED)
+   #error TLS_STREAM_CIPHER_SUPPORT parameter is not valid
+#endif
+
+//CBC block cipher support
+#ifndef TLS_CBC_CIPHER_SUPPORT
+   #define TLS_CBC_CIPHER_SUPPORT ENABLED
+#elif (TLS_CBC_CIPHER_SUPPORT != ENABLED && TLS_CBC_CIPHER_SUPPORT != DISABLED)
+   #error TLS_CBC_CIPHER_SUPPORT parameter is not valid
+#endif
+
+//CCM AEAD support
+#ifndef TLS_CCM_CIPHER_SUPPORT
+   #define TLS_CCM_CIPHER_SUPPORT ENABLED
+#elif (TLS_CCM_CIPHER_SUPPORT != ENABLED && TLS_CCM_CIPHER_SUPPORT != DISABLED)
+   #error TLS_CCM_CIPHER_SUPPORT parameter is not valid
+#endif
+
+//CCM_8 AEAD support
+#ifndef TLS_CCM_8_CIPHER_SUPPORT
+   #define TLS_CCM_8_CIPHER_SUPPORT DISABLED
+#elif (TLS_CCM_8_CIPHER_SUPPORT != ENABLED && TLS_CCM_8_CIPHER_SUPPORT != DISABLED)
+   #error TLS_CCM_8_CIPHER_SUPPORT parameter is not valid
+#endif
+
+//GCM AEAD support
+#ifndef TLS_GCM_CIPHER_SUPPORT
+   #define TLS_GCM_CIPHER_SUPPORT ENABLED
+#elif (TLS_GCM_CIPHER_SUPPORT != ENABLED && TLS_GCM_CIPHER_SUPPORT != DISABLED)
+   #error TLS_GCM_CIPHER_SUPPORT parameter is not valid
+#endif
+
+//ChaCha20Poly1305 AEAD support
+#ifndef TLS_CHACHA20_POLY1305_SUPPORT
+   #define TLS_CHACHA20_POLY1305_SUPPORT DISABLED
+#elif (TLS_CHACHA20_POLY1305_SUPPORT != ENABLED && TLS_CHACHA20_POLY1305_SUPPORT != DISABLED)
+   #error TLS_CHACHA20_POLY1305_SUPPORT parameter is not valid
+#endif
+
+//RC4 cipher support
+#ifndef TLS_RC4_SUPPORT
+   #define TLS_RC4_SUPPORT DISABLED
+#elif (TLS_RC4_SUPPORT != ENABLED && TLS_RC4_SUPPORT != DISABLED)
+   #error TLS_RC4_SUPPORT parameter is not valid
+#endif
+
+//IDEA cipher support
+#ifndef TLS_IDEA_SUPPORT
+   #define TLS_IDEA_SUPPORT DISABLED
+#elif (TLS_IDEA_SUPPORT != ENABLED && TLS_IDEA_SUPPORT != DISABLED)
+   #error TLS_IDEA_SUPPORT parameter is not valid
+#endif
+
+//DES cipher support
+#ifndef TLS_DES_SUPPORT
+   #define TLS_DES_SUPPORT DISABLED
+#elif (TLS_DES_SUPPORT != ENABLED && TLS_DES_SUPPORT != DISABLED)
+   #error TLS_DES_SUPPORT parameter is not valid
+#endif
+
+//Triple DES cipher support
+#ifndef TLS_3DES_SUPPORT
+   #define TLS_3DES_SUPPORT ENABLED
+#elif (TLS_3DES_SUPPORT != ENABLED && TLS_3DES_SUPPORT != DISABLED)
+   #error TLS_3DES_SUPPORT parameter is not valid
+#endif
+
+//AES cipher support
+#ifndef TLS_AES_SUPPORT
+   #define TLS_AES_SUPPORT ENABLED
+#elif (TLS_AES_SUPPORT != ENABLED && TLS_AES_SUPPORT != DISABLED)
+   #error TLS_AES_SUPPORT parameter is not valid
+#endif
+
+//Camellia cipher support
+#ifndef TLS_CAMELLIA_SUPPORT
+   #define TLS_CAMELLIA_SUPPORT ENABLED
+#elif (TLS_CAMELLIA_SUPPORT != ENABLED && TLS_CAMELLIA_SUPPORT != DISABLED)
+   #error TLS_CAMELLIA_SUPPORT parameter is not valid
+#endif
+
+//SEED cipher support
+#ifndef TLS_SEED_SUPPORT
+   #define TLS_SEED_SUPPORT ENABLED
+#elif (TLS_SEED_SUPPORT != ENABLED && TLS_SEED_SUPPORT != DISABLED)
+   #error TLS_SEED_SUPPORT parameter is not valid
+#endif
+
+//ARIA cipher support
+#ifndef TLS_ARIA_SUPPORT
+   #define TLS_ARIA_SUPPORT ENABLED
+#elif (TLS_ARIA_SUPPORT != ENABLED && TLS_ARIA_SUPPORT != DISABLED)
+   #error TLS_ARIA_SUPPORT parameter is not valid
+#endif
+
+//MD5 hash support
+#ifndef TLS_MD5_SUPPORT
+   #define TLS_MD5_SUPPORT DISABLED
+#elif (TLS_MD5_SUPPORT != ENABLED && TLS_MD5_SUPPORT != DISABLED)
+   #error TLS_MD5_SUPPORT parameter is not valid
+#endif
+
+//SHA-1 hash support
+#ifndef TLS_SHA1_SUPPORT
+   #define TLS_SHA1_SUPPORT ENABLED
+#elif (TLS_SHA1_SUPPORT != ENABLED && TLS_SHA1_SUPPORT != DISABLED)
+   #error TLS_SHA1_SUPPORT parameter is not valid
+#endif
+
+//SHA-224 hash support
+#ifndef TLS_SHA224_SUPPORT
+   #define TLS_SHA224_SUPPORT ENABLED
+#elif (TLS_SHA224_SUPPORT != ENABLED && TLS_SHA224_SUPPORT != DISABLED)
+   #error TLS_SHA224_SUPPORT parameter is not valid
+#endif
+
+//SHA-256 hash support
+#ifndef TLS_SHA256_SUPPORT
+   #define TLS_SHA256_SUPPORT ENABLED
+#elif (TLS_SHA256_SUPPORT != ENABLED && TLS_SHA256_SUPPORT != DISABLED)
+   #error TLS_SHA256_SUPPORT parameter is not valid
+#endif
+
+//SHA-384 hash support
+#ifndef TLS_SHA384_SUPPORT
+   #define TLS_SHA384_SUPPORT ENABLED
+#elif (TLS_SHA384_SUPPORT != ENABLED && TLS_SHA384_SUPPORT != DISABLED)
+   #error TLS_SHA384_SUPPORT parameter is not valid
+#endif
+
+//SHA-512 hash support
+#ifndef TLS_SHA512_SUPPORT
+   #define TLS_SHA512_SUPPORT ENABLED
+#elif (TLS_SHA512_SUPPORT != ENABLED && TLS_SHA512_SUPPORT != DISABLED)
+   #error TLS_SHA512_SUPPORT parameter is not valid
+#endif
+
+//secp160k1 elliptic curve support
+#ifndef TLS_SECP160K1_SUPPORT
+   #define TLS_SECP160K1_SUPPORT DISABLED
+#elif (TLS_SECP160K1_SUPPORT != ENABLED && TLS_SECP160K1_SUPPORT != DISABLED)
+   #error TLS_SECP160K1_SUPPORT parameter is not valid
+#endif
+
+//secp160r1 elliptic curve support
+#ifndef TLS_SECP160R1_SUPPORT
+   #define TLS_SECP160R1_SUPPORT DISABLED
+#elif (TLS_SECP160R1_SUPPORT != ENABLED && TLS_SECP160R1_SUPPORT != DISABLED)
+   #error TLS_SECP160R1_SUPPORT parameter is not valid
+#endif
+
+//secp160r2 elliptic curve support
+#ifndef TLS_SECP160R2_SUPPORT
+   #define TLS_SECP160R2_SUPPORT DISABLED
+#elif (TLS_SECP160R2_SUPPORT != ENABLED && TLS_SECP160R2_SUPPORT != DISABLED)
+   #error TLS_SECP160R2_SUPPORT parameter is not valid
+#endif
+
+//secp192k1 elliptic curve support
+#ifndef TLS_SECP192K1_SUPPORT
+   #define TLS_SECP192K1_SUPPORT DISABLED
+#elif (TLS_SECP192K1_SUPPORT != ENABLED && TLS_SECP192K1_SUPPORT != DISABLED)
+   #error TLS_SECP192K1_SUPPORT parameter is not valid
+#endif
+
+//secp192r1 elliptic curve support
+#ifndef TLS_SECP192R1_SUPPORT
+   #define TLS_SECP192R1_SUPPORT ENABLED
+#elif (TLS_SECP192R1_SUPPORT != ENABLED && TLS_SECP192R1_SUPPORT != DISABLED)
+   #error TLS_SECP192R1_SUPPORT parameter is not valid
+#endif
+
+//secp224k1 elliptic curve support
+#ifndef TLS_SECP224K1_SUPPORT
+   #define TLS_SECP224K1_SUPPORT DISABLED
+#elif (TLS_SECP224K1_SUPPORT != ENABLED && TLS_SECP224K1_SUPPORT != DISABLED)
+   #error TLS_SECP224K1_SUPPORT parameter is not valid
+#endif
+
+//secp224r1 elliptic curve support
+#ifndef TLS_SECP224R1_SUPPORT
+   #define TLS_SECP224R1_SUPPORT ENABLED
+#elif (TLS_SECP224R1_SUPPORT != ENABLED && TLS_SECP224R1_SUPPORT != DISABLED)
+   #error TLS_SECP224R1_SUPPORT parameter is not valid
+#endif
+
+//secp256k1 elliptic curve support
+#ifndef TLS_SECP256K1_SUPPORT
+   #define TLS_SECP256K1_SUPPORT DISABLED
+#elif (TLS_SECP256K1_SUPPORT != ENABLED && TLS_SECP256K1_SUPPORT != DISABLED)
+   #error TLS_SECP256K1_SUPPORT parameter is not valid
+#endif
+
+//secp256r1 elliptic curve support
+#ifndef TLS_SECP256R1_SUPPORT
+   #define TLS_SECP256R1_SUPPORT ENABLED
+#elif (TLS_SECP256R1_SUPPORT != ENABLED && TLS_SECP256R1_SUPPORT != DISABLED)
+   #error TLS_SECP256R1_SUPPORT parameter is not valid
+#endif
+
+//secp384r1 elliptic curve support
+#ifndef TLS_SECP384R1_SUPPORT
+   #define TLS_SECP384R1_SUPPORT ENABLED
+#elif (TLS_SECP384R1_SUPPORT != ENABLED && TLS_SECP384R1_SUPPORT != DISABLED)
+   #error TLS_SECP384R1_SUPPORT parameter is not valid
+#endif
+
+//secp521r1 elliptic curve support
+#ifndef TLS_SECP521R1_SUPPORT
+   #define TLS_SECP521R1_SUPPORT ENABLED
+#elif (TLS_SECP521R1_SUPPORT != ENABLED && TLS_SECP521R1_SUPPORT != DISABLED)
+   #error TLS_SECP521R1_SUPPORT parameter is not valid
+#endif
+
+//brainpoolP256r1 elliptic curve support
+#ifndef TLS_BRAINPOOLP256R1_SUPPORT
+   #define TLS_BRAINPOOLP256R1_SUPPORT DISABLED
+#elif (TLS_BRAINPOOLP256R1_SUPPORT != ENABLED && TLS_BRAINPOOLP256R1_SUPPORT != DISABLED)
+   #error TLS_BRAINPOOLP256R1_SUPPORT parameter is not valid
+#endif
+
+//brainpoolP384r1 elliptic curve support
+#ifndef TLS_BRAINPOOLP384R1_SUPPORT
+   #define TLS_BRAINPOOLP384R1_SUPPORT DISABLED
+#elif (TLS_BRAINPOOLP384R1_SUPPORT != ENABLED && TLS_BRAINPOOLP384R1_SUPPORT != DISABLED)
+   #error TLS_BRAINPOOLP384R1_SUPPORT parameter is not valid
+#endif
+
+//brainpoolP512r1 elliptic curve support
+#ifndef TLS_BRAINPOOLP512R1_SUPPORT
+   #define TLS_BRAINPOOLP512R1_SUPPORT DISABLED
+#elif (TLS_BRAINPOOLP512R1_SUPPORT != ENABLED && TLS_BRAINPOOLP512R1_SUPPORT != DISABLED)
+   #error TLS_BRAINPOOLP512R1_SUPPORT parameter is not valid
+#endif
+
+//Minimum acceptable size for Diffie-Hellman prime modulus
+#ifndef TLS_MIN_DH_MODULUS_SIZE
+   #define TLS_MIN_DH_MODULUS_SIZE 1024
+#elif (TLS_MIN_DH_MODULUS_SIZE < 512)
+   #error TLS_MIN_DH_MODULUS_SIZE parameter is not valid
+#endif
+
+//Maximum acceptable size for Diffie-Hellman prime modulus
+#ifndef TLS_MAX_DH_MODULUS_SIZE
+   #define TLS_MAX_DH_MODULUS_SIZE 4096
+#elif (TLS_MAX_DH_MODULUS_SIZE < TLS_MIN_DH_MODULUS_SIZE)
+   #error TLS_MAX_DH_MODULUS_SIZE parameter is not valid
+#endif
+
+//Minimum acceptable size for RSA modulus
+#ifndef TLS_MIN_RSA_MODULUS_SIZE
+   #define TLS_MIN_RSA_MODULUS_SIZE 1024
+#elif (TLS_MIN_RSA_MODULUS_SIZE < 512)
+   #error TLS_MIN_RSA_MODULUS_SIZE parameter is not valid
+#endif
+
+//Maximum acceptable size for RSA modulus
+#ifndef TLS_MAX_RSA_MODULUS_SIZE
+   #define TLS_MAX_RSA_MODULUS_SIZE 4096
+#elif (TLS_MAX_RSA_MODULUS_SIZE < TLS_MIN_RSA_MODULUS_SIZE)
+   #error TLS_MAX_RSA_MODULUS_SIZE parameter is not valid
+#endif
+
+//Minimum acceptable size for DSA prime modulus
+#ifndef TLS_MIN_DSA_MODULUS_SIZE
+   #define TLS_MIN_DSA_MODULUS_SIZE 1024
+#elif (TLS_MIN_DSA_MODULUS_SIZE < 512)
+   #error TLS_MIN_DSA_MODULUS_SIZE parameter is not valid
+#endif
+
+//Maximum acceptable size for DSA prime modulus
+#ifndef TLS_MAX_DSA_MODULUS_SIZE
+   #define TLS_MAX_DSA_MODULUS_SIZE 4096
+#elif (TLS_MAX_DSA_MODULUS_SIZE < TLS_MIN_DSA_MODULUS_SIZE)
+   #error TLS_MAX_DSA_MODULUS_SIZE parameter is not valid
+#endif
+
+//Maximum size for premaster secret
+#ifndef TLS_MAX_PREMASTER_SECRET_SIZE
+   #define TLS_MAX_PREMASTER_SECRET_SIZE 256
+#elif (TLS_MAX_PREMASTER_SECRET_SIZE < 48)
+   #error TLS_MAX_PREMASTER_SECRET_SIZE parameter is not valid
+#endif
+
+//Memory allocation
+#ifndef tlsAllocMem
+   #define tlsAllocMem(size) osAllocMem(size)
+#endif
+
+//Memory deallocation
+#ifndef tlsFreeMem
+   #define tlsFreeMem(p) osFreeMem(p)
+#endif
+
+//Bind TLS to a particular socket
+#define tlsSetSocket(context, socket) tlsSetIoCallbacks(context, (TlsIoHandle) socket, \
+   (TlsIoSendCallback) socketSend, (TlsIoReceiveCallback) socketReceive)
+
+//Maximum plaintext record length
+#define TLS_MAX_RECORD_LENGTH 16384
+//Data overhead caused by record encryption
+#define TLS_MAX_RECORD_OVERHEAD 512
+
+//Forward declaration of TlsContext structure
+struct _TlsContext;
+#define TlsContext struct _TlsContext
+
+
+/**
+ * @brief TLS connection end
+ **/
+
+typedef enum
+{
+   TLS_CONNECTION_END_CLIENT = 0,
+   TLS_CONNECTION_END_SERVER = 1
+} TlsConnectionEnd;
+
+
+/**
+ * @brief Client authentication mode
+ **/
+
+typedef enum
+{
+   TLS_CLIENT_AUTH_NONE     = 0,
+   TLS_CLIENT_AUTH_OPTIONAL = 1,
+   TLS_CLIENT_AUTH_REQUIRED = 2
+} TlsClientAuthMode;
+
+
+/**
+ * @brief Flags used by read and write functions
+ **/
+
+typedef enum
+{
+   TLS_FLAG_WAIT_ALL   = 0x0800,
+   TLS_FLAG_BREAK_CHAR = 0x1000,
+   TLS_FLAG_BREAK_CRLF = 0x100A,
+   TLS_FLAG_WAIT_ACK   = 0x2000,
+   TLS_FLAG_BUFFER     = 0x4000
+} TlsFlags;
+
+
+//The TLS_FLAG_BREAK macro causes the read function to stop reading
+//data whenever the specified break character is encountered
+#define TLS_FLAG_BREAK(c) (TLS_FLAG_BREAK_CHAR | LSB(c))
+
+
+/**
+ * @brief Content type
+ **/
+
+typedef enum
+{
+   TLS_TYPE_NONE               = 0,
+   TLS_TYPE_CHANGE_CIPHER_SPEC = 20,
+   TLS_TYPE_ALERT              = 21,
+   TLS_TYPE_HANDSHAKE          = 22,
+   TLS_TYPE_APPLICATION_DATA   = 23,
+   TLS_TYPE_HEARTBEAT          = 24
+} TlsContentType;
+
+
+/**
+ * @brief Handshake message type
+ **/
+
+typedef enum
+{
+   TLS_TYPE_HELLO_REQUEST        = 0,
+   TLS_TYPE_CLIENT_HELLO         = 1,
+   TLS_TYPE_SERVER_HELLO         = 2,
+   TLS_TYPE_HELLO_VERIFY_REQUEST = 3,
+   TLS_TYPE_NEW_SESSION_TICKET   = 4,
+   TLS_TYPE_CERTIFICATE          = 11,
+   TLS_TYPE_SERVER_KEY_EXCHANGE  = 12,
+   TLS_TYPE_CERTIFICATE_REQUEST  = 13,
+   TLS_TYPE_SERVER_HELLO_DONE    = 14,
+   TLS_TYPE_CERTIFICATE_VERIFY   = 15,
+   TLS_TYPE_CLIENT_KEY_EXCHANGE  = 16,
+   TLS_TYPE_FINISHED             = 20,
+   TLS_TYPE_CERTIFICATE_URL      = 21,
+   TLS_TYPE_CERTIFICATE_STATUS   = 22,
+   TLS_TYPE_SUPPLEMENTAL_DATA    = 23
+} TlsMessageType;
+
+
+/**
+ * @brief Alert level
+ **/
+
+typedef enum
+{
+   TLS_ALERT_LEVEL_WARNING = 1,
+   TLS_ALERT_LEVEL_FATAL   = 2,
+} TlsAlertLevel;
+
+
+/**
+ * @brief Alert description
+ **/
+
+typedef enum
+{
+   TLS_ALERT_CLOSE_NOTIFY                    = 0,
+   TLS_ALERT_UNEXPECTED_MESSAGE              = 10,
+   TLS_ALERT_BAD_RECORD_MAC                  = 20,
+   TLS_ALERT_DECRYPTION_FAILED               = 21,
+   TLS_ALERT_RECORD_OVERFLOW                 = 22,
+   TLS_ALERT_DECOMPRESSION_FAILURE           = 30,
+   TLS_ALERT_HANDSHAKE_FAILURE               = 40,
+   TLS_ALERT_NO_CERTIFICATE                  = 41,
+   TLS_ALERT_BAD_CERTIFICATE                 = 42,
+   TLS_ALERT_UNSUPPORTED_CERTIFICATE         = 43,
+   TLS_ALERT_CERTIFICATE_REVOKED             = 44,
+   TLS_ALERT_CERTIFICATE_EXPIRED             = 45,
+   TLS_ALERT_CERTIFICATE_UNKNOWN             = 46,
+   TLS_ALERT_ILLEGAL_PARAMETER               = 47,
+   TLS_ALERT_UNKNOWN_CA                      = 48,
+   TLS_ALERT_ACCESS_DENIED                   = 49,
+   TLS_ALERT_DECODE_ERROR                    = 50,
+   TLS_ALERT_DECRYPT_ERROR                   = 51,
+   TLS_ALERT_EXPORT_RESTRICTION              = 60,
+   TLS_ALERT_PROTOCOL_VERSION                = 70,
+   TLS_ALERT_INSUFFICIENT_SECURITY           = 71,
+   TLS_ALERT_INTERNAL_ERROR                  = 80,
+   TLS_ALERT_INAPPROPRIATE_FALLBACK          = 86,
+   TLS_ALERT_USER_CANCELED                   = 90,
+   TLS_ALERT_NO_RENEGOTIATION                = 100,
+   TLS_ALERT_UNSUPPORTED_EXTENSION           = 110,
+   TLS_ALERT_CERTIFICATE_UNOBTAINABLE        = 111,
+   TLS_ALERT_UNRECOGNIZED_NAME               = 112,
+   TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE = 113,
+   TLS_ALERT_BAD_CERTIFICATE_HASH_VALUE      = 114,
+   TLS_ALERT_UNKNOWN_PSK_IDENTITY            = 115
+} TlsAlertDescription;
+
+
+/**
+ * @brief Compression methods
+ **/
+
+typedef enum
+{
+   TLS_COMPRESSION_METHOD_NULL    = 0,
+   TLS_COMPRESSION_METHOD_DEFLATE = 1
+} TlsCompressionMethodList;
+
+
+/**
+ * @brief Key exchange methods
+ **/
+
+typedef enum
+{
+   TLS_KEY_EXCH_NONE        = 0,
+   TLS_KEY_EXCH_RSA         = 1,
+   TLS_KEY_EXCH_DH_RSA      = 2,
+   TLS_KEY_EXCH_DHE_RSA     = 3,
+   TLS_KEY_EXCH_DH_DSS      = 4,
+   TLS_KEY_EXCH_DHE_DSS     = 5,
+   TLS_KEY_EXCH_DH_ANON     = 6,
+   TLS_KEY_EXCH_ECDH_RSA    = 7,
+   TLS_KEY_EXCH_ECDHE_RSA   = 8,
+   TLS_KEY_EXCH_ECDH_ECDSA  = 9,
+   TLS_KEY_EXCH_ECDHE_ECDSA = 10,
+   TLS_KEY_EXCH_ECDH_ANON   = 11,
+   TLS_KEY_EXCH_PSK         = 12,
+   TLS_KEY_EXCH_RSA_PSK     = 13,
+   TLS_KEY_EXCH_DHE_PSK     = 14,
+   TLS_KEY_EXCH_ECDHE_PSK   = 15,
+   TLS_KEY_EXCH_SRP_SHA     = 16,
+   TLS_KEY_EXCH_SRP_SHA_RSA = 17,
+   TLS_KEY_EXCH_SRP_SHA_DSS = 18
+} TlsKeyExchMethod;
+
+
+/**
+ * @brief Certificate types
+ **/
+
+typedef enum
+{
+   TLS_CERT_NONE             = 0,
+   TLS_CERT_RSA_SIGN         = 1,
+   TLS_CERT_DSS_SIGN         = 2,
+   TLS_CERT_RSA_FIXED_DH     = 3,
+   TLS_CERT_DSS_FIXED_DH     = 4,
+   TLS_CERT_RSA_EPHEMERAL_DH = 5,
+   TLS_CERT_DSS_EPHEMERAL_DH = 6,
+   TLS_CERT_FORTEZZA_DMS     = 20,
+   TLS_CERT_ECDSA_SIGN       = 64,
+   TLS_CERT_RSA_FIXED_ECDH   = 65,
+   TLS_CERT_ECDSA_FIXED_ECDH = 66
+} TlsCertificateType;
+
+
+/**
+ * @brief Hash algorithms
+ **/
+
+typedef enum
+{
+   TLS_HASH_ALGO_NONE   = 0,
+   TLS_HASH_ALGO_MD5    = 1,
+   TLS_HASH_ALGO_SHA1   = 2,
+   TLS_HASH_ALGO_SHA224 = 3,
+   TLS_HASH_ALGO_SHA256 = 4,
+   TLS_HASH_ALGO_SHA384 = 5,
+   TLS_HASH_ALGO_SHA512 = 6
+} TlsHashAlgo;
+
+
+/**
+ * @brief Signature algorithms
+ **/
+
+typedef enum
+{
+   TLS_SIGN_ALGO_ANONYMOUS = 0,
+   TLS_SIGN_ALGO_RSA       = 1,
+   TLS_SIGN_ALGO_DSA       = 2,
+   TLS_SIGN_ALGO_ECDSA     = 3
+} TlsSignatureAlgo;
+
+
+/**
+ * @brief TLS extension types
+ **/
+
+typedef enum
+{
+   TLS_EXT_SERVER_NAME            = 0,
+   TLS_EXT_MAX_FRAGMENT_LENGTH    = 1,
+   TLS_EXT_CLIENT_CERTIFICATE_URL = 2,
+   TLS_EXT_TRUSTED_CA_KEYS        = 3,
+   TLS_EXT_TRUNCATED_HMAC         = 4,
+   TLS_EXT_STATUS_REQUEST         = 5,
+   TLS_EXT_USER_MAPPING           = 6,
+   TLS_EXT_CLIENT_AUTHZ           = 7,
+   TLS_EXT_SERVER_AUTHZ           = 8,
+   TLS_EXT_CERT_TYPE              = 9,
+   TLS_EXT_ELLIPTIC_CURVES        = 10,
+   TLS_EXT_EC_POINT_FORMATS       = 11,
+   TLS_EXT_SRP                    = 12,
+   TLS_EXT_SIGNATURE_ALGORITHMS   = 13,
+   TLS_EXT_USE_SRTP               = 14,
+   TLS_EXT_HEARTBEAT              = 15,
+   TLS_EXT_ALPN                   = 16,
+   TLS_EXT_SESSION_TICKET         = 35,
+   TLS_EXT_RENEGOTIATION_INFO     = 65281
+} TlsExtensionType;
+
+
+/**
+ * @brief Name type
+ **/
+
+typedef enum
+{
+   TLS_NAME_TYPE_HOSTNAME = 0
+} TlsNameType;
+
+
+/**
+ * @brief EC named curves
+ **/
+
+typedef enum
+{
+   TLS_EC_CURVE_NONE                     = 0,
+   TLS_EC_CURVE_SECT163K1                = 1,     //RFC 4492
+   TLS_EC_CURVE_SECT163R1                = 2,     //RFC 4492
+   TLS_EC_CURVE_SECT163R2                = 3,     //RFC 4492
+   TLS_EC_CURVE_SECT193R1                = 4,     //RFC 4492
+   TLS_EC_CURVE_SECT193R2                = 5,     //RFC 4492
+   TLS_EC_CURVE_SECT233K1                = 6,     //RFC 4492
+   TLS_EC_CURVE_SECT233R1                = 7,     //RFC 4492
+   TLS_EC_CURVE_SECT239K1                = 8,     //RFC 4492
+   TLS_EC_CURVE_SECT283K1                = 9,     //RFC 4492
+   TLS_EC_CURVE_SECT283R1                = 10,    //RFC 4492
+   TLS_EC_CURVE_SECT409K1                = 11,    //RFC 4492
+   TLS_EC_CURVE_SECT409R1                = 12,    //RFC 4492
+   TLS_EC_CURVE_SECT571K1                = 13,    //RFC 4492
+   TLS_EC_CURVE_SECT571R1                = 14,    //RFC 4492
+   TLS_EC_CURVE_SECP160K1                = 15,    //RFC 4492
+   TLS_EC_CURVE_SECP160R1                = 16,    //RFC 4492
+   TLS_EC_CURVE_SECP160R2                = 17,    //RFC 4492
+   TLS_EC_CURVE_SECP192K1                = 18,    //RFC 4492
+   TLS_EC_CURVE_SECP192R1                = 19,    //RFC 4492
+   TLS_EC_CURVE_SECP224K1                = 20,    //RFC 4492
+   TLS_EC_CURVE_SECP224R1                = 21,    //RFC 4492
+   TLS_EC_CURVE_SECP256K1                = 22,    //RFC 4492
+   TLS_EC_CURVE_SECP256R1                = 23,    //RFC 4492
+   TLS_EC_CURVE_SECP384R1                = 24,    //RFC 4492
+   TLS_EC_CURVE_SECP521R1                = 25,    //RFC 4492
+   TLS_EC_CURVE_BRAINPOOLP256R1          = 26,    //RFC 7027
+   TLS_EC_CURVE_BRAINPOOLP384R1          = 27,    //RFC 7027
+   TLS_EC_CURVE_BRAINPOOLP512R1          = 28,    //RFC 7027
+   TLS_EC_CURVE_ECDH_X25519              = 29,    //RFC draft
+   TLS_EC_CURVE_ECDH_X448                = 30,    //RFC draft
+   TLS_EC_CURVE_FFDHE2048                = 256,   //RFC 7919
+   TLS_EC_CURVE_FFDHE3072                = 257,   //RFC 7919
+   TLS_EC_CURVE_FFDHE4096                = 258,   //RFC 7919
+   TLS_EC_CURVE_FFDHE6144                = 259,   //RFC 7919
+   TLS_EC_CURVE_FFDHE8192                = 260,   //RFC 7919
+   TLS_EC_CURVE_ARBITRARY_EXPLICIT_PRIME = 65281, //RFC 4492
+   TLS_EC_CURVE_ARBITRARY_EXPLICIT_CHAR2 = 65282  //RFC 4492
+} TlsEcNamedCurve;
+
+
+/**
+ * @brief EC point formats
+ **/
+
+typedef enum
+{
+   TLS_EC_POINT_FORMAT_UNCOMPRESSED              = 0,
+   TLS_EC_POINT_FORMAT_ANSIX962_COMPRESSED_PRIME = 1,
+   TLS_EC_POINT_FORMAT_ANSIX962_COMPRESSED_CHAR2 = 2
+} TlsEcPointFormat;
+
+
+/**
+ * @brief EC curve types
+ **/
+
+typedef enum
+{
+   TLS_EC_CURVE_TYPE_EXPLICIT_PRIME = 1,
+   TLS_EC_CURVE_TYPE_EXPLICIT_CHAR2 = 2,
+   TLS_EC_CURVE_TYPE_NAMED_CURVE    = 3
+} TlsEcCurveType;
+
+
+/**
+ * @brief TLS FSM states
+ **/
+
+typedef enum
+{
+   TLS_STATE_INIT                      = 0,
+   TLS_STATE_CLIENT_HELLO              = 1,
+   TLS_STATE_SERVER_HELLO              = 2,
+   TLS_STATE_SERVER_CERTIFICATE        = 3,
+   TLS_STATE_SERVER_KEY_EXCHANGE       = 4,
+   TLS_STATE_CERTIFICATE_REQUEST       = 5,
+   TLS_STATE_SERVER_HELLO_DONE         = 6,
+   TLS_STATE_CLIENT_CERTIFICATE        = 7,
+   TLS_STATE_CLIENT_KEY_EXCHANGE       = 8,
+   TLS_STATE_CERTIFICATE_VERIFY        = 9,
+   TLS_STATE_CLIENT_CHANGE_CIPHER_SPEC = 10,
+   TLS_STATE_CLIENT_FINISHED           = 11,
+   TLS_STATE_SERVER_CHANGE_CIPHER_SPEC = 12,
+   TLS_STATE_SERVER_FINISHED           = 13,
+   TLS_STATE_APPLICATION_DATA          = 14,
+   TLS_STATE_CLOSING                   = 15,
+   TLS_STATE_CLOSED                    = 16
+} TlsState;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief Random structure
+ **/
+
+typedef __start_packed struct
+{
+   uint32_t gmtUnixTime;    //0-3
+   uint8_t randomBytes[28]; //4-31
+} __end_packed TlsRandom;
+
+
+/**
+ * @brief Cipher suite
+ **/
+
+typedef uint16_t TlsCipherSuite;
+
+
+/**
+ * @brief Cipher suites
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t length;  //0-1
+   uint16_t value[]; //2
+} __end_packed TlsCipherSuites;
+
+
+/**
+ * @brief Compression method
+ **/
+
+typedef uint8_t TlsCompressionMethod;
+
+
+/**
+ * @brief Compression methods
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t length;  //0
+   uint8_t value[]; //1
+} __end_packed TlsCompressionMethods;
+
+
+/**
+ * @brief Signature algorithm
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t hash;      //0
+   uint8_t signature; //1
+} __end_packed TlsSignHashAlgo;
+
+
+/**
+ * @brief List of signature algorithms
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t length;         //0-1
+   TlsSignHashAlgo value[]; //2
+} __end_packed TlsSignHashAlgos;
+
+
+/**
+ * @brief List of certificate authorities
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t length; //0-1
+   uint8_t value[]; //2
+} __end_packed TlsCertAuthorities;
+
+
+/**
+ * @brief TLS extension
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t type;   //0-1
+   uint16_t length; //2-3
+   uint8_t value[]; //4
+} __end_packed TlsExtension;
+
+
+/**
+ * @brief List of TLS extensions
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t length; //0-1
+   uint8_t value[]; //2
+} __end_packed TlsExtensions;
+
+
+/**
+ * @brief Server name
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;      //0
+   uint16_t length;   //1-2
+   char_t hostname[]; //2
+} __end_packed TlsServerName;
+
+
+/**
+ * @brief List of server names
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t length; //0-1
+   uint8_t value[]; //2
+} __end_packed TlsServerNameList;
+
+
+/**
+ * @brief Protocol name
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t length; //0
+   char_t value[]; //1
+} __end_packed TlsProtocolName;
+
+
+/**
+ * @brief List of protocol names
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t length; //0-1
+   uint8_t value[]; //2
+} __end_packed TlsProtocolNameList;
+
+
+/**
+ * @brief List of supported elliptic curves
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t length;  //0-1
+   uint16_t value[]; //2
+} __end_packed TlsEllipticCurveList;
+
+
+/**
+ * @brief List of supported EC point formats
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t length;  //0
+   uint8_t value[]; //1
+} __end_packed TlsEcPointFormatList;
+
+
+/**
+ * @brief PSK identity
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t length; //0-1
+   uint8_t value[]; //2
+} __end_packed TlsPskIdentity;
+
+
+/**
+ * @brief PSK identity hint
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t length; //0-1
+   uint8_t value[]; //2
+} __end_packed TlsPskIdentityHint;
+
+
+/**
+ * @brief Digitally-signed element (SSL 3.0, TLS 1.0 and TLS 1.1)
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t length; //0-1
+   uint8_t value[]; //2
+} __end_packed TlsDigitalSignature;
+
+
+/**
+ * @brief Digitally-signed element (TLS 1.2)
+ **/
+
+typedef __start_packed struct
+{
+   TlsSignHashAlgo algorithm; //0-1
+   uint16_t length;           //2-3
+   uint8_t value[];           //4
+} __end_packed TlsDigitalSignature2;
+
+
+/**
+ * @brief General format of TLS records
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;     //0
+   uint16_t version; //1-2
+   uint16_t length;  //3-4
+   uint8_t data[];   //5
+} __end_packed TlsRecord;
+
+
+/**
+ * @brief Handshake message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t msgType;   //0
+   uint8_t length[3]; //1-3
+   uint8_t data[];    //4
+} __end_packed TlsHandshake;
+
+
+/**
+ * @brief ClientHello message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t msgType;         //0
+   uint8_t length[3];       //1-3
+   uint16_t clientVersion;  //4-5
+   TlsRandom random;        //6-37
+   uint8_t sessionIdLength; //38
+   uint8_t sessionId[];     //39
+} __end_packed TlsClientHello;
+
+
+/**
+ * @brief ServerHello message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t msgType;         //0
+   uint8_t length[3];       //1-3
+   uint16_t serverVersion;  //4-5
+   TlsRandom random;        //6-37
+   uint8_t sessionIdLength; //38
+   uint8_t sessionId[];     //39
+} __end_packed TlsServerHello;
+
+
+/**
+ * @brief Certificate message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t msgType;                  //0
+   uint8_t length[3];                //1-3
+   uint8_t certificateListLength[3]; //4-6
+   uint8_t certificateList[];        //7
+} __end_packed TlsCertificate;
+
+
+/**
+ * @brief ServerKeyExchange message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t msgType;   //0
+   uint8_t length[3]; //1-3
+   uint8_t data[];    //4
+} __end_packed TlsServerKeyExchange;
+
+
+/**
+ * @brief CertificateRequest message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t msgType;                 //0
+   uint8_t length[3];               //1-3
+   uint8_t certificateTypesLength;  //4
+   uint8_t certificateTypes[];      //5
+} __end_packed TlsCertificateRequest;
+
+
+/**
+ * @brief ServerHelloDone message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t msgType;   //0
+   uint8_t length[3]; //1-3
+} __end_packed TlsServerHelloDone;
+
+
+/**
+ * @brief ClientKeyExchange message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t msgType;   //0
+   uint8_t length[3]; //1-3
+   uint8_t data[];    //4
+} __end_packed TlsClientKeyExchange;
+
+
+/**
+ * @brief CertificateVerify message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t msgType;     //0
+   uint8_t length[3];   //1-3
+   uint8_t signature[]; //4
+} __end_packed TlsCertificateVerify;
+
+
+/**
+ * @brief Finished message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t msgType;      //0
+   uint8_t length[3];    //1-3
+   uint8_t verifyData[]; //4
+} __end_packed TlsFinished;
+
+
+/**
+ * @brief ChangeCipherSpec message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type; //0
+} __end_packed TlsChangeCipherSpec;
+
+
+/**
+ * @brief Alert message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t level;       //0
+   uint8_t description; //1
+} __end_packed TlsAlert;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+/**
+ * @brief Sequence number
+ **/
+
+typedef uint8_t TlsSequenceNumber[8];
+
+
+/**
+ * @brief Handle for I/O operations
+ **/
+
+typedef void *TlsIoHandle;
+
+
+/**
+ * @brief Send callback function
+ **/
+
+typedef error_t (*TlsIoSendCallback)(TlsIoHandle handle,
+   const void *data, size_t length, size_t *written, uint_t flags);
+
+
+/**
+ * @brief Receive callback function
+ **/
+
+typedef error_t (*TlsIoReceiveCallback)(TlsIoHandle handle,
+   void *data, size_t size, size_t *received, uint_t flags);
+
+
+/**
+ * @brief Pre-shared key callback function
+ **/
+
+typedef error_t (*TlsPskCallback)(TlsContext *context,
+   const char_t *pskIdentity);
+
+
+/**
+ * @brief Structure describing a cipher suite
+ **/
+
+typedef struct
+{
+   uint16_t identifier;
+   const char_t *name;
+   TlsKeyExchMethod keyExchMethod;
+   const CipherAlgo *cipherAlgo;
+   CipherMode cipherMode;
+   const HashAlgo *hashAlgo;
+   const HashAlgo *prfHashAlgo;
+   uint8_t macKeyLen;
+   uint8_t encKeyLen;
+   uint8_t fixedIvLen;
+   uint8_t recordIvLen;
+   uint8_t authTagLen;
+   uint8_t verifyDataLen;
+} TlsCipherSuiteInfo;
+
+
+/**
+ * @brief TLS session
+ **/
+
+typedef struct
+{
+   uint8_t id[32];            ///<Session identifier
+   size_t idLength;           ///<Length of the session identifier
+   systime_t timestamp;       ///<Time stamp to manage entry lifetime
+   uint16_t cipherSuite;      ///<Cipher suite identifier
+   uint8_t compressionMethod; ///<Compression method
+   uint8_t masterSecret[48];  ///<Master secret
+} TlsSession;
+
+
+/**
+ * @brief Session cache
+ **/
+
+typedef struct
+{
+   OsMutex mutex;         ///<Mutex preventing simultaneous access to the cache
+   uint_t size;           ///<Maximum number of entries
+   TlsSession sessions[]; ///<Cache entries
+} TlsCache;
+
+
+/**
+ * @brief Certificate descriptor
+ **/
+
+typedef struct
+{
+   const char_t *certChain;    ///<End entity certificate chain (PEM format)
+   size_t certChainLength;     ///<Length of the certificate chain
+   const char_t *privateKey;   ///<Private key (PEM format)
+   size_t privateKeyLength;    ///<Length of the private key
+   TlsCertificateType type;    ///<End entity certificate type
+   TlsSignatureAlgo signAlgo;  ///<Signature algorithm used to sign the end entity certificate
+   TlsHashAlgo hashAlgo;       ///<Hash algorithm used to sign the end entity certificate
+   TlsEcNamedCurve namedCurve; ///<Named curve used to generate the EC public key
+} TlsCertDesc;
+
+
+/**
+ * @brief TLS context
+ *
+ * An opaque data structure that represents a TLS connection
+ *
+ **/
+
+struct _TlsContext
+{
+   TlsState state;                          ///<TLS handshake finite state machine
+   TlsConnectionEnd entity;                 ///<Client or server operation
+
+   TlsIoHandle handle;                      ///<Handle for I/O operations
+   TlsIoSendCallback sendCallback;          ///<Send callback function
+   TlsIoReceiveCallback receiveCallback;    ///<Receive callback function
+   const PrngAlgo *prngAlgo;                ///<Pseudo-random number generator to be used
+   void *prngContext;                       ///<Pseudo-random number generator context
+
+   const uint16_t *cipherSuites;            ///<List of supported cipher suites
+   uint_t numCipherSuites;                  ///<Number of cipher suites in the list
+
+   char_t *serverName;                      ///<Fully qualified DNS hostname of the server
+
+#if (TLS_ALPN_SUPPORT == ENABLED)
+   char_t *protocolList;                    ///<List of supported ALPN protocols
+#endif
+
+#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
+   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   char_t *psk;                             ///<Pre-shared key
+   size_t pskLen;                           ///<Length of the pre-shared key, in bytes
+   char_t *pskIdentity;                     ///<PSK identity
+   char_t *pskIdentityHint;                 ///<PSK identity hint
+   TlsPskCallback pskCallback;              ///<PSK callback function
+#endif
+
+#if (TLS_DH_ANON_SUPPORT == ENABLED || TLS_DHE_RSA_SUPPORT == ENABLED || \
+   TLS_DHE_DSS_SUPPORT == ENABLED || TLS_DHE_PSK_SUPPORT == ENABLED)
+   DhContext dhContext;                     ///<Diffie-Hellman context
+#endif
+
+#if (TLS_ECDH_ANON_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED || \
+   TLS_ECDHE_ECDSA_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   EcdhContext ecdhContext;                 ///<ECDH context
+#endif
+
+   TlsCertDesc certs[TLS_MAX_CERTIFICATES]; //End entity certificates
+   uint_t numCerts;                         //Number of certificates available
+   TlsCertDesc *cert;                       //Pointer to the currently selected certificate
+
+   const char_t *trustedCaList;             ///<List of trusted CA (PEM format)
+   size_t trustedCaListLen;                 ///<Number of trusted CA in the list
+
+   TlsCertificateType peerCertType;         ///<Peer's certificate type
+
+#if (TLS_RSA_SIGN_SUPPORT == ENABLED || TLS_RSA_SUPPORT == ENABLED || \
+   TLS_DHE_RSA_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED)
+   RsaPublicKey peerRsaPublicKey;           ///<Peer's RSA public key
+#endif
+
+#if (TLS_DSA_SIGN_SUPPORT == ENABLED || TLS_DHE_DSS_SUPPORT == ENABLED)
+   DsaPublicKey peerDsaPublicKey;           ///<Peer's DSA public key
+#endif
+
+#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED || TLS_ECDHE_ECDSA_SUPPORT == ENABLED)
+   EcDomainParameters peerEcParams;         ///<Peer's EC domain parameters
+   EcPoint peerEcPublicKey;                 ///<Peer's EC public key
+#endif
+
+   TlsCache *cache;                         ///<TLS session cache
+
+   uint8_t sessionId[32];                   ///<Session identifier
+   size_t sessionIdLen;                     ///<Length of the session identifier
+
+   uint16_t clientVersion;                  ///<Latest version supported by the client
+   uint16_t version;                        ///<Negotiated TLS version
+   uint16_t cipherSuite;                    ///<Negotiated cipher suite
+   uint8_t compressionMethod;               ///<Negotiated compression algorithm
+   uint16_t namedCurve;                     ///<Named curve
+
+   TlsHashAlgo signHashAlgo;                ///<Hash algorithm used for signing
+   TlsKeyExchMethod keyExchMethod;          ///<Key exchange method
+   const CipherAlgo *cipherAlgo;            ///<Bulk cipher algorithm
+   CipherMode cipherMode;                   ///<Cipher mode of operation
+   const HashAlgo *hashAlgo;                ///<Hash algorithm for MAC operations
+   const HashAlgo *prfHashAlgo;             ///<Hash algorithm for PRF operations
+   size_t macKeyLen;                        ///<Number of bytes that are used for generating MAC keys
+   size_t encKeyLen;                        ///<Number of bytes that are used for generating encryption keys
+   size_t fixedIvLen;                       ///<Amount of data needed to be generated for the IV
+   size_t recordIvLen;                      ///<Length of the IV
+   size_t authTagLen;                       ///<Length of the authentication tag
+   size_t verifyDataLen;                    ///<Length of the verify data
+
+//#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_MIN_VERSION <= TLS_VERSION_1_1)
+   Md5Context *handshakeMd5Context;         ///<MD5 context used to compute verify data
+   Sha1Context *handshakeSha1Context;       ///<SHA-1 context used to compute verify data
+//#endif
+
+//#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_MIN_VERSION <= TLS_VERSION_1_2)
+   HashContext *handshakeHashContext;       ///<Hash context used to compute verify data (TLS 1.2)
+//#endif
+
+   uint8_t verifyData[64];                  ///<Verify data
+
+   bool_t ecPointFormatExtFound;            ///<The EcPointFormats extension has been received
+
+   TlsClientAuthMode clientAuthMode;        ///<Client authentication mode
+   bool_t clientCertRequested;              ///<This flag tells whether the client certificate is requested
+
+   bool_t resume;                           ///<The connection is established by resuming a session
+   bool_t changeCipherSpecSent;             ///<A ChangeCipherSpec message has been sent
+   bool_t changeCipherSpecReceived;         ///<A ChangeCipherSpec message has been received from the peer
+   bool_t fatalAlertSent;                   ///<A fatal alert message has been sent
+   bool_t fatalAlertReceived;               ///<A fatal alert message has been received from the peer
+   bool_t closeNotifySent;                  ///<A closure alert has been sent
+   bool_t closeNotifyReceived;              ///<A closure alert has been received from the peer
+
+   HmacContext hmacContext;                 ///<HMAC context
+   void *writeCipherContext;                ///<Bulk cipher context for write operations
+   void *readCipherContext;                 ///<Bulk cipher context for read operations
+#if (TLS_GCM_CIPHER_SUPPORT == ENABLED)
+   GcmContext *writeGcmContext;             ///<GCM context for write operations
+   GcmContext *readGcmContext;              ///<GCM context for read operations
+#endif
+
+   uint8_t *txBuffer;                       ///<TX buffer
+   size_t txBufferSize;                     ///<TX buffer size
+   TlsContentType txBufferType;             ///<Type of data that resides in the TX buffer
+   size_t txBufferLen;                      ///<Number of bytes that are pending to be sent
+   size_t txBufferPos;                      ///<Current position in TX buffer
+   size_t txRecordMaxLen;                   ///<Maximum plaintext fragment length
+   size_t txRecordLen;                      ///<Length of the TLS record
+   size_t txRecordPos;                      ///<Current position in the TLS record
+
+   uint8_t *rxBuffer;                       ///<RX buffer
+   size_t rxBufferSize;                     ///<RX buffer size
+   TlsContentType rxBufferType;             ///<Type of data that resides in the RX buffer
+   size_t rxBufferLen;                      ///<Number of bytes available for reading
+   size_t rxBufferPos;                      ///<Current position in RX buffer
+   size_t rxRecordMaxLen;                   ///<Maximum plaintext fragment length
+   size_t rxRecordLen;                      ///<Length of the TLS record
+   size_t rxRecordPos;                      ///<Current position in the TLS record
+
+   union
+   {
+      struct
+      {
+         TlsRandom clientRandom;            ///<Client random value
+         TlsRandom serverRandom;            ///<Server random value
+      };
+      uint8_t random[64];
+   };
+
+   uint8_t premasterSecret[TLS_MAX_PREMASTER_SECRET_SIZE]; ///<Premaster secret
+   size_t premasterSecretLen;               ///<Length of the premaster secret
+   uint8_t masterSecret[48];                ///<Master secret
+   uint8_t keyBlock[192];                   ///<Key material
+   uint8_t *writeMacKey;                    ///<Write MAC key
+   uint8_t *readMacKey;                     ///<Read MAC key
+   uint8_t *writeEncKey;                    ///<Encryption key that serves for write operations
+   uint8_t *readEncKey;                     ///<Encryption key that serves for read operations
+   uint8_t *writeIv;                        ///<Write IV
+   uint8_t *readIv;                         ///<Read IV
+
+   TlsSequenceNumber writeSeqNum;           ///<Write sequence number
+   TlsSequenceNumber readSeqNum;            ///<Read sequence number
+};
+
+
+//TLS application programming interface (API)
+TlsContext *tlsInit(void);
+
+error_t tlsSetIoCallbacks(TlsContext *context, TlsIoHandle handle,
+   TlsIoSendCallback sendCallback, TlsIoReceiveCallback receiveCallback);
+
+error_t tlsSetConnectionEnd(TlsContext *context, TlsConnectionEnd entity);
+error_t tlsSetPrng(TlsContext *context, const PrngAlgo *prngAlgo, void *prngContext);
+error_t tlsSetServerName(TlsContext *context, const char_t *serverName);
+error_t tlsSetCache(TlsContext *context, TlsCache *cache);
+error_t tlsSetClientAuthMode(TlsContext *context, TlsClientAuthMode mode);
+
+error_t tlsSetBufferSize(TlsContext *context,
+   size_t txBufferSize, size_t rxBufferSize);
+
+error_t tlsSetCipherSuites(TlsContext *context,
+   const uint16_t *cipherSuites, uint_t length);
+
+error_t tlsSetDhParameters(TlsContext *context,
+   const char_t *params, size_t length);
+
+error_t tlsSetAlpnProtocolList(TlsContext *context, const char_t *protocolList);
+const char_t *tlsGetAlpnProtocol(TlsContext *context);
+
+error_t tlsSetPsk(TlsContext *context, const uint8_t *psk, size_t pskLength);
+error_t tlsSetPskIdentity(TlsContext *context, const char_t *pskIdentity);
+error_t tlsSetPskIdentityHint(TlsContext *context, const char_t *pskIdentityHint);
+error_t tlsSetPskCallback(TlsContext *context, TlsPskCallback pskCallback);
+
+error_t tlsSetTrustedCaList(TlsContext *context,
+   const char_t *trustedCaList, size_t length);
+
+error_t tlsAddCertificate(TlsContext *context, const char_t *certChain,
+   size_t certChainLength, const char_t *privateKey, size_t privateKeyLength);
+
+error_t tlsConnect(TlsContext *context);
+
+error_t tlsWrite(TlsContext *context, const void *data,
+   size_t length, size_t *written, uint_t flags);
+
+error_t tlsRead(TlsContext *context, void *data,
+   size_t size, size_t *received, uint_t flags);
+
+error_t tlsShutdown(TlsContext *context);
+error_t tlsShutdownEx(TlsContext *context, bool_t waitForCloseNotify);
+
+void tlsFree(TlsContext *context);
+
+error_t tlsSaveSession(const TlsContext *context, TlsSession *session);
+error_t tlsRestoreSession(TlsContext *context, const TlsSession *session);
+
+TlsCache *tlsInitCache(uint_t size);
+void tlsFreeCache(TlsCache *cache);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls_cache.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,306 @@
+/**
+ * @file tls_cache.c
+ * @brief Session cache management
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TLS_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "tls.h"
+#include "tls_cache.h"
+#include "debug.h"
+
+//Check SSL library configuration
+#if (TLS_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Session cache initialization
+ * @param[in] size Maximum number of cache entries
+ * @return Handle referencing the fully initialized session cache
+ **/
+
+TlsCache *tlsInitCache(uint_t size)
+{
+   size_t n;
+   TlsCache *cache;
+
+   //Make sure the parameter is acceptable
+   if(size < 1)
+      return NULL;
+
+   //Size of the memory required
+   n = sizeof(TlsCache) + size * sizeof(TlsSession);
+
+   //Allocate a memory buffer to hold the session cache
+   cache = tlsAllocMem(n);
+   //Failed to allocate memory?
+   if(cache == NULL)
+      return NULL;
+
+   //Clear memory
+   memset(cache, 0, n);
+
+   //Create a mutex to prevent simultaneous access to the cache
+   if(!osCreateMutex(&cache->mutex))
+   {
+      //Clean up side effects
+      tlsFreeMem(cache);
+      //Report an error
+      return NULL;
+   }
+
+   //Save the maximum number of cache entries
+   cache->size = size;
+
+   //Return a pointer to the newly created cache
+   return cache;
+}
+
+
+/**
+ * @brief Search the session cache for a given session ID
+ * @param[in] cache Pointer to the session cache
+ * @param[in] id Expected session ID
+ * @param[in] length Length of the session ID
+ * @return A pointer to the matching session is returned. NULL is returned
+ *   if the specified ID could not be found in the session cache
+ **/
+
+TlsSession *tlsFindCache(TlsCache *cache, const uint8_t *id, size_t length)
+{
+   uint_t i;
+   systime_t time;
+   TlsSession *session;
+
+   //Check whether session caching is supported
+   if(cache == NULL)
+      return NULL;
+   //Ensure the session ID is valid
+   if(id == NULL || length == 0)
+      return NULL;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Acquire exclusive access to the session cache
+   osAcquireMutex(&cache->mutex);
+
+   //Flush expired entries
+   for(i = 0; i < cache->size; i++)
+   {
+      //Point to the current entry
+      session = &cache->sessions[i];
+
+      //Skip unused entries
+      if(session->idLength)
+      {
+         //Outdated entry?
+         if((time - session->timestamp) >= TLS_SESSION_CACHE_LIFETIME)
+         {
+            //This session is no more valid and should be removed from the cache
+            memset(session, 0, sizeof(TlsSession));
+         }
+      }
+   }
+
+   //Search the cache for the specified session ID
+   for(i = 0; i < cache->size; i++)
+   {
+      //Point to the current entry
+      session = &cache->sessions[i];
+
+      //Check whether the current identifier matches the specified session ID
+      if(session->idLength == length && !memcmp(session->id, id, length))
+      {
+         //Release exclusive access to the session cache
+         osReleaseMutex(&cache->mutex);
+         //Return session parameters
+         return session;
+      }
+   }
+
+   //Release exclusive access to the session cache
+   osReleaseMutex(&cache->mutex);
+   //No matching entry in session cache
+   return NULL;
+}
+
+
+/**
+ * @brief Save current session in cache
+ * @param[in] context TLS context
+ * @return Error code
+ **/
+
+error_t tlsSaveToCache(TlsContext *context)
+{
+   error_t error;
+   uint_t i;
+   TlsSession *session;
+   TlsSession *firstFreeEntry;
+   TlsSession *oldestEntry;
+
+   //Check parameters
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Check whether session caching is supported
+   if(context->cache == NULL)
+      return ERROR_FAILURE;
+   //Ensure the session ID is valid
+   if(context->sessionIdLen == 0)
+      return NO_ERROR;
+
+   //Acquire exclusive access to the session cache
+   osAcquireMutex(&context->cache->mutex);
+
+   //Keep track of the first free entry
+   firstFreeEntry = NULL;
+   //Keep track of the oldest entry
+   oldestEntry = NULL;
+
+   //Search the cache for the specified session ID
+   for(i = 0; i < context->cache->size; i++)
+   {
+      //Point to the current entry
+      session = &context->cache->sessions[i];
+
+      //If the session ID already exists, we are done
+      if(session->idLength == context->sessionIdLen &&
+         !memcmp(session->id, context->sessionId, session->idLength))
+      {
+         //Do not write to session cache
+         firstFreeEntry = NULL;
+         oldestEntry = NULL;
+         //Exit immediately
+         break;
+      }
+
+      //Check whether current entry is free
+      if(!session->idLength)
+      {
+         //Keep track of the first free entry
+         if(!firstFreeEntry)
+            firstFreeEntry = session;
+      }
+      else
+      {
+         //Keep track of the oldest entry in the table
+         if(!oldestEntry || timeCompare(session->timestamp, oldestEntry->timestamp) < 0)
+            oldestEntry = session;
+      }
+   }
+
+   //Add current session to cache if necessary
+   if(firstFreeEntry != NULL)
+      error = tlsSaveSession(context, firstFreeEntry);
+   else if(oldestEntry != NULL)
+      error = tlsSaveSession(context, oldestEntry);
+   else
+      error = NO_ERROR;
+
+   //Release exclusive access to the session cache
+   osReleaseMutex(&context->cache->mutex);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Remove current session from cache
+ * @param[in] context TLS context
+ * @return Error code
+ **/
+
+error_t tlsRemoveFromCache(TlsContext *context)
+{
+   uint_t i;
+   TlsSession *session;
+
+   //Check parameters
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Check whether session caching is supported
+   if(context->cache == NULL)
+      return ERROR_FAILURE;
+
+   //Ensure the session ID is valid
+   if(context->sessionIdLen == 0)
+      return NO_ERROR;
+
+   //Acquire exclusive access to the session cache
+   osAcquireMutex(&context->cache->mutex);
+
+   //Search the cache for the specified session ID
+   for(i = 0; i < context->cache->size; i++)
+   {
+      //Point to the current entry
+      session = &context->cache->sessions[i];
+
+      //Check whether the current identifier matches the specified session ID
+      if(session->idLength == context->sessionIdLen &&
+         !memcmp(session->id, context->sessionId, session->idLength))
+      {
+         //Drop current entry
+         memset(session, 0, sizeof(TlsSession));
+      }
+   }
+
+   //Release exclusive access to the session cache
+   osReleaseMutex(&context->cache->mutex);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Properly dispose a session cache
+ * @param[in] cache Pointer to the session cache to be released
+ **/
+
+void tlsFreeCache(TlsCache *cache)
+{
+   size_t n;
+
+   //Invalid session cache?
+   if(cache == NULL)
+      return;
+
+   //Release previously allocated resources
+   osDeleteMutex(&cache->mutex);
+
+   //Compute the number of bytes allocated for the session cache
+   n = sizeof(TlsCache) + cache->size * sizeof(TlsSession);
+
+   //Clear the session cache before freeing memory
+   memset(cache, 0, n);
+   tlsFreeMem(cache);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls_cache.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,43 @@
+/**
+ * @file tls_cache.h
+ * @brief Session cache management
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TLS_CACHE_H
+#define _TLS_CACHE_H
+
+//Dependencies
+#include "tls.h"
+
+//Session cache management
+TlsCache *tlsInitCache(uint_t size);
+TlsSession *tlsFindCache(TlsCache *cache, const uint8_t *id, size_t length);
+error_t tlsSaveToCache(TlsContext *context);
+error_t tlsRemoveFromCache(TlsContext *context);
+void tlsFreeCache(TlsCache *cache);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls_cipher_suites.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1160 @@
+/**
+ * @file tls_cipher_suites.c
+ * @brief TLS cipher suites
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TLS_TRACE_LEVEL
+
+//Dependencies
+#include "tls.h"
+#include "tls_cipher_suites.h"
+#include "md5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "rc4.h"
+#include "idea.h"
+#include "des.h"
+#include "des3.h"
+#include "aes.h"
+#include "camellia.h"
+#include "seed.h"
+#include "aria.h"
+#include "debug.h"
+
+//Check SSL library configuration
+#if (TLS_SUPPORT == ENABLED)
+
+//List of supported cipher suites
+const TlsCipherSuiteInfo tlsSupportedCipherSuites[] =
+{
+//TLS_RSA_WITH_RC4_128_MD5 cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_RSA_SUPPORT == ENABLED && TLS_STREAM_CIPHER_SUPPORT == ENABLED && TLS_RC4_SUPPORT == ENABLED && TLS_MD5_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_RC4_128_MD5, TLS_KEY_EXCH_RSA, RC4_CIPHER_ALGO, CIPHER_MODE_STREAM, MD5_HASH_ALGO, NULL, 16, 16, 0, 0, 0, 12),
+#endif
+
+//TLS_RSA_WITH_RC4_128_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_RSA_SUPPORT == ENABLED && TLS_STREAM_CIPHER_SUPPORT == ENABLED && TLS_RC4_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_RC4_128_SHA, TLS_KEY_EXCH_RSA, RC4_CIPHER_ALGO, CIPHER_MODE_STREAM, SHA1_HASH_ALGO, NULL, 20, 16, 0, 0, 0, 12),
+#endif
+
+//TLS_RSA_WITH_IDEA_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_IDEA_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_IDEA_CBC_SHA, TLS_KEY_EXCH_RSA, IDEA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 8, 8, 0, 12),
+#endif
+
+//TLS_RSA_WITH_DES_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_DES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_DES_CBC_SHA, TLS_KEY_EXCH_RSA, DES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 8, 8, 8, 0, 12),
+#endif
+
+//TLS_RSA_WITH_3DES_EDE_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_3DES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_EXCH_RSA, DES3_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 24, 8, 8, 0, 12),
+#endif
+
+//TLS_RSA_WITH_AES_128_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_EXCH_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_WITH_AES_256_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_EXCH_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_WITH_AES_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_EXCH_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_WITH_AES_256_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_EXCH_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_WITH_AES_128_CCM cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_SUPPORT == ENABLED && TLS_CCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_AES_128_CCM, TLS_KEY_EXCH_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_RSA_WITH_AES_256_CCM cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_SUPPORT == ENABLED && TLS_CCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_AES_256_CCM, TLS_KEY_EXCH_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_RSA_WITH_AES_128_CCM_8 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_SUPPORT == ENABLED && TLS_CCM_8_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_AES_128_CCM_8, TLS_KEY_EXCH_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 8, 12),
+#endif
+
+//TLS_RSA_WITH_AES_256_CCM_8 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_SUPPORT == ENABLED && TLS_CCM_8_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_AES_256_CCM_8, TLS_KEY_EXCH_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 32, 4, 8, 8, 12),
+#endif
+
+//TLS_RSA_WITH_AES_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_KEY_EXCH_RSA, AES_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_RSA_WITH_AES_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_KEY_EXCH_RSA, AES_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_RSA_WITH_CAMELLIA_128_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, TLS_KEY_EXCH_RSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_WITH_CAMELLIA_256_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, TLS_KEY_EXCH_RSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, TLS_KEY_EXCH_RSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, TLS_KEY_EXCH_RSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256, TLS_KEY_EXCH_RSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384, TLS_KEY_EXCH_RSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_RSA_WITH_SEED_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_SEED_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_SEED_CBC_SHA, TLS_KEY_EXCH_RSA, SEED_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_WITH_ARIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_ARIA_128_CBC_SHA256, TLS_KEY_EXCH_RSA, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_WITH_ARIA_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_ARIA_256_CBC_SHA384, TLS_KEY_EXCH_RSA, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_WITH_ARIA_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_ARIA_128_GCM_SHA256, TLS_KEY_EXCH_RSA, ARIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_RSA_WITH_ARIA_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_WITH_ARIA_256_GCM_SHA384, TLS_KEY_EXCH_RSA, ARIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_DES_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_DES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_DES_CBC_SHA, TLS_KEY_EXCH_DHE_RSA, DES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 8, 8, 8, 0, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_3DES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_EXCH_DHE_RSA, DES3_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 24, 8, 8, 0, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_AES_128_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_EXCH_DHE_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_AES_256_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_EXCH_DHE_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_EXCH_DHE_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_EXCH_DHE_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_AES_128_CCM cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_CCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_AES_128_CCM, TLS_KEY_EXCH_DHE_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_AES_256_CCM cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_CCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_AES_256_CCM, TLS_KEY_EXCH_DHE_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_AES_128_CCM_8 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_CCM_8_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_AES_128_CCM_8, TLS_KEY_EXCH_DHE_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 8, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_AES_256_CCM_8 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_CCM_8_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_AES_256_CCM_8, TLS_KEY_EXCH_DHE_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 32, 4, 8, 8, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_KEY_EXCH_DHE_RSA, AES_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_KEY_EXCH_DHE_RSA, AES_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, TLS_KEY_EXCH_DHE_RSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, TLS_KEY_EXCH_DHE_RSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, TLS_KEY_EXCH_DHE_RSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, TLS_KEY_EXCH_DHE_RSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, TLS_KEY_EXCH_DHE_RSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, TLS_KEY_EXCH_DHE_RSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_SEED_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_SEED_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_SEED_CBC_SHA, TLS_KEY_EXCH_DHE_RSA, SEED_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256, TLS_KEY_EXCH_DHE_RSA, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384, TLS_KEY_EXCH_DHE_RSA, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256, TLS_KEY_EXCH_DHE_RSA, ARIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384, TLS_KEY_EXCH_DHE_RSA, ARIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_RSA_SUPPORT == ENABLED && TLS_CHACHA20_POLY1305_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS_KEY_EXCH_DHE_RSA, NULL, CIPHER_MODE_CHACHA20_POLY1305, NULL, SHA256_HASH_ALGO, 0, 32, 12, 0, 16, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_DES_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_DES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_DES_CBC_SHA, TLS_KEY_EXCH_DHE_DSS, DES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 8, 8, 8, 0, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_3DES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_KEY_EXCH_DHE_DSS, DES3_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 24, 8, 8, 0, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_AES_128_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_KEY_EXCH_DHE_DSS, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_AES_256_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_AES_256_CBC_SHA, TLS_KEY_EXCH_DHE_DSS, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, TLS_KEY_EXCH_DHE_DSS, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, TLS_KEY_EXCH_DHE_DSS, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, TLS_KEY_EXCH_DHE_DSS, AES_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, TLS_KEY_EXCH_DHE_DSS, AES_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA, TLS_KEY_EXCH_DHE_DSS, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA, TLS_KEY_EXCH_DHE_DSS, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256, TLS_KEY_EXCH_DHE_DSS, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256, TLS_KEY_EXCH_DHE_DSS, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256, TLS_KEY_EXCH_DHE_DSS, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384, TLS_KEY_EXCH_DHE_DSS, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_SEED_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_SEED_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_SEED_CBC_SHA, TLS_KEY_EXCH_DHE_DSS, SEED_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256, TLS_KEY_EXCH_DHE_DSS, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384, TLS_KEY_EXCH_DHE_DSS, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256, TLS_KEY_EXCH_DHE_DSS, ARIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_DSS_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384, TLS_KEY_EXCH_DHE_DSS, ARIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_DH_anon_WITH_RC4_128_MD5 cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_STREAM_CIPHER_SUPPORT == ENABLED && TLS_RC4_SUPPORT == ENABLED && TLS_MD5_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_RC4_128_MD5, TLS_KEY_EXCH_DH_ANON, RC4_CIPHER_ALGO, CIPHER_MODE_STREAM, MD5_HASH_ALGO, NULL, 16, 16, 0, 0, 0, 12),
+#endif
+
+//TLS_DH_anon_WITH_DES_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_DES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_DES_CBC_SHA, TLS_KEY_EXCH_DH_ANON, DES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 8, 8, 8, 0, 12),
+#endif
+
+//TLS_DH_anon_WITH_3DES_EDE_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_3DES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA, TLS_KEY_EXCH_DH_ANON, DES3_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 24, 8, 8, 0, 12),
+#endif
+
+//TLS_DH_anon_WITH_AES_128_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_AES_128_CBC_SHA, TLS_KEY_EXCH_DH_ANON, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DH_anon_WITH_AES_256_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_AES_256_CBC_SHA, TLS_KEY_EXCH_DH_ANON, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DH_anon_WITH_AES_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_AES_128_CBC_SHA256, TLS_KEY_EXCH_DH_ANON, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DH_anon_WITH_AES_256_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_AES_256_CBC_SHA256, TLS_KEY_EXCH_DH_ANON, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DH_anon_WITH_AES_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_AES_128_GCM_SHA256, TLS_KEY_EXCH_DH_ANON, AES_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_DH_anon_WITH_AES_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_AES_256_GCM_SHA384, TLS_KEY_EXCH_DH_ANON, AES_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_CAMELLIA_128_CBC_SHA, TLS_KEY_EXCH_DH_ANON, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_CAMELLIA_256_CBC_SHA, TLS_KEY_EXCH_DH_ANON, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_CAMELLIA_128_CBC_SHA256, TLS_KEY_EXCH_DH_ANON, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_CAMELLIA_256_CBC_SHA256, TLS_KEY_EXCH_DH_ANON, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_CAMELLIA_128_GCM_SHA256, TLS_KEY_EXCH_DH_ANON, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_CAMELLIA_256_GCM_SHA384, TLS_KEY_EXCH_DH_ANON, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_DH_anon_WITH_SEED_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_SEED_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_SEED_CBC_SHA, TLS_KEY_EXCH_DH_ANON, SEED_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_ARIA_128_CBC_SHA256, TLS_KEY_EXCH_DH_ANON, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_ARIA_256_CBC_SHA384, TLS_KEY_EXCH_DH_ANON, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_ARIA_128_GCM_SHA256, TLS_KEY_EXCH_DH_ANON, ARIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DH_ANON_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DH_ANON_WITH_ARIA_256_GCM_SHA384, TLS_KEY_EXCH_DH_ANON, ARIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_ECDHE_RSA_WITH_RC4_128_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_ECDHE_RSA_SUPPORT == ENABLED && TLS_STREAM_CIPHER_SUPPORT == ENABLED && TLS_RC4_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLS_KEY_EXCH_ECDHE_RSA, RC4_CIPHER_ALGO, CIPHER_MODE_STREAM, SHA1_HASH_ALGO, NULL, 20, 16, 0, 0, 0, 12),
+#endif
+
+//TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_ECDHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_3DES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_EXCH_ECDHE_RSA, DES3_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 24, 8, 8, 0, 12),
+#endif
+
+//TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_ECDHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_EXCH_ECDHE_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_ECDHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_EXCH_ECDHE_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_EXCH_ECDHE_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_KEY_EXCH_ECDHE_RSA, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_RSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_KEY_EXCH_ECDHE_RSA, AES_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_RSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_KEY_EXCH_ECDHE_RSA, AES_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, TLS_KEY_EXCH_ECDHE_RSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384, TLS_KEY_EXCH_ECDHE_RSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_RSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, TLS_KEY_EXCH_ECDHE_RSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_RSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, TLS_KEY_EXCH_ECDHE_RSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256, TLS_KEY_EXCH_ECDHE_RSA, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_RSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384, TLS_KEY_EXCH_ECDHE_RSA, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_RSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256, TLS_KEY_EXCH_ECDHE_RSA, ARIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_RSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384, TLS_KEY_EXCH_ECDHE_RSA, ARIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_RSA_SUPPORT == ENABLED && TLS_CHACHA20_POLY1305_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS_KEY_EXCH_ECDHE_RSA, NULL, CIPHER_MODE_CHACHA20_POLY1305, NULL, SHA256_HASH_ALGO, 0, 32, 12, 0, 16, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_RC4_128_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_STREAM_CIPHER_SUPPORT == ENABLED && TLS_RC4_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_KEY_EXCH_ECDHE_ECDSA, RC4_CIPHER_ALGO, CIPHER_MODE_STREAM, SHA1_HASH_ALGO, NULL, 20, 16, 0, 0, 0, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_3DES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_EXCH_ECDHE_ECDSA, DES3_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 24, 8, 8, 0, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_KEY_EXCH_ECDHE_ECDSA, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_KEY_EXCH_ECDHE_ECDSA, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_KEY_EXCH_ECDHE_ECDSA, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_KEY_EXCH_ECDHE_ECDSA, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_AES_128_CCM cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_CCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_AES_128_CCM, TLS_KEY_EXCH_ECDHE_ECDSA, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_AES_256_CCM cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_CCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_AES_256_CCM, TLS_KEY_EXCH_ECDHE_ECDSA, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_CCM_8_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, TLS_KEY_EXCH_ECDHE_ECDSA, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 8, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_CCM_8_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, TLS_KEY_EXCH_ECDHE_ECDSA, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 32, 4, 8, 8, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_KEY_EXCH_ECDHE_ECDSA, AES_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_KEY_EXCH_ECDHE_ECDSA, AES_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, TLS_KEY_EXCH_ECDHE_ECDSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, TLS_KEY_EXCH_ECDHE_ECDSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, TLS_KEY_EXCH_ECDHE_ECDSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, TLS_KEY_EXCH_ECDHE_ECDSA, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256, TLS_KEY_EXCH_ECDHE_ECDSA, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384, TLS_KEY_EXCH_ECDHE_ECDSA, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256, TLS_KEY_EXCH_ECDHE_ECDSA, ARIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384, TLS_KEY_EXCH_ECDHE_ECDSA, ARIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_ECDSA_SUPPORT == ENABLED && TLS_CHACHA20_POLY1305_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_KEY_EXCH_ECDHE_ECDSA, NULL, CIPHER_MODE_CHACHA20_POLY1305, NULL, SHA256_HASH_ALGO, 0, 32, 12, 0, 16, 12),
+#endif
+
+//TLS_ECDH_anon_WITH_RC4_128_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_ECDH_ANON_SUPPORT == ENABLED && TLS_STREAM_CIPHER_SUPPORT == ENABLED && TLS_RC4_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDH_ANON_WITH_RC4_128_SHA, TLS_KEY_EXCH_ECDH_ANON, RC4_CIPHER_ALGO, CIPHER_MODE_STREAM, SHA1_HASH_ALGO, NULL, 20, 16, 0, 0, 0, 12),
+#endif
+
+//TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_ECDH_ANON_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_3DES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDH_ANON_WITH_3DES_EDE_CBC_SHA, TLS_KEY_EXCH_ECDH_ANON, DES3_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 24, 8, 8, 0, 12),
+#endif
+
+//TLS_ECDH_anon_WITH_AES_128_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_ECDH_ANON_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDH_ANON_WITH_AES_128_CBC_SHA, TLS_KEY_EXCH_ECDH_ANON, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDH_anon_WITH_AES_256_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_ECDH_ANON_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDH_ANON_WITH_AES_256_CBC_SHA, TLS_KEY_EXCH_ECDH_ANON, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_PSK_WITH_RC4_128_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_PSK_SUPPORT == ENABLED && TLS_STREAM_CIPHER_SUPPORT == ENABLED && TLS_RC4_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_RC4_128_SHA, TLS_KEY_EXCH_PSK, RC4_CIPHER_ALGO, CIPHER_MODE_STREAM, SHA1_HASH_ALGO, NULL, 20, 16, 0, 0, 0, 12),
+#endif
+
+//TLS_PSK_WITH_3DES_EDE_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_3DES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_3DES_EDE_CBC_SHA, TLS_KEY_EXCH_PSK, DES3_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 24, 8, 8, 0, 12),
+#endif
+
+//TLS_PSK_WITH_AES_128_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_AES_128_CBC_SHA, TLS_KEY_EXCH_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_PSK_WITH_AES_256_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_AES_256_CBC_SHA, TLS_KEY_EXCH_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_PSK_WITH_AES_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_AES_128_CBC_SHA256, TLS_KEY_EXCH_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_PSK_WITH_AES_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_AES_256_CBC_SHA384, TLS_KEY_EXCH_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_PSK_WITH_AES_128_CCM cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_PSK_SUPPORT == ENABLED && TLS_CCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_AES_128_CCM, TLS_KEY_EXCH_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_PSK_WITH_AES_256_CCM cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_PSK_SUPPORT == ENABLED && TLS_CCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_AES_256_CCM, TLS_KEY_EXCH_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_PSK_WITH_AES_128_CCM_8 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_PSK_SUPPORT == ENABLED && TLS_CCM_8_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_AES_128_CCM_8, TLS_KEY_EXCH_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 8, 12),
+#endif
+
+//TLS_PSK_WITH_AES_256_CCM_8 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_PSK_SUPPORT == ENABLED && TLS_CCM_8_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_AES_256_CCM_8, TLS_KEY_EXCH_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 32, 4, 8, 8, 12),
+#endif
+
+//TLS_PSK_WITH_AES_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_PSK_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_AES_128_GCM_SHA256, TLS_KEY_EXCH_PSK, AES_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_PSK_WITH_AES_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_PSK_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_AES_256_GCM_SHA384, TLS_KEY_EXCH_PSK, AES_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256, TLS_KEY_EXCH_PSK, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384, TLS_KEY_EXCH_PSK, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_PSK_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256, TLS_KEY_EXCH_PSK, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_PSK_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384, TLS_KEY_EXCH_PSK, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_PSK_WITH_ARIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_ARIA_128_CBC_SHA256, TLS_KEY_EXCH_PSK, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_PSK_WITH_ARIA_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_ARIA_256_CBC_SHA384, TLS_KEY_EXCH_PSK, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_PSK_WITH_ARIA_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_PSK_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_ARIA_128_GCM_SHA256, TLS_KEY_EXCH_PSK, ARIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_PSK_WITH_ARIA_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_PSK_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_ARIA_256_GCM_SHA384, TLS_KEY_EXCH_PSK, ARIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_PSK_SUPPORT == ENABLED && TLS_CHACHA20_POLY1305_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_PSK_WITH_CHACHA20_POLY1305_SHA256, TLS_KEY_EXCH_PSK, NULL, CIPHER_MODE_CHACHA20_POLY1305, NULL, SHA256_HASH_ALGO, 0, 32, 12, 0, 16, 12),
+#endif
+
+//TLS_RSA_PSK_WITH_RC4_128_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_RSA_PSK_SUPPORT == ENABLED && TLS_STREAM_CIPHER_SUPPORT == ENABLED && TLS_RC4_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_PSK_WITH_RC4_128_SHA, TLS_KEY_EXCH_RSA_PSK, RC4_CIPHER_ALGO, CIPHER_MODE_STREAM, SHA1_HASH_ALGO, NULL, 20, 16, 0, 0, 0, 12),
+#endif
+
+//TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_RSA_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_3DES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, TLS_KEY_EXCH_RSA_PSK, DES3_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 24, 8, 8, 0, 12),
+#endif
+
+//TLS_RSA_PSK_WITH_AES_128_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_RSA_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_PSK_WITH_AES_128_CBC_SHA, TLS_KEY_EXCH_RSA_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_PSK_WITH_AES_256_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_RSA_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_PSK_WITH_AES_256_CBC_SHA, TLS_KEY_EXCH_RSA_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, TLS_KEY_EXCH_RSA_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, TLS_KEY_EXCH_RSA_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_PSK_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, TLS_KEY_EXCH_RSA_PSK, AES_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_PSK_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, TLS_KEY_EXCH_RSA_PSK, AES_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256, TLS_KEY_EXCH_RSA_PSK, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384, TLS_KEY_EXCH_RSA_PSK, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_PSK_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256, TLS_KEY_EXCH_RSA_PSK, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_PSK_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384, TLS_KEY_EXCH_RSA_PSK, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256, TLS_KEY_EXCH_RSA_PSK, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384, TLS_KEY_EXCH_RSA_PSK, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_PSK_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256, TLS_KEY_EXCH_RSA_PSK, ARIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_PSK_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384, TLS_KEY_EXCH_RSA_PSK, ARIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_RSA_PSK_SUPPORT == ENABLED && TLS_CHACHA20_POLY1305_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256, TLS_KEY_EXCH_RSA_PSK, NULL, CIPHER_MODE_CHACHA20_POLY1305, NULL, SHA256_HASH_ALGO, 0, 32, 12, 0, 16, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_RC4_128_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_STREAM_CIPHER_SUPPORT == ENABLED && TLS_RC4_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_RC4_128_SHA, TLS_KEY_EXCH_DHE_PSK, RC4_CIPHER_ALGO, CIPHER_MODE_STREAM, SHA1_HASH_ALGO, NULL, 20, 16, 0, 0, 0, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_3DES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, TLS_KEY_EXCH_DHE_PSK, DES3_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 24, 8, 8, 0, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_AES_128_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_AES_128_CBC_SHA, TLS_KEY_EXCH_DHE_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_AES_256_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_AES_256_CBC_SHA, TLS_KEY_EXCH_DHE_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, TLS_KEY_EXCH_DHE_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, TLS_KEY_EXCH_DHE_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_AES_128_CCM cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_CCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_AES_128_CCM, TLS_KEY_EXCH_DHE_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_AES_256_CCM cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_CCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_AES_256_CCM, TLS_KEY_EXCH_DHE_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_AES_128_CCM_8 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_CCM_8_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_AES_128_CCM_8, TLS_KEY_EXCH_DHE_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 8, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_AES_256_CCM_8 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_CCM_8_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_AES_256_CCM_8, TLS_KEY_EXCH_DHE_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CCM, NULL, SHA256_HASH_ALGO, 0, 32, 4, 8, 8, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, TLS_KEY_EXCH_DHE_PSK, AES_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, TLS_KEY_EXCH_DHE_PSK, AES_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, TLS_KEY_EXCH_DHE_PSK, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, TLS_KEY_EXCH_DHE_PSK, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256, TLS_KEY_EXCH_DHE_PSK, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384, TLS_KEY_EXCH_DHE_PSK, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256, TLS_KEY_EXCH_DHE_PSK, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384, TLS_KEY_EXCH_DHE_PSK, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256, TLS_KEY_EXCH_DHE_PSK, ARIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA256_HASH_ALGO, 0, 16, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_GCM_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384, TLS_KEY_EXCH_DHE_PSK, ARIA_CIPHER_ALGO, CIPHER_MODE_GCM, NULL, SHA384_HASH_ALGO, 0, 32, 4, 8, 16, 12),
+#endif
+
+//TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_DHE_PSK_SUPPORT == ENABLED && TLS_CHACHA20_POLY1305_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256, TLS_KEY_EXCH_DHE_PSK, NULL, CIPHER_MODE_CHACHA20_POLY1305, NULL, SHA256_HASH_ALGO, 0, 32, 12, 0, 16, 12),
+#endif
+
+//TLS_ECDHE_PSK_WITH_RC4_128_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_ECDHE_PSK_SUPPORT == ENABLED && TLS_STREAM_CIPHER_SUPPORT == ENABLED && TLS_RC4_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_PSK_WITH_RC4_128_SHA, TLS_KEY_EXCH_ECDHE_PSK, RC4_CIPHER_ALGO, CIPHER_MODE_STREAM, SHA1_HASH_ALGO, NULL, 20, 16, 0, 0, 0, 12),
+#endif
+
+//TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_ECDHE_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_3DES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA, TLS_KEY_EXCH_ECDHE_PSK, DES3_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 24, 8, 8, 0, 12),
+#endif
+
+//TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_ECDHE_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, TLS_KEY_EXCH_ECDHE_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA cipher suite
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_ECDHE_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA1_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, TLS_KEY_EXCH_ECDHE_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA1_HASH_ALGO, NULL, 20, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, TLS_KEY_EXCH_ECDHE_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_AES_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, TLS_KEY_EXCH_ECDHE_PSK, AES_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, TLS_KEY_EXCH_ECDHE_PSK, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_CAMELLIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, TLS_KEY_EXCH_ECDHE_PSK, CAMELLIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256, TLS_KEY_EXCH_ECDHE_PSK, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA256_HASH_ALGO, SHA256_HASH_ALGO, 32, 16, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_PSK_SUPPORT == ENABLED && TLS_CBC_CIPHER_SUPPORT == ENABLED && TLS_ARIA_SUPPORT == ENABLED && TLS_SHA384_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384, TLS_KEY_EXCH_ECDHE_PSK, ARIA_CIPHER_ALGO, CIPHER_MODE_CBC, SHA384_HASH_ALGO, SHA384_HASH_ALGO, 48, 32, 16, 16, 0, 12),
+#endif
+
+//TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 cipher suite
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_ECDHE_PSK_SUPPORT == ENABLED && TLS_CHACHA20_POLY1305_SUPPORT == ENABLED && TLS_SHA256_SUPPORT == ENABLED)
+   TLS_CIPHER_SUITE(TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, TLS_KEY_EXCH_ECDHE_PSK, NULL, CIPHER_MODE_CHACHA20_POLY1305, NULL, SHA256_HASH_ALGO, 0, 32, 12, 0, 16, 12),
+#endif
+};
+
+
+/**
+ * @brief Determine the number of cipher suites supported
+ * @return Number of supported cipher suites
+ **/
+
+uint_t tlsGetNumSupportedCipherSuites(void)
+{
+   //Return the number of supported cipher suites
+   return arraysize(tlsSupportedCipherSuites);
+}
+
+
+/**
+ * @brief Convert cipher suite identifier to string representation
+ * @param[in] identifier Cipher suite identifier
+ * @return Cipher suite name
+ **/
+
+const char_t *tlsGetCipherSuiteName(uint16_t identifier)
+{
+   uint_t i;
+
+   //Default name for unknown cipher suites
+   static const char_t defaultName[] = "Unknown";
+
+   //Parse the list of supported cipher suite
+   for(i = 0; i < arraysize(tlsSupportedCipherSuites); i++)
+   {
+      //The current cipher suite matches the specified identifier?
+      if(tlsSupportedCipherSuites[i].identifier == identifier)
+         return tlsSupportedCipherSuites[i].name;
+   }
+
+   //Unknown cipher suite...
+   return defaultName;
+}
+
+
+/**
+ * @brief Check whether a cipher suite is supported
+ * @param[in] identifier Cipher suite identifier
+ * @return TRUE if the specified cipher suite is supported, else FALSE
+ **/
+
+bool_t tlsIsCipherSuiteSupported(uint16_t identifier)
+{
+   uint_t i;
+
+   //Parse the list of supported cipher suite
+   for(i = 0; i < arraysize(tlsSupportedCipherSuites); i++)
+   {
+      //The current cipher suite matches the specified identifier?
+      if(tlsSupportedCipherSuites[i].identifier == identifier)
+         return TRUE;
+   }
+
+   //The specified cipher suite is not supported...
+   return FALSE;
+}
+
+
+/**
+ * @brief Check whether the specified identifier matches an ECC cipher suite
+ * @param[in] identifier Cipher suite identifier
+ * @return TRUE if the specified cipher suite is supported, else FALSE
+ **/
+
+bool_t tlsIsEccCipherSuite(uint16_t identifier)
+{
+   uint_t i;
+
+   //Parse the list of supported cipher suite
+   for(i = 0; i < arraysize(tlsSupportedCipherSuites); i++)
+   {
+      //The current cipher suite matches the specified identifier?
+      if(tlsSupportedCipherSuites[i].identifier == identifier)
+      {
+         //ECC cipher suite?
+         switch(tlsSupportedCipherSuites[i].keyExchMethod)
+         {
+         case TLS_KEY_EXCH_ECDH_RSA:
+         case TLS_KEY_EXCH_ECDH_ECDSA:
+         case TLS_KEY_EXCH_ECDH_ANON:
+         case TLS_KEY_EXCH_ECDHE_RSA:
+         case TLS_KEY_EXCH_ECDHE_ECDSA:
+         case TLS_KEY_EXCH_ECDHE_PSK:
+            return TRUE;
+         default:
+            return FALSE;
+         }
+      }
+   }
+
+   //Unknown cipher suite...
+   return FALSE;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls_cipher_suites.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,413 @@
+/**
+ * @file tls_cipher_suites.h
+ * @brief TLS cipher suites
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TLS_CIPHER_SUITES_H
+#define _TLS_CIPHER_SUITES_H
+
+//Dependencies
+#include "crypto.h"
+#include "tls.h"
+
+//Macro for defining a cipher suite
+#define TLS_CIPHER_SUITE(identifier, keyExchMethod, cipherAlgo, cipherMode, hashAlgo, prfHashAlgo, \
+   macKeyLen, encKeyLen, fixedIvLen, recordIvLen, authTagLen, verifyDataLen) \
+   {identifier, #identifier, keyExchMethod, cipherAlgo, cipherMode, hashAlgo, prfHashAlgo, \
+   macKeyLen, encKeyLen, fixedIvLen, recordIvLen, authTagLen, verifyDataLen}
+
+
+/**
+ * @brief Cipher suite list
+ **/
+
+typedef enum
+{
+   TLS_NULL_WITH_NULL_NULL                       = 0x0000, //RFC 2246
+
+   TLS_RSA_EXPORT_WITH_RC4_40_MD5                = 0x0003, //RFC 2246
+   TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5            = 0x0006, //RFC 2246
+   TLS_RSA_EXPORT_WITH_DES40_CBC_SHA             = 0x0008, //RFC 2246
+   TLS_RSA_WITH_NULL_MD5                         = 0x0001, //RFC 2246
+   TLS_RSA_WITH_NULL_SHA                         = 0x0002, //RFC 2246
+   TLS_RSA_WITH_NULL_SHA256                      = 0x003B, //RFC 5246
+   TLS_RSA_WITH_RC4_128_MD5                      = 0x0004, //RFC 2246
+   TLS_RSA_WITH_RC4_128_SHA                      = 0x0005, //RFC 2246
+   TLS_RSA_WITH_IDEA_CBC_SHA                     = 0x0007, //RFC 2246
+   TLS_RSA_WITH_DES_CBC_SHA                      = 0x0009, //RFC 2246
+   TLS_RSA_WITH_3DES_EDE_CBC_SHA                 = 0x000A, //RFC 2246
+   TLS_RSA_WITH_AES_128_CBC_SHA                  = 0x002F, //RFC 3268
+   TLS_RSA_WITH_AES_256_CBC_SHA                  = 0x0035, //RFC 3268
+   TLS_RSA_WITH_AES_128_CBC_SHA256               = 0x003C, //RFC 5246
+   TLS_RSA_WITH_AES_256_CBC_SHA256               = 0x003D, //RFC 5246
+   TLS_RSA_WITH_AES_128_CCM                      = 0xC09C, //RFC 6655
+   TLS_RSA_WITH_AES_256_CCM                      = 0xC09D, //RFC 6655
+   TLS_RSA_WITH_AES_128_CCM_8                    = 0xC0A0, //RFC 6655
+   TLS_RSA_WITH_AES_256_CCM_8                    = 0xC0A1, //RFC 6655
+   TLS_RSA_WITH_AES_128_GCM_SHA256               = 0x009C, //RFC 5288
+   TLS_RSA_WITH_AES_256_GCM_SHA384               = 0x009D, //RFC 5288
+   TLS_RSA_WITH_CAMELLIA_128_CBC_SHA             = 0x0041, //RFC 5932
+   TLS_RSA_WITH_CAMELLIA_256_CBC_SHA             = 0x0084, //RFC 5932
+   TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256          = 0x00BA, //RFC 5932
+   TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256          = 0x00C0, //RFC 5932
+   TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256          = 0xC07A, //RFC 6367
+   TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384          = 0xC07B, //RFC 6367
+   TLS_RSA_WITH_SEED_CBC_SHA                     = 0x0096, //RFC 4162
+   TLS_RSA_WITH_ARIA_128_CBC_SHA256              = 0xC03C, //RFC 6209
+   TLS_RSA_WITH_ARIA_256_CBC_SHA384              = 0xC03D, //RFC 6209
+   TLS_RSA_WITH_ARIA_128_GCM_SHA256              = 0xC050, //RFC 6209
+   TLS_RSA_WITH_ARIA_256_GCM_SHA384              = 0xC051, //RFC 6209
+
+   TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA          = 0x000E, //RFC 2246
+   TLS_DH_RSA_WITH_DES_CBC_SHA                   = 0x000F, //RFC 2246
+   TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA              = 0x0010, //RFC 2246
+   TLS_DH_RSA_WITH_AES_128_CBC_SHA               = 0x0031, //RFC 3268
+   TLS_DH_RSA_WITH_AES_256_CBC_SHA               = 0x0037, //RFC 3268
+   TLS_DH_RSA_WITH_AES_128_CBC_SHA256            = 0x003F, //RFC 5246
+   TLS_DH_RSA_WITH_AES_256_CBC_SHA256            = 0x0069, //RFC 5246
+   TLS_DH_RSA_WITH_AES_128_GCM_SHA256            = 0x00A0, //RFC 5288
+   TLS_DH_RSA_WITH_AES_256_GCM_SHA384            = 0x00A1, //RFC 5288
+   TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA          = 0x0043, //RFC 5932
+   TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA          = 0x0086, //RFC 5932
+   TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256       = 0x00BC, //RFC 5932
+   TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256       = 0x00C2, //RFC 5932
+   TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256       = 0xC07E, //RFC 6367
+   TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384       = 0xC07F, //RFC 6367
+   TLS_DH_RSA_WITH_SEED_CBC_SHA                  = 0x0098, //RFC 4162
+   TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256           = 0xC040, //RFC 6209
+   TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384           = 0xC041, //RFC 6209
+   TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256           = 0xC054, //RFC 6209
+   TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384           = 0xC055, //RFC 6209
+
+   TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA         = 0x0014, //RFC 2246
+   TLS_DHE_RSA_WITH_DES_CBC_SHA                  = 0x0015, //RFC 2246
+   TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA             = 0x0016, //RFC 2246
+   TLS_DHE_RSA_WITH_AES_128_CBC_SHA              = 0x0033, //RFC 3268
+   TLS_DHE_RSA_WITH_AES_256_CBC_SHA              = 0x0039, //RFC 3268
+   TLS_DHE_RSA_WITH_AES_128_CBC_SHA256           = 0x0067, //RFC 5246
+   TLS_DHE_RSA_WITH_AES_256_CBC_SHA256           = 0x006B, //RFC 5246
+   TLS_DHE_RSA_WITH_AES_128_CCM                  = 0xC09E, //RFC 6655
+   TLS_DHE_RSA_WITH_AES_256_CCM                  = 0xC09F, //RFC 6655
+   TLS_DHE_RSA_WITH_AES_128_CCM_8                = 0xC0A2, //RFC 6655
+   TLS_DHE_RSA_WITH_AES_256_CCM_8                = 0xC0A3, //RFC 6655
+   TLS_DHE_RSA_WITH_AES_128_GCM_SHA256           = 0x009E, //RFC 5288
+   TLS_DHE_RSA_WITH_AES_256_GCM_SHA384           = 0x009F, //RFC 5288
+   TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA         = 0x0045, //RFC 5932
+   TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA         = 0x0088, //RFC 5932
+   TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256      = 0x00BE, //RFC 5932
+   TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256      = 0x00C4, //RFC 5932
+   TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256      = 0xC07C, //RFC 6367
+   TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384      = 0xC07D, //RFC 6367
+   TLS_DHE_RSA_WITH_SEED_CBC_SHA                 = 0x009A, //RFC 4162
+   TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256          = 0xC044, //RFC 6209
+   TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384          = 0xC045, //RFC 6209
+   TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256          = 0xC052, //RFC 6209
+   TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384          = 0xC053, //RFC 6209
+   TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256     = 0xCCAA, //RFC 7905
+
+   TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA          = 0x000B, //RFC 2246
+   TLS_DH_DSS_WITH_DES_CBC_SHA                   = 0x000C, //RFC 2246
+   TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA              = 0x000D, //RFC 2246
+   TLS_DH_DSS_WITH_AES_128_CBC_SHA               = 0x0030, //RFC 3268
+   TLS_DH_DSS_WITH_AES_256_CBC_SHA               = 0x0036, //RFC 3268
+   TLS_DH_DSS_WITH_AES_128_CBC_SHA256            = 0x003E, //RFC 5246
+   TLS_DH_DSS_WITH_AES_256_CBC_SHA256            = 0x0068, //RFC 5246
+   TLS_DH_DSS_WITH_AES_128_GCM_SHA256            = 0x00A4, //RFC 5288
+   TLS_DH_DSS_WITH_AES_256_GCM_SHA384            = 0x00A5, //RFC 5288
+   TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA          = 0x0042, //RFC 5932
+   TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA          = 0x0085, //RFC 5932
+   TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256       = 0x00BB, //RFC 5932
+   TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256       = 0x00C1, //RFC 5932
+   TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256       = 0xC082, //RFC 6367
+   TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384       = 0xC083, //RFC 6367
+   TLS_DH_DSS_WITH_SEED_CBC_SHA                  = 0x0097, //RFC 4162
+   TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256           = 0xC03E, //RFC 6209
+   TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384           = 0xC03F, //RFC 6209
+   TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256           = 0xC058, //RFC 6209
+   TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384           = 0xC059, //RFC 6209
+
+   TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA         = 0x0011, //RFC 2246
+   TLS_DHE_DSS_WITH_DES_CBC_SHA                  = 0x0012, //RFC 2246
+   TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA             = 0x0013, //RFC 2246
+   TLS_DHE_DSS_WITH_AES_128_CBC_SHA              = 0x0032, //RFC 3268
+   TLS_DHE_DSS_WITH_AES_256_CBC_SHA              = 0x0038, //RFC 3268
+   TLS_DHE_DSS_WITH_AES_128_CBC_SHA256           = 0x0040, //RFC 5246
+   TLS_DHE_DSS_WITH_AES_256_CBC_SHA256           = 0x006A, //RFC 5246
+   TLS_DHE_DSS_WITH_AES_128_GCM_SHA256           = 0x00A2, //RFC 5288
+   TLS_DHE_DSS_WITH_AES_256_GCM_SHA384           = 0x00A3, //RFC 5288
+   TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA         = 0x0044, //RFC 5932
+   TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA         = 0x0087, //RFC 5932
+   TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256      = 0x00BD, //RFC 5932
+   TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256      = 0x00C3, //RFC 5932
+   TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256      = 0xC080, //RFC 6367
+   TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384      = 0xC081, //RFC 6367
+   TLS_DHE_DSS_WITH_SEED_CBC_SHA                 = 0x0099, //RFC 4162
+   TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256          = 0xC042, //RFC 6209
+   TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384          = 0xC043, //RFC 6209
+   TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256          = 0xC056, //RFC 6209
+   TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384          = 0xC057, //RFC 6209
+
+   TLS_DH_ANON_EXPORT_WITH_RC4_40_MD5            = 0x0017, //RFC 2246
+   TLS_DH_ANON_EXPORT_WITH_DES40_CBC_SHA         = 0x0019, //RFC 2246
+   TLS_DH_ANON_WITH_RC4_128_MD5                  = 0x0018, //RFC 2246
+   TLS_DH_ANON_WITH_DES_CBC_SHA                  = 0x001A, //RFC 2246
+   TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA             = 0x001B, //RFC 2246
+   TLS_DH_ANON_WITH_AES_128_CBC_SHA              = 0x0034, //RFC 3268
+   TLS_DH_ANON_WITH_AES_256_CBC_SHA              = 0x003A, //RFC 3268
+   TLS_DH_ANON_WITH_AES_128_CBC_SHA256           = 0x006C, //RFC 5246
+   TLS_DH_ANON_WITH_AES_256_CBC_SHA256           = 0x006D, //RFC 5246
+   TLS_DH_ANON_WITH_AES_128_GCM_SHA256           = 0x00A6, //RFC 5288
+   TLS_DH_ANON_WITH_AES_256_GCM_SHA384           = 0x00A7, //RFC 5288
+   TLS_DH_ANON_WITH_CAMELLIA_128_CBC_SHA         = 0x0046, //RFC 5932
+   TLS_DH_ANON_WITH_CAMELLIA_256_CBC_SHA         = 0x0089, //RFC 5932
+   TLS_DH_ANON_WITH_CAMELLIA_128_CBC_SHA256      = 0x00BF, //RFC 5932
+   TLS_DH_ANON_WITH_CAMELLIA_256_CBC_SHA256      = 0x00C5, //RFC 5932
+   TLS_DH_ANON_WITH_CAMELLIA_128_GCM_SHA256      = 0xC084, //RFC 6367
+   TLS_DH_ANON_WITH_CAMELLIA_256_GCM_SHA384      = 0xC085, //RFC 6367
+   TLS_DH_ANON_WITH_SEED_CBC_SHA                 = 0x009B, //RFC 4162
+   TLS_DH_ANON_WITH_ARIA_128_CBC_SHA256          = 0xC046, //RFC 6209
+   TLS_DH_ANON_WITH_ARIA_256_CBC_SHA384          = 0xC047, //RFC 6209
+   TLS_DH_ANON_WITH_ARIA_128_GCM_SHA256          = 0xC05A, //RFC 6209
+   TLS_DH_ANON_WITH_ARIA_256_GCM_SHA384          = 0xC05B, //RFC 6209
+
+   TLS_ECDH_RSA_WITH_NULL_SHA                    = 0xC00B, //RFC 4492
+   TLS_ECDH_RSA_WITH_RC4_128_SHA                 = 0xC00C, //RFC 4492
+   TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA            = 0xC00D, //RFC 4492
+   TLS_ECDH_RSA_WITH_AES_128_CBC_SHA             = 0xC00E, //RFC 4492
+   TLS_ECDH_RSA_WITH_AES_256_CBC_SHA             = 0xC00F, //RFC 4492
+   TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256          = 0xC029, //RFC 5289
+   TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384          = 0xC02A, //RFC 5289
+   TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256          = 0xC031, //RFC 5289
+   TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384          = 0xC032, //RFC 5289
+   TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256     = 0xC078, //RFC 6367
+   TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384     = 0xC079, //RFC 6367
+   TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256     = 0xC08C, //RFC 6367
+   TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384     = 0xC08D, //RFC 6367
+   TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256         = 0xC04E, //RFC 6209
+   TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384         = 0xC04F, //RFC 6209
+   TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256         = 0xC062, //RFC 6209
+   TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384         = 0xC063, //RFC 6209
+
+   TLS_ECDHE_RSA_WITH_NULL_SHA                   = 0xC010, //RFC 4492
+   TLS_ECDHE_RSA_WITH_RC4_128_SHA                = 0xC011, //RFC 4492
+   TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA           = 0xC012, //RFC 4492
+   TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA            = 0xC013, //RFC 4492
+   TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA            = 0xC014, //RFC 4492
+   TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256         = 0xC027, //RFC 5289
+   TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384         = 0xC028, //RFC 5289
+   TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256         = 0xC02F, //RFC 5289
+   TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384         = 0xC030, //RFC 5289
+   TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256    = 0xC076, //RFC 6367
+   TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384    = 0xC077, //RFC 6367
+   TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256    = 0xC08A, //RFC 6367
+   TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384    = 0xC08B, //RFC 6367
+   TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256        = 0xC04C, //RFC 6209
+   TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384        = 0xC04D, //RFC 6209
+   TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256        = 0xC060, //RFC 6209
+   TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384        = 0xC061, //RFC 6209
+   TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256   = 0xCCA8, //RFC 7905
+
+   TLS_ECDH_ECDSA_WITH_NULL_SHA                  = 0xC001, //RFC 4492
+   TLS_ECDH_ECDSA_WITH_RC4_128_SHA               = 0xC002, //RFC 4492
+   TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA          = 0xC003, //RFC 4492
+   TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA           = 0xC004, //RFC 4492
+   TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA           = 0xC005, //RFC 4492
+   TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256        = 0xC025, //RFC 5289
+   TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384        = 0xC026, //RFC 5289
+   TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256        = 0xC02D, //RFC 5289
+   TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384        = 0xC02E, //RFC 5289
+   TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256   = 0xC074, //RFC 6367
+   TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384   = 0xC075, //RFC 6367
+   TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256   = 0xC088, //RFC 6367
+   TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384   = 0xC089, //RFC 6367
+   TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256       = 0xC04A, //RFC 6209
+   TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384       = 0xC04B, //RFC 6209
+   TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256       = 0xC05E, //RFC 6209
+   TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384       = 0xC05F, //RFC 6209
+
+   TLS_ECDHE_ECDSA_WITH_NULL_SHA                 = 0xC006, //RFC 4492
+   TLS_ECDHE_ECDSA_WITH_RC4_128_SHA              = 0xC007, //RFC 4492
+   TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA         = 0xC008, //RFC 4492
+   TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA          = 0xC009, //RFC 4492
+   TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA          = 0xC00A, //RFC 4492
+   TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256       = 0xC023, //RFC 5289
+   TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384       = 0xC024, //RFC 5289
+   TLS_ECDHE_ECDSA_WITH_AES_128_CCM              = 0xC0AC, //RFC 7251
+   TLS_ECDHE_ECDSA_WITH_AES_256_CCM              = 0xC0AD, //RFC 7251
+   TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8            = 0xC0AE, //RFC 7251
+   TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8            = 0xC0AF, //RFC 7251
+   TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256       = 0xC02B, //RFC 5289
+   TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384       = 0xC02C, //RFC 5289
+   TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256  = 0xC072, //RFC 6367
+   TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384  = 0xC073, //RFC 6367
+   TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256  = 0xC086, //RFC 6367
+   TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384  = 0xC087, //RFC 6367
+   TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256      = 0xC048, //RFC 6209
+   TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384      = 0xC049, //RFC 6209
+   TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256      = 0xC05C, //RFC 6209
+   TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384      = 0xC05D, //RFC 6209
+   TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9, //RFC 7905
+
+   TLS_ECDH_ANON_WITH_NULL_SHA                   = 0xC015, //RFC 4492
+   TLS_ECDH_ANON_WITH_RC4_128_SHA                = 0xC016, //RFC 4492
+   TLS_ECDH_ANON_WITH_3DES_EDE_CBC_SHA           = 0xC017, //RFC 4492
+   TLS_ECDH_ANON_WITH_AES_128_CBC_SHA            = 0xC018, //RFC 4492
+   TLS_ECDH_ANON_WITH_AES_256_CBC_SHA            = 0xC019, //RFC 4492
+
+   TLS_PSK_WITH_NULL_SHA                         = 0x002C, //RFC 4785
+   TLS_PSK_WITH_NULL_SHA256                      = 0x00B0, //RFC 5487
+   TLS_PSK_WITH_NULL_SHA384                      = 0x00B1, //RFC 5487
+   TLS_PSK_WITH_RC4_128_SHA                      = 0x008A, //RFC 4279
+   TLS_PSK_WITH_3DES_EDE_CBC_SHA                 = 0x008B, //RFC 4279
+   TLS_PSK_WITH_AES_128_CBC_SHA                  = 0x008C, //RFC 4279
+   TLS_PSK_WITH_AES_256_CBC_SHA                  = 0x008D, //RFC 4279
+   TLS_PSK_WITH_AES_128_CBC_SHA256               = 0x00AE, //RFC 5487
+   TLS_PSK_WITH_AES_256_CBC_SHA384               = 0x00AF, //RFC 5487
+   TLS_PSK_WITH_AES_128_CCM                      = 0xC0A4, //RFC 6655
+   TLS_PSK_WITH_AES_256_CCM                      = 0xC0A5, //RFC 6655
+   TLS_PSK_WITH_AES_128_CCM_8                    = 0xC0A8, //RFC 6655
+   TLS_PSK_WITH_AES_256_CCM_8                    = 0xC0A9, //RFC 6655
+   TLS_PSK_WITH_AES_128_GCM_SHA256               = 0x00A8, //RFC 5487
+   TLS_PSK_WITH_AES_256_GCM_SHA384               = 0x00A9, //RFC 5487
+   TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256          = 0xC094, //RFC 6367
+   TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384          = 0xC095, //RFC 6367
+   TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256          = 0xC08E, //RFC 6367
+   TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384          = 0xC08F, //RFC 6367
+   TLS_PSK_WITH_ARIA_128_CBC_SHA256              = 0xC064, //RFC 6209
+   TLS_PSK_WITH_ARIA_256_CBC_SHA384              = 0xC065, //RFC 6209
+   TLS_PSK_WITH_ARIA_128_GCM_SHA256              = 0xC06A, //RFC 6209
+   TLS_PSK_WITH_ARIA_256_GCM_SHA384              = 0xC06B, //RFC 6209
+   TLS_PSK_WITH_CHACHA20_POLY1305_SHA256         = 0xCCAB, //RFC 7905
+
+   TLS_RSA_PSK_WITH_NULL_SHA                     = 0x002E, //RFC 4785
+   TLS_RSA_PSK_WITH_NULL_SHA256                  = 0x00B8, //RFC 5487
+   TLS_RSA_PSK_WITH_NULL_SHA384                  = 0x00B9, //RFC 5487
+   TLS_RSA_PSK_WITH_RC4_128_SHA                  = 0x0092, //RFC 4279
+   TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA             = 0x0093, //RFC 4279
+   TLS_RSA_PSK_WITH_AES_128_CBC_SHA              = 0x0094, //RFC 4279
+   TLS_RSA_PSK_WITH_AES_256_CBC_SHA              = 0x0095, //RFC 4279
+   TLS_RSA_PSK_WITH_AES_128_CBC_SHA256           = 0x00B6, //RFC 5487
+   TLS_RSA_PSK_WITH_AES_256_CBC_SHA384           = 0x00B7, //RFC 5487
+   TLS_RSA_PSK_WITH_AES_128_GCM_SHA256           = 0x00AC, //RFC 5487
+   TLS_RSA_PSK_WITH_AES_256_GCM_SHA384           = 0x00AD, //RFC 5487
+   TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256      = 0xC098, //RFC 6367
+   TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384      = 0xC099, //RFC 6367
+   TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256      = 0xC092, //RFC 6367
+   TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384      = 0xC093, //RFC 6367
+   TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256          = 0xC068, //RFC 6209
+   TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384          = 0xC069, //RFC 6209
+   TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256          = 0xC06E, //RFC 6209
+   TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384          = 0xC06F, //RFC 6209
+   TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256     = 0xCCAE, //RFC 7905
+
+   TLS_DHE_PSK_WITH_NULL_SHA                     = 0x002D, //RFC 4785
+   TLS_DHE_PSK_WITH_NULL_SHA256                  = 0x00B4, //RFC 5487
+   TLS_DHE_PSK_WITH_NULL_SHA384                  = 0x00B5, //RFC 5487
+   TLS_DHE_PSK_WITH_RC4_128_SHA                  = 0x008E, //RFC 4279
+   TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA             = 0x008F, //RFC 4279
+   TLS_DHE_PSK_WITH_AES_128_CBC_SHA              = 0x0090, //RFC 4279
+   TLS_DHE_PSK_WITH_AES_256_CBC_SHA              = 0x0091, //RFC 4279
+   TLS_DHE_PSK_WITH_AES_128_CBC_SHA256           = 0x00B2, //RFC 5487
+   TLS_DHE_PSK_WITH_AES_256_CBC_SHA384           = 0x00B3, //RFC 5487
+   TLS_DHE_PSK_WITH_AES_128_CCM                  = 0xC0A6, //RFC 6655
+   TLS_DHE_PSK_WITH_AES_256_CCM                  = 0xC0A7, //RFC 6655
+   TLS_DHE_PSK_WITH_AES_128_CCM_8                = 0xC0AA, //RFC 6655
+   TLS_DHE_PSK_WITH_AES_256_CCM_8                = 0xC0AB, //RFC 6655
+   TLS_DHE_PSK_WITH_AES_128_GCM_SHA256           = 0x00AA, //RFC 5487
+   TLS_DHE_PSK_WITH_AES_256_GCM_SHA384           = 0x00AB, //RFC 5487
+   TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256      = 0xC096, //RFC 6367
+   TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384      = 0xC097, //RFC 6367
+   TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256      = 0xC090, //RFC 6367
+   TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384      = 0xC091, //RFC 6367
+   TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256          = 0xC066, //RFC 6209
+   TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384          = 0xC067, //RFC 6209
+   TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256          = 0xC06C, //RFC 6209
+   TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384          = 0xC06D, //RFC 6209
+   TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256     = 0xCCAD, //RFC 7905
+
+   TLS_ECDHE_PSK_WITH_NULL_SHA                   = 0xC039, //RFC 5489
+   TLS_ECDHE_PSK_WITH_NULL_SHA256                = 0xC03A, //RFC 5489
+   TLS_ECDHE_PSK_WITH_NULL_SHA384                = 0xC03B, //RFC 5489
+   TLS_ECDHE_PSK_WITH_RC4_128_SHA                = 0xC033, //RFC 5489
+   TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA           = 0xC034, //RFC 5489
+   TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA            = 0xC035, //RFC 5489
+   TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA            = 0xC036, //RFC 5489
+   TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256         = 0xC037, //RFC 5489
+   TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384         = 0xC038, //RFC 5489
+   TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256         = 0xD005, //RFC draft
+   TLS_ECDHE_PSK_WITH_AES_256_CCM_SHA384         = 0xD006, //RFC draft
+   TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256       = 0xD003, //RFC draft
+   TLS_ECDHE_PSK_WITH_AES_256_CCM_8_SHA256       = 0xD004, //RFC draft
+   TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256         = 0xD001, //RFC draft
+   TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384         = 0xD002, //RFC draft
+   TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256    = 0xC09A, //RFC 6367
+   TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384    = 0xC09B, //RFC 6367
+   TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256        = 0xC070, //RFC 6209
+   TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384        = 0xC071, //RFC 6209
+   TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256   = 0xCCAC, //RFC 7905
+
+   TLS_KRB5_EXPORT_WITH_RC4_40_MD5               = 0x002B, //RFC 2712
+   TLS_KRB5_EXPORT_WITH_RC4_40_SHA               = 0x0028, //RFC 2712
+   TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5           = 0x002A, //RFC 2712
+   TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA           = 0x0027, //RFC 2712
+   TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5           = 0x0029, //RFC 2712
+   TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA           = 0x0026, //RFC 2712
+   TLS_KRB5_WITH_RC4_128_MD5                     = 0x0024, //RFC 2712
+   TLS_KRB5_WITH_RC4_128_SHA                     = 0x0020, //RFC 2712
+   TLS_KRB5_WITH_IDEA_CBC_MD5                    = 0x0025, //RFC 2712
+   TLS_KRB5_WITH_IDEA_CBC_SHA                    = 0x0021, //RFC 2712
+   TLS_KRB5_WITH_DES_CBC_MD5                     = 0x0022, //RFC 2712
+   TLS_KRB5_WITH_DES_CBC_SHA                     = 0x001E, //RFC 2712
+   TLS_KRB5_WITH_3DES_EDE_CBC_MD5                = 0x0023, //RFC 2712
+   TLS_KRB5_WITH_3DES_EDE_CBC_SHA                = 0x001F, //RFC 2712
+
+   TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA             = 0xC01A, //RFC 5054
+   TLS_SRP_SHA_WITH_AES_128_CBC_SHA              = 0xC01D, //RFC 5054
+   TLS_SRP_SHA_WITH_AES_256_CBC_SHA              = 0xC020, //RFC 5054
+
+   TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA         = 0xC01B, //RFC 5054
+   TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA          = 0xC01E, //RFC 5054
+   TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA          = 0xC021, //RFC 5054
+
+   TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA         = 0xC01C, //RFC 5054
+   TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA          = 0xC01F, //RFC 5054
+   TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA          = 0xC022, //RFC 5054
+
+   TLS_EMPTY_RENEGOTIATION_INFO_SCSV             = 0x00FF  //RFC 5746
+} TlsCipherSuiteList;
+
+
+//List of supported cipher suites
+extern const TlsCipherSuiteInfo tlsSupportedCipherSuites[];
+
+//TLS related functions
+uint_t tlsGetNumSupportedCipherSuites(void);
+const char_t *tlsGetCipherSuiteName(uint16_t identifier);
+bool_t tlsIsCipherSuiteSupported(uint16_t identifier);
+bool_t tlsIsEccCipherSuite(uint16_t identifier);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls_client.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1958 @@
+/**
+ * @file tls_client.c
+ * @brief Handshake message processing (TLS client)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The TLS protocol provides communications security over the Internet. The
+ * protocol allows client/server applications to communicate in a way that
+ * is designed to prevent eavesdropping, tampering, or message forgery
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TLS_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "tls.h"
+#include "tls_cipher_suites.h"
+#include "tls_client.h"
+#include "tls_client_misc.h"
+#include "tls_common.h"
+#include "tls_record.h"
+#include "tls_misc.h"
+#include "pem.h"
+#include "date_time.h"
+#include "debug.h"
+
+//Check SSL library configuration
+#if (TLS_SUPPORT == ENABLED && TLS_CLIENT_SUPPORT == ENABLED)
+
+
+/**
+ * @brief TLS client handshake
+ *
+ * TLS handshake protocol is responsible for the authentication
+ * and key exchange necessary to establish a secure session
+ *
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsClientHandshake(TlsContext *context)
+{
+   error_t error;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Wait for the handshake to complete
+   do
+   {
+      //Flush send buffer
+      if(context->state != TLS_STATE_CLOSED)
+         error = tlsWriteProtocolData(context, NULL, 0, TLS_TYPE_NONE);
+
+      //Check status code
+      if(!error)
+      {
+         //Check whether the handshake is complete
+         if(context->state == TLS_STATE_APPLICATION_DATA)
+         {
+            //At this is point, the handshake is complete and the client
+            //starts to exchange application-layer data
+            break;
+         }
+
+         //The TLS handshake is implemented as a state machine
+         //representing the current location in the protocol
+         switch(context->state)
+         {
+         //Default state?
+         case TLS_STATE_INIT:
+            //The client initiates the TLS handshake by sending a ClientHello
+            //message to the server
+            context->state = TLS_STATE_CLIENT_HELLO;
+            break;
+         //Sending ClientHello message?
+         case TLS_STATE_CLIENT_HELLO:
+            //When a client first connects to a server, it is required to send
+            //the ClientHello as its first message
+            error = tlsSendClientHello(context);
+            break;
+         //Sending Certificate message?
+         case TLS_STATE_CLIENT_CERTIFICATE:
+            //This is the first message the client can send after receiving a
+            //ServerHelloDone message. This message is only sent if the server
+            //requests a certificate
+            error = tlsSendCertificate(context);
+            break;
+         //Sending ClientKeyExchange message?
+         case TLS_STATE_CLIENT_KEY_EXCHANGE:
+            //This message is always sent by the client. It must immediately
+            //follow the client certificate message, if it is sent. Otherwise,
+            //it must be the first message sent by the client after it receives
+            //the ServerHelloDone message
+            error = tlsSendClientKeyExchange(context);
+            break;
+         //Sending CertificateVerify message?
+         case TLS_STATE_CERTIFICATE_VERIFY:
+            //This message is used to provide explicit verification of a client
+            //certificate. This message is only sent following a client certificate
+            //that has signing capability. When sent, it must immediately follow
+            //the clientKeyExchange message
+            error = tlsSendCertificateVerify(context);
+            break;
+         //Sending ChangeCipherSpec message?
+         case TLS_STATE_CLIENT_CHANGE_CIPHER_SPEC:
+            //The ChangeCipherSpec message is sent by the client and to notify the
+            //server that subsequent records will be protected under the newly
+            //negotiated CipherSpec and keys
+            error = tlsSendChangeCipherSpec(context);
+            break;
+         //Sending Finished message?
+         case TLS_STATE_CLIENT_FINISHED:
+            //A Finished message is always sent immediately after a changeCipherSpec
+            //message to verify that the key exchange and authentication processes
+            //were successful
+            error = tlsSendFinished(context);
+            break;
+         //Waiting for a message from the server?
+         case TLS_STATE_SERVER_HELLO:
+         case TLS_STATE_SERVER_CERTIFICATE:
+         case TLS_STATE_SERVER_KEY_EXCHANGE:
+         case TLS_STATE_CERTIFICATE_REQUEST:
+         case TLS_STATE_SERVER_HELLO_DONE:
+         case TLS_STATE_SERVER_CHANGE_CIPHER_SPEC:
+         case TLS_STATE_SERVER_FINISHED:
+            //Parse incoming handshake message
+            error = tlsParseServerMessage(context);
+            break;
+         //Sending Alert message?
+         case TLS_STATE_CLOSING:
+            //Mark the TLS connection as closed
+            context->state = TLS_STATE_CLOSED;
+            break;
+         //TLS connection closed?
+         case TLS_STATE_CLOSED:
+            //Debug message
+            TRACE_WARNING("TLS handshake failure!\r\n");
+            //Report an error
+            error = ERROR_HANDSHAKE_FAILED;
+            break;
+         //Invalid state?
+         default:
+            //Report an error
+            error = ERROR_UNEXPECTED_STATE;
+            break;
+         }
+      }
+
+      //Abort TLS handshake if an error was encountered
+   } while(!error);
+
+   //Any error to report?
+   if(error)
+   {
+      //Send an alert message to the server, if applicable
+      tlsProcessError(context, error);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse incoming handshake message
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsParseServerMessage(TlsContext *context)
+{
+   error_t error;
+   size_t length;
+   void *message;
+   TlsContentType contentType;
+
+   //A message can be fragmented across several records...
+   error = tlsReadProtocolData(context, &message, &length, &contentType);
+
+   //Check status code
+   if(!error)
+   {
+      //Handshake message received?
+      if(contentType == TLS_TYPE_HANDSHAKE)
+      {
+         //Check handshake message type
+         switch(((TlsHandshake *) message)->msgType)
+         {
+         //HelloRequest message received?
+         case TLS_TYPE_HELLO_REQUEST:
+            //The HelloRequest message can be sent at any time but it should be
+            //ignored by the client if it arrives in the middle of a handshake
+            error = NO_ERROR;
+            break;
+         //ServerHello message received?
+         case TLS_TYPE_SERVER_HELLO:
+            //The server will send this message in response to a ClientHello
+            //message when it was able to find an acceptable set of algorithms
+            error = tlsParseServerHello(context, message, length);
+            break;
+         //Certificate message received?
+         case TLS_TYPE_CERTIFICATE:
+            //The server must send a Certificate message whenever the agreed-
+            //upon key exchange method uses certificates for authentication. This
+            //message will always immediately follow the ServerHello message
+            error = tlsParseCertificate(context, message, length);
+            break;
+         //ServerKeyExchange message received?
+         case TLS_TYPE_SERVER_KEY_EXCHANGE:
+            //The ServerKeyExchange message is sent by the server only when the
+            //server Certificate message (if sent) does not contain enough data
+            //to allow the client to exchange a premaster secret
+            error = tlsParseServerKeyExchange(context, message, length);
+            break;
+         //CertificateRequest message received?
+         case TLS_TYPE_CERTIFICATE_REQUEST:
+            //A non-anonymous server can optionally request a certificate from the
+            //client, if appropriate for the selected cipher suite. This message,
+            //if sent, will immediately follow the ServerKeyExchange message
+            error = tlsParseCertificateRequest(context, message, length);
+            break;
+         //ServerHelloDone message received?
+         case TLS_TYPE_SERVER_HELLO_DONE:
+            //The ServerHelloDone message is sent by the server to indicate the
+            //end of the ServerHello and associated messages
+            error = tlsParseServerHelloDone(context, message, length);
+            break;
+         //Finished message received?
+         case TLS_TYPE_FINISHED:
+            //A Finished message is always sent immediately after a changeCipherSpec
+            //message to verify that the key exchange and authentication processes
+            //were successful
+            error = tlsParseFinished(context, message, length);
+            break;
+         //Invalid handshake message received?
+         default:
+            //Report an error
+            error = ERROR_UNEXPECTED_MESSAGE;
+         }
+      }
+      //ChangeCipherSpec message received?
+      else if(contentType == TLS_TYPE_CHANGE_CIPHER_SPEC)
+      {
+         //The ChangeCipherSpec message is sent by the server and to notify the
+         //client that subsequent records will be protected under the newly
+         //negotiated CipherSpec and keys
+         error = tlsParseChangeCipherSpec(context, message, length);
+      }
+      //Alert message received?
+      else if(contentType == TLS_TYPE_ALERT)
+      {
+         //Parse Alert message
+         error = tlsParseAlert(context, message, length);
+      }
+      //Application data received?
+      else
+      {
+         //The server cannot transmit application data
+         //before the handshake is completed
+         error = ERROR_UNEXPECTED_MESSAGE;
+      }
+
+      //Advance data pointer
+      context->rxBufferPos += length;
+      //Number of bytes still pending in the receive buffer
+      context->rxBufferLen -= length;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send ClientHello message
+ *
+ * When a client first connects to a server, it is required to send
+ * the ClientHello as its first message. The client can also send a
+ * ClientHello in response to a HelloRequest or on its own initiative
+ * in order to renegotiate the security parameters in an existing
+ * connection
+ *
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsSendClientHello(TlsContext *context)
+{
+   error_t error;
+   size_t length;
+   TlsClientHello *message;
+
+   //Point to the buffer where to format the message
+   message = (TlsClientHello *) context->txBuffer;
+
+   //Generate the client random value using a cryptographically-safe
+   //pseudorandom number generator
+   error = tlsGenerateRandomValue(context, &context->clientRandom);
+
+   //Check status code
+   if(!error)
+   {
+      //Format ClientHello message
+      error = tlsFormatClientHello(context, message, &length);
+   }
+
+   //Check status code
+   if(!error)
+   {
+      //Debug message
+      TRACE_INFO("Sending ClientHello message (%" PRIuSIZE " bytes)...\r\n", length);
+      TRACE_DEBUG_ARRAY("  ", message, length);
+
+      //Send handshake message
+      error = tlsWriteProtocolData(context, message, length, TLS_TYPE_HANDSHAKE);
+   }
+
+   //Check status code
+   if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
+   {
+      //Prepare to receive ServerHello message...
+      context->state = TLS_STATE_SERVER_HELLO;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send ClientKeyExchange message
+ *
+ * This message is always sent by the client. It must immediately
+ * follow the client Certificate message, if it is sent. Otherwise,
+ * it must be the first message sent by the client after it receives
+ * the ServerHelloDone message
+ *
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsSendClientKeyExchange(TlsContext *context)
+{
+   error_t error;
+   size_t length;
+   TlsClientKeyExchange *message;
+
+   //Point to the buffer where to format the message
+   message = (TlsClientKeyExchange *) context->txBuffer;
+
+   //Format ClientKeyExchange message
+   error = tlsFormatClientKeyExchange(context, message, &length);
+
+   //Check status code
+   if(!error)
+   {
+      //Debug message
+      TRACE_INFO("Sending ClientKeyExchange message (%" PRIuSIZE " bytes)...\r\n", length);
+      TRACE_DEBUG_ARRAY("  ", message, length);
+
+      //Send handshake message
+      error = tlsWriteProtocolData(context, message, length, TLS_TYPE_HANDSHAKE);
+   }
+
+   //Check status code
+   if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
+   {
+      //Derive session keys from the premaster secret
+      error = tlsGenerateKeys(context);
+
+      //Key material successfully generated?
+      if(!error)
+      {
+         //Prepare to send CertificateVerify message...
+         context->state = TLS_STATE_CERTIFICATE_VERIFY;
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send CertificateVerify message
+ *
+ * The CertificateVerify message is used to provide explicit verification
+ * of a client certificate. This message is only sent following a client
+ * certificate that has signing capability
+ *
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsSendCertificateVerify(TlsContext *context)
+{
+   error_t error;
+   size_t length;
+   TlsCertificateVerify *message;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //The CertificateVerify message is only sent following a client
+   //certificate that has signing capability
+   if(context->cert != NULL)
+   {
+      //Check certificate type
+      if(context->cert->type == TLS_CERT_RSA_SIGN ||
+         context->cert->type == TLS_CERT_DSS_SIGN ||
+         context->cert->type == TLS_CERT_ECDSA_SIGN)
+      {
+         //Point to the buffer where to format the message
+         message = (TlsCertificateVerify *) context->txBuffer;
+
+         //Format CertificateVerify message
+         error = tlsFormatCertificateVerify(context, message, &length);
+
+         //Check status code
+         if(!error)
+         {
+            //Debug message
+            TRACE_INFO("Sending CertificateVerify message (%" PRIuSIZE " bytes)...\r\n", length);
+            TRACE_DEBUG_ARRAY("  ", message, length);
+
+            //Send handshake message
+            error = tlsWriteProtocolData(context, message, length, TLS_TYPE_HANDSHAKE);
+         }
+      }
+   }
+
+   //Check status code
+   if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
+   {
+      //Prepare to send ChangeCipherSpec message...
+      context->state = TLS_STATE_CLIENT_CHANGE_CIPHER_SPEC;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Format ClientHello message
+ * @param[in] context Pointer to the TLS context
+ * @param[out] message Buffer where to format the ClientHello message
+ * @param[out] length Length of the resulting ClientHello message
+ * @return Error code
+ **/
+
+error_t tlsFormatClientHello(TlsContext *context,
+   TlsClientHello *message, size_t *length)
+{
+   uint_t i;
+   uint_t n;
+   uint8_t *p;
+   TlsCipherSuites *cipherSuites;
+   TlsCompressionMethods *compressionMethods;
+   TlsExtensions *extensionList;
+
+   //This flag tells whether any ECC cipher suite is proposed by the client
+   bool_t eccCipherSuite = FALSE;
+
+   //Handshake message type
+   message->msgType = TLS_TYPE_CLIENT_HELLO;
+   //Version of the protocol being employed by the client
+   message->clientVersion = HTONS(TLS_MAX_VERSION);
+   //Client random value
+   message->random = context->clientRandom;
+
+   //Point to the session ID
+   p = message->sessionId;
+   //Total length of the message
+   *length = sizeof(TlsClientHello);
+
+#if (TLS_SESSION_RESUME_SUPPORT == ENABLED)
+   //The SessionID value identifies a session the client wishes
+   //to reuse for this connection
+   message->sessionIdLength = (uint8_t) context->sessionIdLen;
+   memcpy(message->sessionId, context->sessionId, context->sessionIdLen);
+#else
+   //Session resumption is not supported
+   message->sessionIdLength = 0;
+#endif
+
+   //Point to the next field
+   p += message->sessionIdLength;
+   //Adjust the length of the message
+   *length += message->sessionIdLength;
+
+   //List of cryptographic algorithms supported by the client
+   cipherSuites = (TlsCipherSuites *) p;
+
+   //Debug message
+   TRACE_DEBUG("Cipher suites:\r\n");
+
+   //User preferred cipher suite list
+   if(context->numCipherSuites > 0)
+   {
+      //Number of cipher suites in the array
+      n = 0;
+
+      //Parse cipher suites
+      for(i = 0; i < context->numCipherSuites; i++)
+      {
+         //Make sure the specified cipher suite is supported
+         if(tlsIsCipherSuiteSupported(context->cipherSuites[i]))
+         {
+            //Copy cipher suite identifier
+            cipherSuites->value[n++] = htons(context->cipherSuites[i]);
+
+            //Debug message
+            TRACE_DEBUG("  0x%04" PRIX16 " (%s)\r\n", context->cipherSuites[i],
+               tlsGetCipherSuiteName(context->cipherSuites[i]));
+
+            //ECC cipher suite?
+            if(tlsIsEccCipherSuite(context->cipherSuites[i]))
+               eccCipherSuite = TRUE;
+         }
+      }
+   }
+   //Default cipher suite list
+   else
+   {
+      //Determine the number of supported cipher suites
+      n = tlsGetNumSupportedCipherSuites();
+
+      //Parse cipher suites
+      for(i = 0; i < n; i++)
+      {
+         //Copy cipher suite identifier
+         cipherSuites->value[i] = htons(tlsSupportedCipherSuites[i].identifier);
+
+         //Debug message
+         TRACE_DEBUG("  0x%04" PRIX16 " (%s)\r\n", tlsSupportedCipherSuites[i].identifier,
+            tlsSupportedCipherSuites[i].name);
+
+         //ECC cipher suite?
+         if(tlsIsEccCipherSuite(tlsSupportedCipherSuites[i].identifier))
+            eccCipherSuite = TRUE;
+      }
+   }
+
+   //Length of the array, in bytes
+   cipherSuites->length = htons(n * 2);
+   //Point to the next field
+   p += sizeof(TlsCipherSuites) + n * 2;
+   //Total length of the message
+   *length += sizeof(TlsCipherSuites) + n * 2;
+
+   //List of compression algorithms supported by the client
+   compressionMethods = (TlsCompressionMethods *) p;
+
+   //The CRIME exploit takes advantage of TLS compression, so conservative
+   //implementations do not enable compression at the TLS level
+   compressionMethods->length = 1;
+   compressionMethods->value[0] = TLS_COMPRESSION_METHOD_NULL;
+
+   //Point to the next field
+   p += sizeof(TlsCompressionMethods) + compressionMethods->length;
+   //Total length of the message
+   *length += sizeof(TlsCompressionMethods) + compressionMethods->length;
+
+   //Clients may request extended functionality from servers by sending
+   //data in the extensions field
+   extensionList = (TlsExtensions *) p;
+   //Total length of the extension list
+   extensionList->length = 0;
+
+   //Point to the first extension of the list
+   p += sizeof(TlsExtensions);
+
+#if (TLS_SNI_SUPPORT == ENABLED)
+   //In order to provide the server name, clients may include a ServerName extension
+   if(context->serverName != NULL)
+   {
+      TlsExtension *extension;
+      TlsServerNameList *serverNameList;
+      TlsServerName *serverName;
+
+      //Determine the length of the server name
+      n = strlen(context->serverName);
+
+      //Add SNI (Server Name Indication) extension
+      extension = (TlsExtension *) p;
+      //Type of the extension
+      extension->type = HTONS(TLS_EXT_SERVER_NAME);
+
+      //Point to the list of server names
+      serverNameList = (TlsServerNameList *) extension->value;
+
+      //Point to the server name
+      serverName = (TlsServerName *) serverNameList->value;
+      //Fill in the type and the length fields
+      serverName->type = TLS_NAME_TYPE_HOSTNAME;
+      serverName->length = htons(n);
+      //Copy server name
+      memcpy(serverName->hostname, context->serverName, n);
+
+      //Compute the length, in byte, of the structure
+      n += sizeof(TlsServerName);
+      //Fix the length of the list
+      serverNameList->length = htons(n);
+
+      //Consider the 2-byte length field that precedes the list
+      n += sizeof(TlsServerNameList);
+      //Fix the length of the extension
+      extension->length = htons(n);
+
+      //Compute the length, in bytes, of the ServerName extension
+      n += sizeof(TlsExtension);
+      //Fix the length of the extension list
+      extensionList->length += n;
+
+      //Point to the next field
+      p += n;
+      //Total length of the message
+      *length += n;
+   }
+#endif
+
+#if (TLS_ALPN_SUPPORT == ENABLED)
+   //The ALPN extension contains the list of protocols advertised by the
+   //client, in descending order of preference
+   if(context->protocolList != NULL)
+   {
+      uint_t j;
+      TlsExtension *extension;
+      TlsProtocolNameList *protocolNameList;
+      TlsProtocolName *protocolName;
+
+      //Add ALPN (Application-Layer Protocol Negotiation) extension
+      extension = (TlsExtension *) p;
+      //Type of the extension
+      extension->type = HTONS(TLS_EXT_ALPN);
+
+      //Point to the list of protocol names
+      protocolNameList = (TlsProtocolNameList *) extension->value;
+
+      //Move back to the beginning of the list
+      i = 0;
+      j = 0;
+      n = 0;
+
+      //Parse the list of supported protocols
+      do
+      {
+         //Delimiter character found?
+         if(context->protocolList[i] == ',' || context->protocolList[i] == '\0')
+         {
+            //Discard empty tokens
+            if((i - j) > 0)
+            {
+               //Point to the protocol name
+               protocolName = (TlsProtocolName *) (protocolNameList->value + n);
+
+               //Fill in the length field
+               protocolName->length = i - j;
+               //Copy protocol name
+               memcpy(protocolName->value, context->protocolList + j, i - j);
+
+               //Adjust the length of the list
+               n += sizeof(TlsProtocolName) + i - j;
+            }
+
+            //Move to the next token
+            j = i + 1;
+         }
+
+         //Loop until the NULL character is reached
+      } while(context->protocolList[i++] != '\0');
+
+      //Fix the length of the list
+      protocolNameList->length = htons(n);
+
+      //Consider the 2-byte length field that precedes the list
+      n += sizeof(TlsProtocolNameList);
+      //Fix the length of the extension
+      extension->length = htons(n);
+
+      //Compute the length, in bytes, of the ALPN extension
+      n += sizeof(TlsExtension);
+      //Fix the length of the extension list
+      extensionList->length += n;
+
+      //Point to the next field
+      p += n;
+      //Total length of the message
+      *length += n;
+   }
+#endif
+
+#if (TLS_ECDH_ANON_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED || \
+   TLS_ECDHE_ECDSA_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   //A client that proposes ECC cipher suites in its ClientHello message
+   //should send the EllipticCurves extension
+   if(eccCipherSuite)
+   {
+      TlsExtension *extension;
+      TlsEllipticCurveList *ellipticCurveList;
+
+      //Add the EllipticCurves extension
+      extension = (TlsExtension *) p;
+      //Type of the extension
+      extension->type = HTONS(TLS_EXT_ELLIPTIC_CURVES);
+
+      //Point to the list of supported elliptic curves
+      ellipticCurveList = (TlsEllipticCurveList *) extension->value;
+      //Items in the list are ordered according to client's preferences
+      n = 0;
+
+#if (TLS_SECP160K1_SUPPORT == ENABLED)
+      //Support for secp160k1 elliptic curve
+      ellipticCurveList->value[n++] = HTONS(TLS_EC_CURVE_SECP160K1);
+#endif
+#if (TLS_SECP160R1_SUPPORT == ENABLED)
+      //Support for secp160r1 elliptic curve
+      ellipticCurveList->value[n++] = HTONS(TLS_EC_CURVE_SECP160R1);
+#endif
+#if (TLS_SECP160R2_SUPPORT == ENABLED)
+      //Support for secp160r2 elliptic curve
+      ellipticCurveList->value[n++] = HTONS(TLS_EC_CURVE_SECP160R2);
+#endif
+#if (TLS_SECP192K1_SUPPORT == ENABLED)
+      //Support for secp192k1 elliptic curve
+      ellipticCurveList->value[n++] = HTONS(TLS_EC_CURVE_SECP192K1);
+#endif
+#if (TLS_SECP192R1_SUPPORT == ENABLED)
+      //Support for secp192r1 elliptic curve
+      ellipticCurveList->value[n++] = HTONS(TLS_EC_CURVE_SECP192R1);
+#endif
+#if (TLS_SECP224K1_SUPPORT == ENABLED)
+      //Support for secp224k1 elliptic curve
+      ellipticCurveList->value[n++] = HTONS(TLS_EC_CURVE_SECP224K1);
+#endif
+#if (TLS_SECP224R1_SUPPORT == ENABLED)
+      //Support for secp224r1 elliptic curve
+      ellipticCurveList->value[n++] = HTONS(TLS_EC_CURVE_SECP224R1);
+#endif
+#if (TLS_SECP256K1_SUPPORT == ENABLED)
+      //Support for secp256k1 elliptic curve
+      ellipticCurveList->value[n++] = HTONS(TLS_EC_CURVE_SECP256K1);
+#endif
+#if (TLS_SECP256R1_SUPPORT == ENABLED)
+      //Support for secp256r1 elliptic curve
+      ellipticCurveList->value[n++] = HTONS(TLS_EC_CURVE_SECP256R1);
+#endif
+#if (TLS_SECP384R1_SUPPORT == ENABLED)
+      //Support for secp384r1 elliptic curve
+      ellipticCurveList->value[n++] = HTONS(TLS_EC_CURVE_SECP384R1);
+#endif
+#if (TLS_SECP521R1_SUPPORT == ENABLED)
+      //Support for secp521r1 elliptic curve
+      ellipticCurveList->value[n++] = HTONS(TLS_EC_CURVE_SECP521R1);
+#endif
+#if (TLS_BRAINPOOLP256R1_SUPPORT == ENABLED)
+      //Support for brainpoolP256r1 elliptic curve
+      ellipticCurveList->value[n++] = HTONS(TLS_EC_CURVE_BRAINPOOLP256R1);
+#endif
+#if (TLS_BRAINPOOLP384R1_SUPPORT == ENABLED)
+      //Support for brainpoolP384r1 elliptic curve
+      ellipticCurveList->value[n++] = HTONS(TLS_EC_CURVE_BRAINPOOLP384R1);
+#endif
+#if (TLS_BRAINPOOLP512R1_SUPPORT == ENABLED)
+      //Support for brainpoolP512r1 elliptic curve
+      ellipticCurveList->value[n++] = HTONS(TLS_EC_CURVE_BRAINPOOLP512R1);
+#endif
+
+      //Compute the length, in bytes, of the list
+      n *= 2;
+      //Fix the length of the list
+      ellipticCurveList->length = htons(n);
+
+      //Consider the 2-byte length field that precedes the list
+      n += sizeof(TlsEllipticCurveList);
+      //Fix the length of the extension
+      extension->length = htons(n);
+
+      //Compute the length, in bytes, of the EllipticCurves extension
+      n += sizeof(TlsExtension);
+      //Fix the length of the extension list
+      extensionList->length += n;
+
+      //Point to the next field
+      p += n;
+      //Total length of the message
+      *length += n;
+   }
+
+   //A client that proposes ECC cipher suites in its ClientHello message
+   //should send the EcPointFormats extension
+   if(eccCipherSuite)
+   {
+      TlsExtension *extension;
+      TlsEcPointFormatList *ecPointFormatList;
+
+      //Add the EcPointFormats extension
+      extension = (TlsExtension *) p;
+      //Type of the extension
+      extension->type = HTONS(TLS_EXT_EC_POINT_FORMATS);
+
+      //Point to the list of supported EC point formats
+      ecPointFormatList = (TlsEcPointFormatList *) extension->value;
+      //Items in the list are ordered according to client's preferences
+      n = 0;
+
+      //The client can parse only the uncompressed point format...
+      ecPointFormatList->value[n++] = TLS_EC_POINT_FORMAT_UNCOMPRESSED;
+      //Fix the length of the list
+      ecPointFormatList->length = n;
+
+      //Consider the 2-byte length field that precedes the list
+      n += sizeof(TlsEcPointFormatList);
+      //Fix the length of the extension
+      extension->length = htons(n);
+
+      //Compute the length, in bytes, of the EcPointFormats extension
+      n += sizeof(TlsExtension);
+      //Fix the length of the extension list
+      extensionList->length += n;
+
+      //Point to the next field
+      p += n;
+      //Total length of the message
+      *length += n;
+   }
+#endif
+
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_MIN_VERSION <= TLS_VERSION_1_2)
+   //Include the SignatureAlgorithms extension only if TLS 1.2 is supported
+   {
+      TlsExtension *extension;
+      TlsSignHashAlgos *supportedSignAlgos;
+
+      //Add the SignatureAlgorithms extension
+      extension = (TlsExtension *) p;
+      //Type of the extension
+      extension->type = HTONS(TLS_EXT_SIGNATURE_ALGORITHMS);
+
+      //Point to the list of the hash/signature algorithm pairs that
+      //the server is able to verify
+      supportedSignAlgos = (TlsSignHashAlgos *) extension->value;
+
+      //Enumerate the hash/signature algorithm pairs in descending
+      //order of preference
+      n = 0;
+
+#if (TLS_RSA_SIGN_SUPPORT == ENABLED)
+      //MD5 with RSA is always supported
+      supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_RSA;
+      supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_MD5;
+      //SHA-1 with RSA is always supported
+      supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_RSA;
+      supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA1;
+#if (TLS_SHA224_SUPPORT == ENABLED)
+      //SHA-224 with RSA support is optional
+      supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_RSA;
+      supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA224;
+#endif
+      //SHA-256 with RSA is always supported
+      supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_RSA;
+      supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA256;
+#if (TLS_SHA384_SUPPORT == ENABLED)
+      //SHA-384 with RSA support is optional
+      supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_RSA;
+      supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA384;
+#endif
+#if (TLS_SHA512_SUPPORT == ENABLED)
+      //SHA-512 with RSA support is optional
+      supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_RSA;
+      supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA512;
+#endif
+#endif
+
+#if (TLS_DSA_SIGN_SUPPORT == ENABLED)
+      //DSA with SHA-1 is always supported
+      supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_DSA;
+      supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA1;
+#if (TLS_SHA224_SUPPORT == ENABLED)
+      //DSA with SHA-224 support is optional
+      supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_DSA;
+      supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA224;
+#endif
+      //DSA with SHA-256 is always supported
+      supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_DSA;
+      supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA256;
+#endif
+
+#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED)
+      //Any ECC cipher suite proposed by the client?
+      if(eccCipherSuite)
+      {
+         //ECDSA with SHA-1 is always supported
+         supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_ECDSA;
+         supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA1;
+#if (TLS_SHA224_SUPPORT == ENABLED)
+         //ECDSA with SHA-224 support is optional
+         supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_ECDSA;
+         supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA224;
+#endif
+         //ECDSA with SHA-256 is always supported
+         supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_ECDSA;
+         supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA256;
+#if (TLS_SHA384_SUPPORT == ENABLED)
+         //ECDSA with SHA-384 support is optional
+         supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_ECDSA;
+         supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA384;
+#endif
+#if (TLS_SHA512_SUPPORT == ENABLED)
+         //ECDSA with SHA-512 support is optional
+         supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_ECDSA;
+         supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA512;
+#endif
+      }
+#endif
+
+      //Compute the length, in bytes, of the list
+      n *= sizeof(TlsSignHashAlgo);
+      //Fix the length of the list
+      supportedSignAlgos->length = htons(n);
+
+      //Consider the 2-byte length field that precedes the list
+      n += sizeof(TlsSignHashAlgos);
+      //Fix the length of the extension
+      extension->length = htons(n);
+
+      //Compute the length, in bytes, of the SignatureAlgorithms extension
+      n += sizeof(TlsExtension);
+      //Fix the length of the extension list
+      extensionList->length += n;
+
+      //Point to the next field
+      p += n;
+      //Total length of the message
+      *length += n;
+   }
+#endif
+
+   //Check whether the extension list is empty
+   if(extensionList->length > 0)
+   {
+      //Convert the length of the extension list to network byte order
+      extensionList->length = htons(extensionList->length);
+      //Total length of the message
+      *length += sizeof(TlsExtensions);
+   }
+
+   //Fix the length field
+   STORE24BE(*length - sizeof(TlsHandshake), message->length);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format ClientKeyExchange message
+ * @param[in] context Pointer to the TLS context
+ * @param[out] message Buffer where to format the ClientKeyExchange message
+ * @param[out] length Length of the resulting ClientKeyExchange message
+ * @return Error code
+ **/
+
+error_t tlsFormatClientKeyExchange(TlsContext *context,
+   TlsClientKeyExchange *message, size_t *length)
+{
+   error_t error;
+   size_t n;
+   uint8_t *p;
+
+   //Handshake message type
+   message->msgType = TLS_TYPE_CLIENT_KEY_EXCHANGE;
+
+   //Point to the body of the handshake message
+   p = message->data;
+   //Length of the handshake message
+   *length = 0;
+
+   //PSK key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
+   {
+      //The client indicates which key to use by including a PSK identity
+      //in the ClientKeyExchange message
+      error = tlsFormatPskIdentity(context, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Advance data pointer
+      p += n;
+      //Adjust the length of the message
+      *length += n;
+   }
+
+   //RSA, Diffie-Hellman or ECDH key exchange method?
+   if(context->keyExchMethod != TLS_KEY_EXCH_PSK)
+   {
+      //Format client's key exchange parameters
+      error = tlsFormatClientKeyParams(context, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Advance data pointer
+      p += n;
+      //Adjust the length of the message
+      *length += n;
+   }
+
+   //PSK key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
+   {
+      //Generate premaster secret
+      error = tlsGeneratePskPremasterSecret(context);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+
+   //Fix the length field
+   STORE24BE(*length, message->length);
+   //Length of the complete handshake message
+   *length += sizeof(TlsHandshake);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format CertificateVerify message
+ * @param[in] context Pointer to the TLS context
+ * @param[out] message Buffer where to format the CertificateVerify message
+ * @param[out] length Length of the resulting CertificateVerify message
+ * @return Error code
+ **/
+
+error_t tlsFormatCertificateVerify(TlsContext *context,
+   TlsCertificateVerify *message, size_t *length)
+{
+   error_t error;
+
+   //Initialize message length
+   *length = 0;
+
+   //Handshake message type
+   message->msgType = TLS_TYPE_CERTIFICATE_VERIFY;
+
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_MIN_VERSION <= TLS_VERSION_1_1)
+   //SSL 3.0, TLS 1.0 or TLS 1.1 currently selected?
+   if(context->version <= TLS_VERSION_1_1)
+   {
+      TlsDigitalSignature *signature;
+
+      //Point to the digitally-signed element
+      signature = (TlsDigitalSignature *) message->signature;
+
+#if (TLS_RSA_SIGN_SUPPORT == ENABLED)
+      //The client's certificate contains a valid RSA public key?
+      if(context->cert->type == TLS_CERT_RSA_SIGN)
+      {
+         RsaPrivateKey privateKey;
+
+         //Initialize RSA private key
+         rsaInitPrivateKey(&privateKey);
+
+         //Digest all the handshake messages starting at ClientHello (using MD5)
+         error = tlsFinalizeHandshakeHash(context, MD5_HASH_ALGO,
+            context->handshakeMd5Context, "", context->verifyData);
+
+         //Check status code
+         if(!error)
+         {
+            //Digest all the handshake messages starting at ClientHello (using SHA-1)
+            error = tlsFinalizeHandshakeHash(context, SHA1_HASH_ALGO,
+               context->handshakeSha1Context, "", context->verifyData + MD5_DIGEST_SIZE);
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Decode the PEM structure that holds the RSA private key
+            error = pemReadRsaPrivateKey(context->cert->privateKey,
+               context->cert->privateKeyLength, &privateKey);
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Generate a RSA signature using the client's private key
+            error = tlsGenerateRsaSignature(&privateKey,
+               context->verifyData, signature->value, length);
+         }
+
+         //Release previously allocated resources
+         rsaFreePrivateKey(&privateKey);
+      }
+      else
+#endif
+#if (TLS_DSA_SIGN_SUPPORT == ENABLED)
+      //The client's certificate contains a valid DSA public key?
+      if(context->cert->type == TLS_CERT_DSS_SIGN)
+      {
+         DsaPrivateKey privateKey;
+
+         //Initialize DSA private key
+         dsaInitPrivateKey(&privateKey);
+
+         //Digest all the handshake messages starting at ClientHello
+         error = tlsFinalizeHandshakeHash(context, SHA1_HASH_ALGO,
+            context->handshakeSha1Context, "", context->verifyData);
+
+         //Check status code
+         if(!error)
+         {
+            //Decode the PEM structure that holds the DSA private key
+            error = pemReadDsaPrivateKey(context->cert->privateKey,
+               context->cert->privateKeyLength, &privateKey);
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Generate a DSA signature using the client's private key
+            error = tlsGenerateDsaSignature(context->prngAlgo,
+               context->prngContext, &privateKey, context->verifyData,
+               SHA1_DIGEST_SIZE, signature->value, length);
+         }
+
+         //Release previously allocated resources
+         dsaFreePrivateKey(&privateKey);
+      }
+      else
+#endif
+#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED)
+      //The client's certificate contains a valid ECDSA public key?
+      if(context->cert->type == TLS_CERT_ECDSA_SIGN)
+      {
+         EcDomainParameters params;
+         Mpi privateKey;
+
+         //Initialize EC domain parameters
+         ecInitDomainParameters(&params);
+         //Initialize EC private key
+         mpiInit(&privateKey);
+
+         //Digest all the handshake messages starting at ClientHello
+         error = tlsFinalizeHandshakeHash(context, SHA1_HASH_ALGO,
+            context->handshakeSha1Context, "", context->verifyData);
+
+         //Check status code
+         if(!error)
+         {
+            //Decode the PEM structure that holds the EC domain parameters
+            error = pemReadEcParameters(context->cert->privateKey,
+               context->cert->privateKeyLength, &params);
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Decode the PEM structure that holds the EC private key
+            error = pemReadEcPrivateKey(context->cert->privateKey,
+               context->cert->privateKeyLength, &privateKey);
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Generate an ECDSA signature using the client's private key
+            error = tlsGenerateEcdsaSignature(&params, context->prngAlgo,
+               context->prngContext, &privateKey, context->verifyData,
+               SHA1_DIGEST_SIZE, signature->value, length);
+         }
+
+         //Release previously allocated resources
+         ecFreeDomainParameters(&params);
+         mpiFree(&privateKey);
+      }
+      else
+#endif
+      //Invalid signature algorithm?
+      {
+         //Report an error
+         error = ERROR_UNSUPPORTED_SIGNATURE_ALGO;
+      }
+
+      //Length of the signature
+      signature->length = htons(*length);
+      //Total length of the digitally-signed element
+      *length += sizeof(TlsDigitalSignature);
+   }
+   else
+#endif
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_MIN_VERSION <= TLS_VERSION_1_2)
+   //TLS 1.2 currently selected?
+   if(context->version == TLS_VERSION_1_2)
+   {
+      TlsDigitalSignature2 *signature;
+      const HashAlgo *hashAlgo;
+
+      //Point to the digitally-signed element
+      signature = (TlsDigitalSignature2 *) message->signature;
+
+      //Retrieve the hash algorithm to be used for signing
+      hashAlgo = tlsGetHashAlgo(context->signHashAlgo);
+
+      //Digest all the handshake messages starting at ClientHello
+      if(hashAlgo == SHA1_HASH_ALGO)
+      {
+         //Use SHA-1 hash algorithm
+         error = tlsFinalizeHandshakeHash(context, SHA1_HASH_ALGO,
+            context->handshakeSha1Context, "", context->verifyData);
+      }
+      else if(hashAlgo == context->prfHashAlgo)
+      {
+         //Use PRF hash algorithm (SHA-256 or SHA-384)
+         error = tlsFinalizeHandshakeHash(context, hashAlgo,
+            context->handshakeHashContext, "", context->verifyData);
+      }
+      else
+      {
+         //The specified hash algorithm is not supported
+         error = ERROR_UNSUPPORTED_SIGNATURE_ALGO;
+      }
+
+      //Handshake message hash successfully computed?
+      if(!error)
+      {
+#if (TLS_RSA_SIGN_SUPPORT == ENABLED)
+         //The client's certificate contains a valid RSA public key?
+         if(context->cert->type == TLS_CERT_RSA_SIGN)
+         {
+            RsaPrivateKey privateKey;
+
+            //Initialize RSA private key
+            rsaInitPrivateKey(&privateKey);
+
+            //Set the relevant signature algorithm
+            signature->algorithm.signature = TLS_SIGN_ALGO_RSA;
+            signature->algorithm.hash = context->signHashAlgo;
+
+            //Decode the PEM structure that holds the RSA private key
+            error = pemReadRsaPrivateKey(context->cert->privateKey,
+               context->cert->privateKeyLength, &privateKey);
+
+            //Check status code
+            if(!error)
+            {
+               //Use the signature algorithm defined in PKCS #1 v1.5
+               error = rsassaPkcs1v15Sign(&privateKey, hashAlgo,
+                  context->verifyData, signature->value, length);
+            }
+
+            //Release previously allocated resources
+            rsaFreePrivateKey(&privateKey);
+         }
+         else
+#endif
+#if (TLS_DSA_SIGN_SUPPORT == ENABLED)
+         //The client's certificate contains a valid DSA public key?
+         if(context->cert->type == TLS_CERT_DSS_SIGN)
+         {
+            DsaPrivateKey privateKey;
+
+            //Initialize DSA private key
+            dsaInitPrivateKey(&privateKey);
+
+            //Set the relevant signature algorithm
+            signature->algorithm.signature = TLS_SIGN_ALGO_DSA;
+            signature->algorithm.hash = context->signHashAlgo;
+
+            //Decode the PEM structure that holds the DSA private key
+            error = pemReadDsaPrivateKey(context->cert->privateKey,
+               context->cert->privateKeyLength, &privateKey);
+
+            //Check status code
+            if(!error)
+            {
+               //Generate a DSA signature using the client's private key
+               error = tlsGenerateDsaSignature(context->prngAlgo,
+                  context->prngContext, &privateKey, context->verifyData,
+                  hashAlgo->digestSize, signature->value, length);
+            }
+
+            //Release previously allocated resources
+            dsaFreePrivateKey(&privateKey);
+         }
+         else
+#endif
+#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED)
+         //The client's certificate contains a valid ECDSA public key?
+         if(context->cert->type == TLS_CERT_ECDSA_SIGN)
+         {
+            EcDomainParameters params;
+            Mpi privateKey;
+
+            //Initialize EC domain parameters
+            ecInitDomainParameters(&params);
+            //Initialize EC private key
+            mpiInit(&privateKey);
+
+            //Set the relevant signature algorithm
+            signature->algorithm.signature = TLS_SIGN_ALGO_ECDSA;
+            signature->algorithm.hash = context->signHashAlgo;
+
+            //Decode the PEM structure that holds the EC domain parameters
+            error = pemReadEcParameters(context->cert->privateKey,
+               context->cert->privateKeyLength, &params);
+
+            //Check status code
+            if(!error)
+            {
+               //Decode the PEM structure that holds the EC private key
+               error = pemReadEcPrivateKey(context->cert->privateKey,
+                  context->cert->privateKeyLength, &privateKey);
+            }
+
+            //Check status code
+            if(!error)
+            {
+               //Generate an ECDSA signature using the client's private key
+               error = tlsGenerateEcdsaSignature(&params, context->prngAlgo,
+                  context->prngContext, &privateKey, context->verifyData,
+                  hashAlgo->digestSize, signature->value, length);
+            }
+
+            //Release previously allocated resources
+            ecFreeDomainParameters(&params);
+            mpiFree(&privateKey);
+         }
+         else
+#endif
+         //Invalid signature algorithm?
+         {
+            //Report an error
+            error = ERROR_UNSUPPORTED_SIGNATURE_ALGO;
+         }
+      }
+
+      //Length of the signature
+      signature->length = htons(*length);
+      //Length of the digitally-signed element
+      *length += sizeof(TlsDigitalSignature2);
+   }
+   else
+#endif
+   {
+      //The negotiated TLS version is not valid
+      error = ERROR_INVALID_VERSION;
+   }
+
+   //Fix the length field
+   STORE24BE(*length, message->length);
+   //Length of the complete handshake message
+   *length += sizeof(TlsHandshake);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse ServerHello message
+ *
+ * The server will send this message in response to a ClientHello
+ * message when it was able to find an acceptable set of algorithms.
+ * If it cannot find such a match, it will respond with a handshake
+ * failure alert
+ *
+ * @param[in] context Pointer to the TLS context
+ * @param[in] message Incoming ServerHello message to parse
+ * @param[in] length Message length
+ * @return Error code
+ **/
+
+error_t tlsParseServerHello(TlsContext *context, const TlsServerHello *message, size_t length)
+{
+   error_t error;
+   size_t n;
+   const uint8_t *p;
+   TlsCipherSuite cipherSuite;
+   TlsCompressionMethod compressionMethod;
+
+   //Debug message
+   TRACE_INFO("ServerHello message received (%" PRIuSIZE " bytes)...\r\n", length);
+   TRACE_DEBUG_ARRAY("  ", message, length);
+
+   //Check the length of the ServerHello message
+   if(length < sizeof(TlsServerHello))
+      return ERROR_DECODING_FAILED;
+
+   //Check current state
+   if(context->state != TLS_STATE_SERVER_HELLO)
+      return ERROR_UNEXPECTED_MESSAGE;
+
+   //Point to the session ID
+   p = (uint8_t *) message + sizeof(TlsServerHello);
+   //Remaining bytes to process
+   n = length - sizeof(TlsServerHello);
+
+   //Check the length of the session ID
+   if(message->sessionIdLength > n)
+      return ERROR_DECODING_FAILED;
+   if(message->sessionIdLength > 32)
+      return ERROR_ILLEGAL_PARAMETER;
+
+   //Point to the next field
+   p += message->sessionIdLength;
+   //Remaining bytes to process
+   n -= message->sessionIdLength;
+
+   //Malformed ServerHello message?
+   if(n < (sizeof(TlsCipherSuite) + sizeof(TlsCompressionMethod)))
+      return ERROR_DECODING_FAILED;
+
+   //Get the negotiated cipher suite
+   cipherSuite = LOAD16BE(p);
+   //Point to the next field
+   p += sizeof(TlsCipherSuite);
+   //Remaining bytes to process
+   n -= sizeof(TlsCipherSuite);
+
+   //Get the negotiated compression method
+   compressionMethod = *p;
+   //Point to the next field
+   p += sizeof(TlsCompressionMethod);
+   //Remaining bytes to process
+   n -= sizeof(TlsCompressionMethod);
+
+   //Server version
+   TRACE_INFO("  serverVersion = 0x%04" PRIX16 " (%s)\r\n", ntohs(message->serverVersion),
+      tlsGetVersionName(ntohs(message->serverVersion)));
+   //Server random value
+   TRACE_INFO("  random\r\n");
+   TRACE_INFO_ARRAY("    ", &message->random, sizeof(TlsRandom));
+   //Session identifier
+   TRACE_INFO("  sessionId\r\n");
+   TRACE_INFO_ARRAY("    ", message->sessionId, message->sessionIdLength);
+   //Cipher suite identifier
+   TRACE_INFO("  cipherSuite = 0x%04" PRIX16 " (%s)\r\n",
+      cipherSuite, tlsGetCipherSuiteName(cipherSuite));
+   //Compression method
+   TRACE_INFO("  compressionMethod = 0x%02" PRIX8 "\r\n", compressionMethod);
+
+#if (TLS_SESSION_RESUME_SUPPORT == ENABLED)
+   //Check whether the session ID matches the value that was supplied by the client
+   if(message->sessionIdLength > 0 && message->sessionIdLength == context->sessionIdLen &&
+      !memcmp(message->sessionId, context->sessionId, context->sessionIdLen))
+   {
+      //For resumed sessions, the selected cipher suite and compression
+      //method shall be the same as the session being resumed
+      if(cipherSuite != context->cipherSuite ||
+         compressionMethod != context->compressionMethod)
+      {
+         //The session ID is no more valid
+         context->sessionIdLen = 0;
+         //When renegotiating, if the server tries to use another
+         //version or compression method than previously, abort
+         return ERROR_HANDSHAKE_FAILED;
+      }
+
+      //Perform abbreviated handshake
+      context->resume = TRUE;
+   }
+   else
+#endif
+   {
+      //Perform a full handshake
+      context->resume = FALSE;
+   }
+
+   //Save server random value
+   context->serverRandom = message->random;
+
+   //Save session identifier
+   memcpy(context->sessionId, message->sessionId, message->sessionIdLength);
+   context->sessionIdLen = message->sessionIdLength;
+
+   //Set the TLS version to use
+   error = tlsSetVersion(context, ntohs(message->serverVersion));
+   //The specified TLS version is not supported?
+   if(error)
+      return error;
+
+   //Set cipher suite
+   error = tlsSetCipherSuite(context, cipherSuite);
+   //The specified cipher suite is not supported?
+   if(error)
+      return error;
+
+   //Set compression method
+   error = tlsSetCompressionMethod(context, compressionMethod);
+   //The specified compression method is not supported?
+   if(error)
+      return error;
+
+   //Initialize handshake message hashing
+   error = tlsInitHandshakeHash(context);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Update the hash value with the incoming handshake message
+   tlsUpdateHandshakeHash(context, message, length);
+
+#if (TLS_SESSION_RESUME_SUPPORT == ENABLED)
+   //Use abbreviated handshake?
+   if(context->resume)
+   {
+      //Derive session keys from the master secret
+      error = tlsGenerateKeys(context);
+      //Unable to generate key material?
+      if(error)
+         return error;
+
+      //At this point, both client and server must send ChangeCipherSpec
+      //messages and proceed directly to Finished messages
+      context->state = TLS_STATE_SERVER_CHANGE_CIPHER_SPEC;
+   }
+   else
+#endif
+   {
+      //Perform a full handshake
+      if(context->keyExchMethod == TLS_KEY_EXCH_PSK ||
+         context->keyExchMethod == TLS_KEY_EXCH_DH_ANON ||
+         context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK ||
+         context->keyExchMethod == TLS_KEY_EXCH_ECDH_ANON ||
+         context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
+      {
+         //The Certificate message is omitted from the server's response
+         context->state = TLS_STATE_SERVER_KEY_EXCHANGE;
+      }
+      else
+      {
+         //The server is required to send a Certificate message
+         context->state = TLS_STATE_SERVER_CERTIFICATE;
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse ServerKeyExchange message
+ *
+ * The ServerKeyExchange message is sent by the server only when the
+ * server Certificate message does not contain enough data to allow
+ * the client to exchange a premaster secret
+ *
+ * @param[in] context Pointer to the TLS context
+ * @param[in] message Incoming ServerKeyExchange message to parse
+ * @param[in] length Message length
+ * @return Error code
+ **/
+
+error_t tlsParseServerKeyExchange(TlsContext *context, const TlsServerKeyExchange *message, size_t length)
+{
+   error_t error;
+   size_t n;
+   size_t paramsLen;
+   const uint8_t *p;
+   const uint8_t *params;
+
+   //Debug message
+   TRACE_INFO("ServerKeyExchange message received (%" PRIuSIZE " bytes)...\r\n", length);
+   TRACE_DEBUG_ARRAY("  ", message, length);
+
+   //Check the length of the ServerKeyExchange message
+   if(length < sizeof(TlsServerKeyExchange))
+      return ERROR_DECODING_FAILED;
+
+   //Check current state
+   if(context->state != TLS_STATE_SERVER_KEY_EXCHANGE)
+      return ERROR_UNEXPECTED_MESSAGE;
+
+   //Update the hash value with the incoming handshake message
+   tlsUpdateHandshakeHash(context, message, length);
+
+   //Point to the body of the handshake message
+   p = message->data;
+   //Remaining bytes to process
+   length -= sizeof(TlsServerKeyExchange);
+
+   //PSK key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
+   {
+      //To help the client in selecting which identity to use, the server
+      //can provide a PSK identity hint in the ServerKeyExchange message
+      error = tlsParsePskIdentityHint(context, p, length, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Point to the next field
+      p += n;
+      //Remaining bytes to process
+      length -= n;
+   }
+
+   //Diffie-Hellman or ECDH key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_DH_ANON ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDH_ANON ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
+   {
+      //Point to the server's key exchange parameters
+      params = p;
+
+      //Parse server's key exchange parameters
+      error = tlsParseServerKeyParams(context, p, length, &paramsLen);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Point to the next field
+      p += paramsLen;
+      //Remaining bytes to process
+      length -= paramsLen;
+   }
+
+   //Non-anonymous Diffie-Hellman and ECDH key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA)
+   {
+      //For non-anonymous Diffie-Hellman and ECDH key exchanges, the signature
+      //over the server's key exchange parameters shall be verified
+      error = tlsVerifyServerKeySignature(context, p, length, params, paramsLen, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Point to the next field
+      p += n;
+      //Remaining bytes to process
+      length -= n;
+   }
+
+   //If the amount of data in the message does not precisely match the format
+   //of the ServerKeyExchange message, then send a fatal alert
+   if(length != 0)
+      return ERROR_DECODING_FAILED;
+
+   //Anomynous server?
+   if(context->keyExchMethod == TLS_KEY_EXCH_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_DH_ANON ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDH_ANON ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
+   {
+      //An anonymous server cannot request client authentication
+      context->state = TLS_STATE_SERVER_HELLO_DONE;
+   }
+   else
+   {
+      //A non-anonymous server can optionally request a certificate from
+      //the client, if appropriate for the selected cipher suite
+      context->state = TLS_STATE_CERTIFICATE_REQUEST;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse CertificateRequest message
+ *
+ * A server can optionally request a certificate from the client, if
+ * appropriate for the selected cipher suite. This message will
+ * immediately follow the ServerKeyExchange message
+ *
+ * @param[in] context Pointer to the TLS context
+ * @param[in] message Incoming CertificateRequest message to parse
+ * @param[in] length Message length
+ * @return Error code
+ **/
+
+error_t tlsParseCertificateRequest(TlsContext *context, const TlsCertificateRequest *message, size_t length)
+{
+   uint_t i;
+   size_t n;
+   uint8_t *p;
+   bool_t acceptable;
+   TlsSignHashAlgos *supportedSignAlgos;
+   TlsCertAuthorities *certAuthorities;
+
+   //Debug message
+   TRACE_INFO("CertificateRequest message received (%" PRIuSIZE " bytes)...\r\n", length);
+   TRACE_DEBUG_ARRAY("  ", message, length);
+
+   //Check the length of the ServerKeyExchange message
+   if(length < sizeof(TlsCertificateRequest))
+      return ERROR_DECODING_FAILED;
+
+   //Check key exchange method
+   if(context->keyExchMethod == TLS_KEY_EXCH_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_DH_ANON ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDH_ANON ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
+   {
+      //It is a fatal handshake failure alert for an anonymous
+      //server to request client authentication
+      return ERROR_HANDSHAKE_FAILED;
+   }
+   else if(context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK)
+   {
+      //If no PSK identity hint is provided by the server, the
+      //ServerKeyExchange message is omitted...
+      if(context->state != TLS_STATE_SERVER_KEY_EXCHANGE &&
+         context->state != TLS_STATE_CERTIFICATE_REQUEST)
+      {
+         //Handshake failure
+         return ERROR_UNEXPECTED_MESSAGE;
+      }
+   }
+   else
+   {
+      //Check current state
+      if(context->state != TLS_STATE_CERTIFICATE_REQUEST)
+         return ERROR_UNEXPECTED_MESSAGE;
+   }
+
+   //Update the hash value with the incoming handshake message
+   tlsUpdateHandshakeHash(context, message, length);
+
+   //The server requests a certificate from the client, so that
+   //the connection can be mutually authenticated
+   context->clientCertRequested = TRUE;
+
+   //Point to the beginning of the message
+   p = (uint8_t *) message;
+   //Remaining bytes to process
+   length -= sizeof(TlsCertificateRequest);
+
+   //Retrieve the size of the list of supported certificate types
+   n = message->certificateTypesLength;
+   //Make sure the length field is valid
+   if(n > length)
+      return ERROR_DECODING_FAILED;
+
+   //Point to the next field
+   p += sizeof(TlsCertificateRequest) + n;
+   //Remaining bytes to process
+   length -= n;
+
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_MIN_VERSION <= TLS_VERSION_1_2)
+   //TLS 1.2 currently selected?
+   if(context->version == TLS_VERSION_1_2)
+   {
+      //Malformed CertificateRequest message?
+      if(length < sizeof(TlsSignHashAlgos))
+         return ERROR_DECODING_FAILED;
+
+      //Point to the list of the hash/signature algorithm pairs that
+      //the server is able to verify
+      supportedSignAlgos = (TlsSignHashAlgos *) p;
+      //Remaining bytes to process
+      length -= sizeof(TlsSignHashAlgos);
+
+      //Get the size of the list
+      n = ntohs(supportedSignAlgos->length);
+      //Make sure the length field is valid
+      if(n > length)
+         return ERROR_DECODING_FAILED;
+
+      //Point to the next field
+      p += sizeof(TlsSignHashAlgos) + n;
+      //Remaining bytes to process
+      length -= n;
+   }
+   //SSL 3.0, TLS 1.0 or TLS 1.1 currently selected?
+   else
+#endif
+   {
+      //Implementations prior to TLS 1.2 do not include a
+      //list of supported hash/signature algorithm pairs
+      supportedSignAlgos = NULL;
+   }
+
+   //Malformed CertificateRequest message?
+   if(length < sizeof(TlsCertAuthorities))
+      return ERROR_DECODING_FAILED;
+
+   //Point to the list of the distinguished names of acceptable
+   //certificate authorities
+   certAuthorities = (TlsCertAuthorities *) p;
+   //Remaining bytes to process
+   length -= sizeof(TlsCertAuthorities);
+
+   //Get the size of the list
+   n = ntohs(certAuthorities->length);
+   //Make sure the length field is valid
+   if(n > length)
+      return ERROR_DECODING_FAILED;
+
+   //No suitable certificate has been found for the moment
+   context->cert = NULL;
+
+   //Loop through the list of available certificates
+   for(i = 0; i < context->numCerts; i++)
+   {
+      //Check whether the current certificate is suitable
+      acceptable = tlsIsCertificateAcceptable(&context->certs[i],
+         message->certificateTypes, message->certificateTypesLength,
+         supportedSignAlgos, NULL, certAuthorities);
+
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_MIN_VERSION <= TLS_VERSION_1_2)
+      //TLS 1.2 requires additional examinations
+      if(acceptable && context->version == TLS_VERSION_1_2)
+      {
+         //The hash and signature algorithms used in the signature of the CertificateVerify
+         //message must be one of those present in the SupportedSignatureAlgorithms field
+         if(tlsSelectSignHashAlgo(context, context->certs[i].signAlgo, supportedSignAlgos))
+            acceptable = FALSE;
+      }
+#endif
+
+      //If all the requirements were met, the certificate can be
+      //used to authenticate the client
+      if(acceptable)
+      {
+         context->cert = &context->certs[i];
+         break;
+      }
+   }
+
+   //Prepare to receive ServerHelloDone message...
+   context->state = TLS_STATE_SERVER_HELLO_DONE;
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse ServerHelloDone message
+ *
+ * The ServerHelloDone message is sent by the server to indicate the
+ * end of the ServerHello and associated messages. After sending this
+ * message, the server will wait for a client response
+ *
+ * @param[in] context Pointer to the TLS context
+ * @param[in] message Incoming ServerHelloDone message to parse
+ * @param[in] length Message length
+ * @return Error code
+ **/
+
+error_t tlsParseServerHelloDone(TlsContext *context, const TlsServerHelloDone *message, size_t length)
+{
+   //Debug message
+   TRACE_INFO("ServerHelloDone message received (%" PRIuSIZE " bytes)...\r\n", length);
+   TRACE_DEBUG_ARRAY("  ", message, length);
+
+   //Check the length of the ServerHelloDone message
+   if(length != sizeof(TlsServerHelloDone))
+      return ERROR_DECODING_FAILED;
+
+   //Check key exchange method
+   if(context->keyExchMethod == TLS_KEY_EXCH_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA)
+   {
+      //The server may omit the CertificateRequest message and go
+      //directly to the ServerHelloDone message
+      if(context->state != TLS_STATE_CERTIFICATE_REQUEST &&
+         context->state != TLS_STATE_SERVER_HELLO_DONE)
+      {
+         //Handshake failure
+         return ERROR_UNEXPECTED_MESSAGE;
+      }
+   }
+   else if(context->keyExchMethod == TLS_KEY_EXCH_PSK)
+   {
+      //If no PSK identity hint is provided by the server, the
+      //ServerKeyExchange message is omitted
+      if(context->state != TLS_STATE_SERVER_KEY_EXCHANGE &&
+         context->state != TLS_STATE_SERVER_HELLO_DONE)
+      {
+         //Handshake failure
+         return ERROR_UNEXPECTED_MESSAGE;
+      }
+   }
+   else if(context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK)
+   {
+      //The server may omit the ServerKeyExchange message and/or
+      //the CertificateRequest message
+      if(context->state != TLS_STATE_SERVER_KEY_EXCHANGE &&
+         context->state != TLS_STATE_CERTIFICATE_REQUEST &&
+         context->state != TLS_STATE_SERVER_HELLO_DONE)
+      {
+         //Handshake failure
+         return ERROR_UNEXPECTED_MESSAGE;
+      }
+   }
+   else
+   {
+      //Check current state
+      if(context->state != TLS_STATE_SERVER_HELLO_DONE)
+         return ERROR_UNEXPECTED_MESSAGE;
+   }
+
+   //Update the hash value with the incoming handshake message
+   tlsUpdateHandshakeHash(context, message, length);
+
+   //Prepare to send client Certificate message...
+   context->state = TLS_STATE_CLIENT_CERTIFICATE;
+   //Successful processing
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls_client.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,65 @@
+/**
+ * @file tls_client.h
+ * @brief Handshake message processing (TLS client)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TLS_CLIENT_H
+#define _TLS_CLIENT_H
+
+//Dependencies
+#include "tls.h"
+
+//TLS client specific functions
+error_t tlsClientHandshake(TlsContext *context);
+error_t tlsParseServerMessage(TlsContext *context);
+
+error_t tlsSendClientHello(TlsContext *context);
+error_t tlsSendClientKeyExchange(TlsContext *context);
+error_t tlsSendCertificateVerify(TlsContext *context);
+
+error_t tlsFormatClientHello(TlsContext *context,
+   TlsClientHello *message, size_t *length);
+
+error_t tlsFormatClientKeyExchange(TlsContext *context,
+   TlsClientKeyExchange *message, size_t *length);
+
+error_t tlsFormatCertificateVerify(TlsContext *context,
+   TlsCertificateVerify *message, size_t *length);
+
+error_t tlsParseServerHello(TlsContext *context,
+   const TlsServerHello *message, size_t length);
+
+error_t tlsParseServerKeyExchange(TlsContext *context,
+   const TlsServerKeyExchange *message, size_t length);
+
+error_t tlsParseCertificateRequest(TlsContext *context,
+   const TlsCertificateRequest *message, size_t length);
+
+error_t tlsParseServerHelloDone(TlsContext *context,
+   const TlsServerHelloDone *message, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls_client_misc.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,840 @@
+/**
+ * @file tls_client_misc.c
+ * @brief Helper functions (TLS client)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TLS_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "tls.h"
+#include "tls_cipher_suites.h"
+#include "tls_client.h"
+#include "tls_client_misc.h"
+#include "tls_common.h"
+#include "tls_record.h"
+#include "tls_cache.h"
+#include "tls_misc.h"
+#include "debug.h"
+
+//Check SSL library configuration
+#if (TLS_SUPPORT == ENABLED && TLS_CLIENT_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Format PSK identity
+ * @param[in] context Pointer to the TLS context
+ * @param[in] p Output stream where to write the PSK identity hint
+ * @param[out] written Total number of bytes that have been written
+ * @return Error code
+ **/
+
+error_t tlsFormatPskIdentity(TlsContext *context,
+   uint8_t *p, size_t *written)
+{
+   size_t n;
+   TlsPskIdentity *pskIdentity;
+
+   //Point to the PSK identity
+   pskIdentity = (TlsPskIdentity *) p;
+
+   //Initialize length field
+   n = 0;
+
+#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
+   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   //Any registered callback?
+   if(context->pskCallback != NULL)
+   {
+      error_t error;
+
+      //Invoke user callback function
+      if(context->pskIdentityHint != NULL)
+         error = context->pskCallback(context, context->pskIdentityHint);
+      else
+         error = context->pskCallback(context, "");
+
+      //Any error to report?
+      if(error)
+         return ERROR_UNKNOWN_IDENTITY;
+   }
+
+   //Any PSK identity defined?
+   if(context->pskIdentity != NULL)
+   {
+      //Determine the length of the PSK identity
+      n = strlen(context->pskIdentity);
+      //Copy PSK identity
+      memcpy(pskIdentity->value, context->pskIdentity, n);
+   }
+#endif
+
+   //The PSK identity is preceded by a 2-byte length field
+   pskIdentity->length = htons(n);
+
+   //Total number of bytes that have been written
+   *written = sizeof(TlsPskIdentity) + n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format client's key exchange parameters
+ * @param[in] context Pointer to the TLS context
+ * @param[in] p Output stream where to write the PSK identity hint
+ * @param[out] written Total number of bytes that have been written
+ * @return Error code
+ **/
+
+error_t tlsFormatClientKeyParams(TlsContext *context,
+   uint8_t *p, size_t *written)
+{
+#if (TLS_RSA_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED)
+   //RSA key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK)
+   {
+      error_t error;
+      size_t n;
+
+      //Sanity check
+      if(TLS_MAX_PREMASTER_SECRET_SIZE < 48)
+         return ERROR_BUFFER_OVERFLOW;
+
+      //If RSA is being used for key agreement and authentication, the
+      //client generates a 48-byte premaster secret
+      context->premasterSecretLen = 48;
+
+      //The first 2 bytes code the latest version supported by the client
+      STORE16BE(TLS_MAX_VERSION, context->premasterSecret);
+
+      //The last 46 bytes contain securely-generated random bytes
+      error = context->prngAlgo->read(context->prngContext,
+         context->premasterSecret + 2, 46);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //The RSA-encrypted premaster secret in a ClientKeyExchange is preceded by
+      //two length bytes. SSL 3.0 implementations do not include these bytes
+      if(context->version > SSL_VERSION_3_0)
+      {
+         //Encrypt the premaster secret using the server public key
+         error = rsaesPkcs1v15Encrypt(context->prngAlgo, context->prngContext,
+            &context->peerRsaPublicKey, context->premasterSecret, 48, p + 2, &n);
+         //RSA encryption failed?
+         if(error)
+            return error;
+
+         //Write the length field
+         STORE16BE(n, p);
+
+         //Length of the resulting octet string
+         n += 2;
+      }
+      else
+      {
+         //Encrypt the premaster secret using the server public key
+         error = rsaesPkcs1v15Encrypt(context->prngAlgo, context->prngContext,
+            &context->peerRsaPublicKey, context->premasterSecret, 48, p, &n);
+         //RSA encryption failed?
+         if(error)
+            return error;
+      }
+
+      //Total number of bytes that have been written
+      *written = n;
+   }
+   else
+#endif
+#if (TLS_DH_ANON_SUPPORT == ENABLED || TLS_DHE_RSA_SUPPORT == ENABLED || \
+   TLS_DHE_DSS_SUPPORT == ENABLED || TLS_DHE_PSK_SUPPORT == ENABLED)
+   //Diffie-Hellman key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_DH_ANON ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK)
+   {
+      error_t error;
+      size_t n;
+
+      //Generate an ephemeral key pair
+      error = dhGenerateKeyPair(&context->dhContext,
+         context->prngAlgo, context->prngContext);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Encode the client's public value to an opaque vector
+      error = tlsWriteMpi(&context->dhContext.ya, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Total number of bytes that have been written
+      *written = n;
+
+      //Calculate the negotiated key Z
+      error = dhComputeSharedSecret(&context->dhContext, context->premasterSecret,
+         TLS_MAX_PREMASTER_SECRET_SIZE, &context->premasterSecretLen);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Leading bytes of Z that contain all zero bits are stripped before
+      //it is used as the premaster secret (RFC 4346, section 8.2.1)
+      for(n = 0; n < context->premasterSecretLen; n++)
+      {
+         if(context->premasterSecret[n] != 0x00)
+            break;
+      }
+
+      //Any leading zero bytes?
+      if(n > 0)
+      {
+         //Strip leading zero bytes from the negotiated key
+         memmove(context->premasterSecret, context->premasterSecret + n,
+            context->premasterSecretLen - n);
+
+         //Adjust the length of the premaster secret
+         context->premasterSecretLen -= n;
+      }
+   }
+   else
+#endif
+#if (TLS_ECDH_ANON_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED || \
+   TLS_ECDHE_ECDSA_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   //ECDH key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_ECDH_ANON ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
+   {
+      error_t error;
+      size_t n;
+
+      //Generate an ephemeral key pair
+      error = ecdhGenerateKeyPair(&context->ecdhContext,
+         context->prngAlgo, context->prngContext);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Encode the client's public key to an opaque vector
+      error = tlsWriteEcPoint(&context->ecdhContext.params,
+         &context->ecdhContext.qa, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Total number of bytes that have been written
+      *written = n;
+
+      //Calculate the negotiated key Z
+      error = ecdhComputeSharedSecret(&context->ecdhContext, context->premasterSecret,
+         TLS_MAX_PREMASTER_SECRET_SIZE, &context->premasterSecretLen);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+   else
+#endif
+   //Invalid key exchange method?
+   {
+      //The specified key exchange method is not supported
+      return ERROR_UNSUPPORTED_KEY_EXCH_METHOD;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse PSK identity hint
+ * @param[in] context Pointer to the TLS context
+ * @param[in] p Input stream where to read the PSK identity hint
+ * @param[in] length Number of bytes available in the input stream
+ * @param[out] consumed Total number of bytes that have been consumed
+ * @return Error code
+ **/
+
+error_t tlsParsePskIdentityHint(TlsContext *context,
+    const uint8_t *p, size_t length, size_t *consumed)
+{
+   size_t n;
+   TlsPskIdentityHint *pskIdentityHint;
+
+   //Malformed ServerKeyExchange message?
+   if(length < sizeof(TlsPskIdentityHint))
+      return ERROR_DECODING_FAILED;
+
+   //Point to the PSK identity hint
+   pskIdentityHint = (TlsPskIdentityHint *) p;
+
+   //Retrieve the length of the PSK identity hint
+   n = ntohs(pskIdentityHint->length);
+
+   //Make sure the length field is valid
+   if(length < (sizeof(TlsPskIdentityHint) + n))
+      return ERROR_DECODING_FAILED;
+
+#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
+   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   //Check whether the PSK identity hint has already been configured
+   if(context->pskIdentityHint != NULL)
+   {
+      //Release memory
+      tlsFreeMem(context->pskIdentityHint);
+      context->pskIdentityHint = NULL;
+   }
+
+   //The PSK identity hint is optional
+   if(n > 0)
+   {
+      //Allocate a memory block to hold the PSK identity hint
+      context->pskIdentityHint = tlsAllocMem(n + 1);
+      //Failed to allocate memory?
+      if(context->pskIdentityHint == NULL)
+         return ERROR_OUT_OF_MEMORY;
+
+      //Save the PSK identity hint
+      memcpy(context->pskIdentityHint, pskIdentityHint->value, n);
+      //Properly terminate the string
+      context->pskIdentityHint[n] = '\0';
+   }
+#endif
+
+   //Total number of bytes that have been consumed
+   *consumed = sizeof(TlsPskIdentityHint) + n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse server's key exchange parameters
+ * @param[in] context Pointer to the TLS context
+ * @param[in] p Input stream where to read the server's key exchange parameters
+ * @param[in] length Number of bytes available in the input stream
+ * @param[out] consumed Total number of bytes that have been consumed
+ * @return Error code
+ **/
+
+error_t tlsParseServerKeyParams(TlsContext *context,
+    const uint8_t *p, size_t length, size_t *consumed)
+{
+   error_t error;
+   const uint8_t *params;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Point to the server's key exchange parameters
+   params = p;
+
+#if (TLS_DH_ANON_SUPPORT == ENABLED || TLS_DHE_RSA_SUPPORT == ENABLED || \
+   TLS_DHE_DSS_SUPPORT == ENABLED || TLS_DHE_PSK_SUPPORT == ENABLED)
+   //Diffie-Hellman key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_DH_ANON ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK)
+   {
+      uint_t k;
+      size_t n;
+
+      //Convert the prime modulus to a multiple precision integer
+      error = tlsReadMpi(&context->dhContext.params.p, p, length, &n);
+
+      //Check status code
+      if(!error)
+      {
+         //Get the length of the prime modulus, in bits
+         k = mpiGetBitLength(&context->dhContext.params.p);
+
+         //Make sure the prime modulus is acceptable
+         if(k < TLS_MIN_DH_MODULUS_SIZE || k > TLS_MAX_DH_MODULUS_SIZE)
+            error = ERROR_ILLEGAL_PARAMETER;
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Advance data pointer
+         p += n;
+         //Remaining bytes to process
+         length -= n;
+
+         //Convert the generator to a multiple precision integer
+         error = tlsReadMpi(&context->dhContext.params.g, p, length, &n);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Advance data pointer
+         p += n;
+         //Remaining bytes to process
+         length -= n;
+
+         //Convert the server's public value to a multiple precision integer
+         error = tlsReadMpi(&context->dhContext.yb, p, length, &n);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Advance data pointer
+         p += n;
+         //Remaining bytes to process
+         length -= n;
+
+         //Verify peer's public value
+         error = dhCheckPublicKey(&context->dhContext.params,
+            &context->dhContext.yb);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Debug message
+         TRACE_DEBUG("Diffie-Hellman parameters:\r\n");
+         TRACE_DEBUG("  Prime modulus:\r\n");
+         TRACE_DEBUG_MPI("    ", &context->dhContext.params.p);
+         TRACE_DEBUG("  Generator:\r\n");
+         TRACE_DEBUG_MPI("    ", &context->dhContext.params.g);
+         TRACE_DEBUG("  Server public value:\r\n");
+         TRACE_DEBUG_MPI("    ", &context->dhContext.yb);
+      }
+   }
+   else
+#endif
+#if (TLS_ECDH_ANON_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED || \
+   TLS_ECDHE_ECDSA_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   //ECDH key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_ECDH_ANON ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
+   {
+      size_t n;
+      uint8_t curveType;
+      uint16_t namedCurve;
+      const EcCurveInfo *curveInfo;
+
+      //Malformed ServerKeyExchange message?
+      if(length < sizeof(curveType))
+         error = ERROR_DECODING_FAILED;
+
+      //Check status code
+      if(!error)
+      {
+         //Retrieve the type of the elliptic curve domain parameters
+         curveType = *p;
+
+         //Advance data pointer
+         p += sizeof(curveType);
+         //Remaining bytes to process
+         length -= sizeof(curveType);
+
+         //Only named curves are supported
+         if(curveType != TLS_EC_CURVE_TYPE_NAMED_CURVE)
+            error = ERROR_ILLEGAL_PARAMETER;
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Malformed ServerKeyExchange message?
+         if(length < sizeof(namedCurve))
+            error = ERROR_DECODING_FAILED;
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Get elliptic curve identifier
+         namedCurve = LOAD16BE(p);
+
+         //Advance data pointer
+         p += sizeof(namedCurve);
+         //Remaining bytes to process
+         length -= sizeof(namedCurve);
+
+         //Retrieve the corresponding EC domain parameters
+         curveInfo = tlsGetCurveInfo(namedCurve);
+
+         //Make sure the elliptic curve is supported
+         if(curveInfo == NULL)
+            error = ERROR_ILLEGAL_PARAMETER;
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Load EC domain parameters
+         error = ecLoadDomainParameters(&context->ecdhContext.params,
+            curveInfo);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Read server's public key
+         error = tlsReadEcPoint(&context->ecdhContext.params,
+            &context->ecdhContext.qb, p, length, &n);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Advance data pointer
+         p += n;
+         //Remaining bytes to process
+         length -= n;
+
+         //Verify peer's public key
+         error = ecdhCheckPublicKey(&context->ecdhContext.params, &context->ecdhContext.qb);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Debug message
+         TRACE_DEBUG("  Server public key X:\r\n");
+         TRACE_DEBUG_MPI("    ", &context->ecdhContext.qb.x);
+         TRACE_DEBUG("  Server public key Y:\r\n");
+         TRACE_DEBUG_MPI("    ", &context->ecdhContext.qb.y);
+      }
+   }
+   else
+#endif
+   //Invalid key exchange method?
+   {
+      //It is not legal to send the ServerKeyExchange message when a key
+      //exchange method other than DHE_DSS, DHE_RSA, DH_anon, ECDHE_RSA,
+      //ECDHE_ECDSA or ECDH_anon is selected
+      error = ERROR_UNEXPECTED_MESSAGE;
+   }
+
+   //Total number of bytes that have been consumed
+   *consumed = p - params;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Verify signature over the server's key exchange parameters
+ * @param[in] context Pointer to the TLS context
+ * @param[in] p Input stream where to read the signature
+ * @param[in] length Number of bytes available in the input stream
+ * @param[in] params Pointer to the server's key exchange parameters
+ * @param[in] paramsLen Length of the server's key exchange parameters
+ * @param[out] consumed Total number of bytes that have been consumed
+ * @return Error code
+ **/
+
+error_t tlsVerifyServerKeySignature(TlsContext *context, const uint8_t *p,
+   size_t length, const uint8_t *params, size_t paramsLen, size_t *consumed)
+{
+   error_t error;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_MIN_VERSION <= TLS_VERSION_1_1)
+   //SSL 3.0, TLS 1.0 or TLS 1.1 currently selected?
+   if(context->version <= TLS_VERSION_1_1)
+   {
+      TlsDigitalSignature *signature;
+
+      //Point to the digitally-signed element
+      signature = (TlsDigitalSignature *) p;
+
+      //Check the length of the digitally-signed element
+      if(length < sizeof(TlsDigitalSignature))
+         return ERROR_DECODING_FAILED;
+      if(length < (sizeof(TlsDigitalSignature) + ntohs(signature->length)))
+         return ERROR_DECODING_FAILED;
+
+#if (TLS_DHE_RSA_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED)
+      //Check whether DHE_RSA or ECDHE_RSA key exchange method is currently used
+      if(context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
+         context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA)
+      {
+         Md5Context *md5Context;
+         Sha1Context *sha1Context;
+
+         //Allocate a memory buffer to hold the MD5 context
+         md5Context = tlsAllocMem(sizeof(Md5Context));
+
+         //Successful memory allocation?
+         if(md5Context != NULL)
+         {
+            //Compute MD5(ClientHello.random + ServerHello.random + ServerDhParams)
+            md5Init(md5Context);
+            md5Update(md5Context, context->random, 64);
+            md5Update(md5Context, params, paramsLen);
+            md5Final(md5Context, context->verifyData);
+
+            //Release previously allocated memory
+            tlsFreeMem(md5Context);
+         }
+         else
+         {
+            //Failed to allocate memory
+            error = ERROR_OUT_OF_MEMORY;
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Allocate a memory buffer to hold the SHA-1 context
+            sha1Context = tlsAllocMem(sizeof(Sha1Context));
+
+            //Successful memory allocation?
+            if(sha1Context != NULL)
+            {
+               //Compute SHA(ClientHello.random + ServerHello.random + ServerDhParams)
+               sha1Init(sha1Context);
+               sha1Update(sha1Context, context->random, 64);
+               sha1Update(sha1Context, params, paramsLen);
+               sha1Final(sha1Context, context->verifyData + MD5_DIGEST_SIZE);
+
+               //Release previously allocated memory
+               tlsFreeMem(sha1Context);
+            }
+            else
+            {
+               //Failed to allocate memory
+               error = ERROR_OUT_OF_MEMORY;
+            }
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //RSA signature verification
+            error = tlsVerifyRsaSignature(&context->peerRsaPublicKey,
+               context->verifyData, signature->value, ntohs(signature->length));
+         }
+      }
+      else
+#endif
+#if (TLS_DHE_DSS_SUPPORT == ENABLED)
+      //Check whether DHE_DSS key exchange method is currently used
+      if(context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS)
+      {
+         Sha1Context *sha1Context;
+
+         //Allocate a memory buffer to hold the SHA-1 context
+         sha1Context = tlsAllocMem(sizeof(Sha1Context));
+
+         //Successful memory allocation?
+         if(sha1Context != NULL)
+         {
+            //Compute SHA(ClientHello.random + ServerHello.random + ServerDhParams)
+            sha1Init(sha1Context);
+            sha1Update(sha1Context, context->random, 64);
+            sha1Update(sha1Context, params, paramsLen);
+            sha1Final(sha1Context, context->verifyData);
+
+            //Release previously allocated memory
+            tlsFreeMem(sha1Context);
+         }
+         else
+         {
+            //Failed to allocate memory
+            error = ERROR_OUT_OF_MEMORY;
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //DSA signature verification
+            error = tlsVerifyDsaSignature(&context->peerDsaPublicKey, context->verifyData,
+               SHA1_DIGEST_SIZE, signature->value, ntohs(signature->length));
+         }
+      }
+      else
+#endif
+#if (TLS_ECDHE_ECDSA_SUPPORT == ENABLED)
+      //Check whether ECDHE_ECDSA key exchange method is currently used
+      if(context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA)
+      {
+         Sha1Context *sha1Context;
+
+         //Allocate a memory buffer to hold the SHA-1 context
+         sha1Context = tlsAllocMem(sizeof(Sha1Context));
+
+         //Successful memory allocation?
+         if(sha1Context != NULL)
+         {
+            //Compute SHA(ClientHello.random + ServerHello.random + ServerDhParams)
+            sha1Init(sha1Context);
+            sha1Update(sha1Context, context->random, 64);
+            sha1Update(sha1Context, params, paramsLen);
+            sha1Final(sha1Context, context->verifyData);
+
+            //Release previously allocated memory
+            tlsFreeMem(sha1Context);
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //ECDSA signature verification
+            error = tlsVerifyEcdsaSignature(&context->peerEcParams,
+               &context->peerEcPublicKey, context->verifyData,
+               SHA1_DIGEST_SIZE, signature->value, ntohs(signature->length));
+         }
+      }
+      else
+#endif
+      //Invalid signature algorithm?
+      {
+         //Report an error
+         error = ERROR_UNSUPPORTED_SIGNATURE_ALGO;
+      }
+
+      //Total number of bytes that have been consumed
+      *consumed = sizeof(TlsDigitalSignature) + ntohs(signature->length);
+   }
+   else
+#endif
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_MIN_VERSION <= TLS_VERSION_1_2)
+   //TLS 1.2 currently selected?
+   if(context->version == TLS_VERSION_1_2)
+   {
+      TlsDigitalSignature2 *signature;
+      const HashAlgo *hashAlgo;
+      HashContext *hashContext;
+
+      //Point to the digitally-signed element
+      signature = (TlsDigitalSignature2 *) p;
+
+      //Check the length of the digitally-signed element
+      if(length < sizeof(TlsDigitalSignature2))
+         return ERROR_DECODING_FAILED;
+      if(length < (sizeof(TlsDigitalSignature2) + ntohs(signature->length)))
+         return ERROR_DECODING_FAILED;
+
+      //Retrieve the hash algorithm used for signing
+      hashAlgo = tlsGetHashAlgo(signature->algorithm.hash);
+
+      //Make sure the hash algorithm is supported
+      if(hashAlgo != NULL)
+      {
+         //Allocate a memory buffer to hold the hash context
+         hashContext = tlsAllocMem(hashAlgo->contextSize);
+
+         //Successful memory allocation?
+         if(hashContext != NULL)
+         {
+            //Compute SHA(ClientHello.random + ServerHello.random + ServerDhParams)
+            hashAlgo->init(hashContext);
+            hashAlgo->update(hashContext, context->random, 64);
+            hashAlgo->update(hashContext, params, paramsLen);
+            hashAlgo->final(hashContext, NULL);
+
+#if (TLS_DHE_RSA_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED)
+            //Check whether DHE_RSA or ECDHE_RSA key exchange method is currently used
+            if((context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
+               context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA) &&
+               signature->algorithm.signature == TLS_SIGN_ALGO_RSA)
+            {
+               //Use the signature verification algorithm defined in PKCS #1 v1.5
+               error = rsassaPkcs1v15Verify(&context->peerRsaPublicKey, hashAlgo,
+                  hashContext->digest, signature->value, ntohs(signature->length));
+            }
+            else
+#endif
+#if (TLS_DHE_DSS_SUPPORT == ENABLED)
+            //Check whether DHE_DSS key exchange method is currently used
+            if(context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS &&
+               signature->algorithm.signature == TLS_SIGN_ALGO_DSA)
+            {
+               //DSA signature verification
+               error = tlsVerifyDsaSignature(&context->peerDsaPublicKey, hashContext->digest,
+                  hashAlgo->digestSize, signature->value, ntohs(signature->length));
+            }
+            else
+#endif
+#if (TLS_ECDHE_ECDSA_SUPPORT == ENABLED)
+            //Check whether DHE_ECDSA key exchange method is currently used
+            if(context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA &&
+               signature->algorithm.signature == TLS_SIGN_ALGO_ECDSA)
+            {
+               //ECDSA signature verification
+               error = tlsVerifyEcdsaSignature(&context->peerEcParams, &context->peerEcPublicKey,
+                  hashContext->digest, hashAlgo->digestSize, signature->value, ntohs(signature->length));
+            }
+            else
+#endif
+            //Invalid signature algorithm?
+            {
+               //Report an error
+               error = ERROR_UNSUPPORTED_SIGNATURE_ALGO;
+            }
+
+            //Release previously allocated memory
+            tlsFreeMem(hashContext);
+         }
+         else
+         {
+            //Failed to allocate memory
+            error = ERROR_OUT_OF_MEMORY;
+         }
+      }
+      else
+      {
+         //Hash algorithm not supported
+         error = ERROR_INVALID_SIGNATURE;
+      }
+
+      //Total number of bytes that have been consumed
+      *consumed = sizeof(TlsDigitalSignature2) + ntohs(signature->length);
+   }
+   else
+#endif
+   {
+      //The negotiated TLS version is not valid
+      error = ERROR_INVALID_VERSION;
+   }
+
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls_client_misc.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,52 @@
+/**
+ * @file tls_client_misc.h
+ * @brief Helper functions (TLS client)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TLS_SERVER_MISC_H
+#define _TLS_SERVER_MISC_H
+
+//Dependencies
+#include "tls.h"
+
+//TLS client specific functions
+error_t tlsFormatPskIdentity(TlsContext *context,
+   uint8_t *p, size_t *written);
+
+error_t tlsFormatClientKeyParams(TlsContext *context,
+   uint8_t *p, size_t *written);
+
+error_t tlsParsePskIdentityHint(TlsContext *context,
+    const uint8_t *p, size_t length, size_t *consumed);
+
+error_t tlsParseServerKeyParams(TlsContext *context,
+    const uint8_t *p, size_t length, size_t *consumed);
+
+error_t tlsVerifyServerKeySignature(TlsContext *context, const uint8_t *p,
+   size_t length, const uint8_t *params, size_t paramsLen, size_t *consumed);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls_common.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1224 @@
+/**
+ * @file tls_common.c
+ * @brief Handshake message processing (TLS client and server)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TLS_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include <ctype.h>
+#include "tls.h"
+#include "tls_cipher_suites.h"
+#include "tls_client.h"
+#include "tls_server.h"
+#include "tls_common.h"
+#include "tls_record.h"
+#include "tls_cache.h"
+#include "tls_misc.h"
+#include "asn1.h"
+#include "oid.h"
+#include "x509.h"
+#include "pem.h"
+#include "debug.h"
+
+//Check SSL library configuration
+#if (TLS_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Perform TLS handshake
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsHandshake(TlsContext *context)
+{
+   error_t error;
+
+#if (TLS_CLIENT_SUPPORT == ENABLED)
+   //TLS operates as a client?
+   if(context->entity == TLS_CONNECTION_END_CLIENT)
+   {
+      //Initiate TLS handshake with the remote server
+      error = tlsClientHandshake(context);
+   }
+   else
+#endif
+#if (TLS_SERVER_SUPPORT == ENABLED)
+   //TLS operates as a server?
+   if(context->entity == TLS_CONNECTION_END_SERVER)
+   {
+      //Initiate TLS handshake with the remote client
+      error = tlsServerHandshake(context);
+   }
+   else
+#endif
+   //Unsupported mode of operation?
+   {
+      //Cannot establish a secure session between the server and the client
+      error = ERROR_INVALID_PARAMETER;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send Certificate message
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsSendCertificate(TlsContext *context)
+{
+   error_t error;
+   size_t length;
+   void *message;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Point to the buffer where to format the message
+   message = (void *) context->txBuffer;
+
+#if (TLS_CLIENT_SUPPORT == ENABLED)
+   //TLS operates as a client?
+   if(context->entity == TLS_CONNECTION_END_CLIENT)
+   {
+      //The client must send a Certificate message if the server requests it
+      if(context->clientCertRequested)
+      {
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_MIN_VERSION <= SSL_VERSION_3_0)
+         //No suitable certificate available?
+         if(context->cert == NULL && context->version == SSL_VERSION_3_0)
+         {
+            //The client should send a no_certificate alert instead
+            error = tlsFormatAlert(context, TLS_ALERT_LEVEL_WARNING,
+               TLS_ALERT_NO_CERTIFICATE, message, &length);
+
+            //Check status code
+            if(!error)
+            {
+               //Debug message
+               TRACE_INFO("Sending Alert message (%" PRIuSIZE " bytes)...\r\n", length);
+               TRACE_INFO_ARRAY("  ", message, length);
+
+               //Send Alert message
+               error = tlsWriteProtocolData(context, message, length, TLS_TYPE_ALERT);
+            }
+         }
+         else
+#endif
+         {
+            //Format Certificate message
+            error = tlsFormatCertificate(context, message, &length);
+
+            //Check status code
+            if(!error)
+            {
+               //Debug message
+               TRACE_INFO("Sending Certificate message (%" PRIuSIZE " bytes)...\r\n", length);
+               TRACE_DEBUG_ARRAY("  ", message, length);
+
+               //Send handshake message
+               error = tlsWriteProtocolData(context, message, length, TLS_TYPE_HANDSHAKE);
+            }
+         }
+      }
+   }
+   else
+#endif
+#if (TLS_SERVER_SUPPORT == ENABLED)
+   //TLS operates as a server?
+   if(context->entity == TLS_CONNECTION_END_SERVER)
+   {
+      //The server must send a Certificate message whenever the agreed-upon
+      //key exchange method uses certificates for authentication
+      if(context->cert != NULL)
+      {
+         //Format Certificate message
+         error = tlsFormatCertificate(context, message, &length);
+
+         //Check status code
+         if(!error)
+         {
+            //Debug message
+            TRACE_INFO("Sending Certificate message (%" PRIuSIZE " bytes)...\r\n", length);
+            TRACE_DEBUG_ARRAY("  ", message, length);
+
+            //Send handshake message
+            error = tlsWriteProtocolData(context, message, length, TLS_TYPE_HANDSHAKE);
+         }
+      }
+   }
+   else
+#endif
+   //Unsupported mode of operation?
+   {
+      //Report an error
+      error = ERROR_FAILURE;
+   }
+
+   //Check status code
+   if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
+   {
+      //Update FSM state
+      if(context->entity == TLS_CONNECTION_END_CLIENT)
+         context->state = TLS_STATE_CLIENT_KEY_EXCHANGE;
+      else
+         context->state = TLS_STATE_SERVER_KEY_EXCHANGE;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send ChangeCipherSpec message
+ *
+ * The change cipher spec message is sent by both the client and the
+ * server to notify the receiving party that subsequent records will be
+ * protected under the newly negotiated CipherSpec and keys
+ *
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsSendChangeCipherSpec(TlsContext *context)
+{
+   error_t error;
+   size_t length;
+   TlsChangeCipherSpec *message;
+
+   //Point to the buffer where to format the message
+   message = (TlsChangeCipherSpec *) context->txBuffer;
+
+   //Format ChangeCipherSpec message
+   error = tlsFormatChangeCipherSpec(context, message, &length);
+
+   //Check status code
+   if(!error)
+   {
+      //Debug message
+      TRACE_INFO("Sending ChangeCipherSpec message (%" PRIuSIZE " bytes)...\r\n", length);
+      TRACE_DEBUG_ARRAY("  ", message, length);
+
+      //Send ChangeCipherSpec message
+      error = tlsWriteProtocolData(context, message, length, TLS_TYPE_CHANGE_CIPHER_SPEC);
+   }
+
+   //Check status code
+   if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
+   {
+      //Initialize encryption engine
+      error = tlsInitEncryptionEngine(context);
+   }
+
+   //Check status code
+   if(!error)
+   {
+      //Inform the record layer that subsequent records will be protected
+      //under the newly negotiated encryption algorithm
+      context->changeCipherSpecSent = TRUE;
+
+      //Prepare to send a Finished message to the peer...
+      if(context->entity == TLS_CONNECTION_END_CLIENT)
+         context->state = TLS_STATE_CLIENT_FINISHED;
+      else
+         context->state = TLS_STATE_SERVER_FINISHED;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send Finished message
+ *
+ * A Finished message is always sent immediately after a change
+ * cipher spec message to verify that the key exchange and
+ * authentication processes were successful. It is essential that a
+ * change cipher spec message be received between the other handshake
+ * messages and the Finished message
+ *
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsSendFinished(TlsContext *context)
+{
+   error_t error;
+   size_t length;
+   TlsFinished *message;
+
+   //Point to the buffer where to format the message
+   message = (TlsFinished *) context->txBuffer;
+
+   //The verify data is generated from all messages in this handshake
+   //up to but not including the Finished message
+   error = tlsComputeVerifyData(context, context->entity);
+
+   //Check status code
+   if(!error)
+   {
+      //Format Finished message
+      error = tlsFormatFinished(context, message, &length);
+   }
+
+   //Check status code
+   if(!error)
+   {
+      //Debug message
+      TRACE_INFO("Sending Finished message (%" PRIuSIZE " bytes)...\r\n", length);
+      TRACE_DEBUG_ARRAY("  ", message, length);
+
+      //Send handshake message
+      error = tlsWriteProtocolData(context, message, length, TLS_TYPE_HANDSHAKE);
+   }
+
+   //Check status code
+   if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
+   {
+      //TLS operates as a client or a server?
+      if(context->entity == TLS_CONNECTION_END_CLIENT)
+      {
+         //Use abbreviated or full handshake?
+         if(context->resume)
+            context->state = TLS_STATE_APPLICATION_DATA;
+         else
+            context->state = TLS_STATE_SERVER_CHANGE_CIPHER_SPEC;
+      }
+      else
+      {
+         //Use abbreviated or full handshake?
+         if(context->resume)
+            context->state = TLS_STATE_CLIENT_CHANGE_CIPHER_SPEC;
+         else
+            context->state = TLS_STATE_APPLICATION_DATA;
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send Alert message
+ * @param[in] context Pointer to the TLS context
+ * @param[in] level Severity of the message (warning or fatal)
+ * @param[in] description Description of the alert
+ * @return Error code
+ **/
+
+error_t tlsSendAlert(TlsContext *context, uint8_t level, uint8_t description)
+{
+   error_t error;
+   size_t length;
+   TlsAlert *message;
+
+   //Point to the buffer where to format the message
+   message = (TlsAlert *) context->txBuffer;
+
+   //Format Alert message
+   error = tlsFormatAlert(context, level, description, message, &length);
+
+   //Check status code
+   if(!error)
+   {
+      //Debug message
+      TRACE_INFO("Sending Alert message (%" PRIuSIZE " bytes)...\r\n", length);
+      TRACE_INFO_ARRAY("  ", message, length);
+
+      //Send Alert message
+      error = tlsWriteProtocolData(context, message, length, TLS_TYPE_ALERT);
+   }
+
+   //Alert messages convey the severity of the message
+   if(level == TLS_ALERT_LEVEL_WARNING)
+   {
+      //If an alert with a level of warning is sent, generally the
+      //connection can continue normally
+      if(description == TLS_ALERT_CLOSE_NOTIFY)
+      {
+         //Either party may initiate a close by sending a close_notify alert
+         context->closeNotifySent = TRUE;
+
+         //Update FSM state
+         context->state = TLS_STATE_CLOSING;
+      }
+   }
+   else if(level == TLS_ALERT_LEVEL_FATAL)
+   {
+      //Alert messages with a level of fatal result in the immediate
+      //termination of the connection
+      context->fatalAlertSent = TRUE;
+
+      //Any connection terminated with a fatal alert must not be resumed
+      if(context->entity == TLS_CONNECTION_END_SERVER)
+         tlsRemoveFromCache(context);
+
+      //Servers and clients must forget any session identifiers
+      memset(context->sessionId, 0, 32);
+      context->sessionIdLen = 0;
+
+      //Update FSM state
+      context->state = TLS_STATE_CLOSING;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Format Certificate message
+ * @param[in] context Pointer to the TLS context
+ * @param[out] message Buffer where to format the Certificate message
+ * @param[out] length Length of the resulting Certificate message
+ * @return Error code
+ **/
+
+error_t tlsFormatCertificate(TlsContext *context,
+   TlsCertificate *message, size_t *length)
+{
+   error_t error;
+   uint8_t *p;
+   const char_t *pemCert;
+   size_t pemCertLength;
+   uint8_t *derCert;
+   size_t derCertSize;
+   size_t derCertLength;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Handshake message type
+   message->msgType = TLS_TYPE_CERTIFICATE;
+
+   //Point to the first certificate of the list
+   p = message->certificateList;
+   //Length of the certificate list in bytes
+   *length = 0;
+
+   //Check whether a certificate is available
+   if(context->cert != NULL)
+   {
+      //Point to the certificate chain
+      pemCert = context->cert->certChain;
+      //Get the total length, in bytes, of the certificate chain
+      pemCertLength = context->cert->certChainLength;
+   }
+   else
+   {
+      //If no suitable certificate is available, the message
+      //contains an empty certificate list
+      pemCert = NULL;
+      pemCertLength = 0;
+   }
+
+   //DER encoded certificate
+   derCert = NULL;
+   derCertSize = 0;
+   derCertLength = 0;
+
+   //Parse the certificate chain
+   while(pemCertLength > 0)
+   {
+      //Decode PEM certificate
+      error = pemReadCertificate(&pemCert, &pemCertLength,
+         &derCert, &derCertSize, &derCertLength);
+
+      //Any error to report?
+      if(error)
+      {
+         //End of file detected
+         error = NO_ERROR;
+         break;
+      }
+
+      //Total length of the certificate list
+      *length += derCertLength + 3;
+
+      //Prevent the buffer from overflowing
+      if((*length + sizeof(TlsCertificate)) > context->txRecordMaxLen)
+      {
+         //Report an error
+         error = ERROR_MESSAGE_TOO_LONG;
+         break;
+      }
+
+      //Each certificate is preceded by a 3-byte length field
+      STORE24BE(derCertLength, p);
+      //Copy the current certificate
+      memcpy(p + 3, derCert, derCertLength);
+
+      //Advance data pointer
+      p += derCertLength + 3;
+   }
+
+   //Free previously allocated memory
+   tlsFreeMem(derCert);
+
+   //A 3-byte length field shall precede the certificate list
+   STORE24BE(*length, message->certificateListLength);
+   //Consider the 3-byte length field
+   *length += 3;
+
+   //Fix the length field
+   STORE24BE(*length, message->length);
+   //Length of the complete handshake message
+   *length += sizeof(TlsHandshake);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Format ChangeCipherSpec message
+ * @param[in] context Pointer to the TLS context
+ * @param[out] message Buffer where to format the ChangeCipherSpec message
+ * @param[out] length Length of the resulting ChangeCipherSpec message
+ * @return Error code
+ **/
+
+error_t tlsFormatChangeCipherSpec(TlsContext *context,
+   TlsChangeCipherSpec *message, size_t *length)
+{
+   //The ChangeCipherSpec message consists of a single byte of value 1
+   message->type = 1;
+
+   //Length of the ChangeCipherSpec message
+   *length = sizeof(TlsChangeCipherSpec);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format Finished message
+ * @param[in] context Pointer to the TLS context
+ * @param[out] message Buffer where to format the Finished message
+ * @param[out] length Length of the resulting Finished message
+ * @return Error code
+ **/
+
+error_t tlsFormatFinished(TlsContext *context,
+   TlsFinished *message, size_t *length)
+{
+   //Handshake message type
+   message->msgType = TLS_TYPE_FINISHED;
+
+   //The length of the verify data depends on the cipher suite
+   STORE24BE(context->verifyDataLen, message->length);
+
+   //Copy the verify data
+   memcpy(message->verifyData, context->verifyData, context->verifyDataLen);
+
+   //Length of the complete handshake message
+   *length = context->verifyDataLen + sizeof(TlsHandshake);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format Alert message
+ * @param[in] context Pointer to the TLS context
+ * @param[in] level Severity of the message (warning or fatal)
+ * @param[in] description Description of the alert
+ * @param[out] message Buffer where to format the Alert message
+ * @param[out] length Length of the resulting Alert message
+ * @return Error code
+ **/
+
+error_t tlsFormatAlert(TlsContext *context, uint8_t level,
+   uint8_t description, TlsAlert *message, size_t *length)
+{
+   //Severity of the message
+   message->level = level;
+   //Description of the alert
+   message->description = description;
+
+   //Length of the Alert message
+   *length = sizeof(TlsAlert);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse Certificate message
+ * @param[in] context Pointer to the TLS context
+ * @param[in] message Incoming Certificate message to parse
+ * @param[in] length Message length
+ * @return Error code
+ **/
+
+error_t tlsParseCertificate(TlsContext *context, const TlsCertificate *message, size_t length)
+{
+   error_t error;
+   const uint8_t *p;
+   size_t n;
+   const char_t *pemCert;
+   size_t pemCertLength;
+   uint8_t *derCert;
+   size_t derCertSize;
+   size_t derCertLength;
+
+   //X.509 certificates
+   X509CertificateInfo *certInfo = NULL;
+   X509CertificateInfo *issuerCertInfo = NULL;
+
+   //Debug message
+   TRACE_INFO("Certificate message received (%" PRIuSIZE " bytes)...\r\n", length);
+   TRACE_DEBUG_ARRAY("  ", message, length);
+
+   //Check the length of the Certificate message
+   if(length < sizeof(TlsCertificate))
+      return ERROR_DECODING_FAILED;
+
+   //TLS operates as a client or a server?
+   if(context->entity == TLS_CONNECTION_END_CLIENT)
+   {
+      //Check current state
+      if(context->state != TLS_STATE_SERVER_CERTIFICATE)
+         return ERROR_UNEXPECTED_MESSAGE;
+   }
+   else
+   {
+      //Check current state
+      if(context->state != TLS_STATE_CLIENT_CERTIFICATE)
+         return ERROR_UNEXPECTED_MESSAGE;
+   }
+
+   //Update the hash value with the incoming handshake message
+   tlsUpdateHandshakeHash(context, message, length);
+
+   //Get the size occupied by the certificate list
+   n = LOAD24BE(message->certificateListLength);
+   //Remaining bytes to process
+   length -= sizeof(TlsCertificate);
+
+   //Ensure that the chain of certificates is valid
+   if(n > length)
+      return ERROR_DECODING_FAILED;
+
+   //Compute the length of the certificate list
+   length = n;
+
+   //The sender's certificate must come first in the list
+   p = message->certificateList;
+
+   //Start of exception handling block
+   do
+   {
+      //Assume an error...
+      error = ERROR_OUT_OF_MEMORY;
+
+      //Allocate a memory buffer to store X.509 certificate info
+      certInfo = tlsAllocMem(sizeof(X509CertificateInfo));
+      //Failed to allocate memory?
+      if(certInfo == NULL)
+         break;
+
+      //Allocate a memory buffer to store the parent certificate
+      issuerCertInfo = tlsAllocMem(sizeof(X509CertificateInfo));
+      //Failed to allocate memory?
+      if(issuerCertInfo == NULL)
+         break;
+
+      //TLS operates as a server?
+      if(context->entity == TLS_CONNECTION_END_SERVER)
+      {
+         //Empty certificate list?
+         if(!length)
+         {
+            //Check whether mutual authentication is required
+            if(context->clientAuthMode == TLS_CLIENT_AUTH_REQUIRED)
+            {
+               //If client authentication is required by the server for the handshake
+               //to continue, it may respond with a fatal handshake failure alert
+               error = ERROR_HANDSHAKE_FAILED;
+               break;
+            }
+            else
+            {
+               //Client authentication is optional
+               context->peerCertType = TLS_CERT_NONE;
+               //Exit immediately
+               error = NO_ERROR;
+               break;
+            }
+         }
+      }
+
+      //Each certificate is preceded by a 3-byte length field
+      if(length < 3)
+      {
+         //Report an error
+         error = ERROR_DECODING_FAILED;
+         break;
+      }
+
+      //Get the size occupied by the certificate
+      n = LOAD24BE(p);
+      //Jump to the beginning of the DER encoded certificate
+      p += 3;
+      length -= 3;
+
+      //Make sure that the certificate is valid
+      if(n > length)
+      {
+         //Report an error
+         error = ERROR_DECODING_FAILED;
+         break;
+      }
+
+      //Display ASN.1 structure
+      error = asn1DumpObject(p, n, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Parse X.509 certificate
+      error = x509ParseCertificate(p, n, certInfo);
+      //Failed to parse the X.509 certificate?
+      if(error)
+         break;
+
+#if (TLS_CLIENT_SUPPORT == ENABLED)
+      //TLS operates as a client?
+      if(context->entity == TLS_CONNECTION_END_CLIENT)
+      {
+         //Check if the hostname must be verified
+         if(context->serverName != NULL)
+         {
+            int_t i;
+            int_t j;
+
+            //Point to the last character of the common name
+            i = certInfo->subject.commonNameLen - 1;
+            //Point to the last character of the hostname
+            j = strlen(context->serverName) - 1;
+
+            //Check the common name in the server certificate against
+            //the actual hostname that is being requested
+            while(i >= 0 && j >= 0)
+            {
+               //Wildcard certificate found?
+               if(certInfo->subject.commonName[i] == '*' && i == 0)
+               {
+                  //The CN is acceptable
+                  j = 0;
+               }
+               //Perform case insensitive character comparison
+               else if(tolower((uint8_t) certInfo->subject.commonName[i]) != context->serverName[j])
+               {
+                  break;
+               }
+
+               //Compare previous characters
+               i--;
+               j--;
+            }
+
+            //If the host names do not match, reject the certificate
+            if(i >= 0 || j >= 0)
+            {
+               //Debug message
+               TRACE_WARNING("Server name mismatch!\r\n");
+               //Report an error
+               error = ERROR_BAD_CERTIFICATE;
+               break;
+            }
+         }
+      }
+#endif
+
+#if (TLS_RSA_SIGN_SUPPORT == ENABLED || TLS_RSA_SUPPORT == ENABLED || \
+   TLS_DHE_RSA_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED)
+      //The certificate contains a valid RSA public key?
+      if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen,
+         RSA_ENCRYPTION_OID, sizeof(RSA_ENCRYPTION_OID)))
+      {
+         uint_t k;
+
+         //Retrieve the RSA public key
+         error = x509ReadRsaPublicKey(certInfo, &context->peerRsaPublicKey);
+         //Any error to report
+         if(error)
+            break;
+
+         //Get the length of the modulus, in bits
+         k = mpiGetBitLength(&context->peerRsaPublicKey.n);
+
+         //Make sure the modulus is acceptable
+         if(k < TLS_MIN_RSA_MODULUS_SIZE || k > TLS_MAX_RSA_MODULUS_SIZE)
+         {
+            //Report an error
+            error = ERROR_BAD_CERTIFICATE;
+            break;
+         }
+
+         //Save the certificate type
+         context->peerCertType = TLS_CERT_RSA_SIGN;
+      }
+      else
+#endif
+#if (TLS_DSA_SIGN_SUPPORT == ENABLED || TLS_DHE_DSS_SUPPORT == ENABLED)
+      //The certificate contains a valid DSA public key?
+      if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen,
+         DSA_OID, sizeof(DSA_OID)))
+      {
+         uint_t k;
+
+         //Retrieve the DSA public key
+         error = x509ReadDsaPublicKey(certInfo, &context->peerDsaPublicKey);
+         //Any error to report
+         if(error)
+            break;
+
+         //Get the length of the prime modulus, in bits
+         k = mpiGetBitLength(&context->peerDsaPublicKey.p);
+
+         //Make sure the prime modulus is acceptable
+         if(k < TLS_MIN_DSA_MODULUS_SIZE || k > TLS_MAX_DSA_MODULUS_SIZE)
+         {
+            //Report an error
+            error = ERROR_BAD_CERTIFICATE;
+            break;
+         }
+
+         //Save the certificate type
+         context->peerCertType = TLS_CERT_DSS_SIGN;
+      }
+      else
+#endif
+#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED || TLS_ECDHE_ECDSA_SUPPORT == ENABLED)
+      //The certificate contains a valid EC public key?
+      if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen,
+         EC_PUBLIC_KEY_OID, sizeof(EC_PUBLIC_KEY_OID)))
+      {
+         const EcCurveInfo *curveInfo;
+
+         //Retrieve EC domain parameters
+         curveInfo = ecGetCurveInfo(certInfo->subjectPublicKeyInfo.ecParams.namedCurve,
+            certInfo->subjectPublicKeyInfo.ecParams.namedCurveLen);
+
+         //Make sure the specified elliptic curve is supported
+         if(curveInfo == NULL)
+         {
+            //Report an error
+            error = ERROR_BAD_CERTIFICATE;
+            //Exit immediately
+            break;
+         }
+
+         //Load EC domain parameters
+         error = ecLoadDomainParameters(&context->peerEcParams, curveInfo);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Retrieve the EC public key
+         error = ecImport(&context->peerEcParams, &context->peerEcPublicKey,
+            certInfo->subjectPublicKeyInfo.ecPublicKey.q, certInfo->subjectPublicKeyInfo.ecPublicKey.qLen);
+         //Any error to report
+         if(error)
+            break;
+
+         //Save the certificate type
+         context->peerCertType = TLS_CERT_ECDSA_SIGN;
+      }
+      else
+#endif
+      //The certificate does not contain any valid public key?
+      {
+         //Report an error
+         error = ERROR_BAD_CERTIFICATE;
+         break;
+      }
+
+      //Next certificate
+      p += n;
+      length -= n;
+
+      //PKIX path validation
+      while(length > 0)
+      {
+         //Each certificate is preceded by a 3-byte length field
+         if(length < 3)
+         {
+            //Report an error
+            error = ERROR_DECODING_FAILED;
+            break;
+         }
+
+         //Get the size occupied by the certificate
+         n = LOAD24BE(p);
+         //Jump to the beginning of the DER encoded certificate
+         p += 3;
+         length -= 3;
+
+         //Ensure that the certificate is valid
+         if(n > length)
+         {
+            //Report an error
+            error = ERROR_DECODING_FAILED;
+            break;
+         }
+
+         //Display ASN.1 structure
+         error = asn1DumpObject(p, n, 0);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Parse X.509 certificate
+         error = x509ParseCertificate(p, n, issuerCertInfo);
+         //Failed to parse the X.509 certificate?
+         if(error)
+            break;
+
+         //Valid trusted CA list?
+         if(context->trustedCaListLen > 0)
+         {
+            //Validate current certificate
+            error = x509ValidateCertificate(certInfo, issuerCertInfo);
+            //Certificate validation failed?
+            if(error)
+               break;
+         }
+
+         //Keep track of the issuer certificate
+         memcpy(certInfo, issuerCertInfo, sizeof(X509CertificateInfo));
+
+         //Next certificate
+         p += n;
+         length -= n;
+      }
+
+      //Propagate exception if necessary...
+      if(error)
+         break;
+
+      //Point to the first trusted CA certificate
+      pemCert = context->trustedCaList;
+      //Get the total length, in bytes, of the trusted CA list
+      pemCertLength = context->trustedCaListLen;
+
+      //DER encoded certificate
+      derCert = NULL;
+      derCertSize = 0;
+      derCertLength = 0;
+
+      //Loop through the list
+      while(pemCertLength > 0)
+      {
+         //Decode PEM certificate
+         error = pemReadCertificate(&pemCert, &pemCertLength,
+            &derCert, &derCertSize, &derCertLength);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Parse X.509 certificate
+         error = x509ParseCertificate(derCert, derCertLength, issuerCertInfo);
+         //Failed to parse the X.509 certificate?
+         if(error)
+            break;
+
+         //Validate the certificate with the current trusted CA
+         error = x509ValidateCertificate(certInfo, issuerCertInfo);
+         //Certificate validation succeeded?
+         if(!error)
+            break;
+      }
+
+      //The certificate could not be matched with a known, trusted CA?
+      if(error == ERROR_END_OF_FILE)
+         error = ERROR_UNKNOWN_CA;
+
+      //Free previously allocated memory
+      tlsFreeMem(derCert);
+
+      //End of exception handling block
+   } while(0);
+
+   //Free previously allocated memory
+   tlsFreeMem(certInfo);
+   tlsFreeMem(issuerCertInfo);
+
+   //Clean up side effects
+   if(error)
+   {
+#if (TLS_RSA_SIGN_SUPPORT == ENABLED || TLS_RSA_SUPPORT == ENABLED || \
+   TLS_DHE_RSA_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED)
+      //Release peer's RSA public key
+      rsaFreePublicKey(&context->peerRsaPublicKey);
+#endif
+#if (TLS_DSA_SIGN_SUPPORT == ENABLED || TLS_DHE_DSS_SUPPORT == ENABLED)
+      //Release peer's DSA public key
+      dsaFreePublicKey(&context->peerDsaPublicKey);
+#endif
+#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED || TLS_ECDHE_ECDSA_SUPPORT == ENABLED)
+      //Release peer's EC domain parameters
+      ecFreeDomainParameters(&context->peerEcParams);
+      //Release peer's EC public key
+      ecFree(&context->peerEcPublicKey);
+#endif
+   }
+
+   //TLS operates as a client or a server?
+   if(context->entity == TLS_CONNECTION_END_CLIENT)
+   {
+      //Update FSM state
+      if(context->keyExchMethod == TLS_KEY_EXCH_RSA)
+         context->state = TLS_STATE_CERTIFICATE_REQUEST;
+      else
+         context->state = TLS_STATE_SERVER_KEY_EXCHANGE;
+   }
+   else
+   {
+      //Prepare to receive ClientKeyExchange message...
+      context->state = TLS_STATE_CLIENT_KEY_EXCHANGE;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse ChangeCipherSpec message
+ * @param[in] context Pointer to the TLS context
+ * @param[in] message Incoming ChangeCipherSpec message to parse
+ * @param[in] length Message length
+ * @return Error code
+ **/
+
+error_t tlsParseChangeCipherSpec(TlsContext *context, const TlsChangeCipherSpec *message, size_t length)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("ChangeCipherSpec message received (%" PRIuSIZE " bytes)...\r\n", length);
+   TRACE_DEBUG_ARRAY("  ", message, length);
+
+   //Check the length of the ChangeCipherSpec message
+   if(length < sizeof(TlsChangeCipherSpec))
+      return ERROR_DECODING_FAILED;
+
+   //TLS operates as a client or a server?
+   if(context->entity == TLS_CONNECTION_END_CLIENT)
+   {
+      //Check current state
+      if(context->state != TLS_STATE_SERVER_CHANGE_CIPHER_SPEC)
+         return ERROR_UNEXPECTED_MESSAGE;
+   }
+   else
+   {
+      //Check current state
+      if(context->state != TLS_STATE_CLIENT_CHANGE_CIPHER_SPEC)
+         return ERROR_UNEXPECTED_MESSAGE;
+   }
+
+   //Initialize decryption engine
+   error = tlsInitDecryptionEngine(context);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Inform the record layer that subsequent records will be protected
+   //under the newly negotiated encryption algorithm
+   context->changeCipherSpecReceived = TRUE;
+
+   //Prepare to receive a Finished message from the peer...
+   if(context->entity == TLS_CONNECTION_END_CLIENT)
+      context->state = TLS_STATE_SERVER_FINISHED;
+   else
+      context->state = TLS_STATE_CLIENT_FINISHED;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse Finished message
+ * @param[in] context Pointer to the TLS context
+ * @param[in] message Incoming Finished message to parse
+ * @param[in] length Message length
+ * @return Error code
+ **/
+
+error_t tlsParseFinished(TlsContext *context, const TlsFinished *message, size_t length)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Finished message received (%" PRIuSIZE " bytes)...\r\n", length);
+   TRACE_DEBUG_ARRAY("  ", message, length);
+
+   //Check the length of the Finished message
+   if(length < sizeof(TlsFinished))
+      return ERROR_DECODING_FAILED;
+
+   //TLS operates as a client or a server?
+   if(context->entity == TLS_CONNECTION_END_CLIENT)
+   {
+      //Check current state
+      if(context->state != TLS_STATE_SERVER_FINISHED)
+         return ERROR_UNEXPECTED_MESSAGE;
+
+      //The verify data is generated from all messages in this
+      //handshake up to but not including the Finished message
+      error = tlsComputeVerifyData(context, TLS_CONNECTION_END_SERVER);
+   }
+   else
+   {
+      //Check current state
+      if(context->state != TLS_STATE_CLIENT_FINISHED)
+         return ERROR_UNEXPECTED_MESSAGE;
+
+      //The verify data is generated from all messages in this
+      //handshake up to but not including the Finished message
+      error = tlsComputeVerifyData(context, TLS_CONNECTION_END_CLIENT);
+   }
+
+   //Unable to generate the verify data?
+   if(error)
+      return error;
+
+   //Check message length
+   if(LOAD24BE(message->length) != context->verifyDataLen)
+      return ERROR_DECODING_FAILED;
+
+   //Check the resulting verify data
+   if(memcmp(message->verifyData, context->verifyData, context->verifyDataLen))
+      return ERROR_INVALID_SIGNATURE;
+
+   //Update the hash value with the incoming handshake message
+   tlsUpdateHandshakeHash(context, message, length);
+
+   //TLS operates as a client or a server?
+   if(context->entity == TLS_CONNECTION_END_CLIENT)
+   {
+      //Use abbreviated or full handshake?
+      if(context->resume)
+         context->state = TLS_STATE_CLIENT_CHANGE_CIPHER_SPEC;
+      else
+         context->state = TLS_STATE_APPLICATION_DATA;
+   }
+   else
+   {
+      //Use abbreviated or full handshake?
+      if(context->resume)
+         context->state = TLS_STATE_APPLICATION_DATA;
+      else
+         context->state = TLS_STATE_SERVER_CHANGE_CIPHER_SPEC;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse Alert message
+ * @param[in] context Pointer to the TLS context
+ * @param[in] message Incoming Alert message to parse
+ * @param[in] length Message length
+ * @return Error code
+ **/
+
+error_t tlsParseAlert(TlsContext *context, const TlsAlert *message, size_t length)
+{
+   //Debug message
+   TRACE_INFO("Alert message received (%" PRIuSIZE " bytes)...\r\n", length);
+   TRACE_INFO_ARRAY("  ", message, length);
+
+   //Check message length
+   if(length != sizeof(TlsAlert))
+      return ERROR_INVALID_LENGTH;
+
+   //Debug message
+   TRACE_DEBUG("  Level = %" PRIu8 "\r\n", message->level);
+   TRACE_DEBUG("  Description = %" PRIu8 "\r\n", message->description);
+
+   //Alert messages convey the severity of the message
+   if(message->level == TLS_ALERT_LEVEL_WARNING)
+   {
+      //Closure alert received?
+      if(message->description == TLS_ALERT_CLOSE_NOTIFY)
+      {
+         //A closure alert has been received
+         context->closeNotifyReceived = TRUE;
+
+         //Close down the connection immediately
+         if(context->state == TLS_STATE_APPLICATION_DATA)
+            context->state = TLS_STATE_CLOSING;
+      }
+   }
+   else if(message->level == TLS_ALERT_LEVEL_FATAL)
+   {
+      //A fatal alert message has been received
+      context->fatalAlertReceived = TRUE;
+
+      //Any connection terminated with a fatal alert must not be resumed
+      if(context->entity == TLS_CONNECTION_END_SERVER)
+         tlsRemoveFromCache(context);
+
+      //Servers and clients must forget any session identifiers
+      memset(context->sessionId, 0, 32);
+      context->sessionIdLen = 0;
+
+      //Alert messages with a level of fatal result in the immediate
+      //termination of the connection
+      context->state = TLS_STATE_CLOSED;
+   }
+   else
+   {
+      //Report an error
+      return ERROR_ILLEGAL_PARAMETER;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls_common.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,68 @@
+/**
+ * @file tls_common.h
+ * @brief Handshake message processing (TLS client and server)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TLS_COMMON_H
+#define _TLS_COMMON_H
+
+//Dependencies
+#include "tls.h"
+
+//TLS related functions
+error_t tlsHandshake(TlsContext *context);
+
+error_t tlsSendCertificate(TlsContext *context);
+error_t tlsSendChangeCipherSpec(TlsContext *context);
+error_t tlsSendFinished(TlsContext *context);
+error_t tlsSendAlert(TlsContext *context, uint8_t level, uint8_t description);
+
+error_t tlsFormatCertificate(TlsContext *context,
+   TlsCertificate *message, size_t *length);
+
+error_t tlsFormatChangeCipherSpec(TlsContext *context,
+   TlsChangeCipherSpec *message, size_t *length);
+
+error_t tlsFormatFinished(TlsContext *context,
+   TlsFinished *message, size_t *length);
+
+error_t tlsFormatAlert(TlsContext *context, uint8_t level,
+   uint8_t description, TlsAlert *message, size_t *length);
+
+error_t tlsParseCertificate(TlsContext *context,
+   const TlsCertificate *message, size_t length);
+
+error_t tlsParseChangeCipherSpec(TlsContext *context,
+   const TlsChangeCipherSpec *message, size_t length);
+
+error_t tlsParseFinished(TlsContext *context,
+   const TlsFinished *message, size_t length);
+
+error_t tlsParseAlert(TlsContext *context,
+   const TlsAlert *message, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls_misc.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,2670 @@
+/**
+ * @file tls_misc.c
+ * @brief Helper functions (TLS client and server)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TLS_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "tls.h"
+#include "tls_cipher_suites.h"
+#include "tls_common.h"
+#include "tls_record.h"
+#include "tls_misc.h"
+#include "ssl_common.h"
+#include "asn1.h"
+#include "oid.h"
+#include "x509.h"
+#include "pem.h"
+#include "debug.h"
+
+//Check SSL library configuration
+#if (TLS_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Translate an error code to an alert message
+ * @param[in] context Pointer to the TLS context
+ * @param[in] errorCode Internal error code
+ * @return Error code
+ **/
+
+void tlsProcessError(TlsContext *context, error_t errorCode)
+{
+   //Check current state
+   if(context->state != TLS_STATE_CLOSED)
+   {
+      //Check status code
+      switch(errorCode)
+      {
+      //The timeout interval has elapsed
+      case ERROR_TIMEOUT:
+         break;
+      //The read/write operation would have blocked
+      case ERROR_WOULD_BLOCK:
+         break;
+      //The read/write operation has failed
+      case ERROR_WRITE_FAILED:
+      case ERROR_READ_FAILED:
+         break;
+      //An inappropriate message was received
+      case ERROR_UNEXPECTED_MESSAGE:
+         tlsSendAlert(context, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE);
+         break;
+      //A record is received with an incorrect MAC
+      case ERROR_BAD_RECORD_MAC:
+         tlsSendAlert(context, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_BAD_RECORD_MAC);
+         break;
+      //Invalid record length
+      case ERROR_RECORD_OVERFLOW:
+         tlsSendAlert(context, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_RECORD_OVERFLOW);
+         break;
+      //Unable to negotiate an acceptable set of security parameters
+      case ERROR_HANDSHAKE_FAILED:
+         tlsSendAlert(context, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_HANDSHAKE_FAILURE);
+         break;
+      //A certificate was corrupt
+      case ERROR_BAD_CERTIFICATE:
+         tlsSendAlert(context, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_BAD_CERTIFICATE);
+         break;
+      //A certificate was of an unsupported type
+      case ERROR_UNSUPPORTED_CERTIFICATE:
+         tlsSendAlert(context, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNSUPPORTED_CERTIFICATE);
+         break;
+      //A certificate has expired or is not currently valid
+      case ERROR_CERTIFICATE_EXPIRED:
+         tlsSendAlert(context, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_CERTIFICATE_EXPIRED);
+         break;
+      //A field in the handshake was out of range or inconsistent with other fields
+      case ERROR_ILLEGAL_PARAMETER:
+         tlsSendAlert(context, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_ILLEGAL_PARAMETER);
+         break;
+      //The certificate could not be matched with a known, trusted CA
+      case ERROR_UNKNOWN_CA:
+         tlsSendAlert(context, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNKNOWN_CA);
+         break;
+      //A message could not be decoded because some field was incorrect
+      case ERROR_DECODING_FAILED:
+         tlsSendAlert(context, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+         break;
+      //A handshake cryptographic operation failed
+      case ERROR_INVALID_SIGNATURE:
+         tlsSendAlert(context, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECRYPT_ERROR);
+         break;
+      //The protocol version the client has attempted to negotiate is not supported
+      case ERROR_VERSION_NOT_SUPPORTED:
+         tlsSendAlert(context, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_PROTOCOL_VERSION);
+         break;
+      //Internal error
+      default:
+         tlsSendAlert(context, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR);
+         break;
+      }
+   }
+}
+
+
+/**
+ * @brief Generate client or server random value
+ * @param[in] context Pointer to the TLS context
+ * @param[out] random Pointer to the random value
+ * @return Error code
+ **/
+
+error_t tlsGenerateRandomValue(TlsContext *context, TlsRandom *random)
+{
+   error_t error;
+   uint32_t time;
+
+   //Verify that the pseudorandom number generator is properly configured
+   if(context->prngAlgo != NULL && context->prngContext != NULL)
+   {
+      //Get current time
+      time = (uint32_t) getCurrentUnixTime();
+
+      //Clocks are not required to be set correctly by the basic TLS protocol
+      if(time != 0)
+      {
+         //Generate the random value. The first four bytes code the current
+         //time and date in standard Unix format
+         random->gmtUnixTime = htonl(time);
+
+         //The last 28 bytes contain securely-generated random bytes
+         error = context->prngAlgo->read(context->prngContext, random->randomBytes, 28);
+      }
+      else
+      {
+         //Generate a 32-byte random value using a cryptographically-safe
+         //pseudorandom number generator
+         error = context->prngAlgo->read(context->prngContext, (uint8_t *) random, 32);
+      }
+   }
+   else
+   {
+      //Report an error
+      error = ERROR_NOT_CONFIGURED;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Set TLS version to use
+ * @param[in] context Pointer to the TLS context
+ * @param[in] version TLS version
+ * @return Error code
+ **/
+
+error_t tlsSetVersion(TlsContext *context, uint16_t version)
+{
+   //Check TLS version
+   if(version < TLS_MIN_VERSION || version > TLS_MAX_VERSION)
+   {
+      //Debug message
+      TRACE_WARNING("TLS version not supported!\r\n");
+      //Report an error
+      return ERROR_VERSION_NOT_SUPPORTED;
+   }
+
+   //Save the TLS protocol version to use
+   context->version = version;
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set cipher suite
+ * @param[in] context Pointer to the TLS context
+ * @param[in] identifier Cipher suite identifier
+ * @return Error code
+ **/
+
+error_t tlsSetCipherSuite(TlsContext *context, uint16_t identifier)
+{
+   error_t error;
+   uint_t i;
+   uint_t n;
+   bool_t acceptable;
+   const TlsCipherSuiteInfo *cipherSuite;
+
+   //Initialize pointer
+   cipherSuite = NULL;
+
+   //Restrict the use of certain cipher suites?
+   if(context->numCipherSuites > 0)
+   {
+      //This flag will be set if the specified cipher suite is acceptable
+      acceptable = FALSE;
+
+      //Loop through allowed cipher suites
+      for(i = 0; i < context->numCipherSuites; i++)
+      {
+         //Compare cipher suite identifiers
+         if(context->cipherSuites[i] == identifier)
+         {
+            acceptable = TRUE;
+            break;
+         }
+      }
+   }
+   else
+   {
+      //The use of the cipher suite is not restricted
+      acceptable = TRUE;
+   }
+
+   //No restrictions exist concerning the use of the specified cipher suite?
+   if(acceptable)
+   {
+      //This flag will be set if the specified cipher suite is acceptable
+      acceptable = FALSE;
+
+      //Determine the number of supported cipher suites
+      n = tlsGetNumSupportedCipherSuites();
+
+      //Loop through the list of supported cipher suites
+      for(i = 0; i < n; i++)
+      {
+         //Point to the current item
+         cipherSuite = &tlsSupportedCipherSuites[i];
+
+         //Compare cipher suite identifiers
+         if(cipherSuite->identifier == identifier)
+         {
+            acceptable = TRUE;
+            break;
+         }
+      }
+   }
+
+   //TLS 1.2 cipher suites must not be negotiated in older versions of TLS
+   if(acceptable && context->version < TLS_VERSION_1_2)
+   {
+      if(cipherSuite->prfHashAlgo != NULL)
+         acceptable = FALSE;
+   }
+
+   //Ensure that the selected cipher suite matches all the criteria
+   if(acceptable)
+   {
+      //Save cipher suite identifier
+      context->cipherSuite = identifier;
+      //Set key exchange method
+      context->keyExchMethod = cipherSuite->keyExchMethod;
+
+      //Set encryption algorithm and hash function
+      context->cipherAlgo = cipherSuite->cipherAlgo;
+      context->cipherMode = cipherSuite->cipherMode;
+      context->hashAlgo = cipherSuite->hashAlgo;
+      context->prfHashAlgo = cipherSuite->prfHashAlgo;
+
+      //Set appropriate length for MAC keys, encryption keys and IVs
+      context->macKeyLen = cipherSuite->macKeyLen;
+      context->encKeyLen = cipherSuite->encKeyLen;
+      context->fixedIvLen = cipherSuite->fixedIvLen;
+      context->recordIvLen = cipherSuite->recordIvLen;
+
+      //Size of the authentication tag
+      context->authTagLen = cipherSuite->authTagLen;
+      //Size of the verify data
+      context->verifyDataLen = cipherSuite->verifyDataLen;
+
+      //PRF with the SHA-256 is used for all cipher suites published
+      //prior than TLS 1.2 when TLS 1.2 is negotiated
+      if(context->prfHashAlgo == NULL)
+         context->prfHashAlgo = SHA256_HASH_ALGO;
+
+      //The length of the verify data depends on the TLS version currently used
+      if(context->version == SSL_VERSION_3_0)
+         context->verifyDataLen = 36;
+      else if(context->version <= TLS_VERSION_1_1)
+         context->verifyDataLen = 12;
+
+      //Successful processing
+      error = NO_ERROR;
+   }
+   else
+   {
+      //Debug message
+      TRACE_ERROR("Cipher suite not supported!\r\n");
+      //The specified cipher suite is not supported
+      error = ERROR_HANDSHAKE_FAILED;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Set compression method
+ * @param[in] context Pointer to the TLS context
+ * @param[in] identifier Compression method identifier
+ * @return Error code
+ **/
+
+error_t tlsSetCompressionMethod(TlsContext *context, uint8_t identifier)
+{
+   //Check if the requested compression algorithm is supported
+   if(identifier != TLS_COMPRESSION_METHOD_NULL)
+      return ERROR_HANDSHAKE_FAILED;
+
+   //Save compression method identifier
+   context->compressionMethod = identifier;
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Select the hash algorithm to be used when generating signatures
+ * @param[in] context Pointer to the TLS context
+ * @param[in] signAlgo Desired signature algorithm (RSA, DSA or ECDSA)
+ * @param[in] supportedSignAlgos List of supported signature/hash algorithm pairs
+ * @return Error code
+ **/
+
+error_t tlsSelectSignHashAlgo(TlsContext *context,
+   TlsSignatureAlgo signAlgo, const TlsSignHashAlgos *supportedSignAlgos)
+{
+   uint_t i;
+   uint_t n;
+
+   //Check whether the peer has provided a list of supported
+   //hash/signature algorithm pairs?
+   if(supportedSignAlgos != NULL)
+   {
+      //Process the list and select the relevant hash algorithm...
+      context->signHashAlgo = TLS_HASH_ALGO_NONE;
+      //Get the number of hash/signature algorithm pairs present in the list
+      n = ntohs(supportedSignAlgos->length) / sizeof(TlsSignHashAlgo);
+
+      //The hash algorithm to be used when generating signatures must be
+      //one of those present in the list
+      for(i = 0; i < n; i++)
+      {
+         //Acceptable signature algorithm found?
+         if(supportedSignAlgos->value[i].signature == signAlgo)
+         {
+            //TLS operates as a client?
+            if(context->entity == TLS_CONNECTION_END_CLIENT)
+            {
+               //Check whether the associated hash algorithm is supported
+               if(supportedSignAlgos->value[i].hash == TLS_HASH_ALGO_SHA1)
+                  context->signHashAlgo = (TlsHashAlgo) supportedSignAlgos->value[i].hash;
+               else if(tlsGetHashAlgo(supportedSignAlgos->value[i].hash) == context->prfHashAlgo)
+                  context->signHashAlgo = (TlsHashAlgo) supportedSignAlgos->value[i].hash;
+            }
+            //TLS operates as a server?
+            else
+            {
+               //Check whether the associated hash algorithm is supported
+               switch(supportedSignAlgos->value[i].hash)
+               {
+               //MD5 hash identifier?
+               case TLS_HASH_ALGO_MD5:
+               //SHA-1 hash identifier?
+               case TLS_HASH_ALGO_SHA1:
+               //SHA-256 hash identifier?
+               case TLS_HASH_ALGO_SHA256:
+#if (TLS_SHA224_SUPPORT == ENABLED)
+               //SHA-224 hash identifier?
+               case TLS_HASH_ALGO_SHA224:
+#endif
+#if (TLS_SHA384_SUPPORT == ENABLED)
+               //SHA-384 hash identifier?
+               case TLS_HASH_ALGO_SHA384:
+#endif
+#if (TLS_SHA512_SUPPORT == ENABLED)
+               //SHA-512 hash identifier?
+               case TLS_HASH_ALGO_SHA512:
+#endif
+                  //Select the hash algorithm to be used for signing
+                  context->signHashAlgo = (TlsHashAlgo) supportedSignAlgos->value[i].hash;
+                  break;
+               default:
+                  break;
+               }
+            }
+
+            //Acceptable hash algorithm found?
+            if(context->signHashAlgo != TLS_HASH_ALGO_NONE)
+               break;
+         }
+      }
+   }
+   else
+   {
+      //Use default hash algorithm when generating RSA, DSA or ECDSA signatures
+      context->signHashAlgo = TLS_HASH_ALGO_SHA1;
+   }
+
+   //If no acceptable choices are presented, return an error
+   if(context->signHashAlgo == TLS_HASH_ALGO_NONE)
+      return ERROR_FAILURE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Select the named curve to be used when performing ECDH key exchange
+ * @param[in] context Pointer to the TLS context
+ * @param[in] curveList Set of elliptic curves supported by the peer
+ * @return Error code
+ **/
+
+error_t tlsSelectNamedCurve(TlsContext *context,
+   const TlsEllipticCurveList *curveList)
+{
+   uint_t i;
+   uint_t n;
+
+   //Check whether a list of elliptic curves has been provided
+   if(curveList != NULL)
+   {
+      //Process the list and select the relevant elliptic curve...
+      context->namedCurve = TLS_EC_CURVE_NONE;
+      //Get the number of named curves present in the list
+      n = ntohs(curveList->length) / sizeof(uint16_t);
+
+      //The named curve to be used when performing ECDH key exchange must be
+      //one of those present in the list
+      for(i = 0; i < n; i++)
+      {
+         //Acceptable elliptic curve found?
+         if(tlsGetCurveInfo(ntohs(curveList->value[i])) != NULL)
+         {
+            //Save the named curve
+            context->namedCurve = ntohs(curveList->value[i]);
+            //We are done
+            break;
+         }
+      }
+   }
+   else
+   {
+      //No list provided
+      context->namedCurve = TLS_EC_CURVE_NONE;
+   }
+
+   //If no acceptable choices are presented, return an error
+   if(context->namedCurve == TLS_EC_CURVE_NONE)
+      return ERROR_FAILURE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize handshake message hashing
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsInitHandshakeHash(TlsContext *context)
+{
+   //Allocate SHA-1 context
+   context->handshakeSha1Context = tlsAllocMem(sizeof(Sha1Context));
+   //Failed to allocate memory?
+   if(context->handshakeSha1Context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize SHA-1 context
+   sha1Init(context->handshakeSha1Context);
+
+   //SSL 3.0, TLS 1.0 or 1.1 currently selected?
+   if(context->version <= TLS_VERSION_1_1)
+   {
+      //Allocate MD5 context
+      context->handshakeMd5Context = tlsAllocMem(sizeof(Md5Context));
+
+      //Failed to allocate memory?
+      if(context->handshakeMd5Context == NULL)
+      {
+         //Clean up side effects
+         tlsFreeMem(context->handshakeSha1Context);
+         //Report an error
+         return ERROR_OUT_OF_MEMORY;
+      }
+
+      //Initialize MD5 context
+      md5Init(context->handshakeMd5Context);
+   }
+   //TLS 1.2 currently selected?
+   else
+   {
+      //Allocate a memory buffer to hold the hash algorithm context
+      context->handshakeHashContext = tlsAllocMem(context->prfHashAlgo->contextSize);
+
+      //Failed to allocate memory?
+      if(context->handshakeHashContext == NULL)
+      {
+         //Clean up side effects
+         tlsFreeMem(context->handshakeSha1Context);
+         //Report an error
+         return ERROR_OUT_OF_MEMORY;
+      }
+
+      //Initialize the hash algorithm context
+      context->prfHashAlgo->init(context->handshakeHashContext);
+   }
+
+#if (TLS_CLIENT_SUPPORT == ENABLED)
+   //TLS operates as a client?
+   if(context->entity == TLS_CONNECTION_END_CLIENT)
+   {
+      size_t length;
+      TlsHandshake *message;
+
+      //Point to the ClientHello message
+      message = (TlsHandshake *) (context->txBuffer + sizeof(TlsRecord));
+      //Retrieve the length of the message
+      length = LOAD24BE(message->length);
+
+      //Sanity check
+      if(length <= context->txRecordMaxLen)
+      {
+         //Update the hash value with ClientHello message contents
+         tlsUpdateHandshakeHash(context, message, length + sizeof(TlsHandshake));
+      }
+   }
+#endif
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Update hash value with a handshake message
+ * @param[in] context Pointer to the TLS context
+ * @param[in] data Pointer to the handshake message being hashed
+ * @param[in] length Length of the message
+ **/
+
+void tlsUpdateHandshakeHash(TlsContext *context, const void *data, size_t length)
+{
+   //Update SHA-1 hash value with message contents
+   if(context->handshakeSha1Context)
+      sha1Update(context->handshakeSha1Context, data, length);
+
+   //SSL 3.0, TLS 1.0 or 1.1 currently selected?
+   if(context->version <= TLS_VERSION_1_1)
+   {
+      //Update MD5 hash value with message contents
+      if(context->handshakeMd5Context)
+         md5Update(context->handshakeMd5Context, data, length);
+   }
+   //TLS 1.2 currently selected?
+   else
+   {
+      //Update hash value with message contents
+      if(context->handshakeHashContext)
+         context->prfHashAlgo->update(context->handshakeHashContext, data, length);
+   }
+}
+
+
+/**
+ * @brief Finalize hash calculation from previous handshake messages
+ * @param[in] context Pointer to the TLS context
+ * @param[in] hash Hash function used to digest the handshake messages
+ * @param[in] hashContext Pointer to the hash context
+ * @param[in] label NULL-terminated string
+ * @param[out] output Buffer where to store the resulting hash value
+ * @return Error code
+ **/
+
+error_t tlsFinalizeHandshakeHash(TlsContext *context, const HashAlgo *hash,
+   const void *hashContext, const char_t *label, uint8_t *output)
+{
+   error_t error;
+   HashContext *tempHashContext;
+
+   //Check parameters
+   if(!context || !hash || !hashContext || !label || !output)
+      return ERROR_INVALID_PARAMETER;
+
+   //Allocate a temporary hash context
+   tempHashContext = tlsAllocMem(hash->contextSize);
+   //Memory allocation failed?
+   if(tempHashContext == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Original hash context must be preserved
+   memcpy(tempHashContext, hashContext, hash->contextSize);
+
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_MIN_VERSION <= SSL_VERSION_3_0)
+   //SSL 3.0 currently selected?
+   if(context->version == SSL_VERSION_3_0)
+   {
+      size_t labelLength;
+      size_t padLength;
+
+      //Compute the length of the label
+      labelLength = strlen(label);
+
+      //The pad character is repeated 48 times for MD5 or 40 times for SHA-1
+      padLength = (hash == MD5_HASH_ALGO) ? 48 : 40;
+
+      //hash(handshakeMessages + label + masterSecret + pad1)
+      hash->update(tempHashContext, label, labelLength);
+      hash->update(tempHashContext, context->masterSecret, 48);
+      hash->update(tempHashContext, sslPad1, padLength);
+      hash->final(tempHashContext, output);
+
+      //hash(masterSecret + pad2 + hash(handshakeMessages + label + masterSecret + pad1))
+      hash->init(tempHashContext);
+      hash->update(tempHashContext, context->masterSecret, 48);
+      hash->update(tempHashContext, sslPad2, padLength);
+      hash->update(tempHashContext, output, hash->digestSize);
+      hash->final(tempHashContext, output);
+
+      //Successful processing
+      error = NO_ERROR;
+   }
+   else
+#endif
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_0 && TLS_MIN_VERSION <= TLS_VERSION_1_2)
+   //TLS 1.0, TLS 1.1 or TLS 1.2 currently selected?
+   if(context->version >= TLS_VERSION_1_0 && context->version <= TLS_VERSION_1_2)
+   {
+      //Compute hash(handshakeMessages)
+      hash->final(tempHashContext, output);
+      //Successful processing
+      error = NO_ERROR;
+   }
+   else
+#endif
+   //The negotiated TLS version is not valid...
+   {
+      //Report an error
+      error = ERROR_INVALID_VERSION;
+   }
+
+   //Release previously allocated resources
+   tlsFreeMem(tempHashContext);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Compute verify data from previous handshake messages
+ * @param[in] context Pointer to the TLS context
+ * @param[in] entity Specifies whether the computation is performed at client or server side
+ * @return Error code
+ **/
+
+error_t tlsComputeVerifyData(TlsContext *context, TlsConnectionEnd entity)
+{
+   error_t error;
+   const char_t *label;
+
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_MIN_VERSION <= SSL_VERSION_3_0)
+   //SSL 3.0 currently selected?
+   if(context->version == SSL_VERSION_3_0)
+   {
+      //Computation is performed at client or server side?
+      label = (entity == TLS_CONNECTION_END_CLIENT) ? "CLNT" : "SRVR";
+
+      //Compute MD5(masterSecret + pad2 + MD5(handshakeMessages + label + masterSecret + pad1))
+      error = tlsFinalizeHandshakeHash(context, MD5_HASH_ALGO,
+         context->handshakeMd5Context, label, context->verifyData);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Compute SHA(masterSecret + pad2 + SHA(handshakeMessages + label + masterSecret + pad1))
+      error = tlsFinalizeHandshakeHash(context, SHA1_HASH_ALGO,
+         context->handshakeSha1Context, label, context->verifyData + MD5_DIGEST_SIZE);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+   else
+#endif
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_0 && TLS_MIN_VERSION <= TLS_VERSION_1_1)
+   //TLS 1.0 or 1.1 currently selected?
+   if(context->version == TLS_VERSION_1_0 || context->version == TLS_VERSION_1_1)
+   {
+      //A temporary buffer is needed to concatenate MD5
+      //and SHA-1 hash values before computing PRF
+      uint8_t buffer[MD5_DIGEST_SIZE + SHA1_DIGEST_SIZE];
+
+      //Finalize MD5 hash computation
+      error = tlsFinalizeHandshakeHash(context, MD5_HASH_ALGO,
+         context->handshakeMd5Context, "", buffer);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Finalize SHA-1 hash computation
+      error = tlsFinalizeHandshakeHash(context, SHA1_HASH_ALGO,
+         context->handshakeSha1Context, "", buffer + MD5_DIGEST_SIZE);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Computation is performed at client or server side?
+      label = (entity == TLS_CONNECTION_END_CLIENT) ? "client finished" : "server finished";
+
+      //Verify data is always 12-byte long for TLS 1.0 and 1.1
+      error = tlsPrf(context->masterSecret, 48, label, buffer,
+         MD5_DIGEST_SIZE + SHA1_DIGEST_SIZE, context->verifyData, 12);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+   else
+#endif
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_MIN_VERSION <= TLS_VERSION_1_2)
+   //TLS 1.2 currently selected?
+   if(context->version == TLS_VERSION_1_2)
+   {
+      //Allocate a memory buffer to hold the hash algorithm context
+      HashContext *hashContext = tlsAllocMem(context->prfHashAlgo->contextSize);
+      //Failed to allocate memory?
+      if(hashContext == NULL)
+         return ERROR_OUT_OF_MEMORY;
+
+      //The original hash context must be preserved
+      memcpy(hashContext, context->handshakeHashContext, context->prfHashAlgo->contextSize);
+      //Finalize hash computation
+      context->prfHashAlgo->final(hashContext, NULL);
+
+      //Computation is performed at client or server side?
+      label = (entity == TLS_CONNECTION_END_CLIENT) ? "client finished" : "server finished";
+
+      //Generate the verify data
+      error = tlsPrf2(context->prfHashAlgo, context->masterSecret, 48, label, hashContext->digest,
+         context->prfHashAlgo->digestSize, context->verifyData, context->verifyDataLen);
+
+      //Release previously allocated memory
+      tlsFreeMem(hashContext);
+
+      //Any error to report?
+      if(error)
+         return error;
+   }
+   else
+#endif
+   //The negotiated TLS version is not valid...
+   {
+      //Report an error
+      return ERROR_INVALID_VERSION;
+   }
+
+   //Debug message
+   TRACE_DEBUG("Verify data:\r\n");
+   TRACE_DEBUG_ARRAY("  ", context->verifyData, context->verifyDataLen);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize encryption engine
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsInitEncryptionEngine(TlsContext *context)
+{
+   error_t error;
+
+   //Check cipher mode of operation
+   if(context->cipherMode == CIPHER_MODE_STREAM ||
+      context->cipherMode == CIPHER_MODE_CBC ||
+      context->cipherMode == CIPHER_MODE_CCM ||
+      context->cipherMode == CIPHER_MODE_GCM)
+   {
+      //Sanity check
+      if(context->cipherAlgo != NULL)
+      {
+         //Allocate a memory buffer to hold the encryption context
+         context->writeCipherContext = tlsAllocMem(context->cipherAlgo->contextSize);
+
+         //Successful memory allocation?
+         if(context->writeCipherContext != NULL)
+         {
+            //Configure the encryption engine with the write key
+            error = context->cipherAlgo->init(context->writeCipherContext,
+               context->writeEncKey, context->encKeyLen);
+         }
+         else
+         {
+            //Failed to allocate memory
+            error = ERROR_OUT_OF_MEMORY;
+         }
+      }
+      else
+      {
+         //Report an error
+         error = ERROR_FAILURE;
+      }
+
+#if (TLS_GCM_CIPHER_SUPPORT == ENABLED)
+      //GCM AEAD cipher?
+      if(context->cipherMode == CIPHER_MODE_GCM)
+      {
+         //Check status code
+         if(!error)
+         {
+            //Allocate a memory buffer to hold the GCM context
+            context->writeGcmContext = tlsAllocMem(sizeof(GcmContext));
+
+            //Successful memory allocation?
+            if(context->writeGcmContext != NULL)
+            {
+               //Initialize GCM context
+               error = gcmInit(context->writeGcmContext,
+                  context->cipherAlgo, context->writeCipherContext);
+            }
+            else
+            {
+               //Failed to allocate memory
+               error = ERROR_OUT_OF_MEMORY;
+            }
+         }
+      }
+#endif
+   }
+   else if(context->cipherMode == CIPHER_MODE_CHACHA20_POLY1305)
+   {
+      //We are done
+      error = NO_ERROR;
+   }
+   else
+   {
+      //Unsupported mode of operation
+      error = ERROR_FAILURE;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Initialize decryption engine
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsInitDecryptionEngine(TlsContext *context)
+{
+   error_t error;
+
+   //Check cipher mode of operation
+   if(context->cipherMode == CIPHER_MODE_STREAM ||
+      context->cipherMode == CIPHER_MODE_CBC ||
+      context->cipherMode == CIPHER_MODE_CCM ||
+      context->cipherMode == CIPHER_MODE_GCM)
+   {
+      //Sanity check
+      if(context->cipherAlgo != NULL)
+      {
+         //Allocate a memory buffer to hold the decryption context
+         context->readCipherContext = tlsAllocMem(context->cipherAlgo->contextSize);
+
+         //Successful memory allocation?
+         if(context->readCipherContext != NULL)
+         {
+            //Configure the decryption engine with the read key
+            error = context->cipherAlgo->init(context->readCipherContext,
+               context->readEncKey, context->encKeyLen);
+         }
+         else
+         {
+            //Failed to allocate memory
+            error = ERROR_OUT_OF_MEMORY;
+         }
+      }
+      else
+      {
+         //Report an error
+         error = ERROR_FAILURE;
+      }
+
+#if (TLS_GCM_CIPHER_SUPPORT == ENABLED)
+      //GCM AEAD cipher?
+      if(context->cipherMode == CIPHER_MODE_GCM)
+      {
+         //Check status code
+         if(!error)
+         {
+            //Allocate a memory buffer to hold the GCM context
+            context->readGcmContext = tlsAllocMem(sizeof(GcmContext));
+
+            //Successful memory allocation?
+            if(context->readGcmContext != NULL)
+            {
+               //Initialize GCM context
+               error = gcmInit(context->readGcmContext,
+                  context->cipherAlgo, context->readCipherContext);
+            }
+            else
+            {
+               //Failed to allocate memory
+               error = ERROR_OUT_OF_MEMORY;
+            }
+         }
+      }
+#endif
+   }
+   else if(context->cipherMode == CIPHER_MODE_CHACHA20_POLY1305)
+   {
+      //We are done
+      error = NO_ERROR;
+   }
+   else
+   {
+      //Unsupported mode of operation
+      error = ERROR_FAILURE;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Encode a multiple precision integer to an opaque vector
+ * @param[in] a Pointer to a multiple precision integer
+ * @param[out] data Buffer where to store the opaque vector
+ * @param[out] length Total number of bytes that have been written
+ * @return Error code
+ **/
+
+error_t tlsWriteMpi(const Mpi *a, uint8_t *data, size_t *length)
+{
+   error_t error;
+   size_t n;
+
+   //Retrieve the actual size of the integer
+   n = mpiGetByteLength(a);
+
+   //The data is preceded by a 2-byte length field
+   STORE16BE(n, data);
+
+   //Convert the integer to an octet string
+   error = mpiWriteRaw(a, data + 2, n);
+   //Conversion failed?
+   if(error)
+      return error;
+
+   //Return the total number of bytes that have been written
+   *length = n + 2;
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Read a multiple precision integer from an opaque vector
+ * @param[out] a Resulting multiple precision integer
+ * @param[in] data Buffer where to read the opaque vector
+ * @param[in] size Total number of bytes available in the buffer
+ * @param[out] length Total number of bytes that have been read
+ * @return Error code
+ **/
+
+error_t tlsReadMpi(Mpi *a, const uint8_t *data, size_t size, size_t *length)
+{
+   error_t error;
+   size_t n;
+
+   //Buffer underrun?
+   if(size < 2)
+      return ERROR_DECODING_FAILED;
+
+   //Decode the length field
+   n = LOAD16BE(data);
+
+   //Buffer underrun?
+   if(size < (n + 2))
+      return ERROR_DECODING_FAILED;
+
+   //Convert the octet string to a multiple precision integer
+   error = mpiReadRaw(a, data + 2, n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Return the total number of bytes that have been read
+   *length = n + 2;
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Encode an EC point to an opaque vector
+ * @param[in] params EC domain parameters
+ * @param[in] a Pointer to an EC point
+ * @param[out] data Buffer where to store the opaque vector
+ * @param[out] length Total number of bytes that have been written
+ * @return Error code
+ **/
+
+error_t tlsWriteEcPoint(const EcDomainParameters *params,
+   const EcPoint *a, uint8_t *data, size_t *length)
+{
+#if (TLS_ECDH_ANON_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED || \
+   TLS_ECDHE_ECDSA_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   error_t error;
+
+   //Convert the EC point to an octet string
+   error = ecExport(params, a, data + 1, length);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Set the length of the opaque vector
+   data[0] = (uint8_t) (*length);
+
+   //Return the total number of bytes that have been written
+   *length += 1;
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Read an EC point from an opaque vector
+ * @param[in] params EC domain parameters
+ * @param[out] a Resulting EC point
+ * @param[in] data Buffer where to read the opaque vector
+ * @param[in] size Total number of bytes available in the buffer
+ * @param[out] length Total number of bytes that have been read
+ * @return Error code
+ **/
+
+error_t tlsReadEcPoint(const EcDomainParameters *params,
+   EcPoint *a, const uint8_t *data, size_t size, size_t *length)
+{
+#if (TLS_ECDH_ANON_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED || \
+   TLS_ECDHE_ECDSA_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   error_t error;
+   size_t n;
+
+   //Buffer underrun?
+   if(size < 1)
+      return ERROR_DECODING_FAILED;
+
+   //The EC point representation is preceded by a length field
+   n = data[0];
+
+   //Valid EC point representation?
+   if(size < (n + 1))
+      return ERROR_DECODING_FAILED;
+
+   //Convert the octet string to an EC point
+   error = ecImport(params, a, data + 1, n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Return the total number of bytes that have been read
+   *length = n + 1;
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Generate RSA signature (SSL 3.0, TLS 1.0 and TLS 1.1)
+ * @param[in] key Signer's RSA private key
+ * @param[in] digest Digest of the message to be signed
+ * @param[out] signature Resulting signature
+ * @param[out] signatureLength Length of the resulting signature
+ * @return Error code
+ **/
+
+error_t tlsGenerateRsaSignature(const RsaPrivateKey *key,
+   const uint8_t *digest, uint8_t *signature, size_t *signatureLength)
+{
+#if (TLS_RSA_SIGN_SUPPORT == ENABLED || TLS_DHE_RSA_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED)
+   error_t error;
+   size_t k;
+   size_t paddingLength;
+   uint8_t *em;
+   Mpi m;
+   Mpi s;
+
+   //Debug message
+   TRACE_DEBUG("RSA signature generation...\r\n");
+   TRACE_DEBUG("  Modulus:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->n);
+   TRACE_DEBUG("  Public exponent:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->e);
+   TRACE_DEBUG("  Private exponent:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->d);
+   TRACE_DEBUG("  Prime 1:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->p);
+   TRACE_DEBUG("  Prime 2:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->q);
+   TRACE_DEBUG("  Prime exponent 1:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->dp);
+   TRACE_DEBUG("  Prime exponent 2:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->dq);
+   TRACE_DEBUG("  Coefficient:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->qinv);
+   TRACE_DEBUG("  Message digest:\r\n");
+   TRACE_DEBUG_ARRAY("    ", digest, MD5_DIGEST_SIZE + SHA1_DIGEST_SIZE);
+
+   //Initialize multiple-precision integers
+   mpiInit(&m);
+   mpiInit(&s);
+
+   //Get the length in octets of the modulus n
+   k = mpiGetByteLength(&key->n);
+
+   //Check the length of the modulus
+   if(k < (MD5_DIGEST_SIZE + SHA1_DIGEST_SIZE + 11))
+      return ERROR_INVALID_KEY;
+
+   //Point to the buffer where the encoded message EM will be generated
+   em = signature;
+
+   //The leading 0x00 octet ensures that the encoded message,
+   //converted to an integer, is less than the modulus
+   em[0] = 0x00;
+   //Block type 0x01 is used for private-key operations
+   em[1] = 0x01;
+
+   //Compute the length of the padding string PS
+   paddingLength = k - (MD5_DIGEST_SIZE + SHA1_DIGEST_SIZE + 3);
+   //Fill the padding string with 0xFF
+   memset(em + 2, 0xFF, paddingLength);
+   //Append a 0x00 octet to PS
+   em[paddingLength + 2] = 0x00;
+
+   //Append the digest value
+   memcpy(em + paddingLength + 3, digest, MD5_DIGEST_SIZE + SHA1_DIGEST_SIZE);
+
+   //Debug message
+   TRACE_DEBUG("  Encoded message\r\n");
+   TRACE_DEBUG_ARRAY("    ", em, k);
+
+   //Start of exception handling block
+   do
+   {
+      //Convert the encoded message EM to an integer message representative m
+      error = mpiReadRaw(&m, em, k);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Apply the RSASP1 signature primitive
+      error = rsasp1(key, &m, &s);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Convert the signature representative s to a signature of length k octets
+      error = mpiWriteRaw(&s, signature, k);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Length of the resulting signature
+      *signatureLength = k;
+
+      //Debug message
+      TRACE_DEBUG("  Signature:\r\n");
+      TRACE_DEBUG_ARRAY("    ", signature, *signatureLength);
+
+      //End of exception handling block
+   } while(0);
+
+   //Free previously allocated memory
+   mpiFree(&m);
+   mpiFree(&s);
+
+   //Return status code
+   return error;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Verify RSA signature (SSL 3.0, TLS 1.0 and TLS 1.1)
+ * @param[in] key Signer's RSA public key
+ * @param[in] digest Digest of the message whose signature is to be verified
+ * @param[in] signature Signature to be verified
+ * @param[in] signatureLength Length of the signature to be verified
+ * @return Error code
+ **/
+
+error_t tlsVerifyRsaSignature(const RsaPublicKey *key,
+   const uint8_t *digest, const uint8_t *signature, size_t signatureLength)
+{
+#if (TLS_RSA_SIGN_SUPPORT == ENABLED || TLS_DHE_RSA_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED)
+   error_t error;
+   uint_t i;
+   uint_t k;
+   uint8_t *em;
+   Mpi s;
+   Mpi m;
+
+   //Debug message
+   TRACE_DEBUG("RSA signature verification...\r\n");
+   TRACE_DEBUG("  Modulus:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->n);
+   TRACE_DEBUG("  Public exponent:\r\n");
+   TRACE_DEBUG_MPI("    ", &key->e);
+   TRACE_DEBUG("  Message digest:\r\n");
+   TRACE_DEBUG_ARRAY("    ", digest, MD5_DIGEST_SIZE + SHA1_DIGEST_SIZE);
+   TRACE_DEBUG("  Signature:\r\n");
+   TRACE_DEBUG_ARRAY("    ", signature, signatureLength);
+
+   //Get the length in octets of the modulus n
+   k = mpiGetByteLength(&key->n);
+
+   //Check the length of the signature
+   if(signatureLength != k)
+      return ERROR_INVALID_SIGNATURE;
+
+   //Initialize multiple-precision integers
+   mpiInit(&s);
+   mpiInit(&m);
+
+   //Allocate a memory buffer to hold the encoded message
+   em = tlsAllocMem(k);
+   //Failed to allocate memory?
+   if(em == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Start of exception handling block
+   do
+   {
+      //Convert the signature to an integer signature representative s
+      error = mpiReadRaw(&s, signature, signatureLength);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Apply the RSAVP1 verification primitive
+      error = rsavp1(key, &s, &m);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Convert the message representative m to an encoded message EM of length k octets
+      error = mpiWriteRaw(&m, em, k);
+      //Conversion failed?
+      if(error)
+         break;
+
+      //Debug message
+      TRACE_DEBUG("  Encoded message\r\n");
+      TRACE_DEBUG_ARRAY("    ", em, k);
+
+      //Assume an error...
+      error = ERROR_INVALID_SIGNATURE;
+
+      //The first octet of EM must have a value of 0x00
+      if(em[0] != 0x00)
+         break;
+      //The block type BT shall be 0x01
+      if(em[1] != 0x01)
+         break;
+
+      //Check the padding string PS
+      for(i = 2; i < k; i++)
+      {
+         //A 0x00 octet indicates the end of the padding string
+         if(em[i] == 0x00)
+            break;
+
+         //Each byte of PS must be set to 0xFF when the block type is 0x01
+         if(em[i] != 0xFF)
+            break;
+      }
+
+      //Check whether the padding string is properly terminated
+      if(i >= k || em[i] != 0x00)
+         break;
+
+      //The length of PS cannot be less than 8 octets
+      if(i < 10)
+         break;
+
+      //Check the length of the digest
+      if((k - i - 1) != (MD5_DIGEST_SIZE + SHA1_DIGEST_SIZE))
+         break;
+      //Check the digest value
+      if(memcmp(digest, em + i + 1, MD5_DIGEST_SIZE + SHA1_DIGEST_SIZE))
+         break;
+
+      //The RSA signature is valid
+      error = NO_ERROR;
+
+      //End of exception handling block
+   } while(0);
+
+   //Release multiple precision integers
+   mpiFree(&s);
+   mpiFree(&m);
+   //Release previously allocated memory
+   tlsFreeMem(em);
+
+   //Return status code
+   return error;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Generate DSA signature
+ * @param[in] prngAlgo PRNG algorithm
+ * @param[in] prngContext Pointer to the PRNG context
+ * @param[in] key Signer's DSA private key
+ * @param[in] digest Digest of the message to be signed
+ * @param[in] digestLength Length in octets of the digest
+ * @param[out] signature Resulting signature
+ * @param[out] signatureLength Length of the resulting signature
+ * @return Error code
+ **/
+
+error_t tlsGenerateDsaSignature(const PrngAlgo *prngAlgo, void *prngContext, const DsaPrivateKey *key,
+   const uint8_t *digest, size_t digestLength, uint8_t *signature, size_t *signatureLength)
+{
+#if (TLS_DSA_SIGN_SUPPORT == ENABLED || TLS_DHE_DSS_SUPPORT == ENABLED)
+   error_t error;
+   DsaSignature dsaSignature;
+
+   //Initialize DSA signature
+   dsaInitSignature(&dsaSignature);
+
+   //Generate DSA signature
+   error = dsaGenerateSignature(prngAlgo, prngContext,
+      key, digest, digestLength, &dsaSignature);
+   //Failed to generate signature?
+   if(error)
+      return error;
+
+   //Encode the resulting (R, S) integer pair using ASN.1
+   error = dsaWriteSignature(&dsaSignature, signature, signatureLength);
+
+   //Free previously allocated resources
+   dsaFreeSignature(&dsaSignature);
+
+   //Return status code
+   return error;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Verify DSA signature
+ * @param[in] key Signer's DSA public key
+ * @param[in] digest Digest of the message whose signature is to be verified
+ * @param[in] digestLength Length in octets of the digest
+ * @param[in] signature Signature to be verified
+ * @param[in] signatureLength Length of the signature to be verified
+ * @return Error code
+ **/
+
+error_t tlsVerifyDsaSignature(const DsaPublicKey *key, const uint8_t *digest,
+   size_t digestLength, const uint8_t *signature, size_t signatureLength)
+{
+#if (TLS_DSA_SIGN_SUPPORT == ENABLED || TLS_DHE_DSS_SUPPORT == ENABLED)
+   error_t error;
+   DsaSignature dsaSignature;
+
+   //Initialize DSA signature
+   dsaInitSignature(&dsaSignature);
+
+   //Read the ASN.1 encoded DSA signature
+   error = dsaReadSignature(signature, signatureLength, &dsaSignature);
+   //Failed to decode ASN.1 structure?
+   if(error)
+      return error;
+
+   //DSA signature verification
+   error = dsaVerifySignature(key, digest, digestLength, &dsaSignature);
+
+   //Free previously allocated resources
+   dsaFreeSignature(&dsaSignature);
+
+   //Return status code
+   return error;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Generate ECDSA signature
+ * @param[in] params EC domain parameters
+ * @param[in] prngAlgo PRNG algorithm
+ * @param[in] prngContext Pointer to the PRNG context
+ * @param[in] key Signer's ECDSA private key
+ * @param[in] digest Digest of the message to be signed
+ * @param[in] digestLength Length in octets of the digest
+ * @param[out] signature Resulting signature
+ * @param[out] signatureLength Length of the resulting signature
+ * @return Error code
+ **/
+
+error_t tlsGenerateEcdsaSignature(const EcDomainParameters *params,
+   const PrngAlgo *prngAlgo, void *prngContext, const Mpi *key, const uint8_t *digest,
+   size_t digestLength, uint8_t *signature, size_t *signatureLength)
+{
+#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED || TLS_ECDHE_ECDSA_SUPPORT == ENABLED)
+   error_t error;
+   EcdsaSignature ecdsaSignature;
+
+   //Initialize ECDSA signature
+   ecdsaInitSignature(&ecdsaSignature);
+
+   //Generate ECDSA signature
+   error = ecdsaGenerateSignature(params, prngAlgo,
+      prngContext, key, digest, digestLength, &ecdsaSignature);
+   //Failed to generate signature?
+   if(error)
+      return error;
+
+   //Encode the resulting (R, S) integer pair using ASN.1
+   error = ecdsaWriteSignature(&ecdsaSignature, signature, signatureLength);
+
+   //Free previously allocated resources
+   ecdsaFreeSignature(&ecdsaSignature);
+
+   //Return status code
+   return error;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Verify ECDSA signature
+ * @param[in] params EC domain parameters
+ * @param[in] key Signer's ECDSA public key
+ * @param[in] digest Digest of the message whose signature is to be verified
+ * @param[in] digestLength Length in octets of the digest
+ * @param[in] signature Signature to be verified
+ * @param[in] signatureLength Length of the signature to be verified
+ * @return Error code
+ **/
+
+error_t tlsVerifyEcdsaSignature(const EcDomainParameters *params,
+   const EcPoint *key, const uint8_t *digest, size_t digestLength,
+   const uint8_t *signature, size_t signatureLength)
+{
+#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED || TLS_ECDHE_ECDSA_SUPPORT == ENABLED)
+   error_t error;
+   EcdsaSignature ecdsaSignature;
+
+   //Initialize ECDSA signature
+   ecdsaInitSignature(&ecdsaSignature);
+
+   //Read the ASN.1 encoded ECDSA signature
+   error = ecdsaReadSignature(signature, signatureLength, &ecdsaSignature);
+   //Failed to decode ASN.1 structure?
+   if(error)
+      return error;
+
+   //ECDSA signature verification
+   error = ecdsaVerifySignature(params, key,
+      digest, digestLength, &ecdsaSignature);
+
+   //Free previously allocated resources
+   ecdsaFreeSignature(&ecdsaSignature);
+
+   //Return status code
+   return error;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Premaster secret generation (for PSK cipher suites)
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsGeneratePskPremasterSecret(TlsContext *context)
+{
+   error_t error;
+
+#if (TLS_PSK_SUPPORT == ENABLED)
+   //PSK key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_PSK)
+   {
+      size_t n;
+
+      //Let N be the length of pre-shared key
+      n = context->pskLen;
+
+      //Check whether the output buffer is large enough to hold the premaster secret
+      if((n * 2 + 4) <= TLS_MAX_PREMASTER_SECRET_SIZE)
+      {
+         //The premaster secret is formed as follows: if the PSK is N octets
+         //long, concatenate a uint16 with the value N, N zero octets, a second
+         //uint16 with the value N, and the PSK itself
+         STORE16BE(n, context->premasterSecret);
+         memset(context->premasterSecret + 2, 0, n);
+         STORE16BE(n, context->premasterSecret + n + 2);
+         memcpy(context->premasterSecret + n + 4, context->psk, n);
+
+         //Save the length of the premaster secret
+         context->premasterSecretLen = n * 2 + 4;
+
+         //Premaster secret successfully generated
+         error = NO_ERROR;
+      }
+      else
+      {
+         //Report an error
+         error = ERROR_BUFFER_OVERFLOW;
+      }
+   }
+   else
+#endif
+#if (TLS_RSA_PSK_SUPPORT == ENABLED || TLS_DHE_PSK_SUPPORT == ENABLED || \
+   TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   //RSA_PSK, DHE_PSK or ECDHE_PSK key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
+   {
+      size_t n;
+
+      //Let N be the length of pre-shared key
+      n = context->pskLen;
+
+      //Check whether the output buffer is large enough to hold the premaster secret
+      if((context->premasterSecretLen + n + 4) <= TLS_MAX_PREMASTER_SECRET_SIZE)
+      {
+         //The "other_secret" field comes from the Diffie-Hellman, ECDH or
+         //RSA exchange (DHE_PSK, ECDH_PSK and RSA_PSK, respectively)
+         memmove(context->premasterSecret + 2, context->premasterSecret,
+            context->premasterSecretLen);
+
+         //The "other_secret" field is preceded by a 2-byte length field
+         STORE16BE(context->premasterSecretLen, context->premasterSecret);
+
+         //if the PSK is N octets long, concatenate a uint16 with the value N
+         STORE16BE(n, context->premasterSecret + context->premasterSecretLen + 2);
+
+         //Concatenate the PSK itself
+         memcpy(context->premasterSecret + context->premasterSecretLen + 4,
+            context->psk, n);
+
+         //Adjust the length of the premaster secret
+         context->premasterSecretLen += n + 4;
+
+         //Premaster secret successfully generated
+         error = NO_ERROR;
+      }
+      else
+      {
+         //Report an error
+         error = ERROR_BUFFER_OVERFLOW;
+      }
+   }
+   else
+#endif
+   //Invalid key exchange method?
+   {
+      //The specified key exchange method is not supported
+      error = ERROR_UNSUPPORTED_KEY_EXCH_METHOD;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Key generation
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsGenerateKeys(TlsContext *context)
+{
+   error_t error;
+   size_t i;
+   size_t n;
+   uint8_t temp;
+
+   //Length of necessary key material
+   n = 2 * (context->macKeyLen + context->encKeyLen + context->fixedIvLen);
+
+   //Make sure that the key block is large enough
+   if(n > sizeof(context->keyBlock))
+      return ERROR_FAILURE;
+
+   //Debug message
+   TRACE_DEBUG("Generating keys...\r\n");
+   TRACE_DEBUG("  Client random bytes:\r\n");
+   TRACE_DEBUG_ARRAY("    ", &context->clientRandom, 32);
+   TRACE_DEBUG("  Server random bytes:\r\n");
+   TRACE_DEBUG_ARRAY("    ", &context->serverRandom, 32);
+
+   //If a full handshake is being performed, the premaster secret
+   //shall be first converted to the master secret
+   if(!context->resume)
+   {
+      //Debug message
+      TRACE_DEBUG("  Premaster secret:\r\n");
+      TRACE_DEBUG_ARRAY("    ", context->premasterSecret, context->premasterSecretLen);
+
+      //Convert the premaster secret to the master secret
+      if(context->version == SSL_VERSION_3_0)
+      {
+         //SSL 3.0 does not use a PRF, instead makes use abundantly of MD5
+         error = sslExpandKey(context->premasterSecret, context->premasterSecretLen,
+            context->random, 64, context->masterSecret, 48);
+      }
+      else if(context->version <= TLS_VERSION_1_1)
+      {
+         //TLS 1.0 and 1.1 use a PRF that combines MD5 and SHA-1
+         error = tlsPrf(context->premasterSecret, context->premasterSecretLen,
+            "master secret", context->random, 64, context->masterSecret, 48);
+      }
+      else
+      {
+         //TLS 1.2 PRF uses SHA-256 or a stronger hash algorithm
+         //as the core function in its construction
+         error = tlsPrf2(context->prfHashAlgo, context->premasterSecret,
+            context->premasterSecretLen, "master secret",
+            context->random, 64, context->masterSecret, 48);
+      }
+
+      //Check the return status
+      if(error)
+         return error;
+
+      //The premaster secret should be deleted from memory
+      //once the master secret has been computed
+      memset(context->premasterSecret, 0, 48);
+   }
+
+   //Debug message
+   TRACE_DEBUG("  Master secret:\r\n");
+   TRACE_DEBUG_ARRAY("    ", context->masterSecret, 48);
+
+   //Exchange client and server random bytes
+   for(i = 0; i < 32; i++)
+   {
+      //Swap each byte
+      temp = context->random[i];
+      context->random[i] = context->random[i + 32];
+      context->random[i + 32] = temp;
+   }
+
+   //Perform key expansion
+   if(context->version == SSL_VERSION_3_0)
+   {
+      //SSL 3.0 does not use a PRF, instead makes use abundantly of MD5
+      error = sslExpandKey(context->masterSecret, 48,
+         context->random, 64, context->keyBlock, n);
+   }
+   else if(context->version <= TLS_VERSION_1_1)
+   {
+      //TLS 1.0 and 1.1 use a PRF that combines MD5 and SHA-1
+      error = tlsPrf(context->masterSecret, 48, "key expansion",
+         context->random, 64, context->keyBlock, n);
+   }
+   else
+   {
+      //TLS 1.2 PRF uses SHA-256 or a stronger hash algorithm
+      //as the core function in its construction
+      error = tlsPrf2(context->prfHashAlgo, context->masterSecret, 48,
+         "key expansion", context->random, 64, context->keyBlock, n);
+   }
+
+   //Exchange client and server random bytes
+   for(i = 0; i < 32; i++)
+   {
+      //Swap each byte
+      temp = context->random[i];
+      context->random[i] = context->random[i + 32];
+      context->random[i + 32] = temp;
+   }
+
+   //Any error while performing key expansion?
+   if(error)
+      return error;
+
+   //Debug message
+   TRACE_DEBUG("  Key block:\r\n");
+   TRACE_DEBUG_ARRAY("    ", context->keyBlock, n);
+
+   //TLS operates as a client?
+   if(context->entity == TLS_CONNECTION_END_CLIENT)
+   {
+      //MAC keys
+      context->writeMacKey = context->keyBlock;
+      context->readMacKey = context->writeMacKey + context->macKeyLen;
+      //Encryption keys
+      context->writeEncKey = context->readMacKey + context->macKeyLen;
+      context->readEncKey = context->writeEncKey + context->encKeyLen;
+      //Initialization vectors
+      context->writeIv = context->readEncKey + context->encKeyLen;
+      context->readIv = context->writeIv + context->fixedIvLen;
+   }
+   //TLS operates as a server?
+   else
+   {
+      //MAC keys
+      context->readMacKey = context->keyBlock;
+      context->writeMacKey = context->readMacKey + context->macKeyLen;
+      //Encryption keys
+      context->readEncKey = context->writeMacKey + context->macKeyLen;
+      context->writeEncKey = context->readEncKey + context->encKeyLen;
+      //Initialization vectors
+      context->readIv = context->writeEncKey + context->encKeyLen;
+      context->writeIv = context->readIv + context->fixedIvLen;
+   }
+
+   //Dump MAC keys for debugging purpose
+   if(context->macKeyLen > 0)
+   {
+      TRACE_DEBUG("  Write MAC key:\r\n");
+      TRACE_DEBUG_ARRAY("    ", context->writeMacKey, context->macKeyLen);
+      TRACE_DEBUG("  Read MAC key:\r\n");
+      TRACE_DEBUG_ARRAY("    ", context->readMacKey, context->macKeyLen);
+   }
+
+   //Dump encryption keys for debugging purpose
+   TRACE_DEBUG("  Write encryption key:\r\n");
+   TRACE_DEBUG_ARRAY("    ", context->writeEncKey, context->encKeyLen);
+   TRACE_DEBUG("  Read encryption key:\r\n");
+   TRACE_DEBUG_ARRAY("    ", context->readEncKey, context->encKeyLen);
+
+   //Dump initialization vectors for debugging purpose
+   if(context->fixedIvLen > 0)
+   {
+      TRACE_DEBUG("  Write IV:\r\n");
+      TRACE_DEBUG_ARRAY("    ", context->writeIv, context->fixedIvLen);
+      TRACE_DEBUG("  Read IV:\r\n");
+      TRACE_DEBUG_ARRAY("    ", context->readIv, context->fixedIvLen);
+   }
+
+   //Key generation is successful
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Pseudorandom function (TLS 1.0 and 1.1)
+ *
+ * The pseudorandom function (PRF) takes as input a secret, a seed, and
+ * an identifying label and produces an output of arbitrary length. This
+ * function is used to expand secrets into blocks of data for the purpose
+ * of key generation
+ *
+ * @param[in] secret Pointer to the secret
+ * @param[in] secretLength Length of the secret
+ * @param[in] label Identifying label (NULL-terminated string)
+ * @param[in] seed Pointer to the seed
+ * @param[in] seedLength Length of the seed
+ * @param[out] output Pointer to the output
+ * @param[in] outputLength Desired output length
+ * @return Error code
+ **/
+
+error_t tlsPrf(const uint8_t *secret, size_t secretLength, const char_t *label,
+   const uint8_t *seed, size_t seedLength, uint8_t *output, size_t outputLength)
+{
+   uint_t i;
+   uint_t j;
+   size_t labelLength;
+   size_t sLength;
+   const uint8_t *s1;
+   const uint8_t *s2;
+   HmacContext *hmacContext;
+   uint8_t a[SHA1_DIGEST_SIZE];
+
+   //Allocate a memory buffer to hold the HMAC context
+   hmacContext = tlsAllocMem(sizeof(HmacContext));
+   //Failed to allocate memory?
+   if(hmacContext == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Compute the length of the label
+   labelLength = strlen(label);
+
+   //The secret is partitioned into two halves S1 and S2
+   //with the possibility of one shared byte
+   sLength = (secretLength + 1) / 2;
+   //S1 is taken from the first half of the secret
+   s1 = secret;
+   //S2 is taken from the second half
+   s2 = secret + secretLength - sLength;
+
+   //First compute A(1) = HMAC_MD5(S1, label + seed)
+   hmacInit(hmacContext, MD5_HASH_ALGO, s1, sLength);
+   hmacUpdate(hmacContext, label, labelLength);
+   hmacUpdate(hmacContext, seed, seedLength);
+   hmacFinal(hmacContext, a);
+
+   //Apply the data expansion function P_MD5
+   for(i = 0; i < outputLength; )
+   {
+      //Compute HMAC_MD5(S1, A(i) + label + seed)
+      hmacInit(hmacContext, MD5_HASH_ALGO, s1, sLength);
+      hmacUpdate(hmacContext, a, MD5_DIGEST_SIZE);
+      hmacUpdate(hmacContext, label, labelLength);
+      hmacUpdate(hmacContext, seed, seedLength);
+      hmacFinal(hmacContext, NULL);
+
+      //Copy the resulting digest
+      for(j = 0; i < outputLength && j < MD5_DIGEST_SIZE; i++, j++)
+         output[i] = hmacContext->digest[j];
+
+      //Compute A(i + 1) = HMAC_MD5(S1, A(i))
+      hmacInit(hmacContext, MD5_HASH_ALGO, s1, sLength);
+      hmacUpdate(hmacContext, a, MD5_DIGEST_SIZE);
+      hmacFinal(hmacContext, a);
+   }
+
+   //First compute A(1) = HMAC_SHA1(S2, label + seed)
+   hmacInit(hmacContext, SHA1_HASH_ALGO, s2, sLength);
+   hmacUpdate(hmacContext, label, labelLength);
+   hmacUpdate(hmacContext, seed, seedLength);
+   hmacFinal(hmacContext, a);
+
+   //Apply the data expansion function P_SHA1
+   for(i = 0; i < outputLength; )
+   {
+      //Compute HMAC_SHA1(S2, A(i) + label + seed)
+      hmacInit(hmacContext, SHA1_HASH_ALGO, s2, sLength);
+      hmacUpdate(hmacContext, a, SHA1_DIGEST_SIZE);
+      hmacUpdate(hmacContext, label, labelLength);
+      hmacUpdate(hmacContext, seed, seedLength);
+      hmacFinal(hmacContext, NULL);
+
+      //Copy the resulting digest
+      for(j = 0; i < outputLength && j < SHA1_DIGEST_SIZE; i++, j++)
+         output[i] ^= hmacContext->digest[j];
+
+      //Compute A(i + 1) = HMAC_SHA1(S2, A(i))
+      hmacInit(hmacContext, SHA1_HASH_ALGO, s2, sLength);
+      hmacUpdate(hmacContext, a, SHA1_DIGEST_SIZE);
+      hmacFinal(hmacContext, a);
+   }
+
+   //Free previously allocated memory
+   tlsFreeMem(hmacContext);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Pseudorandom function (TLS 1.2)
+ *
+ * The pseudorandom function (PRF) takes as input a secret, a seed, and
+ * an identifying label and produces an output of arbitrary length. This
+ * function is used to expand secrets into blocks of data for the purpose
+ * of key generation
+ *
+ * @param[in] hash Hash function used to compute PRF
+ * @param[in] secret Pointer to the secret
+ * @param[in] secretLength Length of the secret
+ * @param[in] label Identifying label (NULL-terminated string)
+ * @param[in] seed Pointer to the seed
+ * @param[in] seedLength Length of the seed
+ * @param[out] output Pointer to the output
+ * @param[in] outputLength Desired output length
+ * @return Error code
+ **/
+
+error_t tlsPrf2(const HashAlgo *hash, const uint8_t *secret, size_t secretLength,
+   const char_t *label, const uint8_t *seed, size_t seedLength, uint8_t *output, size_t outputLength)
+{
+   size_t n;
+   size_t labelLength;
+   HmacContext *hmacContext;
+   uint8_t a[MAX_HASH_DIGEST_SIZE];
+
+   //Allocate a memory buffer to hold the HMAC context
+   hmacContext = tlsAllocMem(sizeof(HmacContext));
+   //Failed to allocate memory?
+   if(hmacContext == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Compute the length of the label
+   labelLength = strlen(label);
+
+   //First compute A(1) = HMAC_hash(secret, label + seed)
+   hmacInit(hmacContext, hash, secret, secretLength);
+   hmacUpdate(hmacContext, label, labelLength);
+   hmacUpdate(hmacContext, seed, seedLength);
+   hmacFinal(hmacContext, a);
+
+   //Apply the data expansion function P_hash
+   while(outputLength > 0)
+   {
+      //Compute HMAC_hash(secret, A(i) + label + seed)
+      hmacInit(hmacContext, hash, secret, secretLength);
+      hmacUpdate(hmacContext, a, hash->digestSize);
+      hmacUpdate(hmacContext, label, labelLength);
+      hmacUpdate(hmacContext, seed, seedLength);
+      hmacFinal(hmacContext, NULL);
+
+      //Calculate the number of bytes to copy
+      n = MIN(outputLength, hash->digestSize);
+      //Copy the resulting digest
+      memcpy(output, hmacContext->digest, n);
+
+      //Compute A(i + 1) = HMAC_hash(secret, A(i))
+      hmacInit(hmacContext, hash, secret, secretLength);
+      hmacUpdate(hmacContext, a, hash->digestSize);
+      hmacFinal(hmacContext, a);
+
+      //Advance data pointer
+      output += n;
+      //Decrement byte counter
+      outputLength -= n;
+   }
+
+   //Free previously allocated memory
+   tlsFreeMem(hmacContext);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Check whether a certificate is acceptable
+ * @param[in] cert End entity certificate
+ * @param[in] certTypes List of supported certificate types
+ * @param[in] numCertTypes Size of the list that contains the supported certificate types
+ * @param[in] signHashAlgos List of supported signature algorithms
+ * @param[in] curveList List of supported elliptic curves
+ * @param[in] certAuthorities List of trusted CA
+ * @return TRUE if the specified certificate conforms to the requirements, else FALSE
+ **/
+
+bool_t tlsIsCertificateAcceptable(const TlsCertDesc *cert,
+   const uint8_t *certTypes, size_t numCertTypes, const TlsSignHashAlgos *signHashAlgos,
+   const TlsEllipticCurveList *curveList, const TlsCertAuthorities *certAuthorities)
+{
+   size_t i;
+   size_t n;
+   size_t length;
+   bool_t acceptable;
+
+   //Make sure that a valid certificate has been loaded
+   if(!cert->certChain || !cert->certChainLength)
+      return FALSE;
+
+   //This flag tells whether the certificate is acceptable
+   acceptable = TRUE;
+
+   //Filter out certificates with unsupported type
+   if(numCertTypes > 0)
+   {
+      //Loop through the list of supported certificate types
+      for(acceptable = FALSE, i = 0; i < numCertTypes; i++)
+      {
+         //Check whether the certificate type is acceptable
+         if(certTypes[i] == cert->type)
+         {
+            acceptable = TRUE;
+            break;
+         }
+      }
+   }
+
+   //Filter out certificates that are signed with an unsupported
+   //hash/signature algorithm
+   if(acceptable && signHashAlgos != NULL)
+   {
+      //Retrieve the number of items in the list
+      n = ntohs(signHashAlgos->length) / sizeof(TlsSignHashAlgo);
+
+      //Loop through the list of supported hash/signature algorithm pairs
+      for(acceptable = FALSE, i = 0; i < n; i++)
+      {
+         //The certificate must be signed using a valid hash/signature algorithm pair
+         if(signHashAlgos->value[i].signature == cert->signAlgo &&
+            signHashAlgos->value[i].hash == cert->hashAlgo)
+         {
+            acceptable = TRUE;
+            break;
+         }
+      }
+   }
+
+   //Check whether the certificate contains an ECDSA public key
+   if(cert->type == TLS_CERT_ECDSA_SIGN)
+   {
+      //Filter out ECDSA certificates that use an unsupported elliptic curve
+      if(acceptable && curveList != NULL)
+      {
+         //Retrieve the number of items in the list
+         n = ntohs(curveList->length) / sizeof(uint16_t);
+
+         //Loop through the list of supported elliptic curves
+         for(acceptable = FALSE, i = 0; i < n; i++)
+         {
+            //Check whether the elliptic curve is supported
+            if(ntohs(curveList->value[i]) == cert->namedCurve)
+            {
+               acceptable = TRUE;
+               break;
+            }
+         }
+      }
+   }
+
+   //Filter out certificates that are issued by a non trusted CA
+   if(acceptable && certAuthorities != NULL)
+   {
+      //Retrieve the length of the list
+      length = ntohs(certAuthorities->length);
+
+      //If the certificate authorities list is empty, then the client
+      //may send any certificate of the appropriate type
+      if(length > 0)
+      {
+         error_t error;
+         const uint8_t *p;
+         const char_t *pemCert;
+         size_t pemCertLength;
+         uint8_t *derCert;
+         size_t derCertSize;
+         size_t derCertLength;
+         X509CertificateInfo *certInfo;
+
+         //The list of acceptable certificate authorities describes the known roots CA
+         acceptable = FALSE;
+
+         //Point to the first distinguished name
+         p = certAuthorities->value;
+
+         //Point to the end entity certificate
+         pemCert = cert->certChain;
+         //Get the total length, in bytes, of the certificate chain
+         pemCertLength = cert->certChainLength;
+
+         //DER encoded certificate
+         derCert = NULL;
+         derCertSize = 0;
+         derCertLength = 0;
+
+         //Start of exception handling block
+         do
+         {
+            //Allocate a memory buffer to store X.509 certificate info
+            certInfo = tlsAllocMem(sizeof(X509CertificateInfo));
+            //Failed to allocate memory?
+            if(certInfo == NULL)
+               break;
+
+            //Point to the last certificate of the chain
+            do
+            {
+               //Read PEM certificates, one by one
+               error = pemReadCertificate(&pemCert, &pemCertLength,
+                  &derCert, &derCertSize, &derCertLength);
+
+               //Loop as long as necessary
+            } while(!error);
+
+            //Any error to report?
+            if(error != ERROR_END_OF_FILE)
+               break;
+
+            //Parse the last certificate of the chain
+            error = x509ParseCertificate(derCert, derCertLength, certInfo);
+            //Failed to parse the X.509 certificate?
+            if(error)
+               break;
+
+            //Parse each distinguished name of the list
+            while(length > 0)
+            {
+               //Sanity check
+               if(length < 2)
+                  break;
+
+               //Each distinguished name is preceded by a 2-byte length field
+               n = LOAD16BE(p);
+
+               //Make sure the length field is valid
+               if(length < (n + 2))
+                  break;
+
+               //Check if the distinguished name matches the root CA
+               if(n == certInfo->issuer.rawDataLen && !memcmp(p + 2, certInfo->issuer.rawData, n))
+               {
+                  acceptable = TRUE;
+                  break;
+               }
+
+               //Advance data pointer
+               p += n + 2;
+               //Number of bytes left in the list
+               length -= n + 2;
+            }
+
+            //End of exception handling block
+         } while(0);
+
+         //Release previously allocated memory
+         tlsFreeMem(derCert);
+         tlsFreeMem(certInfo);
+      }
+   }
+
+   //The return value specifies whether all the criteria were matched
+   return acceptable;
+}
+
+
+/**
+ * @brief Retrieve the certificate type
+ * @param[in] certInfo X.509 certificate
+ * @param[out] certType Certificate type
+ * @param[out] certSignAlgo Signature algorithm that has been used to sign the certificate
+ * @param[out] certHashAlgo Hash algorithm that has been used to sign the certificate
+ * @param[out] namedCurve Elliptic curve (only for ECDSA certificates)
+ * @return Error code
+ **/
+
+error_t tlsGetCertificateType(const X509CertificateInfo *certInfo, TlsCertificateType *certType,
+   TlsSignatureAlgo *certSignAlgo, TlsHashAlgo *certHashAlgo, TlsEcNamedCurve *namedCurve)
+{
+   //Check parameters
+   if(certInfo == NULL || certType == NULL || certSignAlgo == NULL ||
+      certHashAlgo == NULL || namedCurve == NULL)
+   {
+      //Report an error
+      return ERROR_INVALID_PARAMETER;
+   }
+
+#if (TLS_RSA_SIGN_SUPPORT == ENABLED || TLS_RSA_SUPPORT == ENABLED || \
+   TLS_DHE_RSA_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED)
+   //A valid RSA public key has been found?
+   if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen,
+      RSA_ENCRYPTION_OID, sizeof(RSA_ENCRYPTION_OID)))
+   {
+      //Save certificate type
+      *certType = TLS_CERT_RSA_SIGN;
+      //Elliptic curve cryptography is not used
+      *namedCurve = TLS_EC_CURVE_NONE;
+   }
+   else
+#endif
+#if (TLS_DSA_SIGN_SUPPORT == ENABLED || TLS_DHE_DSS_SUPPORT == ENABLED)
+   //A valid DSA public key has been found?
+   if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen,
+      DSA_OID, sizeof(DSA_OID)))
+   {
+      //Save certificate type
+      *certType = TLS_CERT_DSS_SIGN;
+      //Elliptic curve cryptography is not used
+      *namedCurve = TLS_EC_CURVE_NONE;
+   }
+   else
+#endif
+#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED || TLS_ECDHE_ECDSA_SUPPORT == ENABLED)
+   //A valid EC public key has been found?
+   if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen,
+      EC_PUBLIC_KEY_OID, sizeof(EC_PUBLIC_KEY_OID)))
+   {
+      //Save certificate type
+      *certType = TLS_CERT_ECDSA_SIGN;
+
+      //Retrieve the named curve that has been used to generate the EC public key
+      *namedCurve = tlsGetNamedCurve(certInfo->subjectPublicKeyInfo.ecParams.namedCurve,
+         certInfo->subjectPublicKeyInfo.ecParams.namedCurveLen);
+   }
+   else
+#endif
+   //The certificate does not contain any valid public key...
+   {
+      //Report an error
+      return ERROR_BAD_CERTIFICATE;
+   }
+
+   //Retrieve the signature algorithm that has been used to sign the certificate
+   if(certInfo->signatureAlgo == NULL)
+   {
+      //Invalid certificate
+      return ERROR_BAD_CERTIFICATE;
+   }
+#if (TLS_RSA_SIGN_SUPPORT == ENABLED)
+   else if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      MD5_WITH_RSA_ENCRYPTION_OID, sizeof(MD5_WITH_RSA_ENCRYPTION_OID)))
+   {
+      //MD5 with RSA signature algorithm
+      *certSignAlgo = TLS_SIGN_ALGO_RSA;
+      *certHashAlgo = TLS_HASH_ALGO_MD5;
+   }
+   else if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      SHA1_WITH_RSA_ENCRYPTION_OID, sizeof(SHA1_WITH_RSA_ENCRYPTION_OID)))
+   {
+      //SHA-1 with RSA signature algorithm
+      *certSignAlgo = TLS_SIGN_ALGO_RSA;
+      *certHashAlgo = TLS_HASH_ALGO_SHA1;
+   }
+   else if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      SHA256_WITH_RSA_ENCRYPTION_OID, sizeof(SHA256_WITH_RSA_ENCRYPTION_OID)))
+   {
+      //SHA-256 with RSA signature algorithm
+      *certSignAlgo = TLS_SIGN_ALGO_RSA;
+      *certHashAlgo = TLS_HASH_ALGO_SHA256;
+   }
+   else if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      SHA384_WITH_RSA_ENCRYPTION_OID, sizeof(SHA384_WITH_RSA_ENCRYPTION_OID)))
+   {
+      //SHA-384 with RSA signature algorithm
+      *certSignAlgo = TLS_SIGN_ALGO_RSA;
+      *certHashAlgo = TLS_HASH_ALGO_SHA384;
+   }
+   else if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      SHA512_WITH_RSA_ENCRYPTION_OID, sizeof(SHA512_WITH_RSA_ENCRYPTION_OID)))
+   {
+      //SHA-512 with RSA signature algorithm
+      *certSignAlgo = TLS_SIGN_ALGO_RSA;
+      *certHashAlgo = TLS_HASH_ALGO_SHA512;
+   }
+#endif
+#if (TLS_DSA_SIGN_SUPPORT == ENABLED)
+   else if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      DSA_WITH_SHA1_OID, sizeof(DSA_WITH_SHA1_OID)))
+   {
+      //DSA with SHA-1 signature algorithm
+      *certSignAlgo = TLS_SIGN_ALGO_DSA;
+      *certHashAlgo = TLS_HASH_ALGO_SHA1;
+   }
+   else if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      DSA_WITH_SHA224_OID, sizeof(DSA_WITH_SHA224_OID)))
+   {
+      //DSA with SHA-224 signature algorithm
+      *certSignAlgo = TLS_SIGN_ALGO_DSA;
+      *certHashAlgo = TLS_HASH_ALGO_SHA224;
+   }
+   else if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      DSA_WITH_SHA256_OID, sizeof(DSA_WITH_SHA256_OID)))
+   {
+      //DSA with SHA-256 signature algorithm
+      *certSignAlgo = TLS_SIGN_ALGO_DSA;
+      *certHashAlgo = TLS_HASH_ALGO_SHA256;
+   }
+#endif
+#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED)
+   else if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      ECDSA_WITH_SHA1_OID, sizeof(ECDSA_WITH_SHA1_OID)))
+   {
+      //ECDSA with SHA-1 signature algorithm
+      *certSignAlgo = TLS_SIGN_ALGO_ECDSA;
+      *certHashAlgo = TLS_HASH_ALGO_SHA1;
+   }
+   else if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      ECDSA_WITH_SHA224_OID, sizeof(ECDSA_WITH_SHA224_OID)))
+   {
+      //ECDSA with SHA-224 signature algorithm
+      *certSignAlgo = TLS_SIGN_ALGO_ECDSA;
+      *certHashAlgo = TLS_HASH_ALGO_SHA224;
+   }
+   else if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      ECDSA_WITH_SHA256_OID, sizeof(ECDSA_WITH_SHA256_OID)))
+   {
+      //ECDSA with SHA-256 signature algorithm
+      *certSignAlgo = TLS_SIGN_ALGO_ECDSA;
+      *certHashAlgo = TLS_HASH_ALGO_SHA256;
+   }
+   else if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      ECDSA_WITH_SHA384_OID, sizeof(ECDSA_WITH_SHA384_OID)))
+   {
+      //ECDSA with SHA-384 signature algorithm
+      *certSignAlgo = TLS_SIGN_ALGO_ECDSA;
+      *certHashAlgo = TLS_HASH_ALGO_SHA384;
+   }
+   else if(!oidComp(certInfo->signatureAlgo, certInfo->signatureAlgoLen,
+      ECDSA_WITH_SHA512_OID, sizeof(ECDSA_WITH_SHA512_OID)))
+   {
+      //ECDSA with SHA-512 signature algorithm
+      *certSignAlgo = TLS_SIGN_ALGO_ECDSA;
+      *certHashAlgo = TLS_HASH_ALGO_SHA512;
+   }
+#endif
+   else
+   {
+      //The signature algorithm is not supported...
+      return ERROR_BAD_CERTIFICATE;
+   }
+
+   //X.509 certificate successfully parsed
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Find the specified extension
+ * @param[in] data Pointer to the list of extensions
+ * @param[in] length Length in bytes of the list
+ * @param[in] type Expected extension type
+ * @return If the specified extension was found, a pointer to the corresponding
+ *   extension is returned. Otherwise NULL pointer is returned
+ **/
+
+const TlsExtension *tlsGetExtension(const uint8_t *data, size_t length, uint16_t type)
+{
+   size_t n;
+   const TlsExtension *extension;
+
+   //Sanity check
+   if(length < 2)
+      return NULL;
+
+   //Retrieve the length of the list
+   n = LOAD16BE(data);
+
+   //Sanity check
+   if(length < (n + 2))
+      return NULL;
+
+   //Point to the first extension of the list
+   data += 2;
+
+   //Parse the list of extensions offered by the peer
+   while(n > 0)
+   {
+      //Point to the current extension
+      extension = (TlsExtension *) data;
+
+      //Check the length of the extension
+      if(n < sizeof(TlsExtension))
+         break;
+      if(n < (sizeof(TlsExtension) + ntohs(extension->length)))
+         break;
+
+      //Check whether the extension type matches the specified one
+      if(ntohs(extension->type) == type)
+         return extension;
+
+      //Jump to the next extension
+      data += sizeof(TlsExtension) + ntohs(extension->length);
+      //Remaining bytes to process
+      n -= sizeof(TlsExtension) + ntohs(extension->length);
+   }
+
+   //The specified extension type was not found
+   return NULL;
+}
+
+
+/**
+ * @brief Convert TLS version to string representation
+ * @param[in] version Version number
+ * @return Cipher suite name
+ **/
+
+const char_t *tlsGetVersionName(uint16_t version)
+{
+   //TLS versions
+   static const char_t *label[] = {"SSL 3.0", "TLS 1.0", "TLS 1.1", "TLS 1.2", "Unknown"};
+
+   //Check current version
+   if(version == SSL_VERSION_3_0)
+      return label[0];
+   else if(version == TLS_VERSION_1_0)
+      return label[1];
+   else if(version == TLS_VERSION_1_1)
+      return label[2];
+   else if(version == TLS_VERSION_1_2)
+      return label[3];
+   else
+      return label[4];
+}
+
+
+/**
+ * @brief Get the hash algorithm that matches the specified identifier
+ * @param[in] hashAlgoId Hash algorithm identifier
+ * @return Cipher suite name
+ **/
+
+const HashAlgo *tlsGetHashAlgo(uint8_t hashAlgoId)
+{
+   //MD5 hash identifier?
+   if(hashAlgoId == TLS_HASH_ALGO_MD5)
+      return MD5_HASH_ALGO;
+   //SHA-1 hash identifier?
+   else if(hashAlgoId == TLS_HASH_ALGO_SHA1)
+      return SHA1_HASH_ALGO;
+   //SHA-256 hash identifier?
+   else if(hashAlgoId == TLS_HASH_ALGO_SHA256)
+      return SHA256_HASH_ALGO;
+#if (TLS_SHA224_SUPPORT == ENABLED)
+   //SHA-224 hash identifier?
+   else if(hashAlgoId == TLS_HASH_ALGO_SHA224)
+      return SHA224_HASH_ALGO;
+#endif
+#if (TLS_SHA384_SUPPORT == ENABLED)
+   //SHA-384 hash identifier?
+   else if(hashAlgoId == TLS_HASH_ALGO_SHA384)
+      return SHA384_HASH_ALGO;
+#endif
+#if (TLS_SHA512_SUPPORT == ENABLED)
+   //SHA-512 hash identifier?
+   else if(hashAlgoId == TLS_HASH_ALGO_SHA512)
+      return SHA512_HASH_ALGO;
+#endif
+   //Unknown hash identifier?
+   else
+      return NULL;
+}
+
+
+/**
+ * @brief Get the EC domain parameters that match the specified named curve
+ * @param[in] namedCurve Elliptic curve identifier
+ * @return Elliptic curve domain parameters
+ **/
+
+const EcCurveInfo *tlsGetCurveInfo(uint16_t namedCurve)
+{
+#if (TLS_ECDH_ANON_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED || \
+   TLS_ECDHE_ECDSA_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   //Explicit prime elliptic curves are not supported
+   if(namedCurve == TLS_EC_CURVE_ARBITRARY_EXPLICIT_PRIME)
+      return NULL;
+   //Explicit characteristic-2 elliptic curves are not supported
+   else if(namedCurve == TLS_EC_CURVE_ARBITRARY_EXPLICIT_CHAR2)
+      return NULL;
+#if (TLS_SECP160K1_SUPPORT == ENABLED)
+   //secp160k1 elliptic curve?
+   else if(namedCurve == TLS_EC_CURVE_SECP160K1)
+      return SECP160K1_CURVE;
+#endif
+#if (TLS_SECP160R1_SUPPORT == ENABLED)
+   //secp160r1 elliptic curve?
+   else if(namedCurve == TLS_EC_CURVE_SECP160R1)
+      return SECP160R1_CURVE;
+#endif
+#if (TLS_SECP160R2_SUPPORT == ENABLED)
+   //secp160r2 elliptic curve?
+   else if(namedCurve == TLS_EC_CURVE_SECP160R2)
+      return SECP160R2_CURVE;
+#endif
+#if (TLS_SECP192K1_SUPPORT == ENABLED)
+   //secp192k1 elliptic curve?
+   else if(namedCurve == TLS_EC_CURVE_SECP192K1)
+      return SECP192K1_CURVE;
+#endif
+#if (TLS_SECP192R1_SUPPORT == ENABLED)
+   //secp192r1 elliptic curve?
+   else if(namedCurve == TLS_EC_CURVE_SECP192R1)
+      return SECP192R1_CURVE;
+#endif
+#if (TLS_SECP224K1_SUPPORT == ENABLED)
+   //secp224k1 elliptic curve?
+   else if(namedCurve == TLS_EC_CURVE_SECP224K1)
+      return SECP224K1_CURVE;
+#endif
+#if (TLS_SECP224R1_SUPPORT == ENABLED)
+   //secp224r1 elliptic curve?
+   else if(namedCurve == TLS_EC_CURVE_SECP224R1)
+      return SECP224R1_CURVE;
+#endif
+#if (TLS_SECP256K1_SUPPORT == ENABLED)
+   //secp256k1 elliptic curve?
+   else if(namedCurve == TLS_EC_CURVE_SECP256K1)
+      return SECP256K1_CURVE;
+#endif
+#if (TLS_SECP256R1_SUPPORT == ENABLED)
+   //secp256r1 elliptic curve?
+   else if(namedCurve == TLS_EC_CURVE_SECP256R1)
+      return SECP256R1_CURVE;
+#endif
+#if (TLS_SECP384R1_SUPPORT == ENABLED)
+   //secp384r1 elliptic curve?
+   else if(namedCurve == TLS_EC_CURVE_SECP384R1)
+      return SECP384R1_CURVE;
+#endif
+#if (TLS_SECP521R1_SUPPORT == ENABLED)
+   //secp521r1 elliptic curve?
+   else if(namedCurve == TLS_EC_CURVE_SECP521R1)
+      return SECP521R1_CURVE;
+#endif
+#if (TLS_BRAINPOOLP256R1_SUPPORT == ENABLED)
+   //brainpoolP256r1 elliptic curve?
+   else if(namedCurve == TLS_EC_CURVE_BRAINPOOLP256R1)
+      return BRAINPOOLP256R1_CURVE;
+#endif
+#if (TLS_BRAINPOOLP384R1_SUPPORT == ENABLED)
+   //brainpoolP384r1 elliptic curve?
+   else if(namedCurve == TLS_EC_CURVE_BRAINPOOLP384R1)
+      return BRAINPOOLP384R1_CURVE;
+#endif
+#if (TLS_BRAINPOOLP512R1_SUPPORT == ENABLED)
+   //brainpoolP512r1 elliptic curve?
+   else if(namedCurve == TLS_EC_CURVE_BRAINPOOLP512R1)
+      return BRAINPOOLP512R1_CURVE;
+#endif
+   //Unknown identifier?
+   else
+      return NULL;
+#else
+   //ECC not supported
+   return NULL;
+#endif
+}
+
+
+/**
+ * @brief Get the named curve that matches the specified OID
+ * @param[in] oid Object identifier
+ * @param[in] length OID length
+ * @return Named curve
+ **/
+
+TlsEcNamedCurve tlsGetNamedCurve(const uint8_t *oid, size_t length)
+{
+#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED || TLS_ECDHE_ECDSA_SUPPORT == ENABLED)
+   //Make sure the object identifier is valid
+   if(oid == NULL)
+      return TLS_EC_CURVE_NONE;
+#if (TLS_SECP160K1_SUPPORT == ENABLED)
+   //secp160k1 elliptic curve?
+   else if(!oidComp(oid, length, SECP160K1_OID, sizeof(SECP160K1_OID)))
+      return TLS_EC_CURVE_SECP160K1;
+#endif
+#if (TLS_SECP160R1_SUPPORT == ENABLED)
+   //secp160r1 elliptic curve?
+   else if(!oidComp(oid, length, SECP160R1_OID, sizeof(SECP160R1_OID)))
+      return TLS_EC_CURVE_SECP160R1;
+#endif
+#if (TLS_SECP160R2_SUPPORT == ENABLED)
+   //secp160r2 elliptic curve?
+   else if(!oidComp(oid, length, SECP160R2_OID, sizeof(SECP160R2_OID)))
+      return TLS_EC_CURVE_SECP160R2;
+#endif
+#if (TLS_SECP192K1_SUPPORT == ENABLED)
+   //secp192k1 elliptic curve?
+   else if(!oidComp(oid, length, SECP192K1_OID, sizeof(SECP192K1_OID)))
+      return TLS_EC_CURVE_SECP192K1;
+#endif
+#if (TLS_SECP192R1_SUPPORT == ENABLED)
+   //secp192r1 elliptic curve?
+   else if(!oidComp(oid, length, SECP192R1_OID, sizeof(SECP192R1_OID)))
+      return TLS_EC_CURVE_SECP192R1;
+#endif
+#if (TLS_SECP224K1_SUPPORT == ENABLED)
+   //secp224k1 elliptic curve?
+   else if(!oidComp(oid, length, SECP224K1_OID, sizeof(SECP224K1_OID)))
+      return TLS_EC_CURVE_SECP224K1;
+#endif
+#if (TLS_SECP224R1_SUPPORT == ENABLED)
+   //secp224r1 elliptic curve?
+   else if(!oidComp(oid, length, SECP224R1_OID, sizeof(SECP224R1_OID)))
+      return TLS_EC_CURVE_SECP224R1;
+#endif
+#if (TLS_SECP256K1_SUPPORT == ENABLED)
+   //secp256k1 elliptic curve?
+   else if(!oidComp(oid, length, SECP256K1_OID, sizeof(SECP256K1_OID)))
+      return TLS_EC_CURVE_SECP256K1;
+#endif
+#if (TLS_SECP256R1_SUPPORT == ENABLED)
+   //secp256r1 elliptic curve?
+   else if(!oidComp(oid, length, SECP256R1_OID, sizeof(SECP256R1_OID)))
+      return TLS_EC_CURVE_SECP256R1;
+#endif
+#if (TLS_SECP384R1_SUPPORT == ENABLED)
+   //secp384r1 elliptic curve?
+   else if(!oidComp(oid, length, SECP384R1_OID, sizeof(SECP384R1_OID)))
+      return TLS_EC_CURVE_SECP384R1;
+#endif
+#if (TLS_SECP521R1_SUPPORT == ENABLED)
+   //secp521r1 elliptic curve?
+   else if(!oidComp(oid, length, SECP521R1_OID, sizeof(SECP521R1_OID)))
+      return TLS_EC_CURVE_SECP521R1;
+#endif
+#if (TLS_BRAINPOOLP256R1_SUPPORT == ENABLED)
+   //brainpoolP256r1 elliptic curve?
+   else if(!oidComp(oid, length, BRAINPOOLP256R1_OID, sizeof(BRAINPOOLP256R1_OID)))
+      return TLS_EC_CURVE_BRAINPOOLP256R1;
+#endif
+#if (TLS_BRAINPOOLP384R1_SUPPORT == ENABLED)
+   //brainpoolP384r1 elliptic curve?
+   else if(!oidComp(oid, length, BRAINPOOLP384R1_OID, sizeof(BRAINPOOLP384R1_OID)))
+      return TLS_EC_CURVE_BRAINPOOLP384R1;
+#endif
+#if (TLS_BRAINPOOLP512R1_SUPPORT == ENABLED)
+   //brainpoolP512r1 elliptic curve?
+   else if(!oidComp(oid, length, BRAINPOOLP512R1_OID, sizeof(BRAINPOOLP512R1_OID)))
+      return TLS_EC_CURVE_BRAINPOOLP512R1;
+#endif
+   //Unknown identifier?
+   else
+      return TLS_EC_CURVE_NONE;
+#else
+   //ECC not supported
+   return TLS_EC_CURVE_NONE;
+#endif
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls_misc.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,114 @@
+/**
+ * @file tls_misc.h
+ * @brief Helper functions (TLS client and server)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TLS_MISC_H
+#define _TLS_MISC_H
+
+//Dependencies
+#include "tls.h"
+#include "x509.h"
+
+//TLS related functions
+void tlsProcessError(TlsContext *context, error_t errorCode);
+
+error_t tlsGenerateRandomValue(TlsContext *context, TlsRandom *random);
+
+error_t tlsSetVersion(TlsContext *context, uint16_t version);
+error_t tlsSetCipherSuite(TlsContext *context, uint16_t identifier);
+error_t tlsSetCompressionMethod(TlsContext *context, uint8_t identifier);
+
+error_t tlsSelectSignHashAlgo(TlsContext *context,
+   TlsSignatureAlgo signAlgo, const TlsSignHashAlgos *supportedSignAlgos);
+
+error_t tlsSelectNamedCurve(TlsContext *context,
+   const TlsEllipticCurveList *curveList);
+
+error_t tlsInitHandshakeHash(TlsContext *context);
+void tlsUpdateHandshakeHash(TlsContext *context, const void *data, size_t length);
+
+error_t tlsFinalizeHandshakeHash(TlsContext *context, const HashAlgo *hash,
+   const void *hashContext, const char_t *label, uint8_t *output);
+
+error_t tlsComputeVerifyData(TlsContext *context, TlsConnectionEnd entity);
+
+error_t tlsInitEncryptionEngine(TlsContext *context);
+error_t tlsInitDecryptionEngine(TlsContext *context);
+
+error_t tlsWriteMpi(const Mpi *a, uint8_t *data, size_t *length);
+error_t tlsReadMpi(Mpi *a, const uint8_t *data, size_t size, size_t *length);
+
+error_t tlsWriteEcPoint(const EcDomainParameters *params,
+   const EcPoint *a, uint8_t *data, size_t *length);
+
+error_t tlsReadEcPoint(const EcDomainParameters *params,
+   EcPoint *a, const uint8_t *data, size_t size, size_t *length);
+
+error_t tlsGenerateRsaSignature(const RsaPrivateKey *key,
+   const uint8_t *digest, uint8_t *signature, size_t *signatureLength);
+
+error_t tlsVerifyRsaSignature(const RsaPublicKey *key,
+   const uint8_t *digest, const uint8_t *signature, size_t signatureLength);
+
+error_t tlsGenerateDsaSignature(const PrngAlgo *prngAlgo, void *prngContext, const DsaPrivateKey *key,
+   const uint8_t *digest, size_t digestLength, uint8_t *signature, size_t *signatureLength);
+
+error_t tlsVerifyDsaSignature(const DsaPublicKey *key, const uint8_t *digest,
+   size_t digestLength, const uint8_t *signature, size_t signatureLength);
+
+error_t tlsGenerateEcdsaSignature(const EcDomainParameters *params,
+   const PrngAlgo *prngAlgo, void *prngContext, const Mpi *key, const uint8_t *digest,
+   size_t digestLength, uint8_t *signature, size_t *signatureLength);
+
+error_t tlsVerifyEcdsaSignature(const EcDomainParameters *params,
+   const EcPoint *key, const uint8_t *digest, size_t digestLength,
+   const uint8_t *signature, size_t signatureLength);
+
+error_t tlsGeneratePskPremasterSecret(TlsContext *context);
+error_t tlsGenerateKeys(TlsContext *context);
+
+error_t tlsPrf(const uint8_t *secret, size_t secretLength, const char_t *label,
+   const uint8_t *seed, size_t seedLength, uint8_t *output, size_t outputLength);
+
+error_t tlsPrf2(const HashAlgo *hash, const uint8_t *secret, size_t secretLength,
+   const char_t *label, const uint8_t *seed, size_t seedLength, uint8_t *output, size_t outputLength);
+
+bool_t tlsIsCertificateAcceptable(const TlsCertDesc *cert,
+   const uint8_t *certTypes, size_t numCertTypes, const TlsSignHashAlgos *signHashAlgos,
+   const TlsEllipticCurveList *curveList, const TlsCertAuthorities *certAuthorities);
+
+error_t tlsGetCertificateType(const X509CertificateInfo *certInfo, TlsCertificateType *certType,
+   TlsSignatureAlgo *certSignAlgo, TlsHashAlgo *certHashAlgo, TlsEcNamedCurve *namedCurve);
+
+const TlsExtension *tlsGetExtension(const uint8_t *data, size_t length, uint16_t type);
+const char_t *tlsGetVersionName(uint16_t version);
+const HashAlgo *tlsGetHashAlgo(uint8_t hashAlgoId);
+const EcCurveInfo *tlsGetCurveInfo(uint16_t namedCurve);
+TlsEcNamedCurve tlsGetNamedCurve(const uint8_t *oid, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls_record.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1151 @@
+/**
+ * @file tls_record.c
+ * @brief TLS record protocol
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TLS_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "tls.h"
+#include "tls_common.h"
+#include "tls_record.h"
+#include "tls_misc.h"
+#include "ssl_common.h"
+#include "cipher_mode_cbc.h"
+#include "cipher_mode_ccm.h"
+#include "cipher_mode_gcm.h"
+#include "chacha20_poly1305.h"
+#include "debug.h"
+
+//Check SSL library configuration
+#if (TLS_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Write protocol data
+ * @param[in] context Pointer to the TLS context
+ * @param[in] data Pointer to the data buffer
+ * @param[in] length Number of data bytes to be written
+ * @param[in] contentType Higher level protocol
+ * @return Error code
+ **/
+
+error_t tlsWriteProtocolData(TlsContext *context,
+   const void *data, size_t length, TlsContentType contentType)
+{
+   error_t error;
+   size_t n;
+   uint8_t *p;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Fragmentation process
+   while(!error)
+   {
+      if(context->txBufferLen == 0)
+      {
+         //Check the length of the data
+         if(length > context->txRecordMaxLen)
+         {
+            //Report an error
+            error = ERROR_MESSAGE_TOO_LONG;
+         }
+         else if(length > 0)
+         {
+            //The hash value is updated for each handshake message,
+            //except for HelloRequest messages
+            if(contentType == TLS_TYPE_HANDSHAKE)
+               tlsUpdateHandshakeHash(context, data, length);
+
+            //Make room for the encryption overhead
+            memmove(context->txBuffer + context->txBufferSize - length, data, length);
+
+            //Save record type
+            context->txBufferType = contentType;
+            //Set the length of the buffer
+            context->txBufferLen = length;
+            //Point to the beginning of the buffer
+            context->txBufferPos = 0;
+         }
+         else
+         {
+            //We are done
+            break;
+         }
+      }
+      else if(context->txBufferPos < context->txBufferLen)
+      {
+         //Number of bytes left to send
+         n = context->txBufferLen - context->txBufferPos;
+         //Point to the current fragment
+         p = context->txBuffer + context->txBufferSize - n;
+         //The record length must not exceed 16384 bytes
+         n = MIN(n, TLS_MAX_RECORD_LENGTH);
+
+         //Send TLS record
+         error = tlsWriteRecord(context, p, n, context->txBufferType);
+
+         //Check status code
+         if(!error)
+         {
+            //Advance data pointer
+            context->txBufferPos += n;
+         }
+      }
+      else
+      {
+         //Prepare to send new protocol data
+         context->txBufferLen = 0;
+         context->txBufferPos = 0;
+
+         //We are done
+         break;
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Read protocol data
+ * @param[in] context Pointer to the TLS context
+ * @param[out] data Pointer to the received data
+ * @param[out] length Number of data bytes that were received
+ * @param[out] contentType Higher level protocol
+ * @return Error code
+ **/
+
+error_t tlsReadProtocolData(TlsContext *context,
+   void **data, size_t *length, TlsContentType *contentType)
+{
+   error_t error;
+   size_t n;
+   TlsContentType type;
+   TlsHandshake *message;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Fragment reassembly process
+   do
+   {
+      //Empty receive buffer?
+      if(context->rxBufferLen == 0)
+      {
+         //Read a TLS record
+         error = tlsReadRecord(context, context->rxBuffer,
+            context->rxBufferSize, &n, &type);
+
+         //Check status code
+         if(!error)
+         {
+            //Save record type
+            context->rxBufferType = type;
+            //Number of bytes available for reading
+            context->rxBufferLen = n;
+            //Rewind to the beginning of the buffer
+            context->rxBufferPos = 0;
+         }
+      }
+      //Imcomplete message received?
+      else if(error == ERROR_MORE_DATA_REQUIRED)
+      {
+         //Make room at the end of the buffer
+         if(context->rxBufferPos > 0)
+         {
+            //Move unread data to the beginning of the buffer
+            memmove(context->rxBuffer, context->rxBuffer +
+               context->rxBufferPos, context->rxBufferLen);
+
+            //Rewind to the beginning of the buffer
+            context->rxBufferPos = 0;
+         }
+
+         //Read a TLS record
+         error = tlsReadRecord(context, context->rxBuffer + context->rxBufferLen,
+            context->rxBufferSize - context->rxBufferLen, &n, &type);
+
+         //Check status code
+         if(!error)
+         {
+            //Fragmented records with mixed types cannot be interleaved
+            if(type != context->rxBufferType)
+               error = ERROR_UNEXPECTED_MESSAGE;
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Number of bytes available for reading
+            context->rxBufferLen += n;
+         }
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Handshake message received?
+         if(context->rxBufferType == TLS_TYPE_HANDSHAKE)
+         {
+            //A message may be fragmented across several records
+            if(context->rxBufferLen < sizeof(TlsHandshake))
+            {
+               //Read an additional record
+               error = ERROR_MORE_DATA_REQUIRED;
+            }
+            else
+            {
+               //Point to the handshake message
+               message = (TlsHandshake *) (context->rxBuffer + context->rxBufferPos);
+               //Retrieve the length of the handshake message
+               n = sizeof(TlsHandshake) + LOAD24BE(message->length);
+
+               //A message may be fragmented across several records
+               if(context->rxBufferLen < n)
+               {
+                  //Read an additional record
+                  error = ERROR_MORE_DATA_REQUIRED;
+               }
+               else
+               {
+                  //Pass the handshake message to the higher layer
+                  error = NO_ERROR;
+               }
+            }
+         }
+         //ChangeCipherSpec message received?
+         else if(context->rxBufferType == TLS_TYPE_CHANGE_CIPHER_SPEC)
+         {
+            //A message may be fragmented across several records
+            if(context->rxBufferLen < sizeof(TlsChangeCipherSpec))
+            {
+               //Read an additional record
+               error = ERROR_MORE_DATA_REQUIRED;
+            }
+            else
+            {
+               //Length of the ChangeCipherSpec message
+               n = sizeof(TlsChangeCipherSpec);
+               //Pass the ChangeCipherSpec message to the higher layer
+               error = NO_ERROR;
+            }
+         }
+         //Alert message received?
+         else if(context->rxBufferType == TLS_TYPE_ALERT)
+         {
+            //A message may be fragmented across several records
+            if(context->rxBufferLen < sizeof(TlsAlert))
+            {
+               //Read an additional record
+               error = ERROR_MORE_DATA_REQUIRED;
+            }
+            else
+            {
+               //Length of the Alert message
+               n = sizeof(TlsAlert);
+               //Pass the Alert message to the higher layer
+               error = NO_ERROR;
+            }
+         }
+         //Application data received?
+         else if(context->rxBufferType == TLS_TYPE_APPLICATION_DATA)
+         {
+            //Length of the application data
+            n = context->rxBufferLen;
+            //Pass the application data to the higher layer
+            error = NO_ERROR;
+         }
+         //Unknown content type?
+         else
+         {
+            //Report an error
+            error = ERROR_UNEXPECTED_MESSAGE;
+         }
+      }
+
+      //Read as many records as necessary to reassemble the data
+   } while(error == ERROR_MORE_DATA_REQUIRED);
+
+   //Successful processing?
+   if(!error)
+   {
+      //Pointer to the received data
+      *data = context->rxBuffer + context->rxBufferPos;
+      //Length, in byte, of the data
+      *length = n;
+      //Protocol type
+      *contentType = context->rxBufferType;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send a TLS record
+ * @param[in] context Pointer to the TLS context
+ * @param[in] data Pointer to the record data
+ * @param[in] length Length of the record data
+ * @param[in] contentType Record type
+ * @return Error code
+ **/
+
+error_t tlsWriteRecord(TlsContext *context, const uint8_t *data,
+   size_t length, TlsContentType contentType)
+{
+   error_t error;
+   size_t n;
+   TlsRecord *record;
+
+   //Point to the TLS record
+   record = (TlsRecord *) context->txBuffer;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Send process
+   while(!error)
+   {
+      //Send as much data as possible
+      if(context->txRecordLen == 0)
+      {
+         //Format TLS record
+         record->type = contentType;
+         record->version = htons(context->version);
+         record->length = htons(length);
+
+         //Copy record data
+         memmove(record->data, data, length);
+
+         //Debug message
+         TRACE_DEBUG("Sending TLS record (%" PRIuSIZE " bytes)...\r\n", length);
+         TRACE_DEBUG_ARRAY("  ", record, length + sizeof(TlsRecord));
+
+         //Protect record payload?
+         if(context->changeCipherSpecSent)
+         {
+            //Encrypt TLS record
+            error = tlsEncryptRecord(context, record);
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Actual length of the record data
+            context->txRecordLen = sizeof(TlsRecord) + ntohs(record->length);
+            //Point to the beginning of the record
+            context->txRecordPos = 0;
+         }
+      }
+      else if(context->txRecordPos < context->txRecordLen)
+      {
+         //Total number of bytes that have been written
+         n = 0;
+
+         //Send more data
+         error = context->sendCallback(context->handle,
+            context->txBuffer + context->txRecordPos,
+            context->txRecordLen - context->txRecordPos, &n, 0);
+
+         //Check status code
+         if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
+         {
+            //Advance data pointer
+            context->txRecordPos += n;
+         }
+         else
+         {
+            //The write operation has failed
+            error = ERROR_WRITE_FAILED;
+         }
+      }
+      else
+      {
+         //Prepare to send the next TLS record
+         context->txRecordLen = 0;
+         context->txRecordPos = 0;
+
+         //We are done
+         break;
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Receive a TLS record
+ * @param[in] context Pointer to the TLS context
+ * @param[out] data Buffer where to store the record data
+ * @param[in] size Maximum acceptable size for the incoming record
+ * @param[out] length Length of the record data
+ * @param[out] contentType Record type
+ * @return Error code
+ **/
+
+error_t tlsReadRecord(TlsContext *context, uint8_t *data,
+   size_t size, size_t *length, TlsContentType *contentType)
+{
+   error_t error;
+   size_t n;
+   TlsRecord *record;
+
+   //Point to the buffer where to store the incoming TLS record
+   record = (TlsRecord *) data;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Receive process
+   while(!error)
+   {
+      //Read as much data as possible
+      if(context->rxRecordPos < sizeof(TlsRecord))
+      {
+         //Make sure that the buffer is large enough to hold the record header
+         if(size >= sizeof(TlsRecord))
+         {
+            //Total number of bytes that have been received
+            n = 0;
+
+            //Read TLS record header
+            error = context->receiveCallback(context->handle,
+               data + context->rxRecordPos,
+               sizeof(TlsRecord) - context->rxRecordPos, &n, 0);
+
+            //Check status code
+            if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
+            {
+               //Advance data pointer
+               context->rxRecordPos += n;
+
+               //TLS record header successfully received?
+               if(context->rxRecordPos >= sizeof(TlsRecord))
+               {
+                  //Debug message
+                  TRACE_DEBUG("Record header received:\r\n");
+                  TRACE_DEBUG_ARRAY("  ", record, sizeof(record));
+
+                  //Retrieve the length of the TLS record
+                  context->rxRecordLen = sizeof(TlsRecord) + ntohs(record->length);
+               }
+            }
+            else
+            {
+               //The read operation has failed
+               error = ERROR_READ_FAILED;
+            }
+         }
+         else
+         {
+            //Report an error
+            error = ERROR_RECORD_OVERFLOW;
+         }
+      }
+      else if(context->rxRecordPos < context->rxRecordLen)
+      {
+         //Make sure that the buffer is large enough to hold the entire record
+         if(size >= context->rxRecordLen)
+         {
+            //Total number of bytes that have been received
+            n = 0;
+
+            //Read TLS record contents
+            error = context->receiveCallback(context->handle,
+               data + context->rxRecordPos,
+               context->rxRecordLen - context->rxRecordPos, &n, 0);
+
+            //Check status code
+            if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
+            {
+               //Advance data pointer
+               context->rxRecordPos += n;
+            }
+            else
+            {
+               //The read operation has failed
+               error = ERROR_READ_FAILED;
+            }
+         }
+         else
+         {
+            //Report an error
+            error = ERROR_RECORD_OVERFLOW;
+         }
+      }
+      else
+      {
+         //Check current state
+         if(context->state > TLS_STATE_SERVER_HELLO)
+         {
+            //Once the server has sent the ServerHello message, enforce
+            //incoming record versions
+            if(ntohs(record->version) != context->version)
+               error = ERROR_VERSION_NOT_SUPPORTED;
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Record payload is protected?
+            if(context->changeCipherSpecReceived)
+            {
+               //Decrypt TLS record
+               error = tlsDecryptRecord(context, record);
+            }
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Actual length of the record data
+            *length = ntohs(record->length);
+            //Record type
+            *contentType = (TlsContentType) record->type;
+
+            //Debug message
+            TRACE_DEBUG("TLS record received (%" PRIuSIZE " bytes)...\r\n", *length);
+            TRACE_DEBUG_ARRAY("  ", record, *length + sizeof(TlsRecord));
+
+            //Discard record header
+            memmove(data, record->data, *length);
+
+            //Prepare to receive the next TLS record
+            context->rxRecordLen = 0;
+            context->rxRecordPos = 0;
+
+            //We are done
+            break;
+         }
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Encrypt an outgoing TLS record
+ * @param[in] context Pointer to the TLS context
+ * @param[in,out] record TLS record to be encrypted
+ * @return Error code
+ **/
+
+error_t tlsEncryptRecord(TlsContext *context, TlsRecord *record)
+{
+   error_t error;
+   size_t length;
+
+   //Convert the length field to host byte order
+   length = ntohs(record->length);
+
+   //Message authentication is required?
+   if(context->hashAlgo != NULL)
+   {
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_MIN_VERSION <= SSL_VERSION_3_0)
+      //Check whether SSL 3.0 is currently used
+      if(context->version == SSL_VERSION_3_0)
+      {
+         //SSL 3.0 uses an older obsolete version of the HMAC construction
+         error = sslComputeMac(context, context->writeMacKey,
+            context->writeSeqNum, record, record->data, length, record->data + length);
+         //Any error to report?
+         if(error)
+            return error;
+      }
+      else
+#endif
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_0 && TLS_MIN_VERSION <= TLS_VERSION_1_2)
+      //Check whether TLS 1.0, TLS 1.1 or TLS 1.2 is currently used
+      if(context->version >= TLS_VERSION_1_0)
+      {
+         //TLS uses a HMAC construction
+         hmacInit(&context->hmacContext, context->hashAlgo,
+            context->writeMacKey, context->macKeyLen);
+
+         //Compute MAC over the sequence number and the record contents
+         hmacUpdate(&context->hmacContext, context->writeSeqNum, sizeof(TlsSequenceNumber));
+         hmacUpdate(&context->hmacContext, record, length + sizeof(TlsRecord));
+
+         //Append the resulting MAC to the message
+         hmacFinal(&context->hmacContext, record->data + length);
+      }
+      else
+#endif
+      //The negotiated TLS version is not valid...
+      {
+         //Report an error
+         return ERROR_INVALID_VERSION;
+      }
+
+      //Debug message
+      TRACE_DEBUG("Write sequence number:\r\n");
+      TRACE_DEBUG_ARRAY("  ", context->writeSeqNum, sizeof(TlsSequenceNumber));
+      TRACE_DEBUG("Computed MAC:\r\n");
+      TRACE_DEBUG_ARRAY("  ", record->data + length, context->hashAlgo->digestSize);
+
+      //Adjust the length of the message
+      length += context->hashAlgo->digestSize;
+      //Fix length field
+      record->length = htons(length);
+
+      //Increment sequence number
+      tlsIncSequenceNumber(context->writeSeqNum);
+   }
+
+   //Encryption is required?
+   if(context->cipherMode != CIPHER_MODE_NULL)
+   {
+      //Debug message
+      TRACE_DEBUG("Record to be encrypted (%" PRIuSIZE " bytes):\r\n", length);
+      TRACE_DEBUG_ARRAY("  ", record, length + sizeof(TlsRecord));
+
+#if (TLS_STREAM_CIPHER_SUPPORT == ENABLED)
+      //Stream cipher?
+      if(context->cipherMode == CIPHER_MODE_STREAM)
+      {
+         //Encrypt record contents
+         context->cipherAlgo->encryptStream(context->writeCipherContext,
+            record->data, record->data, length);
+      }
+      else
+#endif
+#if (TLS_CBC_CIPHER_SUPPORT == ENABLED)
+      //CBC block cipher?
+      if(context->cipherMode == CIPHER_MODE_CBC)
+      {
+         size_t i;
+         size_t paddingLength;
+
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_1 && TLS_MIN_VERSION <= TLS_VERSION_1_2)
+         //TLS 1.1 and 1.2 use an explicit IV
+         if(context->version >= TLS_VERSION_1_1)
+         {
+            //Make room for the IV at the beginning of the data
+            memmove(record->data + context->recordIvLen, record->data, length);
+
+            //The initialization vector should be chosen at random
+            error = context->prngAlgo->read(context->prngContext,
+               record->data, context->recordIvLen);
+            //Any error to report?
+            if(error)
+               return error;
+
+            //Adjust the length of the message
+            length += context->recordIvLen;
+         }
+#endif
+         //Get the actual amount of bytes in the last block
+         paddingLength = (length + 1) % context->cipherAlgo->blockSize;
+
+         //Padding is added to force the length of the plaintext to be
+         //an integral multiple of the cipher's block length
+         if(paddingLength > 0)
+            paddingLength = context->cipherAlgo->blockSize - paddingLength;
+
+         //Write padding bytes
+         for(i = 0; i <= paddingLength; i++)
+            record->data[length + i] = (uint8_t) paddingLength;
+
+         //Compute the length of the resulting message
+         length += paddingLength + 1;
+         //Fix length field
+         record->length = htons(length);
+
+         //Debug message
+         TRACE_DEBUG("Record with padding (%" PRIuSIZE " bytes):\r\n", length);
+         TRACE_DEBUG_ARRAY("  ", record, length + sizeof(TlsRecord));
+
+         //CBC encryption
+         error = cbcEncrypt(context->cipherAlgo, context->writeCipherContext,
+            context->writeIv, record->data, record->data, length);
+         //Any error to report?
+         if(error)
+            return error;
+      }
+      else
+#endif
+#if (TLS_CCM_CIPHER_SUPPORT == ENABLED || TLS_CCM_8_CIPHER_SUPPORT == ENABLED || \
+   TLS_GCM_CIPHER_SUPPORT == ENABLED)
+      //CCM or GCM AEAD cipher?
+      if(context->cipherMode == CIPHER_MODE_CCM ||
+         context->cipherMode == CIPHER_MODE_GCM)
+      {
+         uint8_t *data;
+         uint8_t *tag;
+         size_t nonceLength;
+         uint8_t nonce[12];
+         uint8_t a[13];
+
+         //Determine the total length of the nonce
+         nonceLength = context->fixedIvLen + context->recordIvLen;
+         //The salt is the implicit part of the nonce and is not sent in the packet
+         memcpy(nonce, context->writeIv, context->fixedIvLen);
+
+         //The explicit part of the nonce is chosen by the sender
+         error = context->prngAlgo->read(context->prngContext,
+            nonce + context->fixedIvLen, context->recordIvLen);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //Make room for the explicit nonce at the beginning of the record
+         memmove(record->data + context->recordIvLen, record->data, length);
+         //The explicit part of the nonce is carried in each TLS record
+         memcpy(record->data, nonce + context->fixedIvLen, context->recordIvLen);
+
+         //Additional data to be authenticated
+         memcpy(a, context->writeSeqNum, sizeof(TlsSequenceNumber));
+         memcpy(a + sizeof(TlsSequenceNumber), record, sizeof(TlsRecord));
+
+         //Point to the plaintext
+         data = record->data + context->recordIvLen;
+         //Point to the buffer where to store the authentication tag
+         tag = data + length;
+
+#if (TLS_CCM_CIPHER_SUPPORT == ENABLED || TLS_CCM_8_CIPHER_SUPPORT == ENABLED)
+         //CCM AEAD cipher?
+         if(context->cipherMode == CIPHER_MODE_CCM)
+         {
+            //Authenticated encryption using CCM
+            error = ccmEncrypt(context->cipherAlgo, context->writeCipherContext,
+               nonce, nonceLength, a, 13, data, data, length, tag, context->authTagLen);
+         }
+         else
+#endif
+#if (TLS_GCM_CIPHER_SUPPORT == ENABLED)
+         //GCM AEAD cipher?
+         if(context->cipherMode == CIPHER_MODE_GCM)
+         {
+            //Authenticated encryption using GCM
+            error = gcmEncrypt(context->writeGcmContext, nonce, nonceLength,
+               a, 13, data, data, length, tag, context->authTagLen);
+         }
+         else
+#endif
+         //Invalid cipher mode?
+         {
+            //The specified cipher mode is not supported
+            error = ERROR_UNSUPPORTED_CIPHER_MODE;
+         }
+
+         //Failed to encrypt data?
+         if(error)
+            return error;
+
+         //Compute the length of the resulting message
+         length += context->recordIvLen + context->authTagLen;
+         //Fix length field
+         record->length = htons(length);
+
+         //Increment sequence number
+         tlsIncSequenceNumber(context->writeSeqNum);
+      }
+      else
+#endif
+#if (TLS_CHACHA20_POLY1305_SUPPORT == ENABLED)
+      //ChaCha20Poly1305 AEAD cipher?
+      if(context->cipherMode == CIPHER_MODE_CHACHA20_POLY1305)
+      {
+         size_t i;
+         uint8_t *tag;
+         uint8_t nonce[12];
+         uint8_t a[13];
+
+         //The 64-bit record sequence number is serialized as an 8-byte,
+         //big-endian value and padded on the left with four 0x00 bytes
+         memcpy(nonce + 4, context->writeSeqNum, 8);
+         memset(nonce, 0, 4);
+
+         //The padded sequence number is XORed with the write IV to form
+         //the 96-bit nonce
+         for(i = 0; i < context->fixedIvLen; i++)
+            nonce[i] ^= context->writeIv[i];
+
+         //Additional data to be authenticated
+         memcpy(a, context->writeSeqNum, sizeof(TlsSequenceNumber));
+         memcpy(a + sizeof(TlsSequenceNumber), record, sizeof(TlsRecord));
+
+         //Point to the buffer where to store the authentication tag
+         tag = record->data + length;
+
+         //Authenticated encryption using ChaCha20Poly1305
+         error = chacha20Poly1305Encrypt(context->writeEncKey, context->encKeyLen,
+            nonce, 12, a, 13, record->data, record->data, length, tag, context->authTagLen);
+         //Failed to encrypt data?
+         if(error)
+            return error;
+
+         //Compute the length of the resulting message
+         length += context->authTagLen;
+         //Fix length field
+         record->length = htons(length);
+
+         //Increment sequence number
+         tlsIncSequenceNumber(context->writeSeqNum);
+      }
+      else
+#endif
+      //Invalid cipher mode?
+      {
+         //The specified cipher mode is not supported
+         return ERROR_UNSUPPORTED_CIPHER_MODE;
+      }
+
+      //Debug message
+      TRACE_DEBUG("Encrypted record (%" PRIuSIZE " bytes):\r\n", length);
+      TRACE_DEBUG_ARRAY("  ", record, length + sizeof(TlsRecord));
+   }
+
+   //Successful encryption
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Decrypt an incoming TLS record
+ * @param[in] context Pointer to the TLS context
+ * @param[in,out] record TLS record to be decrypted
+ * @return Error code
+ **/
+
+error_t tlsDecryptRecord(TlsContext *context, TlsRecord *record)
+{
+   error_t error;
+   size_t length;
+
+   //Convert the length field to host byte order
+   length = ntohs(record->length);
+
+   //Decrypt record if necessary
+   if(context->cipherMode != CIPHER_MODE_NULL)
+   {
+      //Debug message
+      TRACE_DEBUG("Record to be decrypted (%" PRIuSIZE " bytes):\r\n", length);
+      TRACE_DEBUG_ARRAY("  ", record, length + sizeof(TlsRecord));
+
+#if (TLS_STREAM_CIPHER_SUPPORT == ENABLED)
+      //Stream cipher?
+      if(context->cipherMode == CIPHER_MODE_STREAM)
+      {
+         //Decrypt record contents
+         context->cipherAlgo->decryptStream(context->readCipherContext,
+            record->data, record->data, length);
+      }
+      else
+#endif
+#if (TLS_CBC_CIPHER_SUPPORT == ENABLED)
+      //CBC block cipher?
+      if(context->cipherMode == CIPHER_MODE_CBC)
+      {
+         size_t i;
+         size_t paddingLength;
+
+         //The length of the data must be a multiple of the block size
+         if((length % context->cipherAlgo->blockSize) != 0)
+            return ERROR_DECODING_FAILED;
+
+         //CBC decryption
+         error = cbcDecrypt(context->cipherAlgo, context->readCipherContext,
+            context->readIv, record->data, record->data, length);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //Debug message
+         TRACE_DEBUG("Record with padding (%" PRIuSIZE " bytes):\r\n", length);
+         TRACE_DEBUG_ARRAY("  ", record, length + sizeof(TlsRecord));
+
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_1 && TLS_MIN_VERSION <= TLS_VERSION_1_2)
+         //TLS 1.1 and 1.2 use an explicit IV
+         if(context->version >= TLS_VERSION_1_1)
+         {
+            //Make sure the message length is acceptable
+            if(length < context->recordIvLen)
+               return ERROR_DECODING_FAILED;
+
+            //Adjust the length of the message
+            length -= context->recordIvLen;
+            //Discard the first cipher block (corresponding to the explicit IV)
+            memmove(record->data, record->data + context->recordIvLen, length);
+         }
+#endif
+         //Make sure the message length is acceptable
+         if(length < context->cipherAlgo->blockSize)
+            return ERROR_DECODING_FAILED;
+
+         //Compute the length of the padding string
+         paddingLength = record->data[length - 1];
+         //Erroneous padding length?
+         if(paddingLength >= length)
+            return ERROR_BAD_RECORD_MAC;
+
+         //The receiver must check the padding
+         for(i = 0; i <= paddingLength; i++)
+         {
+            //Each byte in the padding data must be filled
+            //with the padding length value
+            if(record->data[length - 1 - i] != paddingLength)
+               return ERROR_BAD_RECORD_MAC;
+         }
+
+         //Remove padding bytes
+         length -= paddingLength + 1;
+         //Fix the length field of the TLS record
+         record->length = htons(length);
+      }
+      else
+#endif
+#if (TLS_CCM_CIPHER_SUPPORT == ENABLED || TLS_CCM_8_CIPHER_SUPPORT == ENABLED || \
+   TLS_GCM_CIPHER_SUPPORT == ENABLED)
+      //CCM or GCM AEAD cipher?
+      if(context->cipherMode == CIPHER_MODE_CCM ||
+         context->cipherMode == CIPHER_MODE_GCM)
+      {
+         uint8_t *ciphertext;
+         uint8_t *tag;
+         size_t nonceLength;
+         uint8_t nonce[12];
+         uint8_t a[13];
+
+         //Make sure the message length is acceptable
+         if(length < (context->recordIvLen + context->authTagLen))
+            return ERROR_DECODING_FAILED;
+
+         //Determine the total length of the nonce
+         nonceLength = context->fixedIvLen + context->recordIvLen;
+         //The salt is the implicit part of the nonce and is not sent in the packet
+         memcpy(nonce, context->readIv, context->fixedIvLen);
+         //The explicit part of the nonce is chosen by the sender
+         memcpy(nonce + context->fixedIvLen, record->data, context->recordIvLen);
+
+         //Calculate the length of the ciphertext
+         length -= context->recordIvLen + context->authTagLen;
+         //Fix the length field of the TLS record
+         record->length = htons(length);
+
+         //Additional data to be authenticated
+         memcpy(a, context->readSeqNum, sizeof(TlsSequenceNumber));
+         memcpy(a + sizeof(TlsSequenceNumber), record, sizeof(TlsRecord));
+
+         //Point to the ciphertext
+         ciphertext = record->data + context->recordIvLen;
+         //Point to the authentication tag
+         tag = ciphertext + length;
+
+#if (TLS_CCM_CIPHER_SUPPORT == ENABLED || TLS_CCM_8_CIPHER_SUPPORT == ENABLED)
+         //CCM AEAD cipher?
+         if(context->cipherMode == CIPHER_MODE_CCM)
+         {
+            //Authenticated decryption using CCM
+            error = ccmDecrypt(context->cipherAlgo, context->readCipherContext,
+               nonce, nonceLength, a, 13, ciphertext, ciphertext, length, tag, context->authTagLen);
+         }
+         else
+#endif
+#if (TLS_GCM_CIPHER_SUPPORT == ENABLED)
+         //GCM AEAD cipher?
+         if(context->cipherMode == CIPHER_MODE_GCM)
+         {
+            //Authenticated decryption using GCM
+            error = gcmDecrypt(context->readGcmContext, nonce, nonceLength,
+               a, 13, ciphertext, ciphertext, length, tag, context->authTagLen);
+         }
+         else
+#endif
+         //Invalid cipher mode?
+         {
+            //The specified cipher mode is not supported
+            error = ERROR_UNSUPPORTED_CIPHER_MODE;
+         }
+
+         //Wrong authentication tag?
+         if(error)
+            return ERROR_BAD_RECORD_MAC;
+
+         //Discard the explicit part of the nonce
+         memmove(record->data, record->data + context->recordIvLen, length);
+
+         //Increment sequence number
+         tlsIncSequenceNumber(context->readSeqNum);
+      }
+      else
+#endif
+#if (TLS_CHACHA20_POLY1305_SUPPORT == ENABLED)
+      //ChaCha20Poly1305 AEAD cipher?
+      if(context->cipherMode == CIPHER_MODE_CHACHA20_POLY1305)
+      {
+         size_t i;
+         uint8_t *tag;
+         uint8_t nonce[12];
+         uint8_t a[13];
+
+         //Make sure the message length is acceptable
+         if(length < context->authTagLen)
+            return ERROR_DECODING_FAILED;
+
+         //The 64-bit record sequence number is serialized as an 8-byte,
+         //big-endian value and padded on the left with four 0x00 bytes
+         memcpy(nonce + 4, context->readSeqNum, 8);
+         memset(nonce, 0, 4);
+
+         //The padded sequence number is XORed with the read IV to form
+         //the 96-bit nonce
+         for(i = 0; i < context->fixedIvLen; i++)
+            nonce[i] ^= context->readIv[i];
+
+         //Calculate the length of the ciphertext
+         length -= context->authTagLen;
+         //Fix the length field of the TLS record
+         record->length = htons(length);
+
+         //Additional data to be authenticated
+         memcpy(a, context->readSeqNum, sizeof(TlsSequenceNumber));
+         memcpy(a + sizeof(TlsSequenceNumber), record, sizeof(TlsRecord));
+
+         //Point to the authentication tag
+         tag = record->data + length;
+
+         //Authenticated decryption using ChaCha20Poly1305
+         error = chacha20Poly1305Decrypt(context->readEncKey, context->encKeyLen,
+            nonce, 12, a, 13, record->data, record->data, length, tag, context->authTagLen);
+         //Wrong authentication tag?
+         if(error)
+            return ERROR_BAD_RECORD_MAC;
+
+         //Increment sequence number
+         tlsIncSequenceNumber(context->readSeqNum);
+      }
+      else
+#endif
+      //Invalid cipher mode?
+      {
+         //The specified cipher mode is not supported
+         return ERROR_UNSUPPORTED_CIPHER_MODE;
+      }
+
+      //Debug message
+      TRACE_DEBUG("Decrypted record (%" PRIuSIZE " bytes):\r\n", length);
+      TRACE_DEBUG_ARRAY("  ", record, length + sizeof(TlsRecord));
+   }
+
+   //Check message authentication code if necessary
+   if(context->hashAlgo != NULL)
+   {
+      //Make sure the message length is acceptable
+      if(length < context->hashAlgo->digestSize)
+         return ERROR_DECODING_FAILED;
+
+      //Adjust the length of the message
+      length -= context->hashAlgo->digestSize;
+      //Fix the length field of the TLS record
+      record->length = htons(length);
+
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_MIN_VERSION <= SSL_VERSION_3_0)
+      //Check whether SSL 3.0 is currently used
+      if(context->version == SSL_VERSION_3_0)
+      {
+         //SSL 3.0 uses an older obsolete version of the HMAC construction
+         error = sslComputeMac(context, context->readMacKey, context->readSeqNum,
+            record, record->data, length, context->hmacContext.digest);
+         //Any error to report?
+         if(error)
+            return error;
+      }
+      else
+#endif
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_0 && TLS_MIN_VERSION <= TLS_VERSION_1_2)
+      //Check whether TLS 1.0, TLS 1.1 or TLS 1.2 is currently used
+      if(context->version >= TLS_VERSION_1_0)
+      {
+         //TLS uses a HMAC construction
+         hmacInit(&context->hmacContext, context->hashAlgo,
+            context->readMacKey, context->macKeyLen);
+
+         //Compute MAC over the sequence number and the record contents
+         hmacUpdate(&context->hmacContext, context->readSeqNum, sizeof(TlsSequenceNumber));
+         hmacUpdate(&context->hmacContext, record, sizeof(TlsRecord));
+         hmacUpdate(&context->hmacContext, record->data, length);
+         hmacFinal(&context->hmacContext, NULL);
+      }
+      else
+#endif
+      //The negotiated TLS version is not valid...
+      {
+         //Report an error
+         return ERROR_INVALID_VERSION;
+      }
+
+      //Debug message
+      TRACE_DEBUG("Read sequence number:\r\n");
+      TRACE_DEBUG_ARRAY("  ", context->readSeqNum, sizeof(TlsSequenceNumber));
+      TRACE_DEBUG("Computed MAC:\r\n");
+      TRACE_DEBUG_ARRAY("  ", context->hmacContext.digest, context->hashAlgo->digestSize);
+
+      //Check the message authentication code
+      if(memcmp(record->data + length, context->hmacContext.digest, context->hashAlgo->digestSize))
+         return ERROR_BAD_RECORD_MAC;
+
+      //Increment sequence number
+      tlsIncSequenceNumber(context->readSeqNum);
+   }
+
+   //Successful decryption
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Increment sequence number
+ * @param[in] seqNum Sequence number to increment
+ **/
+
+void tlsIncSequenceNumber(TlsSequenceNumber seqNum)
+{
+   int_t i;
+
+   //Sequence numbers are stored MSB first
+   for(i = 7; i >= 0; i--)
+   {
+      //Increment the current byte
+      seqNum[i]++;
+      //Propagate the carry if necessary
+      if(seqNum[i] != 0)
+         break;
+   }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls_record.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,54 @@
+/**
+ * @file tls_record.h
+ * @brief TLS record protocol
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TLS_RECORD_H
+#define _TLS_RECORD_H
+
+//Dependencies
+#include "tls.h"
+
+//TLS related functions
+error_t tlsWriteProtocolData(TlsContext *context,
+   const void *data, size_t length, TlsContentType contentType);
+
+error_t tlsReadProtocolData(TlsContext *context,
+   void **data, size_t *length, TlsContentType *contentType);
+
+error_t tlsWriteRecord(TlsContext *context, const uint8_t *data,
+   size_t length, TlsContentType contentType);
+
+error_t tlsReadRecord(TlsContext *context, uint8_t *data,
+   size_t size, size_t *length, TlsContentType *contentType);
+
+error_t tlsEncryptRecord(TlsContext *context, TlsRecord *record);
+error_t tlsDecryptRecord(TlsContext *context, TlsRecord *record);
+
+void tlsIncSequenceNumber(TlsSequenceNumber seqNum);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls_server.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1739 @@
+/**
+ * @file tls_server.c
+ * @brief Handshake message processing (TLS server)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The TLS protocol provides communications security over the Internet. The
+ * protocol allows client/server applications to communicate in a way that
+ * is designed to prevent eavesdropping, tampering, or message forgery
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TLS_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "tls.h"
+#include "tls_cipher_suites.h"
+#include "tls_server.h"
+#include "tls_server_misc.h"
+#include "tls_common.h"
+#include "tls_record.h"
+#include "tls_cache.h"
+#include "tls_misc.h"
+#include "x509.h"
+#include "pem.h"
+#include "date_time.h"
+#include "debug.h"
+
+//Check SSL library configuration
+#if (TLS_SUPPORT == ENABLED && TLS_SERVER_SUPPORT == ENABLED)
+
+
+/**
+ * @brief TLS server handshake
+ *
+ * TLS handshake protocol is responsible for the authentication
+ * and key exchange necessary to establish a secure session
+ *
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsServerHandshake(TlsContext *context)
+{
+   error_t error;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Wait for the handshake to complete
+   do
+   {
+      //Flush send buffer
+      if(context->state != TLS_STATE_CLOSED)
+         error = tlsWriteProtocolData(context, NULL, 0, TLS_TYPE_NONE);
+
+      //Check status code
+      if(!error)
+      {
+         //Check whether the handshake is complete
+         if(context->state == TLS_STATE_APPLICATION_DATA)
+         {
+            //At this is point, the handshake is complete and the server
+            //starts to exchange application-layer data
+            break;
+         }
+
+         //The TLS handshake is implemented as a state machine
+         //representing the current location in the protocol
+         switch(context->state)
+         {
+         //Default state?
+         case TLS_STATE_INIT:
+            //The client initiates the TLS handshake by sending a ClientHello
+            //message to the server
+            context->state = TLS_STATE_CLIENT_HELLO;
+            break;
+         //Sending ServerHello message?
+         case TLS_STATE_SERVER_HELLO:
+            //The server will send this message in response to a ClientHello
+            //message when it was able to find an acceptable set of algorithms
+            error = tlsSendServerHello(context);
+            break;
+         //Sending Certificate message?
+         case TLS_STATE_SERVER_CERTIFICATE:
+            //The server must send a Certificate message whenever the agreed-
+            //upon key exchange method uses certificates for authentication. This
+            //message will always immediately follow the ServerHello message
+            error = tlsSendCertificate(context);
+            break;
+         //Sending ServerKeyExchange message?
+         case TLS_STATE_SERVER_KEY_EXCHANGE:
+            //The ServerKeyExchange message is sent by the server only when the
+            //server Certificate message (if sent) does not contain enough data
+            //to allow the client to exchange a premaster secret
+            error = tlsSendServerKeyExchange(context);
+            break;
+         //Sending Certificate message?
+         case TLS_STATE_CERTIFICATE_REQUEST:
+            //A non-anonymous server can optionally request a certificate from the
+            //client, if appropriate for the selected cipher suite. This message,
+            //if sent, will immediately follow the ServerKeyExchange message
+            error = tlsSendCertificateRequest(context);
+            break;
+         //Sending ServerHelloDone message?
+         case TLS_STATE_SERVER_HELLO_DONE:
+            //The ServerHelloDone message is sent by the server to indicate the
+            //end of the ServerHello and associated messages
+            error = tlsSendServerHelloDone(context);
+            break;
+         //Sending ChangeCipherSpec message?
+         case TLS_STATE_SERVER_CHANGE_CIPHER_SPEC:
+            //The ChangeCipherSpec message is sent by the server and to notify the
+            //client that subsequent records will be protected under the newly
+            //negotiated CipherSpec and keys
+            error = tlsSendChangeCipherSpec(context);
+            break;
+         //Sending Finished message?
+         case TLS_STATE_SERVER_FINISHED:
+            //A Finished message is always sent immediately after a changeCipherSpec
+            //message to verify that the key exchange and authentication processes
+            //were successful
+            error = tlsSendFinished(context);
+            break;
+         //Waiting for a message from the client?
+         case TLS_STATE_CLIENT_HELLO:
+         case TLS_STATE_CLIENT_CERTIFICATE:
+         case TLS_STATE_CLIENT_KEY_EXCHANGE:
+         case TLS_STATE_CERTIFICATE_VERIFY:
+         case TLS_STATE_CLIENT_CHANGE_CIPHER_SPEC:
+         case TLS_STATE_CLIENT_FINISHED:
+            //Parse incoming handshake message
+            error = tlsParseClientMessage(context);
+            break;
+         //Sending Alert message?
+         case TLS_STATE_CLOSING:
+            //Mark the TLS connection as closed
+            context->state = TLS_STATE_CLOSED;
+            break;
+         //TLS connection closed?
+         case TLS_STATE_CLOSED:
+            //Debug message
+            TRACE_WARNING("TLS handshake failure!\r\n");
+            //Report an error
+            error = ERROR_HANDSHAKE_FAILED;
+            break;
+         //Invalid state?
+         default:
+            //Report an error
+            error = ERROR_UNEXPECTED_STATE;
+            break;
+         }
+      }
+
+      //Abort TLS handshake if an error was encountered
+   } while(!error);
+
+   //Successful TLS handshake?
+   if(!error)
+   {
+      //Save current session in the session cache for further reuse
+      tlsSaveToCache(context);
+   }
+   else
+   {
+      //Send an alert message to the client, if applicable
+      tlsProcessError(context, error);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse incoming handshake message
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsParseClientMessage(TlsContext *context)
+{
+   error_t error;
+   size_t length;
+   void *message;
+   TlsContentType contentType;
+
+   //A message can be fragmented across several records...
+   error = tlsReadProtocolData(context, &message, &length, &contentType);
+
+   //Check status code
+   if(!error)
+   {
+      //Handshake message received?
+      if(contentType == TLS_TYPE_HANDSHAKE)
+      {
+         //Check handshake message type
+         switch(((TlsHandshake *) message)->msgType)
+         {
+         //ClientHello message received?
+         case TLS_TYPE_CLIENT_HELLO:
+            //When a client first connects to a server, it is required to send
+            //the ClientHello as its first message
+            error = tlsParseClientHello(context, message, length);
+            break;
+         //Certificate message received?
+         case TLS_TYPE_CERTIFICATE:
+            //This is the first message the client can send after receiving a
+            //ServerHelloDone message. This message is only sent if the server
+            //requests a certificate
+            error = tlsParseCertificate(context, message, length);
+            break;
+         //ClientKeyExchange message received?
+         case TLS_TYPE_CLIENT_KEY_EXCHANGE:
+            //This message is always sent by the client. It must immediately
+            //follow the client certificate message, if it is sent. Otherwise,
+            //it must be the first message sent by the client after it receives
+            //the ServerHelloDone message
+            error = tlsParseClientKeyExchange(context, message, length);
+            break;
+         //CertificateVerify message received?
+         case TLS_TYPE_CERTIFICATE_VERIFY:
+            //This message is used to provide explicit verification of a client
+            //certificate. This message is only sent following a client certificate
+            //that has signing capability. When sent, it must immediately follow
+            //the clientKeyExchange message
+            error = tlsParseCertificateVerify(context, message, length);
+            break;
+         //Finished message received?
+         case TLS_TYPE_FINISHED:
+            //A Finished message is always sent immediately after a changeCipherSpec
+            //message to verify that the key exchange and authentication processes
+            //were successful
+            error = tlsParseFinished(context, message, length);
+            break;
+         //Invalid handshake message received?
+         default:
+            //Report an error
+            error = ERROR_UNEXPECTED_MESSAGE;
+            break;
+         }
+      }
+      //ChangeCipherSpec message received?
+      else if(contentType == TLS_TYPE_CHANGE_CIPHER_SPEC)
+      {
+         //The ChangeCipherSpec message is sent by the client and to notify the
+         //server that subsequent records will be protected under the newly
+         //negotiated CipherSpec and keys
+         error = tlsParseChangeCipherSpec(context, message, length);
+      }
+      //Alert message received?
+      else if(contentType == TLS_TYPE_ALERT)
+      {
+         //Parse Alert message
+         error = tlsParseAlert(context, message, length);
+      }
+      //Application data received?
+      else
+      {
+         //The client cannot transmit application data
+         //before the handshake is completed
+         error = ERROR_UNEXPECTED_MESSAGE;
+      }
+
+      //Advance data pointer
+      context->rxBufferPos += length;
+      //Number of bytes still pending in the receive buffer
+      context->rxBufferLen -= length;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send ServerHello message
+ *
+ * The server will send this message in response to a ClientHello
+ * message when it was able to find an acceptable set of algorithms.
+ * If it cannot find such a match, it will respond with a handshake
+ * failure alert
+ *
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsSendServerHello(TlsContext *context)
+{
+   error_t error;
+   size_t length;
+   TlsServerHello *message;
+
+   //Point to the buffer where to format the message
+   message = (TlsServerHello *) context->txBuffer;
+
+   //Generate the server random value using a cryptographically-safe
+   //pseudorandom number generator
+   error = tlsGenerateRandomValue(context, &context->serverRandom);
+
+   //Check status code
+   if(!error)
+   {
+      //Format ServerHello message
+      error = tlsFormatServerHello(context, message, &length);
+   }
+
+   //Check status code
+   if(!error)
+   {
+      //Debug message
+      TRACE_INFO("Sending ServerHello message (%" PRIuSIZE " bytes)...\r\n", length);
+      TRACE_DEBUG_ARRAY("  ", message, length);
+
+      //Send handshake message
+      error = tlsWriteProtocolData(context, message, length, TLS_TYPE_HANDSHAKE);
+   }
+
+   //Check status code
+   if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
+   {
+#if (TLS_SESSION_RESUME_SUPPORT == ENABLED)
+      //Use abbreviated handshake?
+      if(context->resume)
+      {
+         //Derive session keys from the master secret
+         error = tlsGenerateKeys(context);
+
+         //Key material successfully generated?
+         if(!error)
+         {
+            //At this point, both client and server must send ChangeCipherSpec
+            //messages and proceed directly to Finished messages
+            context->state = TLS_STATE_SERVER_CHANGE_CIPHER_SPEC;
+         }
+      }
+      else
+#endif
+      {
+         //Perform a full handshake
+         context->state = TLS_STATE_SERVER_CERTIFICATE;
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send ServerKeyExchange message
+ *
+ * The ServerKeyExchange message is sent by the server only when the
+ * server Certificate message does not contain enough data to allow
+ * the client to exchange a premaster secret
+ *
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsSendServerKeyExchange(TlsContext *context)
+{
+   error_t error;
+   size_t length;
+   TlsServerKeyExchange *message;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Point to the buffer where to format the message
+   message = (TlsServerKeyExchange *) context->txBuffer;
+   //Initialize length
+   length = 0;
+
+   //The ServerKeyExchange message is sent by the server only when the server
+   //Certificate message (if sent) does not contain enough data to allow the
+   //client to exchange a premaster secret
+   if(context->keyExchMethod == TLS_KEY_EXCH_DH_ANON ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDH_ANON ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
+   {
+      //Format ServerKeyExchange message
+      error = tlsFormatServerKeyExchange(context, message, &length);
+   }
+   else if(context->keyExchMethod == TLS_KEY_EXCH_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK)
+   {
+#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
+   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+      //If no PSK identity hint is provided by the server, the
+      //ServerKeyExchange message is omitted...
+      if(context->pskIdentityHint != NULL)
+      {
+         //Format ServerKeyExchange message
+         error = tlsFormatServerKeyExchange(context, message, &length);
+      }
+#endif
+   }
+
+   //Check status code
+   if(!error)
+   {
+      //Any message to send?
+      if(length > 0)
+      {
+         //Debug message
+         TRACE_INFO("Sending ServerKeyExchange message (%" PRIuSIZE " bytes)...\r\n", length);
+         TRACE_DEBUG_ARRAY("  ", message, length);
+
+         //Send handshake message
+         error = tlsWriteProtocolData(context, message, length, TLS_TYPE_HANDSHAKE);
+      }
+   }
+
+   //Check status code
+   if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
+   {
+      //Prepare to send a CertificateRequest message...
+      context->state = TLS_STATE_CERTIFICATE_REQUEST;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send CertificateRequest message
+ *
+ * A server can optionally request a certificate from the client, if
+ * appropriate for the selected cipher suite. This message will
+ * immediately follow the ServerKeyExchange message
+ *
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsSendCertificateRequest(TlsContext *context)
+{
+   error_t error;
+   size_t length;
+   TlsCertificateRequest *message;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+#if (TLS_RSA_SIGN_SUPPORT == ENABLED || TLS_DSA_SIGN_SUPPORT == ENABLED || TLS_ECDSA_SIGN_SUPPORT == ENABLED)
+   //A server can optionally request a certificate from the client
+   if(context->clientAuthMode != TLS_CLIENT_AUTH_NONE)
+   {
+      //Non-anonymous key exchange?
+      if(context->keyExchMethod == TLS_KEY_EXCH_RSA ||
+         context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
+         context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS ||
+         context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
+         context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA ||
+         context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK)
+      {
+         //Point to the buffer where to format the message
+         message = (TlsCertificateRequest *) context->txBuffer;
+
+         //Format CertificateRequest message
+         error = tlsFormatCertificateRequest(context, message, &length);
+
+         //Check status code
+         if(!error)
+         {
+            //Debug message
+            TRACE_INFO("Sending CertificateRequest message (%" PRIuSIZE " bytes)...\r\n", length);
+            TRACE_DEBUG_ARRAY("  ", message, length);
+
+            //Send handshake message
+            error = tlsWriteProtocolData(context, message, length, TLS_TYPE_HANDSHAKE);
+         }
+      }
+   }
+#endif
+
+   //Check status code
+   if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
+   {
+      //Prepare to send a ServerHelloDone message...
+      context->state = TLS_STATE_SERVER_HELLO_DONE;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send ServerHelloDone message
+ *
+ * The ServerHelloDone message is sent by the server to indicate the
+ * end of the ServerHello and associated messages. After sending this
+ * message, the server will wait for a client response
+ *
+ * @param[in] context Pointer to the TLS context
+ * @return Error code
+ **/
+
+error_t tlsSendServerHelloDone(TlsContext *context)
+{
+   error_t error;
+   size_t length;
+   TlsServerHelloDone *message;
+
+   //Point to the buffer where to format the message
+   message = (TlsServerHelloDone *) context->txBuffer;
+
+   //Format ServerHelloDone message
+   error = tlsFormatServerHelloDone(context, message, &length);
+
+   //Check status code
+   if(!error)
+   {
+      //Debug message
+      TRACE_INFO("Sending ServerHelloDone message (%" PRIuSIZE " bytes)...\r\n", length);
+      TRACE_DEBUG_ARRAY("  ", message, length);
+
+      //Send handshake message
+      error = tlsWriteProtocolData(context, message, length, TLS_TYPE_HANDSHAKE);
+   }
+
+   //Check status code
+   if(error == NO_ERROR || error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
+   {
+      //The client must send a Certificate message if the server requests it
+      if(context->clientAuthMode != TLS_CLIENT_AUTH_NONE)
+         context->state = TLS_STATE_CLIENT_CERTIFICATE;
+      else
+         context->state = TLS_STATE_CLIENT_KEY_EXCHANGE;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Format ServerHello message
+ * @param[in] context Pointer to the TLS context
+ * @param[out] message Buffer where to format the ServerHello message
+ * @param[out] length Length of the resulting ServerHello message
+ * @return Error code
+ **/
+
+error_t tlsFormatServerHello(TlsContext *context,
+   TlsServerHello *message, size_t *length)
+{
+   uint8_t *p;
+   TlsExtensions *extensionList;
+
+   //Handshake message type
+   message->msgType = TLS_TYPE_SERVER_HELLO;
+
+   //This field contains the lower of the version suggested by the client
+   //in the ClientHello and the highest supported by the server
+   message->serverVersion = htons(context->version);
+
+   //Server random value
+   message->random = context->serverRandom;
+
+   //Point to the session ID
+   p = message->sessionId;
+   //Total length of the message
+   *length = sizeof(TlsServerHello);
+
+#if (TLS_SESSION_RESUME_SUPPORT == ENABLED)
+   //The SessionID uniquely identifies the current session
+   message->sessionIdLength = (uint8_t)context->sessionIdLen;
+   memcpy(message->sessionId, context->sessionId, context->sessionIdLen);
+#else
+   //The server may return an empty session ID to indicate that the session
+   //will not be cached and therefore cannot be resumed
+   message->sessionIdLength = 0;
+#endif
+
+   //Debug message
+   TRACE_INFO("Session ID (%" PRIu8 " bytes):\r\n", message->sessionIdLength);
+   TRACE_INFO_ARRAY("  ", message->sessionId, message->sessionIdLength);
+
+   //Advance data pointer
+   p += message->sessionIdLength;
+   //Adjust the length of the message
+   *length += message->sessionIdLength;
+
+   //The single cipher suite selected by the server
+   STORE16BE(context->cipherSuite, p);
+   //Advance data pointer
+   p += sizeof(TlsCipherSuite);
+   //Adjust the length of the message
+   *length += sizeof(TlsCipherSuite);
+
+   //The single compression algorithm selected by the server
+   *p = context->compressionMethod;
+   //Advance data pointer
+   p += sizeof(TlsCompressionMethod);
+   //Adjust the length of the message
+   *length += sizeof(TlsCompressionMethod);
+
+   //Only extensions offered by the client can appear in the server's list
+   extensionList = (TlsExtensions *) p;
+   //Total length of the extension list
+   extensionList->length = 0;
+
+   //Point to the first extension of the list
+   p += sizeof(TlsExtensions);
+
+#if (TLS_ECDH_ANON_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED || \
+   TLS_ECDHE_ECDSA_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   //A server that selects an ECC cipher suite in response to a ClientHello
+   //message including an EcPointFormats extension appends this extension
+   //to its ServerHello message
+   if(tlsIsEccCipherSuite(context->cipherSuite) && context->ecPointFormatExtFound)
+   {
+      uint_t n;
+      TlsExtension *extension;
+      TlsEcPointFormatList *ecPointFormatList;
+
+      //Add the EcPointFormats extension
+      extension = (TlsExtension *) p;
+      //Type of the extension
+      extension->type = HTONS(TLS_EXT_EC_POINT_FORMATS);
+
+      //Point to the list of supported EC point formats
+      ecPointFormatList = (TlsEcPointFormatList *) extension->value;
+      //Items in the list are ordered according to server's preferences
+      n = 0;
+
+      //The server can parse only the uncompressed point format...
+      ecPointFormatList->value[n++] = TLS_EC_POINT_FORMAT_UNCOMPRESSED;
+      //Fix the length of the list
+      ecPointFormatList->length = n;
+
+      //Consider the 2-byte length field that precedes the list
+      n += sizeof(TlsEcPointFormatList);
+      //Fix the length of the extension
+      extension->length = htons(n);
+
+      //Compute the length, in bytes, of the EcPointFormats extension
+      n += sizeof(TlsExtension);
+      //Fix the length of the extension list
+      extensionList->length += n;
+
+      //Point to the next field
+      p += n;
+      //Total length of the message
+      *length += n;
+   }
+#endif
+
+   //Check whether the extension list is empty
+   if(extensionList->length > 0)
+   {
+      //Convert the length of the extension list to network byte order
+      extensionList->length = htons(extensionList->length);
+      //Total length of the message
+      *length += sizeof(TlsExtensions);
+   }
+
+   //Fix the length field
+   STORE24BE(*length - sizeof(TlsHandshake), message->length);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format ServerKeyExchange message
+ * @param[in] context Pointer to the TLS context
+ * @param[out] message Buffer where to format the ServerKeyExchange message
+ * @param[out] length Length of the resulting ServerKeyExchange message
+ * @return Error code
+ **/
+
+error_t tlsFormatServerKeyExchange(TlsContext *context,
+   TlsServerKeyExchange *message, size_t *length)
+{
+   error_t error;
+   size_t n;
+   size_t paramsLen;
+   uint8_t *p;
+   uint8_t *params;
+
+   //Handshake message type
+   message->msgType = TLS_TYPE_SERVER_KEY_EXCHANGE;
+
+   //Point to the body of the handshake message
+   p = message->data;
+   //Length of the handshake message
+   *length = 0;
+
+   //PSK key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
+   {
+      //To help the client in selecting which identity to use, the server
+      //can provide a PSK identity hint in the ServerKeyExchange message
+      error = tlsFormatPskIdentityHint(context, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Advance data pointer
+      p += n;
+      //Adjust the length of the message
+      *length += n;
+   }
+
+   //Diffie-Hellman or ECDH key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_DH_ANON ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDH_ANON ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
+   {
+      //Point to the server's key exchange parameters
+      params = p;
+
+      //Format server's key exchange parameters
+      error = tlsFormatServerKeyParams(context, p, &paramsLen);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Advance data pointer
+      p += paramsLen;
+      //Adjust the length of the message
+      *length += paramsLen;
+   }
+
+   //For non-anonymous Diffie-Hellman and ECDH key exchanges, a signature
+   //over the server's key exchange parameters shall be generated
+   if(context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA)
+   {
+      //Sign server's key exchange parameters
+      error = tlsGenerateServerKeySignature(context, p, params, paramsLen, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Advance data pointer
+      p += n;
+      //Adjust the length of the message
+      *length += n;
+   }
+
+   //Fix the length field
+   STORE24BE(*length, message->length);
+   //Length of the complete handshake message
+   *length += sizeof(TlsHandshake);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format CertificateRequest message
+ * @param[in] context Pointer to the TLS context
+ * @param[out] message Buffer where to format the CertificateRequest message
+ * @param[out] length Length of the resulting CertificateRequest message
+ * @return Error code
+ **/
+
+error_t tlsFormatCertificateRequest(TlsContext *context,
+   TlsCertificateRequest *message, size_t *length)
+{
+   error_t error;
+   size_t n;
+   uint8_t *p;
+   const char_t *pemCert;
+   size_t pemCertLength;
+   uint8_t *derCert;
+   size_t derCertSize;
+   size_t derCertLength;
+   X509CertificateInfo *certInfo;
+   TlsCertAuthorities *certAuthorities;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Handshake message type
+   message->msgType = TLS_TYPE_CERTIFICATE_REQUEST;
+
+   //Enumerate the types of certificate types that the client may offer
+   n = 0;
+
+#if (TLS_RSA_SIGN_SUPPORT == ENABLED)
+   //Accept certificates that contain a RSA public key
+   message->certificateTypes[n++] = TLS_CERT_RSA_SIGN;
+#endif
+#if (TLS_DSA_SIGN_SUPPORT == ENABLED)
+   //Accept certificates that contain a DSA public key
+   message->certificateTypes[n++] = TLS_CERT_DSS_SIGN;
+#endif
+#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED)
+   //Accept certificates that contain an ECDSA public key
+   message->certificateTypes[n++] = TLS_CERT_ECDSA_SIGN;
+#endif
+
+   //Set the length of the list
+   message->certificateTypesLength = (uint8_t) n;
+   //Total length of the message
+   *length = sizeof(TlsCertificateRequest) + n;
+
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_MIN_VERSION <= TLS_VERSION_1_2)
+   //Check whether TLS 1.2 is currently used
+   if(context->version == TLS_VERSION_1_2)
+   {
+      TlsSignHashAlgos *supportedSignAlgos;
+
+      //Point to the list of the hash/signature algorithm pairs that
+      //the server is able to verify
+      supportedSignAlgos = PTR_OFFSET(message, *length);
+
+      //Enumerate the hash/signature algorithm pairs in descending
+      //order of preference
+      n = 0;
+
+#if (TLS_RSA_SIGN_SUPPORT == ENABLED)
+      //SHA-1 with RSA is always supported
+      supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_RSA;
+      supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA1;
+
+      //The hash algorithm used for PRF operations can also be used for signing
+      if(context->prfHashAlgo == SHA256_HASH_ALGO)
+      {
+         //SHA-256 with RSA is supported
+         supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_RSA;
+         supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA256;
+      }
+#if (TLS_SHA384_SUPPORT == ENABLED)
+      else if(context->prfHashAlgo == SHA384_HASH_ALGO)
+      {
+         //SHA-384 with RSA is supported
+         supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_RSA;
+         supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA384;
+      }
+#endif
+#endif
+#if (TLS_DSA_SIGN_SUPPORT == ENABLED)
+      //DSA with SHA-1 is always supported
+      supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_DSA;
+      supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA1;
+
+      //The hash algorithm used for PRF operations can also be used for signing
+      if(context->prfHashAlgo == SHA256_HASH_ALGO)
+      {
+         //DSA with SHA-256 is supported
+         supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_DSA;
+         supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA256;
+      }
+#endif
+#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED)
+      //ECDSA with SHA-1 is always supported
+      supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_ECDSA;
+      supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA1;
+
+      //The hash algorithm used for PRF operations can also be used for signing
+      if(context->prfHashAlgo == SHA256_HASH_ALGO)
+      {
+         //ECDSA with SHA-256 is supported
+         supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_ECDSA;
+         supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA256;
+      }
+#if (TLS_SHA384_SUPPORT == ENABLED)
+      else if(context->prfHashAlgo == SHA384_HASH_ALGO)
+      {
+         //ECDSA with SHA-384 is supported
+         supportedSignAlgos->value[n].signature = TLS_SIGN_ALGO_ECDSA;
+         supportedSignAlgos->value[n++].hash = TLS_HASH_ALGO_SHA384;
+      }
+#endif
+#endif
+      //Fix the length of the list
+      supportedSignAlgos->length = htons(n * sizeof(TlsSignHashAlgo));
+      //Total length of the message
+      *length += sizeof(TlsSignHashAlgos) + n * sizeof(TlsSignHashAlgo);
+   }
+#endif
+
+   //Point to the list of the distinguished names of acceptable
+   //certificate authorities
+   certAuthorities = PTR_OFFSET(message, *length);
+   //Total length of the message
+   *length += sizeof(TlsCertAuthorities);
+
+   //Point to the first certificate authority
+   p = certAuthorities->value;
+   //Length of the list in bytes
+   n = 0;
+
+   //Point to the first trusted CA certificate
+   pemCert = context->trustedCaList;
+   //Get the total length, in bytes, of the trusted CA list
+   pemCertLength = context->trustedCaListLen;
+
+   //DER encoded certificate
+   derCert = NULL;
+   derCertSize = 0;
+   derCertLength = 0;
+
+   //Allocate a memory buffer to store X.509 certificate info
+   certInfo = tlsAllocMem(sizeof(X509CertificateInfo));
+
+   //Successful memory allocation?
+   if(certInfo != NULL)
+   {
+      //Loop through the list of trusted CA certificates
+      while(pemCertLength > 0)
+      {
+         //Decode PEM certificate
+         error = pemReadCertificate(&pemCert, &pemCertLength,
+            &derCert, &derCertSize, &derCertLength);
+
+         //Any error to report?
+         if(error)
+         {
+            //End of file detected
+            error = NO_ERROR;
+            break;
+         }
+
+         //Parse X.509 certificate
+         error = x509ParseCertificate(derCert, derCertLength, certInfo);
+         //Failed to parse the X.509 certificate?
+         if(error)
+            break;
+
+         //Total length of the message
+         *length += certInfo->subject.rawDataLen + 2;
+
+         //Prevent the buffer from overflowing
+         if(*length > context->txRecordMaxLen)
+         {
+            //Report an error
+            error = ERROR_MESSAGE_TOO_LONG;
+            break;
+         }
+
+         //Each distinguished name is preceded by a 2-byte length field
+         STORE16BE(certInfo->subject.rawDataLen, p);
+         //The distinguished name shall be DER encoded
+         memcpy(p + 2, certInfo->subject.rawData, certInfo->subject.rawDataLen);
+
+         //Advance data pointer
+         p += certInfo->subject.rawDataLen + 2;
+         //Adjust the length of the list
+         n += certInfo->subject.rawDataLen + 2;
+      }
+
+      //Free previously allocated memory
+      tlsFreeMem(derCert);
+      tlsFreeMem(certInfo);
+
+      //Fix the length of the list
+      certAuthorities->length = htons(n);
+
+      //Fix the length field
+      STORE24BE(*length - sizeof(TlsHandshake), message->length);
+   }
+   else
+   {
+      //Report an error
+      error = ERROR_OUT_OF_MEMORY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Format ServerHelloDone message
+ * @param[in] context Pointer to the TLS context
+ * @param[out] message Buffer where to format the ServerHelloDone message
+ * @param[out] length Length of the resulting ServerHelloDone message
+ * @return Error code
+ **/
+
+error_t tlsFormatServerHelloDone(TlsContext *context,
+   TlsServerHelloDone *message, size_t *length)
+{
+   //Handshake message type
+   message->msgType = TLS_TYPE_SERVER_HELLO_DONE;
+
+   //The ServerHelloDone message does not contain any data
+   STORE24BE(0, message->length);
+
+   //Length of the complete handshake message
+   *length = sizeof(TlsHandshake);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse ClientHello message
+ *
+ * When a client first connects to a server, it is required to send
+ * the ClientHello as its first message. The client can also send a
+ * ClientHello in response to a HelloRequest or on its own initiative
+ * in order to renegotiate the security parameters in an existing
+ * connection
+ *
+ * @param[in] context Pointer to the TLS context
+ * @param[in] message Incoming ClientHello message to parse
+ * @param[in] length Message length
+ * @return Error code
+ **/
+
+error_t tlsParseClientHello(TlsContext *context, const TlsClientHello *message, size_t length)
+{
+   error_t error;
+   size_t i;
+   size_t j;
+   size_t k;
+   size_t n;
+   bool_t acceptable;
+   uint8_t certType;
+   const uint8_t *p;
+   const TlsCipherSuites *cipherSuites;
+   const TlsCompressionMethods *compressionMethods;
+   const TlsExtension *extension;
+   const TlsSignHashAlgos *supportedSignAlgos;
+   const TlsEllipticCurveList *curveList;
+
+   //Debug message
+   TRACE_INFO("ClientHello message received (%" PRIuSIZE " bytes)...\r\n", length);
+   TRACE_DEBUG_ARRAY("  ", message, length);
+
+   //Check the length of the ClientHello message
+   if(length < sizeof(TlsClientHello))
+      return ERROR_DECODING_FAILED;
+
+   //Check current state
+   if(context->state != TLS_STATE_CLIENT_HELLO)
+      return ERROR_UNEXPECTED_MESSAGE;
+
+   //Point to the session ID
+   p = (uint8_t *) message + sizeof(TlsClientHello);
+   //Remaining bytes to process
+   n = length - sizeof(TlsClientHello);
+
+   //Check the length of the session ID
+   if(message->sessionIdLength > n)
+      return ERROR_DECODING_FAILED;
+   if(message->sessionIdLength > 32)
+      return ERROR_ILLEGAL_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Session ID (%" PRIu8 " bytes):\r\n", message->sessionIdLength);
+   TRACE_INFO_ARRAY("  ", message->sessionId, message->sessionIdLength);
+
+   //Point to the next field
+   p += message->sessionIdLength;
+   //Remaining bytes to process
+   n -= message->sessionIdLength;
+
+   //Malformed ClientHello message?
+   if(n < sizeof(TlsCipherSuites))
+      return ERROR_DECODING_FAILED;
+
+   //List of cryptographic algorithms supported by the client
+   cipherSuites = (TlsCipherSuites *) p;
+   //Remaining bytes to process
+   n -= sizeof(TlsCipherSuites);
+
+   //Check the length of the list
+   if(ntohs(cipherSuites->length) < 2)
+      return ERROR_ILLEGAL_PARAMETER;
+   if(ntohs(cipherSuites->length) > n)
+      return ERROR_DECODING_FAILED;
+
+   //Get the number of cipher suite identifiers present in the list
+   k = ntohs(cipherSuites->length) / 2;
+
+   //Debug message
+   TRACE_DEBUG("Cipher suites:\r\n");
+
+   //Dump the list of cipher suites
+   for(i = 0; i < k; i++)
+   {
+      //Debug message
+      TRACE_DEBUG("  0x%04" PRIX16 " (%s)\r\n", ntohs(cipherSuites->value[i]),
+         tlsGetCipherSuiteName(ntohs(cipherSuites->value[i])));
+   }
+
+   //Point to the next field
+   p += sizeof(TlsCipherSuites) + ntohs(cipherSuites->length);
+   //Remaining bytes to process
+   n -= ntohs(cipherSuites->length);
+
+   //Malformed ClientHello message?
+   if(n < sizeof(TlsCompressionMethods))
+      return ERROR_DECODING_FAILED;
+
+   //List of compression algorithms supported by the client
+   compressionMethods = (TlsCompressionMethods *) p;
+   //Remaining bytes to process
+   n -= sizeof(TlsCompressionMethods);
+
+   //Check the length of the list
+   if(compressionMethods->length < 1)
+      return ERROR_ILLEGAL_PARAMETER;
+   if(compressionMethods->length > n)
+      return ERROR_DECODING_FAILED;
+
+   //Point to the next field
+   p += sizeof(TlsCompressionMethods) + compressionMethods->length;
+   //Remaining bytes to process
+   n -= compressionMethods->length;
+
+   //Parse the list of extensions offered by the client
+   extension = tlsGetExtension(p, n, TLS_EXT_ELLIPTIC_CURVES);
+
+   //The EllipticCurves extension was found?
+   if(extension)
+   {
+      //This extension allows a client to enumerate the elliptic curves it supports
+      curveList = (TlsEllipticCurveList *) extension->value;
+
+      //Check the length of the list
+      if(ntohs(extension->length) < sizeof(TlsEllipticCurveList))
+         return ERROR_DECODING_FAILED;
+      if(ntohs(extension->length) < (sizeof(TlsEllipticCurveList) + ntohs(curveList->length)))
+         return ERROR_DECODING_FAILED;
+   }
+   else
+   {
+      //The client may omit the SignatureAlgorithms extension
+      curveList = NULL;
+   }
+
+   //Parse the list of extensions offered by the client
+   extension = tlsGetExtension(p, n, TLS_EXT_EC_POINT_FORMATS);
+
+   //The EcPointFormats extension was found?
+   if(extension)
+      context->ecPointFormatExtFound = TRUE;
+   else
+      context->ecPointFormatExtFound = FALSE;
+
+   //Parse the list of extensions offered by the client
+   extension = tlsGetExtension(p, n, TLS_EXT_SIGNATURE_ALGORITHMS);
+
+   //The SignatureAlgorithms extension was found?
+   if(extension)
+   {
+      //Point to the list of supported hash/signature algorithm pairs
+      supportedSignAlgos = (TlsSignHashAlgos *) extension->value;
+
+      //Check the length of the list
+      if(ntohs(extension->length) < sizeof(TlsSignHashAlgos))
+         return ERROR_DECODING_FAILED;
+      if(ntohs(extension->length) < (sizeof(TlsSignHashAlgos) + ntohs(supportedSignAlgos->length)))
+         return ERROR_DECODING_FAILED;
+   }
+   else
+   {
+      //The client may omit the SignatureAlgorithms extension
+      supportedSignAlgos = NULL;
+   }
+
+   //Get the version the client wishes to use during this session
+   context->clientVersion = ntohs(message->clientVersion);
+
+   //If a TLS server receives a ClientHello containing a version number
+   //greater than the highest version supported by the server, it must
+   //reply according to the highest version supported by the server
+   error = tlsSetVersion(context, MIN(context->clientVersion, TLS_MAX_VERSION));
+   //The specified TLS version is not supported?
+   if(error)
+      return error;
+
+   //Save client random value
+   context->clientRandom = message->random;
+
+#if (TLS_SESSION_RESUME_SUPPORT == ENABLED)
+   //Check whether session caching is supported
+   if(context->cache != NULL)
+   {
+      //If the session ID was non-empty, the server will look in
+      //its session cache for a match
+      TlsSession *session = tlsFindCache(context->cache,
+         message->sessionId, message->sessionIdLength);
+
+      //Check whether a matching entry has been found in the cache
+      if(session != NULL)
+      {
+         //Restore session parameters
+         tlsRestoreSession(context, session);
+
+         //Select the relevant cipher suite
+         error = tlsSetCipherSuite(context, session->cipherSuite);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //Perform abbreviated handshake
+         context->resume = TRUE;
+      }
+      else
+      {
+         //Generate a new random ID
+         error = context->prngAlgo->read(context->prngContext, context->sessionId, 32);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //Session ID is limited to 32 bytes
+         context->sessionIdLen = 32;
+         //Perform a full handshake
+         context->resume = FALSE;
+      }
+   }
+   else
+#endif
+   {
+      //This session cannot be resumed
+      context->sessionIdLen = 0;
+      //Perform a full handshake
+      context->resume = FALSE;
+   }
+
+   //Full handshake?
+   if(!context->resume)
+   {
+      //Get the size of the cipher suite list
+      k = ntohs(cipherSuites->length) / 2;
+
+      //The cipher suite list contains the combinations of cryptographic algorithms
+      //supported by the client in order of the client's preference
+      for(i = 0; i < k; i++)
+      {
+         //Check whether the current cipher suite is supported
+         error = tlsSetCipherSuite(context, ntohs(cipherSuites->value[i]));
+
+         //Successful processing?
+         if(!error)
+         {
+            //ECC cipher suite?
+            if(context->keyExchMethod == TLS_KEY_EXCH_ECDH_ANON ||
+               context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
+               context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA ||
+               context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
+            {
+               //One of the proposed ECC cipher suites must be negotiated only
+               //if the server can successfully complete the handshake while
+               //using the curves and point formats supported by the client
+               error = tlsSelectNamedCurve(context, curveList);
+            }
+         }
+
+         //Successful processing?
+         if(!error)
+         {
+            //The server requires a valid certificate whenever the agreed-upon
+            //key exchange method uses certificates for authentication
+            if(context->keyExchMethod == TLS_KEY_EXCH_RSA ||
+               context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
+               context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
+               context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK)
+            {
+               //RSA, DHE_RSA, ECDHE_RSA and RAS_PSK key exchange methods
+               //require a RSA certificate
+               certType = TLS_CERT_RSA_SIGN;
+            }
+            else if(context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS)
+            {
+               //DHE_DSS key exchange method requires a DSA certificate
+               certType = TLS_CERT_DSS_SIGN;
+            }
+            else if(context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA)
+            {
+               //ECDHE_ECDSA key exchange method requires an ECDSA certificate
+               certType = TLS_CERT_ECDSA_SIGN;
+            }
+            else
+            {
+               //DH_anon and ECDH_anon key exchange methods do not require any certificate
+               certType = TLS_CERT_NONE;
+            }
+
+            //Check whether a certificate is required
+            if(certType != TLS_CERT_NONE)
+            {
+               //Do not accept the specified cipher suite unless a suitable
+               //certificate has been previously loaded by the user
+               error = ERROR_NO_CERTIFICATE;
+
+               //Loop through the list of available certificates
+               for(j = 0; j < context->numCerts; j++)
+               {
+                  //Check whether the current certificate is acceptable
+                  acceptable = tlsIsCertificateAcceptable(&context->certs[j],
+                     &certType, 1, supportedSignAlgos, curveList, NULL);
+
+                  //Is the certificate suitable for the selected cipher suite?
+                  if(acceptable)
+                  {
+                     //The hash algorithm to be used when generating signatures must be
+                     //one of those present in the SignatureAlgorithms extension
+                     error = tlsSelectSignHashAlgo(context,
+                        context->certs[j].signAlgo, supportedSignAlgos);
+
+                     //If all the requirements were met, the certificate can be
+                     //used in conjunction with the selected cipher suite
+                     if(!error)
+                     {
+                        context->cert = &context->certs[j];
+                        break;
+                     }
+                  }
+               }
+            }
+         }
+
+         //If the list contains cipher suites the server does not recognize,
+         //support, or wish to use, the server must ignore those cipher
+         //suites, and process the remaining ones as usual
+         if(!error)
+            break;
+      }
+
+      //If no acceptable choices are presented, return a handshake failure
+      //alert and close the connection
+      if(error)
+         return ERROR_HANDSHAKE_FAILED;
+
+      //The list of the compression methods supported by the client
+      //is sorted by client preference
+      for(i = 0; i < compressionMethods->length; i++)
+      {
+         //Check whether the algorithm to be used for data compression is supported
+         error = tlsSetCompressionMethod(context, compressionMethods->value[i]);
+
+         //If the compression method is not supported, process the remaining ones
+         if(!error)
+            break;
+      }
+
+      //If no compression algorithm is acceptable, return a handshake failure
+      //alert and close the connection
+      if(error)
+         return ERROR_HANDSHAKE_FAILED;
+   }
+
+   //Initialize handshake message hashing
+   error = tlsInitHandshakeHash(context);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Update the hash value with the incoming handshake message
+   tlsUpdateHandshakeHash(context, message, length);
+
+   //Prepare to send ServerHello message...
+   context->state = TLS_STATE_SERVER_HELLO;
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse ClientKeyExchange message
+ *
+ * This message is always sent by the client. It must immediately
+ * follow the client Certificate message, if it is sent. Otherwise,
+ * it must be the first message sent by the client after it receives
+ * the ServerHelloDone message
+ *
+ * @param[in] context Pointer to the TLS context
+ * @param[in] message Incoming ClientKeyExchange message to parse
+ * @param[in] length Message length
+ * @return Error code
+ **/
+
+error_t tlsParseClientKeyExchange(TlsContext *context, const TlsClientKeyExchange *message, size_t length)
+{
+   error_t error;
+   size_t n;
+   const uint8_t *p;
+
+   //Debug message
+   TRACE_INFO("ClientKeyExchange message received (%" PRIuSIZE " bytes)...\r\n", length);
+   TRACE_DEBUG_ARRAY("  ", message, length);
+
+   //Check the length of the ClientKeyExchange message
+   if(length < sizeof(TlsClientKeyExchange))
+      return ERROR_DECODING_FAILED;
+
+   //Check current state
+   if(context->state == TLS_STATE_CLIENT_CERTIFICATE)
+   {
+      //A an non-anonymous server can optionally request a certificate from the client
+      if(context->keyExchMethod == TLS_KEY_EXCH_RSA ||
+         context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
+         context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS ||
+         context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
+         context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA ||
+         context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK)
+      {
+         //If client authentication is required by the server for the handshake
+         //to continue, it may respond with a fatal handshake failure alert
+         if(context->clientAuthMode == TLS_CLIENT_AUTH_REQUIRED)
+            return ERROR_HANDSHAKE_FAILED;
+      }
+   }
+   else if(context->state != TLS_STATE_CLIENT_KEY_EXCHANGE)
+   {
+      //Send a fatal alert to the client
+      return ERROR_UNEXPECTED_MESSAGE;
+   }
+
+   //Update the hash value with the incoming handshake message
+   tlsUpdateHandshakeHash(context, message, length);
+
+   //Point to the body of the handshake message
+   p = message->data;
+   //Remaining bytes to process
+   length -= sizeof(TlsClientKeyExchange);
+
+   //PSK key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
+   {
+      //The PSK identity is sent in cleartext
+      error = tlsParsePskIdentity(context, p, length, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Point to the next field
+      p += n;
+      //Remaining bytes to process
+      length -= n;
+   }
+
+   //RSA, Diffie-Hellman or ECDH key exchange method?
+   if(context->keyExchMethod != TLS_KEY_EXCH_PSK)
+   {
+      //Parse client's key exchange parameters
+      error = tlsParseClientKeyParams(context, p, length, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Point to the next field
+      p += n;
+      //Remaining bytes to process
+      length -= n;
+   }
+
+   //If the amount of data in the message does not precisely match the format
+   //of the ClientKeyExchange message, then send a fatal alert
+   if(length != 0)
+      return ERROR_DECODING_FAILED;
+
+   //PSK key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
+   {
+      //Generate premaster secret
+      error = tlsGeneratePskPremasterSecret(context);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+
+   //Derive session keys from the premaster secret
+   error = tlsGenerateKeys(context);
+   //Unable to generate key material?
+   if(error)
+      return error;
+
+   //Update FSM state
+   if(context->peerCertType != TLS_CERT_NONE)
+      context->state = TLS_STATE_CERTIFICATE_VERIFY;
+   else
+      context->state = TLS_STATE_CLIENT_CHANGE_CIPHER_SPEC;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse CertificateVerify message
+ *
+ * The CertificateVerify message is used to provide explicit verification
+ * of a client certificate. This message is only sent following a client
+ * certificate that has signing capability
+ *
+ * @param[in] context Pointer to the TLS context
+ * @param[in] message Incoming CertificateVerify message to parse
+ * @param[in] length Message length
+ * @return Error code
+ **/
+
+error_t tlsParseCertificateVerify(TlsContext *context, const TlsCertificateVerify *message, size_t length)
+{
+   error_t error;
+   size_t n;
+
+   //Debug message
+   TRACE_INFO("CertificateVerify message received (%" PRIuSIZE " bytes)...\r\n", length);
+   TRACE_DEBUG_ARRAY("  ", message, length);
+
+   //Check the length of the CertificateVerify message
+   if(length < sizeof(TlsCertificateVerify))
+      return ERROR_DECODING_FAILED;
+
+   //Check current state
+   if(context->state != TLS_STATE_CERTIFICATE_VERIFY)
+      return ERROR_UNEXPECTED_MESSAGE;
+
+   //Remaining bytes to process
+   n = length - sizeof(TlsCertificateVerify);
+
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_MIN_VERSION <= TLS_VERSION_1_1)
+   //SSL 3.0, TLS 1.0 or TLS 1.1 currently selected?
+   if(context->version <= TLS_VERSION_1_1)
+   {
+      //Point to the digitally-signed element
+      TlsDigitalSignature *signature = (TlsDigitalSignature *) message->signature;
+
+      //Check the length of the digitally-signed element
+      if(n < sizeof(TlsDigitalSignature))
+         return ERROR_DECODING_FAILED;
+      if(n < (sizeof(TlsDigitalSignature) + ntohs(signature->length)))
+         return ERROR_DECODING_FAILED;
+
+#if (TLS_RSA_SIGN_SUPPORT == ENABLED)
+      //The client's certificate contains a valid RSA public key?
+      if(context->peerCertType == TLS_CERT_RSA_SIGN)
+      {
+         //Digest all the handshake messages starting at ClientHello (using MD5)
+         error = tlsFinalizeHandshakeHash(context, MD5_HASH_ALGO,
+            context->handshakeMd5Context, "", context->verifyData);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //Digest all the handshake messages starting at ClientHello (using SHA-1)
+         error = tlsFinalizeHandshakeHash(context, SHA1_HASH_ALGO,
+            context->handshakeSha1Context, "", context->verifyData + MD5_DIGEST_SIZE);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //Verify RSA signature using client's public key
+         error = tlsVerifyRsaSignature(&context->peerRsaPublicKey,
+            context->verifyData, signature->value, ntohs(signature->length));
+      }
+      else
+#endif
+#if (TLS_DSA_SIGN_SUPPORT == ENABLED)
+      //The client's certificate contains a valid DSA public key?
+      if(context->peerCertType == TLS_CERT_DSS_SIGN)
+      {
+         //Digest all the handshake messages starting at ClientHello
+         error = tlsFinalizeHandshakeHash(context, SHA1_HASH_ALGO,
+            context->handshakeSha1Context, "", context->verifyData);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //Verify DSA signature using client's public key
+         error = tlsVerifyDsaSignature(&context->peerDsaPublicKey, context->verifyData,
+            SHA1_DIGEST_SIZE, signature->value, ntohs(signature->length));
+      }
+      else
+#endif
+#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED)
+      //The client's certificate contains a valid ECDSA public key?
+      if(context->peerCertType == TLS_CERT_ECDSA_SIGN)
+      {
+         //Digest all the handshake messages starting at ClientHello
+         error = tlsFinalizeHandshakeHash(context, SHA1_HASH_ALGO,
+            context->handshakeSha1Context, "", context->verifyData);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //Verify ECDSA signature using client's public key
+         error = tlsVerifyEcdsaSignature(&context->peerEcParams, &context->peerEcPublicKey,
+            context->verifyData, SHA1_DIGEST_SIZE, signature->value, ntohs(signature->length));
+      }
+      else
+#endif
+      //Invalid signature algorithm?
+      {
+         //Report an error
+         error = ERROR_UNSUPPORTED_SIGNATURE_ALGO;
+      }
+   }
+   else
+#endif
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_MIN_VERSION <= TLS_VERSION_1_2)
+   //TLS 1.2 currently selected?
+   if(context->version == TLS_VERSION_1_2)
+   {
+      const HashAlgo *hashAlgo;
+
+      //Point to the digitally-signed element
+      TlsDigitalSignature2 *signature = (TlsDigitalSignature2 *) message->signature;
+
+      //Check the length of the digitally-signed element
+      if(n < sizeof(TlsDigitalSignature2))
+         return ERROR_DECODING_FAILED;
+      if(n < (sizeof(TlsDigitalSignature2) + ntohs(signature->length)))
+         return ERROR_DECODING_FAILED;
+
+      //Retrieve the hash algorithm used for signing
+      hashAlgo = tlsGetHashAlgo(signature->algorithm.hash);
+
+      //Digest all the handshake messages starting at ClientHello
+      if(hashAlgo == SHA1_HASH_ALGO)
+      {
+         //Use SHA-1 hash algorithm
+         error = tlsFinalizeHandshakeHash(context, SHA1_HASH_ALGO,
+            context->handshakeSha1Context, "", context->verifyData);
+      }
+      else if(hashAlgo == context->prfHashAlgo)
+      {
+         //Use PRF hash algorithm (SHA-256 or SHA-384)
+         error = tlsFinalizeHandshakeHash(context, hashAlgo,
+            context->handshakeHashContext, "", context->verifyData);
+      }
+      else
+      {
+         //The specified hash algorithm is not supported
+         error = ERROR_UNSUPPORTED_SIGNATURE_ALGO;
+      }
+
+      //Any error to report?
+      if(error)
+         return error;
+
+#if (TLS_RSA_SIGN_SUPPORT == ENABLED)
+      //The client's certificate contains a valid RSA public key?
+      if(signature->algorithm.signature == TLS_SIGN_ALGO_RSA)
+      {
+         //Use the signature verification algorithm defined in PKCS #1 v1.5
+         error = rsassaPkcs1v15Verify(&context->peerRsaPublicKey, hashAlgo,
+            context->verifyData, signature->value, ntohs(signature->length));
+      }
+      else
+#endif
+#if (TLS_DSA_SIGN_SUPPORT == ENABLED)
+      //The client's certificate contains a valid DSA public key?
+      if(signature->algorithm.signature == TLS_SIGN_ALGO_DSA)
+      {
+         //Verify DSA signature using client's public key
+         error = tlsVerifyDsaSignature(&context->peerDsaPublicKey, context->verifyData,
+            hashAlgo->digestSize, signature->value, ntohs(signature->length));
+      }
+      else
+#endif
+#if (TLS_ECDSA_SIGN_SUPPORT == ENABLED)
+      //The client's certificate contains a valid ECDSA public key?
+      if(signature->algorithm.signature == TLS_SIGN_ALGO_ECDSA)
+      {
+         //Verify ECDSA signature using client's public key
+         error = tlsVerifyEcdsaSignature(&context->peerEcParams, &context->peerEcPublicKey,
+            context->verifyData, hashAlgo->digestSize, signature->value, ntohs(signature->length));
+      }
+      else
+#endif
+      //Invalid signature algorithm?
+      {
+         //Report an error
+         error = ERROR_UNSUPPORTED_SIGNATURE_ALGO;
+      }
+   }
+   else
+#endif
+   {
+      //The negotiated TLS version is not valid
+      error = ERROR_INVALID_VERSION;
+   }
+
+   //Update the hash value with the incoming handshake message
+   tlsUpdateHandshakeHash(context, message, length);
+
+   //Prepare to receive a ChangeCipherSpec message...
+   context->state = TLS_STATE_CLIENT_CHANGE_CIPHER_SPEC;
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls_server.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,66 @@
+/**
+ * @file tls_server.h
+ * @brief Handshake message processing (TLS server)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TLS_SERVER_H
+#define _TLS_SERVER_H
+
+//Dependencies
+#include "tls.h"
+
+//TLS server specific functions
+error_t tlsServerHandshake(TlsContext *context);
+error_t tlsParseClientMessage(TlsContext *context);
+
+error_t tlsSendServerHello(TlsContext *context);
+error_t tlsSendServerKeyExchange(TlsContext *context);
+error_t tlsSendCertificateRequest(TlsContext *context);
+error_t tlsSendServerHelloDone(TlsContext *context);
+
+error_t tlsFormatServerHello(TlsContext *context,
+   TlsServerHello *message, size_t *length);
+
+error_t tlsFormatServerKeyExchange(TlsContext *context,
+   TlsServerKeyExchange *message, size_t *length);
+
+error_t tlsFormatCertificateRequest(TlsContext *context,
+   TlsCertificateRequest *message, size_t *length);
+
+error_t tlsFormatServerHelloDone(TlsContext *context,
+   TlsServerHelloDone *message, size_t *length);
+
+error_t tlsParseClientHello(TlsContext *context,
+   const TlsClientHello *message, size_t length);
+
+error_t tlsParseClientKeyExchange(TlsContext *context,
+   const TlsClientKeyExchange *message, size_t length);
+
+error_t tlsParseCertificateVerify(TlsContext *context,
+   const TlsCertificateVerify *message, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls_server_misc.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,950 @@
+/**
+ * @file tls_server_misc.c
+ * @brief Helper functions (TLS server)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TLS_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "tls.h"
+#include "tls_cipher_suites.h"
+#include "tls_server.h"
+#include "tls_server_misc.h"
+#include "tls_common.h"
+#include "tls_record.h"
+#include "tls_cache.h"
+#include "tls_misc.h"
+#include "pem.h"
+#include "debug.h"
+
+//Check SSL library configuration
+#if (TLS_SUPPORT == ENABLED && TLS_SERVER_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Format PSK identity hint
+ * @param[in] context Pointer to the TLS context
+ * @param[in] p Output stream where to write the PSK identity hint
+ * @param[out] written Total number of bytes that have been written
+ * @return Error code
+ **/
+
+error_t tlsFormatPskIdentityHint(TlsContext *context,
+   uint8_t *p, size_t *written)
+{
+   size_t n;
+   TlsPskIdentityHint *pskIdentityHint;
+
+   //Point to the PSK identity hint
+   pskIdentityHint = (TlsPskIdentityHint *) p;
+
+   //Initialize length field
+   n = 0;
+
+#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
+   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   //Any PSK identity hint defined?
+   if(context->pskIdentityHint != NULL)
+   {
+      //Determine the length of the PSK identity hint
+      n = strlen(context->pskIdentityHint);
+      //Copy PSK identity hint
+      memcpy(pskIdentityHint->value, context->pskIdentityHint, n);
+   }
+#endif
+
+   //The PSK identity hint is preceded by a 2-byte length field
+   pskIdentityHint->length = htons(n);
+
+   //Total number of bytes that have been written
+   *written = sizeof(TlsPskIdentityHint) + n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format server's key exchange parameters
+ * @param[in] context Pointer to the TLS context
+ * @param[in] p Output stream where to write the PSK identity hint
+ * @param[out] written Total number of bytes that have been written
+ * @return Error code
+ **/
+
+error_t tlsFormatServerKeyParams(TlsContext *context,
+    uint8_t *p, size_t *written)
+{
+   error_t error;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Total number of bytes that have been written
+   *written = 0;
+
+#if (TLS_DH_ANON_SUPPORT == ENABLED || TLS_DHE_RSA_SUPPORT == ENABLED || \
+   TLS_DHE_DSS_SUPPORT == ENABLED || TLS_DHE_PSK_SUPPORT == ENABLED)
+   //Diffie-Hellman key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_DH_ANON ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK)
+   {
+      size_t n;
+
+      //Generate an ephemeral key pair
+      error = dhGenerateKeyPair(&context->dhContext,
+         context->prngAlgo, context->prngContext);
+
+      //Check status code
+      if(!error)
+      {
+         //Debug message
+         TRACE_DEBUG("Diffie-Hellman parameters:\r\n");
+         TRACE_DEBUG("  Prime modulus:\r\n");
+         TRACE_DEBUG_MPI("    ", &context->dhContext.params.p);
+         TRACE_DEBUG("  Generator:\r\n");
+         TRACE_DEBUG_MPI("    ", &context->dhContext.params.g);
+         TRACE_DEBUG("  Server public value:\r\n");
+         TRACE_DEBUG_MPI("    ", &context->dhContext.ya);
+
+         //Encode the prime modulus to an opaque vector
+         error = tlsWriteMpi(&context->dhContext.params.p, p, &n);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Advance data pointer
+         p += n;
+         //Total number of bytes that have been written
+         *written += n;
+
+         //Encode the generator to an opaque vector
+         error = tlsWriteMpi(&context->dhContext.params.g, p, &n);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Advance data pointer
+         p += n;
+         //Total number of bytes that have been written
+         *written += n;
+
+         //Encode the server's public value to an opaque vector
+         error = tlsWriteMpi(&context->dhContext.ya, p, &n);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Advance data pointer
+         p += n;
+         //Adjust the length of the key exchange parameters
+         *written += n;
+      }
+   }
+   else
+#endif
+#if (TLS_ECDH_ANON_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED || \
+   TLS_ECDHE_ECDSA_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   //ECDH key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_ECDH_ANON ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
+   {
+      size_t n;
+      const EcCurveInfo *curveInfo;
+
+      //Retrieve the elliptic curve to be used
+      curveInfo = tlsGetCurveInfo(context->namedCurve);
+
+      //Make sure the elliptic curve is supported
+      if(curveInfo != NULL)
+      {
+         //Load EC domain parameters
+         error = ecLoadDomainParameters(&context->ecdhContext.params,
+            curveInfo);
+
+         //Check status code
+         if(!error)
+         {
+            //Generate an ephemeral key pair
+            error = ecdhGenerateKeyPair(&context->ecdhContext,
+               context->prngAlgo, context->prngContext);
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Debug message
+            TRACE_DEBUG("  Server public key X:\r\n");
+            TRACE_DEBUG_MPI("    ", &context->ecdhContext.qa.x);
+            TRACE_DEBUG("  Server public key Y:\r\n");
+            TRACE_DEBUG_MPI("    ", &context->ecdhContext.qa.y);
+
+            //Set the type of the elliptic curve domain parameters
+            *p = TLS_EC_CURVE_TYPE_NAMED_CURVE;
+
+            //Advance data pointer
+            p += sizeof(uint8_t);
+            //Total number of bytes that have been written
+            *written += sizeof(uint8_t);
+
+            //Write elliptic curve identifier
+            STORE16BE(context->namedCurve, p);
+
+            //Advance data pointer
+            p += sizeof(uint16_t);
+            //Total number of bytes that have been written
+            *written += sizeof(uint16_t);
+
+            //Write server's public key
+            error = tlsWriteEcPoint(&context->ecdhContext.params,
+               &context->ecdhContext.qa, p, &n);
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Advance data pointer
+            p +=n;
+            //Total number of bytes that have been written
+            *written += n;
+         }
+      }
+      else
+      {
+         //The specified elliptic curve is not supported
+         error = ERROR_FAILURE;
+      }
+   }
+   else
+#endif
+   //Any other exchange method?
+   {
+      //It is not legal to send the ServerKeyExchange message when a key
+      //exchange method other than DHE_DSS, DHE_RSA, DH_anon, ECDHE_RSA,
+      //ECDHE_ECDSA or ECDH_anon is selected
+      error = ERROR_FAILURE;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Generate signature over the server's key exchange parameters
+ * @param[in] context Pointer to the TLS context
+ * @param[in] p Output stream where to write the PSK identity hint
+ * @param[in] params Pointer to the server's key exchange parameters
+ * @param[in] paramsLen Length of the server's key exchange parameters
+ * @param[out] written Total number of bytes that have been written
+ * @return Error code
+ **/
+
+error_t tlsGenerateServerKeySignature(TlsContext *context,
+   uint8_t *p, const uint8_t *params, size_t paramsLen, size_t *written)
+{
+   error_t error;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Total number of bytes that have been written
+   *written = 0;
+
+#if (TLS_MAX_VERSION >= SSL_VERSION_3_0 && TLS_MIN_VERSION <= TLS_VERSION_1_1)
+   //SSL 3.0, TLS 1.0 or TLS 1.1 currently selected?
+   if(context->version <= TLS_VERSION_1_1)
+   {
+      TlsDigitalSignature *signature;
+
+      //Point to the digitally-signed element
+      signature = (TlsDigitalSignature *) p;
+
+#if (TLS_DHE_RSA_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED)
+      //Check whether DHE_RSA or ECDHE_RSA key exchange method is currently used
+      if(context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
+         context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA)
+      {
+         Md5Context *md5Context;
+         Sha1Context *sha1Context;
+         RsaPrivateKey privateKey;
+
+         //Initialize RSA private key
+         rsaInitPrivateKey(&privateKey);
+
+         //Allocate a memory buffer to hold the MD5 context
+         md5Context = tlsAllocMem(sizeof(Md5Context));
+
+         //Successful memory allocation?
+         if(md5Context != NULL)
+         {
+            //Compute MD5(ClientHello.random + ServerHello.random + ServerDhParams)
+            md5Init(md5Context);
+            md5Update(md5Context, context->random, 64);
+            md5Update(md5Context, params, paramsLen);
+            md5Final(md5Context, context->verifyData);
+
+            //Release previously allocated memory
+            tlsFreeMem(md5Context);
+         }
+         else
+         {
+            //Failed to allocate memory
+            error = ERROR_OUT_OF_MEMORY;
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Allocate a memory buffer to hold the SHA-1 context
+            sha1Context = tlsAllocMem(sizeof(Sha1Context));
+
+            //Successful memory allocation?
+            if(sha1Context != NULL)
+            {
+               //Compute SHA(ClientHello.random + ServerHello.random + ServerDhParams)
+               sha1Init(sha1Context);
+               sha1Update(sha1Context, context->random, 64);
+               sha1Update(sha1Context, params, paramsLen);
+               sha1Final(sha1Context, context->verifyData + MD5_DIGEST_SIZE);
+
+               //Release previously allocated memory
+               tlsFreeMem(sha1Context);
+            }
+            else
+            {
+               //Failed to allocate memory
+               error = ERROR_OUT_OF_MEMORY;
+            }
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Decode the PEM structure that holds the RSA private key
+            error = pemReadRsaPrivateKey(context->cert->privateKey,
+               context->cert->privateKeyLength, &privateKey);
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Sign the key exchange parameters using RSA
+            error = tlsGenerateRsaSignature(&privateKey,
+               context->verifyData, signature->value, written);
+         }
+
+         //Release previously allocated resources
+         rsaFreePrivateKey(&privateKey);
+      }
+      else
+#endif
+#if (TLS_DHE_DSS_SUPPORT == ENABLED)
+      //Check whether DHE_DSS key exchange method is currently used
+      if(context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS)
+      {
+         Sha1Context *sha1Context;
+         DsaPrivateKey privateKey;
+
+         //Initialize DSA private key
+         dsaInitPrivateKey(&privateKey);
+
+         //Allocate a memory buffer to hold the SHA-1 context
+         sha1Context = tlsAllocMem(sizeof(Sha1Context));
+
+         //Successful memory allocation?
+         if(sha1Context != NULL)
+         {
+            //Compute SHA(ClientHello.random + ServerHello.random + ServerDhParams)
+            sha1Init(sha1Context);
+            sha1Update(sha1Context, context->random, 64);
+            sha1Update(sha1Context, params, paramsLen);
+            sha1Final(sha1Context, context->verifyData);
+
+            //Release previously allocated memory
+            tlsFreeMem(sha1Context);
+         }
+         else
+         {
+            //Failed to allocate memory
+            error = ERROR_OUT_OF_MEMORY;
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Decode the PEM structure that holds the DSA private key
+            error = pemReadDsaPrivateKey(context->cert->privateKey,
+               context->cert->privateKeyLength, &privateKey);
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Sign the key exchange parameters using DSA
+            error = tlsGenerateDsaSignature(context->prngAlgo,
+               context->prngContext, &privateKey, context->verifyData,
+               SHA1_DIGEST_SIZE, signature->value, written);
+         }
+
+         //Release previously allocated resources
+         dsaFreePrivateKey(&privateKey);
+      }
+      else
+#endif
+#if (TLS_ECDHE_ECDSA_SUPPORT == ENABLED)
+      //Check whether ECDHE_ECDSA key exchange method is currently used
+      if(context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA)
+      {
+         Sha1Context *sha1Context;
+         EcDomainParameters ecParams;
+         Mpi privateKey;
+
+         //Initialize EC domain parameters
+         ecInitDomainParameters(&ecParams);
+         //Initialize EC private key
+         mpiInit(&privateKey);
+
+         //Allocate a memory buffer to hold the SHA-1 context
+         sha1Context = tlsAllocMem(sizeof(Sha1Context));
+
+         //Successful memory allocation?
+         if(sha1Context != NULL)
+         {
+            //Compute SHA(ClientHello.random + ServerHello.random + ServerDhParams)
+            sha1Init(sha1Context);
+            sha1Update(sha1Context, context->random, 64);
+            sha1Update(sha1Context, params, paramsLen);
+            sha1Final(sha1Context, context->verifyData);
+
+            //Release previously allocated memory
+            tlsFreeMem(sha1Context);
+         }
+         else
+         {
+            //Failed to allocate memory
+            error = ERROR_OUT_OF_MEMORY;
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Decode the PEM structure that holds the EC domain parameters
+            error = pemReadEcParameters(context->cert->privateKey,
+               context->cert->privateKeyLength, &ecParams);
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Decode the PEM structure that holds the EC private key
+            error = pemReadEcPrivateKey(context->cert->privateKey,
+               context->cert->privateKeyLength, &privateKey);
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Sign the key exchange parameters using ECDSA
+            error = tlsGenerateEcdsaSignature(&ecParams, context->prngAlgo,
+               context->prngContext, &privateKey, context->verifyData,
+               SHA1_DIGEST_SIZE, signature->value, written);
+         }
+
+         //Release previously allocated resources
+         ecFreeDomainParameters(&ecParams);
+         mpiFree(&privateKey);
+      }
+      else
+#endif
+      //Invalid signature algorithm?
+      {
+         //Report an error
+         error = ERROR_UNSUPPORTED_SIGNATURE_ALGO;
+      }
+
+      //Fix the length of the digitally-signed element
+      signature->length = htons(*written);
+      //Adjust the length of the signature
+      *written += sizeof(TlsDigitalSignature);
+   }
+   else
+#endif
+#if (TLS_MAX_VERSION >= TLS_VERSION_1_2 && TLS_MIN_VERSION <= TLS_VERSION_1_2)
+   //TLS 1.2 currently selected?
+   if(context->version == TLS_VERSION_1_2)
+   {
+      TlsDigitalSignature2 *signature;
+      const HashAlgo *hashAlgo;
+      HashContext *hashContext;
+
+      //Point to the digitally-signed element
+      signature = (TlsDigitalSignature2 *) p;
+
+      //Retrieve the hash algorithm used for signing
+      hashAlgo = tlsGetHashAlgo(context->signHashAlgo);
+
+      //Make sure the hash algorithm is supported
+      if(hashAlgo != NULL)
+      {
+         //Allocate a memory buffer to hold the hash context
+         hashContext = tlsAllocMem(hashAlgo->contextSize);
+
+         //Successful memory allocation?
+         if(hashContext != NULL)
+         {
+            //Compute SHA(ClientHello.random + ServerHello.random + ServerDhParams)
+            hashAlgo->init(hashContext);
+            hashAlgo->update(hashContext, context->random, 64);
+            hashAlgo->update(hashContext, params, paramsLen);
+            hashAlgo->final(hashContext, NULL);
+
+#if (TLS_DHE_RSA_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED)
+            //Check whether DHE_RSA or ECDHE_RSA key exchange method is currently used
+            if(context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
+               context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA)
+            {
+               RsaPrivateKey privateKey;
+
+               //Initialize RSA private key
+               rsaInitPrivateKey(&privateKey);
+
+               //Set the relevant signature algorithm
+               signature->algorithm.signature = TLS_SIGN_ALGO_RSA;
+               signature->algorithm.hash = context->signHashAlgo;
+
+               //Decode the PEM structure that holds the RSA private key
+               error = pemReadRsaPrivateKey(context->cert->privateKey,
+                  context->cert->privateKeyLength, &privateKey);
+
+               //Check status code
+               if(!error)
+               {
+                  //Use the signature algorithm defined in PKCS #1 v1.5
+                  error = rsassaPkcs1v15Sign(&privateKey, hashAlgo,
+                     hashContext->digest, signature->value, written);
+               }
+
+               //Release previously allocated resources
+               rsaFreePrivateKey(&privateKey);
+            }
+            else
+#endif
+#if (TLS_DHE_DSS_SUPPORT == ENABLED)
+            //Check whether DHE_DSS key exchange method is currently used
+            if(context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS)
+            {
+               DsaPrivateKey privateKey;
+
+               //Initialize DSA private key
+               dsaInitPrivateKey(&privateKey);
+
+               //Set the relevant signature algorithm
+               signature->algorithm.signature = TLS_SIGN_ALGO_DSA;
+               signature->algorithm.hash = context->signHashAlgo;
+
+               //Decode the PEM structure that holds the DSA private key
+               error = pemReadDsaPrivateKey(context->cert->privateKey,
+                  context->cert->privateKeyLength, &privateKey);
+
+               //Check status code
+               if(!error)
+               {
+                  //Sign the key exchange parameters using DSA
+                  error = tlsGenerateDsaSignature(context->prngAlgo,
+                     context->prngContext, &privateKey, hashContext->digest,
+                     hashAlgo->digestSize, signature->value, written);
+               }
+
+               //Release previously allocated resources
+               dsaFreePrivateKey(&privateKey);
+            }
+            else
+#endif
+#if (TLS_ECDHE_ECDSA_SUPPORT == ENABLED)
+            //Check whether ECDHE_ECDSA key exchange method is currently used
+            if(context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA)
+            {
+               EcDomainParameters ecParams;
+               Mpi privateKey;
+
+               //Initialize EC domain parameters
+               ecInitDomainParameters(&ecParams);
+               //Initialize EC private key
+               mpiInit(&privateKey);
+
+               //Set the relevant signature algorithm
+               signature->algorithm.signature = TLS_SIGN_ALGO_ECDSA;
+               signature->algorithm.hash = context->signHashAlgo;
+
+               //Decode the PEM structure that holds the EC domain parameters
+               error = pemReadEcParameters(context->cert->privateKey,
+                  context->cert->privateKeyLength, &ecParams);
+
+               //Check status code
+               if(!error)
+               {
+                  //Decode the PEM structure that holds the EC private key
+                  error = pemReadEcPrivateKey(context->cert->privateKey,
+                     context->cert->privateKeyLength, &privateKey);
+               }
+
+               //Check status code
+               if(!error)
+               {
+                  //Sign the key exchange parameters using ECDSA
+                  error = tlsGenerateEcdsaSignature(&ecParams, context->prngAlgo,
+                     context->prngContext, &privateKey, hashContext->digest,
+                     hashAlgo->digestSize, signature->value, written);
+               }
+
+               //Release previously allocated memory
+               ecFreeDomainParameters(&ecParams);
+               mpiFree(&privateKey);
+            }
+            else
+#endif
+            //Invalid signature algorithm?
+            {
+               //Report an error
+               error = ERROR_UNSUPPORTED_SIGNATURE_ALGO;
+            }
+
+            //Release previously allocated memory
+            tlsFreeMem(hashContext);
+         }
+         else
+         {
+            //Failed to allocate memory
+            error = ERROR_OUT_OF_MEMORY;
+         }
+      }
+      else
+      {
+         //Hash algorithm not supported
+         error = ERROR_INVALID_SIGNATURE;
+      }
+
+      //Fix the length of the digitally-signed element
+      signature->length = htons(*written);
+      //Adjust the length of the message
+      *written += sizeof(TlsDigitalSignature2);
+   }
+   else
+#endif
+   {
+      //The negotiated TLS version is not valid
+      error = ERROR_INVALID_VERSION;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse PSK identity
+ * @param[in] context Pointer to the TLS context
+ * @param[in] p Input stream where to read the PSK identity hint
+ * @param[in] length Number of bytes available in the input stream
+ * @param[out] consumed Total number of bytes that have been consumed
+ * @return Error code
+ **/
+
+error_t tlsParsePskIdentity(TlsContext *context,
+    const uint8_t *p, size_t length, size_t *consumed)
+{
+   size_t n;
+   TlsPskIdentity *pskIdentity;
+
+   //Malformed ClientKeyExchange message?
+   if(length < sizeof(TlsPskIdentity))
+      return ERROR_DECODING_FAILED;
+
+   //Point to the PSK identity
+   pskIdentity = (TlsPskIdentity *) p;
+
+   //Retrieve the length of the PSK identity
+   n = ntohs(pskIdentity->length);
+
+   //Make sure the length field is valid
+   if(length < (sizeof(TlsPskIdentity) + n))
+      return ERROR_DECODING_FAILED;
+
+#if (TLS_PSK_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED || \
+   TLS_DHE_PSK_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   //Check whether the PSK identity has already been configured
+   if(context->pskIdentity != NULL)
+   {
+      //Release memory
+      tlsFreeMem(context->pskIdentity);
+      context->pskIdentity = NULL;
+   }
+
+   //Valid PSK identity?
+   if(n > 0)
+   {
+      //Allocate a memory block to hold the PSK identity
+      context->pskIdentity = tlsAllocMem(n + 1);
+      //Failed to allocate memory?
+      if(context->pskIdentity == NULL)
+         return ERROR_OUT_OF_MEMORY;
+
+      //Save the PSK identity
+      memcpy(context->pskIdentity, pskIdentity->value, n);
+      //Properly terminate the string
+      context->pskIdentity[n] = '\0';
+   }
+
+   //Any registered callback?
+   if(context->pskCallback != NULL)
+   {
+      error_t error;
+
+      //Invoke user callback function
+      if(context->pskIdentity != NULL)
+         error = context->pskCallback(context, context->pskIdentity);
+      else
+         error = context->pskCallback(context, "");
+
+      //Any error to report?
+      if(error)
+         return ERROR_UNKNOWN_IDENTITY;
+   }
+#endif
+
+   //Total number of bytes that have been consumed
+   *consumed = sizeof(TlsPskIdentity) + n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse client's key exchange parameters
+ * @param[in] context Pointer to the TLS context
+ * @param[in] p Input stream where to read the client's key exchange parameters
+ * @param[in] length Number of bytes available in the input stream
+ * @param[out] consumed Total number of bytes that have been consumed
+ * @return Error code
+ **/
+
+error_t tlsParseClientKeyParams(TlsContext *context,
+    const uint8_t *p, size_t length, size_t *consumed)
+{
+   error_t error;
+
+#if (TLS_RSA_SUPPORT == ENABLED || TLS_RSA_PSK_SUPPORT == ENABLED)
+   //RSA key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_RSA_PSK)
+   {
+      size_t n;
+      uint16_t version;
+      RsaPrivateKey privateKey;
+
+      //The RSA-encrypted premaster secret in a ClientKeyExchange is preceded by
+      //two length bytes. SSL 3.0 implementations do not include these bytes
+      if(context->version > SSL_VERSION_3_0)
+      {
+         //Malformed ClientKeyExchange message?
+         if(length < 2)
+            return ERROR_DECODING_FAILED;
+
+         //Decode the length field
+         n = LOAD16BE(p);
+
+         //Check the length of the RSA-encrypted premaster secret
+         if(n > (length - 2))
+            return ERROR_DECODING_FAILED;
+
+         //Save the length of the RSA-encrypted premaster secret
+         length = n;
+         //Advance the pointer over the length field
+         p += 2;
+         //Total number of bytes that have been consumed
+         *consumed = length + 2;
+      }
+      else
+      {
+         //Total number of bytes that have been consumed
+         *consumed = length;
+      }
+
+      //Initialize RSA private key
+      rsaInitPrivateKey(&privateKey);
+
+      //Decode the PEM structure that holds the RSA private key
+      error = pemReadRsaPrivateKey(context->cert->privateKey,
+         context->cert->privateKeyLength, &privateKey);
+
+      //Check status code
+      if(!error)
+      {
+         //Decrypt the premaster secret using the server private key
+         error = rsaesPkcs1v15Decrypt(&privateKey, p, length, context->premasterSecret,
+            TLS_MAX_PREMASTER_SECRET_SIZE, &context->premasterSecretLen);
+      }
+
+      //Release RSA private key
+      rsaFreePrivateKey(&privateKey);
+
+      //Retrieve the latest version supported by the client. This is used
+      //to detect version roll-back attacks
+      version = LOAD16BE(context->premasterSecret);
+
+      //The best way to avoid vulnerability to the Bleichenbacher attack is to
+      //treat incorrectly formatted messages in a manner indistinguishable from
+      //correctly formatted RSA blocks
+      if(error || context->premasterSecretLen != 48 || version != context->clientVersion)
+      {
+         //When it receives an incorrectly formatted RSA block, the server
+         //should generate a random 48-byte value and proceed using it as
+         //the premaster secret
+         error = context->prngAlgo->read(context->prngContext, context->premasterSecret, 48);
+
+         //Fix the length of the premaster secret
+         context->premasterSecretLen = 48;
+      }
+   }
+   else
+#endif
+#if (TLS_DH_ANON_SUPPORT == ENABLED || TLS_DHE_RSA_SUPPORT == ENABLED || \
+   TLS_DHE_DSS_SUPPORT == ENABLED || TLS_DHE_PSK_SUPPORT == ENABLED)
+   //Diffie-Hellman key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_DH_ANON ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_DSS ||
+      context->keyExchMethod == TLS_KEY_EXCH_DHE_PSK)
+   {
+      size_t n;
+
+      //Convert the client's public value to a multiple precision integer
+      error = tlsReadMpi(&context->dhContext.yb, p, length, &n);
+
+      //Check status code
+      if(!error)
+      {
+         //Total number of bytes that have been consumed
+         *consumed = n;
+
+         //Verify client's public value
+         error = dhCheckPublicKey(&context->dhContext.params, &context->dhContext.yb);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Calculate the negotiated key Z
+         error = dhComputeSharedSecret(&context->dhContext, context->premasterSecret,
+            TLS_MAX_PREMASTER_SECRET_SIZE, &context->premasterSecretLen);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Leading bytes of Z that contain all zero bits are stripped before
+         //it is used as the premaster secret (RFC 4346, section 8.2.1)
+         for(n = 0; n < context->premasterSecretLen; n++)
+         {
+            if(context->premasterSecret[n] != 0x00)
+               break;
+         }
+
+         //Any leading zero bytes?
+         if(n > 0)
+         {
+            //Strip leading zero bytes from the negotiated key
+            memmove(context->premasterSecret, context->premasterSecret + n,
+               context->premasterSecretLen - n);
+
+            //Adjust the length of the premaster secret
+            context->premasterSecretLen -= n;
+         }
+      }
+   }
+   else
+#endif
+#if (TLS_ECDH_ANON_SUPPORT == ENABLED || TLS_ECDHE_RSA_SUPPORT == ENABLED || \
+   TLS_ECDHE_ECDSA_SUPPORT == ENABLED || TLS_ECDHE_PSK_SUPPORT == ENABLED)
+   //ECDH key exchange method?
+   if(context->keyExchMethod == TLS_KEY_EXCH_ECDH_ANON ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_RSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_ECDSA ||
+      context->keyExchMethod == TLS_KEY_EXCH_ECDHE_PSK)
+   {
+      size_t n;
+
+      //Decode client's public key
+      error = tlsReadEcPoint(&context->ecdhContext.params,
+         &context->ecdhContext.qb, p, length, &n);
+
+      //Check status code
+      if(!error)
+      {
+         //Total number of bytes that have been consumed
+         *consumed = n;
+
+         //Verify client's public key and make sure that it is on the same
+         //elliptic curve as the server's ECDH key
+         error = ecdhCheckPublicKey(&context->ecdhContext.params,
+            &context->ecdhContext.qb);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Calculate the shared secret Z. Leading zeros found in this octet
+         //string must not be truncated (see RFC 4492, section 5.10)
+         error = ecdhComputeSharedSecret(&context->ecdhContext, context->premasterSecret,
+            TLS_MAX_PREMASTER_SECRET_SIZE, &context->premasterSecretLen);
+      }
+   }
+   else
+#endif
+   //Invalid key exchange method?
+   {
+      //The specified key exchange method is not supported
+      error = ERROR_UNSUPPORTED_KEY_EXCH_METHOD;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_ssl/tls_server_misc.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,52 @@
+/**
+ * @file tls_server_misc.h
+ * @brief Helper functions (TLS server)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TLS_SERVER_MISC_H
+#define _TLS_SERVER_MISC_H
+
+//Dependencies
+#include "tls.h"
+
+//TLS server specific functions
+error_t tlsFormatPskIdentityHint(TlsContext *context,
+   uint8_t *p, size_t *written);
+
+error_t tlsFormatServerKeyParams(TlsContext *context,
+    uint8_t *p, size_t *written);
+
+error_t tlsGenerateServerKeySignature(TlsContext *context,
+   uint8_t *p, const uint8_t *params, size_t paramsLen, size_t *written);
+
+error_t tlsParsePskIdentity(TlsContext *context,
+    const uint8_t *p, size_t length, size_t *consumed);
+
+error_t tlsParseClientKeyParams(TlsContext *context,
+    const uint8_t *p, size_t length, size_t *consumed);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/bsd_socket.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,2056 @@
+/**
+ * @file bsd_socket.c
+ * @brief BSD socket API
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL BSD_SOCKET_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "core/net.h"
+#include "core/bsd_socket.h"
+#include "core/socket.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (BSD_SOCKET_SUPPORT == ENABLED)
+
+//Common IPv6 addresses
+const in6_addr in6addr_any =
+   {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
+
+const in6_addr in6addr_loopback =
+   {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}};
+
+
+/**
+ * @brief Translate error code
+ * @param[in] error Error code to be translated
+ * @return BSD error code
+ **/
+
+int_t socketTranslateErrorCode(error_t error)
+{
+   int_t ret;
+
+   //Translate error code
+   switch(error)
+   {
+   case NO_ERROR:
+      ret = 0;
+      break;
+   case ERROR_TIMEOUT:
+      ret = EWOULDBLOCK;
+      break;
+   case ERROR_INVALID_PARAMETER:
+      ret = EINVAL;
+      break;
+   case ERROR_CONNECTION_RESET:
+      ret = ECONNRESET;
+      break;
+   case ERROR_ALREADY_CONNECTED:
+      ret = EISCONN;
+      break;
+   case ERROR_NOT_CONNECTED:
+      ret = ENOTCONN;
+      break;
+   case ERROR_CONNECTION_CLOSING:
+      ret = ESHUTDOWN;
+      break;
+   case ERROR_CONNECTION_FAILED:
+      ret = ECONNREFUSED;
+      break;
+   default:
+      ret = EFAULT;
+      break;
+   }
+
+   //Return BSD status code
+   return ret;
+}
+
+
+/**
+ * @brief Create a socket that is bound to a specific transport service provider
+ * @param[in] family Address family
+ * @param[in] type Type specification for the new socket
+ * @param[in] protocol Protocol to be used
+ * @return On success, a file descriptor for the new socket is returned.
+ *   On failure, SOCKET_ERROR is returned
+ **/
+
+int_t socket(int_t family, int_t type, int_t protocol)
+{
+   Socket *sock;
+
+   //Check address family
+   if(family == AF_INET || family == AF_INET6)
+   {
+      //Create a socket
+      sock = socketOpen(type, protocol);
+   }
+   else if(family == AF_PACKET)
+   {
+      //Create a socket
+      sock = socketOpen(SOCKET_TYPE_RAW_ETH, ntohs(protocol));
+   }
+   else
+   {
+      //The address family is not valid
+      return SOCKET_ERROR;
+   }
+
+   //Failed to create a new socket?
+   if(!sock)
+   {
+      //Report an error
+      return SOCKET_ERROR;
+   }
+
+   //Return the socket descriptor
+   return sock->descriptor;
+}
+
+
+/**
+ * @brief Associate a local address with a socket
+ * @param[in] s Descriptor identifying an unbound socket
+ * @param[in] addr Local address to assign to the bound socket
+ * @param[in] addrlen Length in bytes of the address
+ * @return If no error occurs, bind returns SOCKET_SUCCESS.
+ *   Otherwise, it returns SOCKET_ERROR
+ **/
+
+int_t bind(int_t s, const sockaddr *addr, socklen_t addrlen)
+{
+   error_t error;
+   uint16_t port;
+   IpAddr ipAddr;
+   Socket *sock;
+
+   //Make sure the socket descriptor is valid
+   if(s < 0 || s >= SOCKET_MAX_COUNT)
+   {
+      return SOCKET_ERROR;
+   }
+
+   //Point to the socket structure
+   sock = &socketTable[s];
+
+   //Check the length of the address
+   if(addrlen < (socklen_t) sizeof(sockaddr))
+   {
+      //Report an error
+      sock->errnoCode = EINVAL;
+      return SOCKET_ERROR;
+   }
+
+#if (IPV4_SUPPORT == ENABLED)
+   //IPv4 address?
+   if(addr->sa_family == AF_INET && addrlen >= (socklen_t) sizeof(sockaddr_in))
+   {
+      //Point to the IPv4 address information
+      sockaddr_in *sa = (sockaddr_in *) addr;
+      //Get port number
+      port = ntohs(sa->sin_port);
+
+      //Copy IPv4 address
+      if(sa->sin_addr.s_addr == INADDR_ANY)
+      {
+         ipAddr.length = 0;
+         ipAddr.ipv4Addr = IPV4_UNSPECIFIED_ADDR;
+      }
+      else
+      {
+         ipAddr.length = sizeof(Ipv4Addr);
+         ipAddr.ipv4Addr = sa->sin_addr.s_addr;
+      }
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 address?
+   if(addr->sa_family == AF_INET6 && addrlen >= (socklen_t) sizeof(sockaddr_in6))
+   {
+      //Point to the IPv6 address information
+      sockaddr_in6 *sa = (sockaddr_in6 *) addr;
+      //Get port number
+      port = ntohs(sa->sin6_port);
+
+      //Copy IPv6 address
+      if(ipv6CompAddr(sa->sin6_addr.s6_addr, &in6addr_any))
+      {
+         ipAddr.length = 0;
+         ipAddr.ipv6Addr = IPV6_UNSPECIFIED_ADDR;
+      }
+      else
+      {
+         ipAddr.length = sizeof(Ipv6Addr);
+         ipv6CopyAddr(&ipAddr.ipv6Addr, sa->sin6_addr.s6_addr);
+      }
+   }
+   else
+#endif
+   //Invalid address?
+   {
+      //Report an error
+      sock->errnoCode = EINVAL;
+      return SOCKET_ERROR;
+   }
+
+   //Associate the local address with the socket
+   error = socketBind(sock, &ipAddr, port);
+
+   //Any error to report?
+   if(error)
+   {
+      sock->errnoCode = socketTranslateErrorCode(error);
+      return SOCKET_ERROR;
+   }
+
+   //Successful processing
+   return SOCKET_SUCCESS;
+}
+
+
+/**
+ * @brief Establish a connection to a specified socket
+ * @param[in] s Descriptor identifying an unconnected socket
+ * @param[in] addr Address to which the connection should be established
+ * @param[in] addrlen Length in bytes of the address
+ * @return If no error occurs, connect returns SOCKET_SUCCESS.
+ *   Otherwise, it returns SOCKET_ERROR
+ **/
+
+int_t connect(int_t s, const sockaddr *addr, socklen_t addrlen)
+{
+   error_t error;
+   uint16_t port;
+   IpAddr ipAddr;
+   Socket *sock;
+
+   //Make sure the socket descriptor is valid
+   if(s < 0 || s >= SOCKET_MAX_COUNT)
+   {
+      return SOCKET_ERROR;
+   }
+
+   //Point to the socket structure
+   sock = &socketTable[s];
+
+   //Check the length of the address
+   if(addrlen < (socklen_t) sizeof(sockaddr))
+   {
+      sock->errnoCode = EINVAL;
+      return SOCKET_ERROR;
+   }
+
+#if (IPV4_SUPPORT == ENABLED)
+   //IPv4 address?
+   if(addr->sa_family == AF_INET && addrlen >= (socklen_t) sizeof(sockaddr_in))
+   {
+      //Point to the IPv4 address information
+      sockaddr_in *sa = (sockaddr_in *) addr;
+      //Get port number
+      port = ntohs(sa->sin_port);
+
+      //Copy IPv4 address
+      if(sa->sin_addr.s_addr == INADDR_ANY)
+      {
+         ipAddr.length = 0;
+         ipAddr.ipv4Addr = IPV4_UNSPECIFIED_ADDR;
+      }
+      else
+      {
+         ipAddr.length = sizeof(Ipv4Addr);
+         ipAddr.ipv4Addr = sa->sin_addr.s_addr;
+      }
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 address?
+   if(addr->sa_family == AF_INET6 && addrlen >= (socklen_t) sizeof(sockaddr_in6))
+   {
+      //Point to the IPv6 address information
+      sockaddr_in6 *sa = (sockaddr_in6 *) addr;
+      //Get port number
+      port = ntohs(sa->sin6_port);
+
+      //Copy IPv6 address
+      if(ipv6CompAddr(sa->sin6_addr.s6_addr, &in6addr_any))
+      {
+         ipAddr.length = 0;
+         ipAddr.ipv6Addr = IPV6_UNSPECIFIED_ADDR;
+      }
+      else
+      {
+         ipAddr.length = sizeof(Ipv6Addr);
+         ipv6CopyAddr(&ipAddr.ipv6Addr, sa->sin6_addr.s6_addr);
+      }
+   }
+   else
+#endif
+   //Invalid address?
+   {
+      //Report an error
+      sock->errnoCode = EINVAL;
+      return SOCKET_ERROR;
+   }
+
+   //Establish connection
+   error = socketConnect(sock, &ipAddr, port);
+
+   //Any error to report?
+   if(error)
+   {
+      sock->errnoCode = socketTranslateErrorCode(error);
+      return SOCKET_ERROR;
+   }
+
+   //Successful processing
+   return SOCKET_SUCCESS;
+}
+
+
+/**
+ * @brief Place a socket in the listening state
+ *
+ * Place a socket in a state in which it is listening for an incoming connection
+ *
+ * @param[in] s Descriptor identifying a bound, unconnected socket
+ * @param[in] backlog Maximum length of the queue of pending connections
+ * @return If no error occurs, listen returns SOCKET_SUCCESS.
+ *   Otherwise, it returns SOCKET_ERROR
+ **/
+
+int_t listen(int_t s, int_t backlog)
+{
+   error_t error;
+   Socket *sock;
+
+   //Make sure the socket descriptor is valid
+   if(s < 0 || s >= SOCKET_MAX_COUNT)
+   {
+      return SOCKET_ERROR;
+   }
+
+   //Point to the socket structure
+   sock = &socketTable[s];
+
+   //Place the socket in the listening state
+   error = socketListen(sock, backlog);
+
+   //Any error to report?
+   if(error)
+   {
+      sock->errnoCode = socketTranslateErrorCode(error);
+      return SOCKET_ERROR;
+   }
+
+   //Successful processing
+   return SOCKET_SUCCESS;
+}
+
+
+/**
+ * @brief Permit an incoming connection attempt on a socket
+ * @param[in] s Descriptor that identifies a socket in the listening state
+ * @param[out] addr Address of the connecting entity (optional)
+ * @param[in,out] addrlen Length in bytes of the address (optional)
+ * @return If no error occurs, accept returns a descriptor for the new socket.
+ *   Otherwise, it returns SOCKET_ERROR
+ **/
+
+int_t accept(int_t s, sockaddr *addr, socklen_t *addrlen)
+{
+   uint16_t port;
+   IpAddr ipAddr;
+   Socket *sock;
+   Socket *newSock;
+
+   //Make sure the socket descriptor is valid
+   if(s < 0 || s >= SOCKET_MAX_COUNT)
+   {
+      return SOCKET_ERROR;
+   }
+
+   //Point to the socket structure
+   sock = &socketTable[s];
+
+   //Permit an incoming connection attempt on a socket
+   newSock = socketAccept(sock, &ipAddr, &port);
+
+   //No connection request is pending in the SYN queue?
+   if(newSock == NULL)
+   {
+      //Report an error
+      sock->errnoCode = EWOULDBLOCK;
+      return SOCKET_ERROR;
+   }
+
+   //The address is optional
+   if(addr != NULL && addrlen != NULL)
+   {
+#if (IPV4_SUPPORT == ENABLED)
+      //IPv4 address?
+      if(ipAddr.length == sizeof(Ipv4Addr) && *addrlen >= (socklen_t) sizeof(sockaddr_in))
+      {
+         //Point to the IPv4 address information
+         sockaddr_in *sa = (sockaddr_in *) addr;
+
+         //Set address family and port number
+         sa->sin_family = AF_INET;
+         sa->sin_port = htons(port);
+         //Copy IPv4 address
+         sa->sin_addr.s_addr = ipAddr.ipv4Addr;
+
+         //Return the actual length of the address
+         *addrlen = sizeof(sockaddr_in);
+      }
+      else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+      //IPv6 address?
+      if(ipAddr.length == sizeof(Ipv6Addr) && *addrlen >= (socklen_t) sizeof(sockaddr_in6))
+      {
+         //Point to the IPv6 address information
+         sockaddr_in6 *sa = (sockaddr_in6 *) addr;
+
+         //Set address family and port number
+         sa->sin6_family = AF_INET6;
+         sa->sin6_port = htons(port);
+         //Copy IPv6 address
+         ipv6CopyAddr(sa->sin6_addr.s6_addr, &ipAddr.ipv6Addr);
+
+         //Return the actual length of the address
+         *addrlen = sizeof(sockaddr_in6);
+      }
+      else
+#endif
+      //Invalid address?
+      {
+         //Close socket
+         socketClose(newSock);
+         //Report an error
+         sock->errnoCode = EINVAL;
+         return SOCKET_ERROR;
+      }
+   }
+
+   //Return the descriptor to the new socket
+   return newSock->descriptor;
+}
+
+
+/**
+ * @brief Send data to a connected socket
+ * @param[in] s Descriptor that identifies a connected socket
+ * @param[in] data Pointer to a buffer containing the data to be transmitted
+ * @param[in] length Number of bytes to be transmitted
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return If no error occurs, send returns the total number of bytes sent,
+ *   which can be less than the number requested to be sent in the
+ *   length parameter. Otherwise, a value of SOCKET_ERROR is returned
+ **/
+
+int_t send(int_t s, const void *data, size_t length, int_t flags)
+{
+   error_t error;
+   size_t written;
+   Socket *sock;
+
+   //Make sure the socket descriptor is valid
+   if(s < 0 || s >= SOCKET_MAX_COUNT)
+   {
+      return SOCKET_ERROR;
+   }
+
+   //Point to the socket structure
+   sock = &socketTable[s];
+
+   //Send data
+   error = socketSend(sock, data, length, &written, flags << 8);
+
+   //Any error to report?
+   if(error == ERROR_TIMEOUT)
+   {
+      //Check whether some data has been written
+      if(written > 0)
+      {
+         //If a timeout error occurs and some data has been written, the
+         //count of bytes transferred so far is returned...
+      }
+      else
+      {
+         //If no data has been written, a value of SOCKET_ERROR is returned
+         sock->errnoCode = socketTranslateErrorCode(error);
+         return SOCKET_ERROR;
+      }
+   }
+   else if(error != NO_ERROR)
+   {
+      //Otherwise, a value of SOCKET_ERROR is returned
+      sock->errnoCode = socketTranslateErrorCode(error);
+      return SOCKET_ERROR;
+   }
+
+   //Return the number of bytes transferred so far
+   return written;
+}
+
+
+/**
+ * @brief Send a datagram to a specific destination
+ * @param[in] s Descriptor that identifies a socket
+ * @param[in] data Pointer to a buffer containing the data to be transmitted
+ * @param[in] length Number of bytes to be transmitted
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @param[in] addr Destination address
+ * @param[in] addrlen Length in bytes of the destination address
+ * @return If no error occurs, sendto returns the total number of bytes sent,
+ *   which can be less than the number requested to be sent in the
+ *   length parameter. Otherwise, a value of SOCKET_ERROR is returned
+ **/
+
+int_t sendto(int_t s, const void *data, size_t length,
+   int_t flags, const sockaddr *addr, socklen_t addrlen)
+{
+   error_t error;
+   size_t written;
+   uint16_t port;
+   IpAddr ipAddr;
+   Socket *sock;
+
+   //Make sure the socket descriptor is valid
+   if(s < 0 || s >= SOCKET_MAX_COUNT)
+   {
+      return SOCKET_ERROR;
+   }
+
+   //Point to the socket structure
+   sock = &socketTable[s];
+
+   //Check the length of the address
+   if(addrlen < (socklen_t) sizeof(sockaddr))
+   {
+      //Report an error
+      sock->errnoCode = EINVAL;
+      return SOCKET_ERROR;
+   }
+
+#if (IPV4_SUPPORT == ENABLED)
+   //IPv4 address?
+   if(addr->sa_family == AF_INET && addrlen >= (socklen_t) sizeof(sockaddr_in))
+   {
+      //Point to the IPv4 address information
+      sockaddr_in *sa = (sockaddr_in *) addr;
+
+      //Get port number
+      port = ntohs(sa->sin_port);
+      //Copy IPv4 address
+      ipAddr.length = sizeof(Ipv4Addr);
+      ipAddr.ipv4Addr = sa->sin_addr.s_addr;
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 address?
+   if(addr->sa_family == AF_INET6 && addrlen >= (socklen_t) sizeof(sockaddr_in6))
+   {
+      //Point to the IPv6 address information
+      sockaddr_in6 *sa = (sockaddr_in6 *) addr;
+
+      //Get port number
+      port = ntohs(sa->sin6_port);
+      //Copy IPv6 address
+      ipAddr.length = sizeof(Ipv6Addr);
+      ipv6CopyAddr(&ipAddr.ipv6Addr, sa->sin6_addr.s6_addr);
+   }
+   else
+#endif
+   //Invalid address?
+   {
+      //Report an error
+      sock->errnoCode = EINVAL;
+      return SOCKET_ERROR;
+   }
+
+   //Send data
+   error = socketSendTo(sock, &ipAddr, port, data, length, &written, flags << 8);
+
+   //Any error to report?
+   if(error == ERROR_TIMEOUT)
+   {
+      //Check whether some data has been written
+      if(written > 0)
+      {
+         //If a timeout error occurs and some data has been written, the
+         //count of bytes transferred so far is returned...
+      }
+      else
+      {
+         //If no data has been written, a value of SOCKET_ERROR is returned
+         sock->errnoCode = socketTranslateErrorCode(error);
+         return SOCKET_ERROR;
+      }
+   }
+   else if(error != NO_ERROR)
+   {
+      //Otherwise, a value of SOCKET_ERROR is returned
+      sock->errnoCode = socketTranslateErrorCode(error);
+      return SOCKET_ERROR;
+   }
+
+   //Return the number of bytes transferred so far
+   return written;
+}
+
+
+/**
+ * @brief Receive data from a connected socket
+ * @param[in] s Descriptor that identifies a connected socket
+ * @param[out] data Buffer where to store the incoming data
+ * @param[in] size Maximum number of bytes that can be received
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return If no error occurs, recv returns the number of bytes received. If the
+ *   connection has been gracefully closed, the return value is zero.
+ *   Otherwise, a value of SOCKET_ERROR is returned
+ **/
+
+int_t recv(int_t s, void *data, size_t size, int_t flags)
+{
+   error_t error;
+   size_t received;
+   Socket *sock;
+
+   //Make sure the socket descriptor is valid
+   if(s < 0 || s >= SOCKET_MAX_COUNT)
+   {
+      return SOCKET_ERROR;
+   }
+
+   //Point to the socket structure
+   sock = &socketTable[s];
+
+   //Receive data
+   error = socketReceive(sock, data, size, &received, flags << 8);
+
+   //Any error to report?
+   if(error == ERROR_END_OF_STREAM)
+   {
+      //If the connection has been gracefully closed, the return value is zero
+      return 0;
+   }
+   else if(error != NO_ERROR)
+   {
+      //Otherwise, a value of SOCKET_ERROR is returned
+      sock->errnoCode = socketTranslateErrorCode(error);
+      return SOCKET_ERROR;
+   }
+
+   //Return the number of bytes received
+   return received;
+}
+
+
+/**
+ * @brief Receive a datagram
+ * @param[in] s Descriptor that identifies a socket
+ * @param[out] data Buffer where to store the incoming data
+ * @param[in] size Maximum number of bytes that can be received
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @param[out] addr Source address upon return (optional)
+ * @param[in,out] addrlen Length in bytes of the address (optional)
+ * @return If no error occurs, recvfrom returns the number of bytes received.
+ *   If the connection has been gracefully closed, the return value is
+ *   zero. Otherwise, a value of SOCKET_ERROR is returned
+ **/
+
+int_t recvfrom(int_t s, void *data, size_t size,
+   int_t flags, sockaddr *addr, socklen_t *addrlen)
+{
+   error_t error;
+   size_t received;
+   uint16_t port;
+   IpAddr ipAddr;
+   Socket *sock;
+
+   //Make sure the socket descriptor is valid
+   if(s < 0 || s >= SOCKET_MAX_COUNT)
+   {
+      return SOCKET_ERROR;
+   }
+
+   //Point to the socket structure
+   sock = &socketTable[s];
+
+   //Receive data
+   error = socketReceiveFrom(sock, &ipAddr, &port, data, size, &received, flags << 8);
+
+   //Any error to report?
+   if(error == ERROR_END_OF_STREAM)
+   {
+      //If the connection has been gracefully closed, the return value is zero
+      return 0;
+   }
+   else if(error != NO_ERROR)
+   {
+      //Otherwise, a value of SOCKET_ERROR is returned
+      sock->errnoCode = socketTranslateErrorCode(error);
+      return SOCKET_ERROR;
+   }
+
+   //The address is optional
+   if(addr != NULL && addrlen != NULL)
+   {
+#if (IPV4_SUPPORT == ENABLED)
+      //IPv4 address?
+      if(ipAddr.length == sizeof(Ipv4Addr) && *addrlen >= (socklen_t) sizeof(sockaddr_in))
+      {
+         //Point to the IPv4 address information
+         sockaddr_in *sa = (sockaddr_in *) addr;
+
+         //Set address family and port number
+         sa->sin_family = AF_INET;
+         sa->sin_port = htons(port);
+         //Copy IPv4 address
+         sa->sin_addr.s_addr = ipAddr.ipv4Addr;
+
+         //Return the actual length of the address
+         *addrlen = sizeof(sockaddr_in);
+      }
+      else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+      //IPv6 address?
+      if(ipAddr.length == sizeof(Ipv6Addr) && *addrlen >= (socklen_t) sizeof(sockaddr_in6))
+      {
+         //Point to the IPv6 address information
+         sockaddr_in6 *sa = (sockaddr_in6 *) addr;
+
+         //Set address family and port number
+         sa->sin6_family = AF_INET6;
+         sa->sin6_port = htons(port);
+         //Copy IPv6 address
+         ipv6CopyAddr(sa->sin6_addr.s6_addr, &ipAddr.ipv6Addr);
+
+         //Return the actual length of the address
+         *addrlen = sizeof(sockaddr_in6);
+      }
+      else
+#endif
+      //Invalid address?
+      {
+         //Report an error
+         sock->errnoCode = EINVAL;
+         return SOCKET_ERROR;
+      }
+   }
+
+   //Return the number of bytes received
+   return received;
+}
+
+
+/**
+ * @brief Retrieves the local name for a socket
+ * @param[in] s Descriptor identifying a socket
+ * @param[out] addr Address of the socket
+ * @param[in,out] addrlen Length in bytes of the address
+ * @return If no error occurs, getsockname returns SOCKET_SUCCESS
+ *   Otherwise, it returns SOCKET_ERROR
+ **/
+
+int_t getsockname(int_t s, sockaddr *addr, socklen_t *addrlen)
+{
+   int_t ret;
+   Socket *sock;
+
+   //Make sure the socket descriptor is valid
+   if(s < 0 || s >= SOCKET_MAX_COUNT)
+   {
+      return SOCKET_ERROR;
+   }
+
+   //Point to the socket structure
+   sock = &socketTable[s];
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Check whether the socket has been bound to an address
+   if(sock->localIpAddr.length != 0)
+   {
+#if (IPV4_SUPPORT == ENABLED)
+      //IPv4 address?
+      if(sock->localIpAddr.length == sizeof(Ipv4Addr) && *addrlen >= (socklen_t) sizeof(sockaddr_in))
+      {
+         //Point to the IPv4 address information
+         sockaddr_in *sa = (sockaddr_in *) addr;
+
+         //Set address family and port number
+         sa->sin_family = AF_INET;
+         sa->sin_port = htons(sock->localPort);
+
+         //Copy IPv4 address
+         sa->sin_addr.s_addr = sock->localIpAddr.ipv4Addr;
+
+         //Return the actual length of the address
+         *addrlen = sizeof(sockaddr_in);
+         //Successful processing
+         ret = SOCKET_SUCCESS;
+      }
+      else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+      //IPv6 address?
+      if(sock->localIpAddr.length == sizeof(Ipv6Addr) && *addrlen >= (socklen_t) sizeof(sockaddr_in6))
+      {
+         //Point to the IPv6 address information
+         sockaddr_in6 *sa = (sockaddr_in6 *) addr;
+
+         //Set address family and port number
+         sa->sin6_family = AF_INET6;
+         sa->sin6_port = htons(sock->localPort);
+
+         //Copy IPv6 address
+         ipv6CopyAddr(sa->sin6_addr.s6_addr, &sock->localIpAddr.ipv6Addr);
+
+         //Return the actual length of the address
+         *addrlen = sizeof(sockaddr_in6);
+         //Successful processing
+         ret = SOCKET_SUCCESS;
+      }
+      else
+#endif
+      {
+         //The specified length is not valid
+         sock->errnoCode = EINVAL;
+         ret = SOCKET_ERROR;
+      }
+   }
+   else
+   {
+      //The socket is not bound to any address
+      sock->errnoCode = ENOTCONN;
+      ret = SOCKET_ERROR;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //return status code
+   return ret;
+}
+
+
+/**
+ * @brief Retrieves the address of the peer to which a socket is connected
+ * @param[in] s Descriptor identifying a socket
+ * @param[out] addr Address of the peer
+ * @param[in,out] addrlen Length in bytes of the address
+ * @return If no error occurs, getpeername returns SOCKET_SUCCESS
+ *   Otherwise, it returns SOCKET_ERROR
+ **/
+
+int_t getpeername(int_t s, sockaddr *addr, socklen_t *addrlen)
+{
+   int_t ret;
+   Socket *sock;
+
+   //Make sure the socket descriptor is valid
+   if(s < 0 || s >= SOCKET_MAX_COUNT)
+   {
+      return SOCKET_ERROR;
+   }
+
+   //Point to the socket structure
+   sock = &socketTable[s];
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Check whether the socket is connected to a peer
+   if(sock->remoteIpAddr.length != 0)
+   {
+#if (IPV4_SUPPORT == ENABLED)
+      //IPv4 address?
+      if(sock->remoteIpAddr.length == sizeof(Ipv4Addr) && *addrlen >= (socklen_t) sizeof(sockaddr_in))
+      {
+         //Point to the IPv4 address information
+         sockaddr_in *sa = (sockaddr_in *) addr;
+
+         //Set address family and port number
+         sa->sin_family = AF_INET;
+         sa->sin_port = htons(sock->remotePort);
+
+         //Copy IPv4 address
+         sa->sin_addr.s_addr = sock->remoteIpAddr.ipv4Addr;
+
+         //Return the actual length of the address
+         *addrlen = sizeof(sockaddr_in);
+         //Successful processing
+         ret = SOCKET_SUCCESS;
+      }
+      else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+      //IPv6 address?
+      if(sock->remoteIpAddr.length == sizeof(Ipv6Addr) && *addrlen >= (socklen_t) sizeof(sockaddr_in6))
+      {
+         //Point to the IPv6 address information
+         sockaddr_in6 *sa = (sockaddr_in6 *) addr;
+
+         //Set address family and port number
+         sa->sin6_family = AF_INET6;
+         sa->sin6_port = htons(sock->remotePort);
+
+         //Copy IPv6 address
+         ipv6CopyAddr(sa->sin6_addr.s6_addr, &sock->remoteIpAddr.ipv6Addr);
+
+         //Return the actual length of the address
+         *addrlen = sizeof(sockaddr_in6);
+         //Successful processing
+         ret = SOCKET_SUCCESS;
+      }
+      else
+#endif
+      {
+         //The specified length is not valid
+         sock->errnoCode = EINVAL;
+         ret = SOCKET_ERROR;
+      }
+   }
+   else
+   {
+      //The socket is not connected to any peer
+      sock->errnoCode = ENOTCONN;
+      ret = SOCKET_ERROR;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //return status code
+   return ret;
+}
+
+
+/**
+ * @brief The setsockopt function sets a socket option
+ * @param[in] s Descriptor that identifies a socket
+ * @param[in] level The level at which the option is defined
+ * @param[in] optname The socket option for which the value is to be set
+ * @param[in] optval A pointer to the buffer in which the value for the requested option is specified
+ * @param[in] optlen The size, in bytes, of the buffer pointed to by the optval parameter
+ * @return If no error occurs, setsockopt returns SOCKET_SUCCESS
+ *   Otherwise, it returns SOCKET_ERROR
+ **/
+
+int_t setsockopt(int_t s, int_t level, int_t optname,
+   const void *optval, socklen_t optlen)
+{
+   int_t ret;
+   int_t *val;
+   timeval *t;
+   Socket *sock;
+
+   //Make sure the socket descriptor is valid
+   if(s < 0 || s >= SOCKET_MAX_COUNT)
+   {
+      return SOCKET_ERROR;
+   }
+
+   //Point to the socket structure
+   sock = &socketTable[s];
+
+   //Make sure the option is valid
+   if(optval != NULL)
+   {
+      //Level at which the option is defined?
+      if(level == SOL_SOCKET)
+      {
+         //Check option type
+         switch(optname)
+         {
+         //The socket can be bound to an address which is already in use
+         case SO_REUSEADDR:
+            //Ignore the specified option
+            ret = SOCKET_SUCCESS;
+            //We are done
+            break;
+
+         //Allow transmission and receipt of broadcast messages
+         case SO_BROADCAST:
+            //Ignore the specified option
+            ret = SOCKET_SUCCESS;
+            //We are done
+            break;
+
+         //Set send buffer size
+         case SO_SNDBUF:
+            //Check the length of the option
+            if(optlen >= (socklen_t) sizeof(int_t))
+            {
+               //Cast the option value to the relevant type
+               val = (int_t *) optval;
+               //Adjust the size of the send buffer
+               socketSetTxBufferSize(sock, *val);
+               //Successful processing
+               ret = SOCKET_SUCCESS;
+            }
+            else
+            {
+               //The option length is not valid
+               sock->errnoCode = EFAULT;
+               ret = SOCKET_ERROR;
+            }
+
+            //We are done
+            break;
+
+         //Set receive buffer size
+         case SO_RCVBUF:
+            //Check the length of the option
+            if(optlen >= (socklen_t) sizeof(int_t))
+            {
+               //Cast the option value to the relevant type
+               val = (int_t *) optval;
+               //Adjust the size of the receive buffer
+               socketSetRxBufferSize(sock, *val);
+               //Successful processing
+               ret = SOCKET_SUCCESS;
+            }
+            else
+            {
+               //The option length is not valid
+               sock->errnoCode = EFAULT;
+               ret = SOCKET_ERROR;
+            }
+
+            //We are done
+            break;
+
+         //Set send timeout or receive timeout
+         case SO_SNDTIMEO:
+         case SO_RCVTIMEO:
+            //Check the length of the option
+            if(optlen >= (socklen_t) sizeof(timeval))
+            {
+               //Cast the option value to the relevant type
+               t = (timeval *) optval;
+
+               //If the specified value is of zero, I/O operations shall not time out
+               if(!t->tv_sec && !t->tv_usec)
+                  socketSetTimeout(sock, INFINITE_DELAY);
+               else
+                  socketSetTimeout(sock, t->tv_sec * 1000 + t->tv_usec / 1000);
+
+               //Successful processing
+               ret = SOCKET_SUCCESS;
+            }
+            else
+            {
+               //The option length is not valid
+               sock->errnoCode = EFAULT;
+               ret = SOCKET_ERROR;
+            }
+
+            //We are done
+            break;
+
+         //Unknown option
+         default:
+            //Report an error
+            sock->errnoCode = ENOPROTOOPT;
+            ret = SOCKET_ERROR;
+            break;
+         }
+      }
+      else
+      {
+         //The specified level is not valid
+         sock->errnoCode = EINVAL;
+         ret = SOCKET_ERROR;
+      }
+   }
+   else
+   {
+      //The option is not valid
+      sock->errnoCode = EFAULT;
+      ret = SOCKET_ERROR;
+   }
+
+   //return status code
+   return ret;
+}
+
+
+/**
+ * @brief The getsockopt function retrieves a socket option
+ * @param[in] s Descriptor that identifies a socket
+ * @param[in] level The level at which the option is defined
+ * @param[in] optname The socket option for which the value is to be retrieved
+ * @param[out] optval A pointer to the buffer in which the value for the requested option is to be returned
+ * @param[in,out] optlen The size, in bytes, of the buffer pointed to by the optval parameter
+ * @return If no error occurs, getsockopt returns SOCKET_SUCCESS
+ *   Otherwise, it returns SOCKET_ERROR
+ **/
+
+int_t getsockopt(int_t s, int_t level, int_t optname,
+   void *optval, socklen_t *optlen)
+{
+   int_t ret;
+   int_t *val;
+   timeval *t;
+   Socket *sock;
+
+   //Make sure the socket descriptor is valid
+   if(s < 0 || s >= SOCKET_MAX_COUNT)
+   {
+      return SOCKET_ERROR;
+   }
+
+   //Point to the socket structure
+   sock = &socketTable[s];
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Make sure the option is valid
+   if(optval != NULL)
+   {
+      //Level at which the option is defined?
+      if(level == SOL_SOCKET)
+      {
+         //Check option type
+         switch(optname)
+         {
+#if (TCP_SUPPORT == ENABLED)
+         //Get send buffer size
+         case SO_SNDBUF:
+            //Check the length of the option
+            if(*optlen >= (socklen_t) sizeof(int_t))
+            {
+               //Cast the option value to the relevant type
+               val = (int_t *) optval;
+               //Return the size of the send buffer
+               *val = sock->txBufferSize;
+               //Return the actual length of the option
+               *optlen = sizeof(int_t);
+               //Successful processing
+               ret = SOCKET_SUCCESS;
+            }
+            else
+            {
+               //The option length is not valid
+               sock->errnoCode = EFAULT;
+               ret = SOCKET_ERROR;
+            }
+
+            //We are done
+            break;
+
+         //Get receive buffer size
+         case SO_RCVBUF:
+            //Check the length of the option
+            if(*optlen >= (socklen_t) sizeof(int_t))
+            {
+               //Cast the option value to the relevant type
+               val = (int_t *) optval;
+               //Return the size of the receive buffer
+               *val = sock->rxBufferSize;
+               //Return the actual length of the option
+               *optlen = sizeof(int_t);
+               //Successful processing
+               ret = SOCKET_SUCCESS;
+            }
+            else
+            {
+               //The option length is not valid
+               sock->errnoCode = EFAULT;
+               ret = SOCKET_ERROR;
+            }
+
+            //We are done
+            break;
+#endif
+         //Get send timeout or receive timeout
+         case SO_SNDTIMEO:
+         case SO_RCVTIMEO:
+            //Check the length of the option
+            if(*optlen >= (socklen_t) sizeof(timeval))
+            {
+               //Cast the option value to the relevant type
+               t = (timeval *) optval;
+
+               //Return the timeout value
+               if(sock->timeout == INFINITE_DELAY)
+               {
+                  t->tv_sec = 0;
+                  t->tv_usec = 0;
+               }
+               else
+               {
+                  t->tv_sec = sock->timeout / 1000;
+                  t->tv_usec = (sock->timeout % 1000) * 1000;
+               }
+
+               //Return the actual length of the option
+               *optlen = sizeof(timeval);
+               //Successful processing
+               ret = SOCKET_SUCCESS;
+            }
+            else
+            {
+               //The option length is not valid
+               sock->errnoCode = EFAULT;
+               ret = SOCKET_ERROR;
+            }
+
+            //We are done
+            break;
+
+         //Get error status
+         case SO_ERROR:
+            //Check the length of the option
+            if(*optlen >= (socklen_t) sizeof(int_t))
+            {
+               //Cast the option value to the relevant type
+               val = (int_t *) optval;
+               //Return the error code
+               *val = sock->errnoCode;
+               //Return the actual length of the option
+               *optlen = sizeof(int_t);
+               //Successful processing
+               ret = SOCKET_SUCCESS;
+            }
+            else
+            {
+               //The option length is not valid
+               sock->errnoCode = EFAULT;
+               ret = SOCKET_ERROR;
+            }
+
+            //We are done
+            break;
+
+         //Unknown option
+         default:
+            //Report an error
+            sock->errnoCode = ENOPROTOOPT;
+            ret = SOCKET_ERROR;
+            break;
+         }
+      }
+      else
+      {
+         //The specified level is not valid
+         sock->errnoCode = EINVAL;
+         ret = SOCKET_ERROR;
+      }
+   }
+   else
+   {
+      //The option is not valid
+      sock->errnoCode = EFAULT;
+      ret = SOCKET_ERROR;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //return status code
+   return ret;
+}
+
+
+/**
+ * @brief Control the I/O mode of a socket
+ * @param[in] s Descriptor that identifies a socket
+ * @param[in] cmd A command to perform on the socket
+ * @param[in,out] arg A pointer to a parameter
+ * @return If no error occurs, setsockopt returns SOCKET_SUCCESS
+ *   Otherwise, it returns SOCKET_ERROR
+ **/
+
+int_t ioctlsocket(int_t s, uint32_t cmd, void *arg)
+{
+   int_t ret;
+   int_t *val;
+   Socket *sock;
+
+   //Make sure the socket descriptor is valid
+   if(s < 0 || s >= SOCKET_MAX_COUNT)
+   {
+      return SOCKET_ERROR;
+   }
+
+   //Point to the socket structure
+   sock = &socketTable[s];
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Make sure the parameter is valid
+   if(arg != NULL)
+   {
+      //Check command type
+      switch(cmd)
+      {
+#if (TCP_SUPPORT == ENABLED)
+      //Get the number of characters waiting to be read
+      case FIONREAD:
+         //Cast the parameter to the relevant type
+         val = (int_t *) arg;
+         //Return the number of characters in the receive buffer
+         *val = sock->rcvUser;
+         //Successful processing
+         ret = SOCKET_SUCCESS;
+         break;
+#endif
+      //Enable or disable non-blocking mode
+      case FIONBIO:
+         //Cast the parameter to the relevant type
+         val = (int_t *) arg;
+         //Enable blocking or non-blocking operation
+         sock->timeout = (*val != 0) ? 0 : INFINITE_DELAY;
+         //Successful processing
+         ret = SOCKET_SUCCESS;
+         break;
+
+      //Unknown command?
+      default:
+         //Report an error
+         sock->errnoCode = EINVAL;
+         ret = SOCKET_ERROR;
+         break;
+      }
+   }
+   else
+   {
+      //The parameter is not valid
+      sock->errnoCode = EFAULT;
+      ret = SOCKET_ERROR;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //return status code
+   return ret;
+}
+
+
+/**
+ * @brief Perform specific operation
+ * @param[in] s Descriptor that identifies a socket
+ * @param[in] cmd A command to perform on the socket
+ * @param[in,out] arg A pointer to a parameter
+ * @return If no error occurs, setsockopt returns SOCKET_SUCCESS
+ *   Otherwise, it returns SOCKET_ERROR
+ **/
+
+int_t fcntl(int_t s, int_t cmd, void *arg)
+{
+   int_t ret;
+   int_t *flags;
+   Socket *sock;
+
+   //Make sure the socket descriptor is valid
+   if(s < 0 || s >= SOCKET_MAX_COUNT)
+   {
+      return SOCKET_ERROR;
+   }
+
+   //Point to the socket structure
+   sock = &socketTable[s];
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Make sure the parameter is valid
+   if(arg != NULL)
+   {
+      //Check command type
+      switch(cmd)
+      {
+      //Get the file status flags?
+      case F_GETFL:
+         //Cast the parameter to the relevant type
+         flags = (int_t *) arg;
+         //Check whether non-blocking mode is currently enabled
+         *flags = (sock->timeout == 0) ? O_NONBLOCK : 0;
+         //Successful processing
+         ret = SOCKET_SUCCESS;
+         break;
+
+      //Set the file status flags?
+      case F_SETFL:
+         //Cast the parameter to the relevant type
+         flags = (int_t *) arg;
+         //Enable blocking or non-blocking operation
+         sock->timeout = (*flags & O_NONBLOCK) ? 0 : INFINITE_DELAY;
+         //Successful processing
+         ret = SOCKET_SUCCESS;
+         break;
+
+      //Unknown command?
+      default:
+         //Report an error
+         sock->errnoCode = EINVAL;
+         ret = SOCKET_ERROR;
+         break;
+      }
+   }
+   else
+   {
+      //Report an error
+      sock->errnoCode = EFAULT;
+      ret = SOCKET_ERROR;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //return status code
+   return ret;
+}
+
+
+/**
+ * @brief The shutdown function disables sends or receives on a socket
+ * @param[in] s Descriptor that identifies a socket
+ * @param[in] how A flag that describes what types of operation will no longer be allowed
+ * @return If no error occurs, shutdown returns SOCKET_SUCCESS
+ *   Otherwise, it returns SOCKET_ERROR
+ **/
+
+int_t shutdown(int_t s, int_t how)
+{
+   error_t error;
+   Socket *sock;
+
+   //Make sure the socket descriptor is valid
+   if(s < 0 || s >= SOCKET_MAX_COUNT)
+   {
+      return SOCKET_ERROR;
+   }
+
+   //Point to the socket structure
+   sock = &socketTable[s];
+
+   //Shut down socket
+   error = socketShutdown(sock, how);
+
+   //Any error to report?
+   if(error)
+   {
+      sock->errnoCode = socketTranslateErrorCode(error);
+      return SOCKET_ERROR;
+   }
+
+   //Successful processing
+   return SOCKET_SUCCESS;
+}
+
+
+/**
+ * @brief The closesocket function closes an existing socket
+ * @param[in] s Descriptor that identifies a socket
+ * @return If no error occurs, closesocket returns SOCKET_SUCCESS
+ *   Otherwise, it returns SOCKET_ERROR
+ **/
+
+int_t closesocket(int_t s)
+{
+   Socket *sock;
+
+   //Make sure the socket descriptor is valid
+   if(s < 0 || s >= SOCKET_MAX_COUNT)
+   {
+      return SOCKET_ERROR;
+   }
+
+   //Point to the socket structure
+   sock = &socketTable[s];
+
+   //Close socket
+   socketClose(sock);
+
+   //Successful processing
+   return SOCKET_SUCCESS;
+}
+
+
+/**
+ * @brief Determine the status of one or more sockets
+ *
+ * The select function determines the status of one or more sockets,
+ * waiting if necessary, to perform synchronous I/O
+ *
+ * @param[in] nfds Unused parameter included only for compatibility with Berkeley socket
+ * @param[in,out] readfds An optional pointer to a set of sockets to be checked for readability
+ * @param[in,out] writefds An optional pointer to a set of sockets to be checked for writability
+ * @param[in,out] exceptfds An optional pointer to a set of sockets to be checked for errors
+ * @param[in] timeout The maximum time for select to wait. Set the timeout
+ *   parameter to null for blocking operations
+ * @return The select function returns the total number of socket handles that
+ *   are ready and contained in the fd_set structures, zero if the time
+ *   limit expired, or SOCKET_ERROR if an error occurred
+ **/
+
+int_t select(int_t nfds, fd_set *readfds, fd_set *writefds,
+   fd_set *exceptfds, const timeval *timeout)
+{
+   int_t i;
+   int_t j;
+   int_t n;
+   int_t s;
+   systime_t time;
+   uint_t eventMask;
+   uint_t eventFlags;
+   OsEvent event;
+   fd_set *fds;
+
+   //Parse all the descriptor sets
+   for(i = 0; i < 3; i ++)
+   {
+      //Select the suitable descriptor set
+      switch(i)
+      {
+      case 0:
+         //Set of sockets to be checked for readability
+         fds = readfds;
+         break;
+      case 1:
+         //Set of sockets to be checked for writability
+         fds = writefds;
+         break;
+      default:
+         //Set of sockets to be checked for errors
+         fds = exceptfds;
+         break;
+      }
+
+      //Each descriptor is optional and may be omitted
+      if(fds != NULL)
+      {
+         //Parse the current set of sockets
+         for(j = 0; j < fds->fd_count; j++)
+         {
+            //Invalid socket descriptor?
+            if(fds->fd_array[j] < 0 || fds->fd_array[j] >= SOCKET_MAX_COUNT)
+            {
+               //Report an error
+               return SOCKET_ERROR;
+            }
+         }
+      }
+   }
+
+   //Create an event object to get notified of socket events
+   if(!osCreateEvent(&event))
+   {
+      //Failed to create event
+      return SOCKET_ERROR;
+   }
+
+   //Parse all the descriptor sets
+   for(i = 0; i < 3; i ++)
+   {
+      //Select the suitable descriptor set
+      switch(i)
+      {
+      case 0:
+         //Set of sockets to be checked for readability
+         fds = readfds;
+         eventMask = SOCKET_EVENT_RX_READY;
+         break;
+      case 1:
+         //Set of sockets to be checked for writability
+         fds = writefds;
+         eventMask = SOCKET_EVENT_TX_READY;
+         break;
+      default:
+         //Set of sockets to be checked for errors
+         fds = exceptfds;
+         eventMask = SOCKET_EVENT_CLOSED;
+         break;
+      }
+
+      //Each descriptor is optional and may be omitted
+      if(fds != NULL)
+      {
+         //Parse the current set of sockets
+         for(j = 0; j < fds->fd_count; j++)
+         {
+            //Get the descriptor associated with the current entry
+            s = fds->fd_array[j];
+            //Subscribe to the requested events
+            socketRegisterEvents(&socketTable[s], &event, eventMask);
+         }
+      }
+   }
+
+   //Retrieve timeout value
+   if(timeout != NULL)
+      time = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
+   else
+      time = INFINITE_DELAY;
+
+   //Block the current task until an event occurs
+   osWaitForEvent(&event, time);
+
+   //Count the number of events in the signaled state
+   n = 0;
+
+   //Parse all the descriptor sets
+   for(i = 0; i < 3; i ++)
+   {
+      //Select the suitable descriptor set
+      switch(i)
+      {
+      case 0:
+         //Set of sockets to be checked for readability
+         fds = readfds;
+         eventMask = SOCKET_EVENT_RX_READY;
+         break;
+      case 1:
+         //Set of sockets to be checked for writability
+         fds = writefds;
+         eventMask = SOCKET_EVENT_TX_READY;
+         break;
+      default:
+         //Set of sockets to be checked for errors
+         fds = exceptfds;
+         eventMask = SOCKET_EVENT_CLOSED;
+         break;
+      }
+
+      //Each descriptor is optional and may be omitted
+      if(fds != NULL)
+      {
+         //Parse the current set of sockets
+         for(j = 0; j < fds->fd_count; )
+         {
+            //Get the descriptor associated with the current entry
+            s = fds->fd_array[j];
+            //Retrieve event flags for the current socket
+            socketGetEvents(&socketTable[s], &eventFlags);
+            //Unsubscribe previously registered events
+            socketUnregisterEvents(&socketTable[s]);
+
+            //Event flag is set?
+            if(eventFlags & eventMask)
+            {
+               //Track the number of events in the signaled state
+               n++;
+               //Jump to the next socket descriptor
+               j++;
+            }
+            else
+            {
+               //Remove descriptor from the current set
+               selectFdClr(fds, s);
+            }
+         }
+      }
+   }
+
+   //Delete event object
+   osDeleteEvent(&event);
+   //Return the number of events in the signaled state
+   return n;
+}
+
+
+/**
+ * @brief Initializes a descriptor set
+ * @param[in] fds Pointer to a descriptor set
+ **/
+
+void selectFdZero(fd_set *fds)
+{
+   //Reset the descriptor count
+   fds->fd_count = 0;
+}
+
+
+/**
+ * @brief Add a descriptor to an existing set
+ * @param[in] fds Pointer to a descriptor set
+ * @param[in] s Descriptor that identifies the socket to add
+ **/
+
+void selectFdSet(fd_set *fds, int_t s)
+{
+   int_t i;
+
+   //Loop through descriptors
+   for(i = 0; i < fds->fd_count; i++)
+   {
+      //The specified descriptor is already set?
+      if(fds->fd_array[i] == s)
+         return;
+   }
+
+   //Ensure the descriptor set is not full
+   if(i < FD_SETSIZE)
+   {
+      //The specified descriptor can be safely added
+      fds->fd_array[i] = s;
+      //Adjust the size of the descriptor set
+      fds->fd_count++;
+   }
+}
+
+
+/**
+ * @brief Remove a descriptor from an existing set
+ * @param[in] fds Pointer to a descriptor set
+ * @param[in] s Descriptor that identifies the socket to remove
+ **/
+
+void selectFdClr(fd_set *fds, int_t s)
+{
+   int_t i;
+   int_t j;
+
+   //Loop through descriptors
+   for(i = 0; i < fds->fd_count; i++)
+   {
+      //Specified descriptor found?
+      if(fds->fd_array[i] == s)
+      {
+         //Adjust the size of the descriptor set
+         fds->fd_count--;
+
+         //Remove the entry from the descriptor set
+         for(j = i; j < fds->fd_count; j++)
+            fds->fd_array[j] = fds->fd_array[j + 1];
+
+         //Return immediately
+         return;
+      }
+   }
+}
+
+
+/**
+ * @brief Check whether a descriptor is set
+ * @param[in] fds Pointer to a descriptor set
+ * @param[in] s Descriptor that identifies the socket to test
+ * @return Nonzero if s is a member of the set. Otherwise, zero
+ **/
+
+int_t selectFdIsSet(fd_set *fds, int_t s)
+{
+   int_t i;
+
+   //Loop through descriptors
+   for(i = 0; i < fds->fd_count; i++)
+   {
+      //Check whether the specified descriptor is set
+      if(fds->fd_array[i] == s)
+         return TRUE;
+   }
+
+   //The specified descriptor is not currently set
+   return FALSE;
+}
+
+
+/**
+ * @brief Retrieve host address corresponding to a host name
+ * @param[in] name Name of the host to resolve
+ * @param[out] info Address of the specified host
+ * @return If no error occurs, gethostbyname returns 0. Otherwise
+ *   it returns an appropriate error code
+ **/
+
+int_t gethostbyname(const char_t *name, hostent *info)
+{
+   error_t error;
+   IpAddr ipAddr;
+
+   //Check input parameters
+   if(name == NULL || info == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Resolve host address
+   error = getHostByName(NULL, name, &ipAddr, 0);
+   //Address resolution failed?
+   if(error)
+      return error;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //IPv4 address?
+   if(ipAddr.length == sizeof(Ipv4Addr))
+   {
+      //Set address family
+      info->h_addrtype = AF_INET;
+      //Copy IPv4 address
+      info->h_length = sizeof(Ipv4Addr);
+      ipv4CopyAddr(info->h_addr, &ipAddr.ipv4Addr);
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 address?
+   if(ipAddr.length == sizeof(Ipv6Addr))
+   {
+      //Set address family
+      info->h_addrtype = AF_INET6;
+      //Copy IPv6 address
+      info->h_length = sizeof(Ipv6Addr);
+      ipv6CopyAddr(info->h_addr, &ipAddr.ipv6Addr);
+   }
+   else
+#endif
+   //Invalid address?
+   {
+      //Report an error
+      return ERROR_FAILURE;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Convert a dot-decimal string into binary data in network byte order
+ * @param[in] cp NULL-terminated string representing the IPv4 address
+ * @return Binary data in network byte order
+ **/
+
+in_addr_t inet_addr(const char_t *cp)
+{
+#if (IPV4_SUPPORT == ENABLED)
+   error_t error;
+   Ipv4Addr ipv4Addr;
+
+   //Convert a dot-decimal string to a binary IPv4 address
+   error = ipv4StringToAddr(cp, &ipv4Addr);
+
+   //Check status code
+   if(error)
+   {
+      //The input is invalid
+      return INADDR_NONE;
+   }
+   else
+   {
+      //Return the binary representation
+      return ipv4Addr;
+   }
+#else
+   //IPv4 is not implemented
+   return INADDR_NONE;
+#endif
+}
+
+
+/**
+ * @brief Convert a dot-decimal string into binary form
+ * @param[in] cp NULL-terminated string representing the IPv4 address
+ * @param[out] inp Binary data in network byte order
+ * @return The function returns non-zero if the address is valid, zero if not
+ **/
+
+int_t inet_aton(const char_t *cp, in_addr *inp)
+{
+#if (IPV4_SUPPORT == ENABLED)
+   error_t error;
+   Ipv4Addr ipv4Addr;
+
+   //Convert a dot-decimal string to a binary IPv4 address
+   error = ipv4StringToAddr(cp, &ipv4Addr);
+
+   //Check status code
+   if(error)
+   {
+      //The input is invalid
+      return 0;
+   }
+   else
+   {
+      //Copy the binary representation of the IPv4 address
+      inp->s_addr = ipv4Addr;
+      //The conversion succeeded
+      return 1;
+   }
+#else
+   //IPv4 is not implemented
+   return 0;
+#endif
+}
+
+
+/**
+ * @brief Convert a binary IPv4 address to dot-decimal notation
+ * @param[in] in Binary representation of the IPv4 address
+ * @param[out] cp Pointer to the buffer where to format the string
+ * @return Pointer to the formatted string
+ **/
+
+const char_t *inet_ntoa(in_addr in, char_t *cp)
+{
+#if (IPV4_SUPPORT == ENABLED)
+   //Convert the binary IPv4 address to dot-decimal notation
+   return ipv4AddrToString(in.s_addr, cp);
+#else
+   //Properly terminate the string
+   cp[0] = '\0';
+   //Return a pointer to the formatted string
+   return cp;
+#endif
+}
+
+
+/**
+ * @brief Convert an IPv4 or IPv6 address from text to binary form
+ * @param[in] af Address family
+ * @param[in] src NULL-terminated string representing the IP address
+ * @param[out] dst Binary representation of the IP address
+ * @return The function returns 1 on success. 0 is returned if the address
+ *   is not valid. If the address family is not valid, -1 is returned
+ **/
+
+int_t inet_pton(int_t af, const char_t *src, void *dst)
+{
+   error_t error;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //IPv4 address?
+   if(af == AF_INET)
+   {
+      Ipv4Addr ipv4Addr;
+
+      //Convert the IPv4 address from text to binary form
+      error = ipv4StringToAddr(src, &ipv4Addr);
+
+      //Check status code
+      if(error)
+      {
+         //The input is invalid
+         return 0;
+      }
+      else
+      {
+         //Copy the binary representation of the IPv4 address
+         ipv4CopyAddr(dst, &ipv4Addr);
+         //The conversion succeeded
+         return 1;
+      }
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 address?
+   if(af == AF_INET6)
+   {
+      Ipv6Addr ipv6Addr;
+
+      //Convert the IPv6 address from text to binary form
+      error = ipv6StringToAddr(src, &ipv6Addr);
+
+      //Check status code
+      if(error)
+      {
+         //The input is invalid
+         return 0;
+      }
+      else
+      {
+         //Copy the binary representation of the IPv6 address
+         ipv6CopyAddr(dst, &ipv6Addr);
+         //The conversion succeeded
+         return 1;
+      }
+   }
+   else
+#endif
+   //Invalid address family?
+   {
+      //Report an error
+      return -1;
+   }
+}
+
+
+/**
+ * @brief Convert an IPv4 or IPv6 address from binary to text
+ * @param[in] af Address family
+ * @param[in] src Binary representation of the IP address
+ * @param[out] dst NULL-terminated string representing the IP address
+ * @param[in] size Number of bytes available in the buffer
+ * @return On success, the function returns a pointer to the formatted string.
+ *   NULL is returned if there was an error
+ **/
+
+const char_t *inet_ntop(int_t af, const void *src, char_t *dst, socklen_t size)
+{
+#if (IPV4_SUPPORT == ENABLED)
+   //IPv4 address?
+   if(af == AF_INET)
+   {
+      Ipv4Addr ipv4Addr;
+
+      //Copy the binary representation of the IPv4 address
+      ipv4CopyAddr(&ipv4Addr, src);
+
+      //Convert the IPv4 address from text to binary form
+      return ipv4AddrToString(ipv4Addr, dst);
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 address?
+   if(af == AF_INET6)
+   {
+      Ipv6Addr ipv6Addr;
+
+      //Copy the binary representation of the IPv6 address
+      ipv6CopyAddr(&ipv6Addr, src);
+
+      //Convert the IPv6 address from text to binary form
+      return ipv6AddrToString(&ipv6Addr, dst);
+   }
+   else
+#endif
+   //Invalid address family?
+   {
+      //Report an error
+      return NULL;
+   }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/bsd_socket.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,321 @@
+/**
+ * @file bsd_socket.h
+ * @brief BSD socket API
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _BSD_SOCKET_H
+#define _BSD_SOCKET_H
+
+//BSD socket support
+#ifndef BSD_SOCKET_SUPPORT
+   #define BSD_SOCKET_SUPPORT ENABLED
+#elif (BSD_SOCKET_SUPPORT != ENABLED && BSD_SOCKET_SUPPORT != DISABLED)
+   #error BSD_SOCKET_SUPPORT parameter is not valid
+#endif
+
+//Keil RTX port?
+#if defined(USE_RTX) && !defined(RTX_CUSTOM_HEADER)
+
+//No support for BSD sockets
+#undef BSD_SOCKET_SUPPORT
+#define BSD_SOCKET_SUPPORT DISABLED
+
+//Windows port?
+#elif defined(_WIN32) && !defined(_DONT_USE_WINSOCK)
+
+//Undefine conflicting definitions
+#undef htons
+#undef htonl
+#undef ntohs
+#undef ntohl
+
+//Dependencies
+#include <winsock2.h>
+
+#else
+
+//Dependencies
+#include "os_port.h"
+
+//Address families
+#define AF_INET          2
+#define AF_INET6         10
+#define AF_PACKET        17
+
+//Socket types
+#define SOCK_STREAM      1
+#define SOCK_DGRAM       2
+#define SOCK_RAW         3
+
+//IP protocol identifiers
+#define IPPROTO_ICMP     1
+#define IPPROTO_IGMP     2
+#define IPPROTO_TCP      6
+#define IPPROTO_UDP      17
+#define IPPROTO_ICMPV6   58
+
+//Ethernet protocol identifiers
+#define ETH_P_ALL        0x0000
+#define ETH_P_IP         0x0800
+#define ETH_P_ARP        0x0806
+#define ETH_P_IPV6       0x86DD
+
+//Option levels
+#define SOL_SOCKET       0xFFFF
+
+//Common addresses
+#define INADDR_ANY       0x00000000
+#define INADDR_LOOPBACK  0x7F000001
+#define INADDR_BROADCAST 0xFFFFFFFF
+
+//Flags used by I/O functions
+#define MSG_PEEK         0x02
+#define MSG_DONTROUTE    0x04
+#define MSG_WAITALL      0x08
+#define MSG_DONTWAIT     0x01
+
+//Flags used by shutdown function
+#define SD_RECEIVE       0
+#define SD_SEND          1
+#define SD_BOTH          2
+
+//Socket level options
+#define SO_REUSEADDR     0x0004
+#define SO_KEEPALIVE     0x0008
+#define SO_DONTROUTE     0x0010
+#define SO_BROADCAST     0x0020
+#define SO_LINGER        0x0080
+#define SO_SNDBUF        0x1001
+#define SO_RCVBUF        0x1002
+#define SO_SNDTIMEO      0x1005
+#define SO_RCVTIMEO      0x1006
+#define SO_ERROR         0x1007
+#define SO_TYPE          0x1008
+#define SO_MAX_MSG_SIZE  0x2003
+#define SO_BINDTODEVICE  0x3000
+
+//TCP level options
+#define TCP_NODELAY      0x0001
+
+//IOCTL commands
+#define FIONREAD         0x400466FF
+#define FIONBIO          0x800466FE
+
+//FCNTL commands
+#define F_GETFL          3
+#define F_SETFL          4
+
+//FCNTL  flags
+#define O_NONBLOCK       0x0004
+
+//Status codes
+#define SOCKET_SUCCESS 0
+#define SOCKET_ERROR (-1)
+
+//Error codes
+#define EAGAIN       11
+#define EWOULDBLOCK  11
+#define EFAULT       14
+#define EINVAL       22
+#define ENOPROTOOPT  92
+#define ECONNRESET   104
+#define EISCONN      106
+#define ENOTCONN     107
+#define ESHUTDOWN    108
+#define ECONNREFUSED 111
+
+//Return codes
+#define INADDR_NONE ((in_addr_t) (-1))
+
+//Macros for manipulating and checking descriptor sets
+#define FD_SETSIZE 8
+#define FD_ZERO(fds) selectFdZero(fds)
+#define FD_SET(s, fds) selectFdSet(fds, s)
+#define FD_CLR(s, fds) selectFdClr(fds, s)
+#define FD_ISSET(s, fds) selectFdIsSet(fds, s)
+
+
+/**
+ * @brief Length type
+ **/
+
+typedef int_t socklen_t;
+
+
+/**
+ * @brief IPv4 address
+ **/
+
+typedef  uint32_t in_addr_t;
+
+
+/**
+ * @brief Socket address
+ **/
+
+typedef struct sockaddr
+{
+   uint16_t sa_family;
+   uint8_t sa_data[14];
+} sockaddr;
+
+
+/**
+ * @brief Structure that represents an IPv4 address
+ **/
+
+typedef struct in_addr
+{
+   in_addr_t s_addr;
+} in_addr;
+
+
+/**
+ * @brief IPv4 address information
+ **/
+
+typedef struct sockaddr_in
+{
+   uint16_t sin_family;
+   uint16_t sin_port;
+   in_addr sin_addr;
+   uint8_t sin_zero[8];
+} sockaddr_in;
+
+
+/**
+ * @brief Structure that represents an IPv6 address
+ **/
+
+typedef struct in6_addr
+{
+   uint8_t s6_addr[16];
+} in6_addr;
+
+
+/**
+ * @brief IPv6 address information
+ **/
+
+typedef struct sockaddr_in6
+{
+   uint16_t sin6_family;
+   uint16_t sin6_port;
+   in6_addr sin6_addr;
+} sockaddr_in6;
+
+
+/**
+ * @brief Set of sockets
+ **/
+
+typedef struct fd_set
+{
+  int_t fd_count;
+  int_t fd_array[FD_SETSIZE];
+} fd_set;
+
+
+/**
+ * @brief Timeout structure
+ **/
+
+typedef struct timeval
+{
+   uint32_t tv_sec;
+   uint32_t tv_usec;
+} timeval;
+
+
+/**
+ * @brief Information about a given host
+ **/
+
+typedef struct hostent
+{
+   uint16_t h_addrtype;
+   uint16_t h_length;
+   uint8_t h_addr[16];
+} hostent;
+
+
+//BSD socket related constants
+extern const in6_addr in6addr_any;
+extern const in6_addr in6addr_loopback;
+
+//BSD socket API
+int_t socket(int_t family, int_t type, int_t protocol);
+int_t bind(int_t s, const sockaddr *addr, socklen_t addrlen);
+int_t connect(int_t s, const sockaddr *addr, socklen_t addrlen);
+int_t listen(int_t s, int_t backlog);
+int_t accept(int_t s, sockaddr *addr, socklen_t *addrlen);
+int_t send(int_t s, const void *data, size_t length, int_t flags);
+
+int_t sendto(int_t s, const void *data, size_t length,
+   int_t flags, const sockaddr *addr, socklen_t addrlen);
+
+int_t recv(int_t s, void *data, size_t size, int_t flags);
+
+int_t recvfrom(int_t s, void *data, size_t size,
+   int_t flags, sockaddr *addr, socklen_t *addrlen);
+
+int_t getsockname(int_t s, sockaddr *addr, socklen_t *addrlen);
+int_t getpeername(int_t s, sockaddr *addr, socklen_t *addrlen);
+
+int_t setsockopt(int_t s, int_t level, int_t optname,
+   const void *optval, socklen_t optlen);
+
+int_t getsockopt(int_t s, int_t level, int_t optname,
+   void *optval, socklen_t *optlen);
+
+int_t ioctlsocket(int_t s, uint32_t cmd, void *arg);
+int_t fcntl(int_t s, int_t cmd, void *arg);
+
+int_t shutdown(int_t s, int_t how);
+int_t closesocket(int_t s);
+
+int_t select(int_t nfds, fd_set *readfds, fd_set *writefds,
+   fd_set *exceptfds, const timeval *timeout);
+
+void selectFdZero(fd_set *fds);
+void selectFdSet(fd_set *fds, int_t s);
+void selectFdClr(fd_set *fds, int_t s);
+int_t selectFdIsSet(fd_set *fds, int_t s);
+
+int_t gethostbyname(const char_t *name, hostent *info);
+
+in_addr_t inet_addr(const char_t *cp);
+
+int_t inet_aton(const char_t *cp, in_addr *inp);
+const char_t *inet_ntoa(in_addr in, char_t *cp);
+
+int_t inet_pton(int_t af, const char_t *src, void *dst);
+const char_t *inet_ntop(int_t af, const void *src, char_t *dst, socklen_t size);
+
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/ethernet.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1017 @@
+/**
+ * @file ethernet.c
+ * @brief Ethernet
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL ETH_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "core/net.h"
+#include "core/nic.h"
+#include "core/ethernet.h"
+#include "core/socket.h"
+#include "core/raw_socket.h"
+#include "core/tcp_timer.h"
+#include "ipv4/arp.h"
+#include "ipv4/ipv4.h"
+#include "ipv6/ipv6.h"
+#include "mibs/mib2_module.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (ETH_SUPPORT == ENABLED)
+
+//Unspecified MAC address
+const MacAddr MAC_UNSPECIFIED_ADDR = {{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}};
+//Broadcast MAC address
+const MacAddr MAC_BROADCAST_ADDR = {{{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}};
+//Unspecified EUI-64 address
+const Eui64 EUI64_UNSPECIFIED_ADDR = {{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}};
+
+//Padding bytes
+const uint8_t ethPadding[64] =
+{
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+//A lookup table can be used to speed up CRC calculation
+#if (ETH_FAST_CRC_SUPPORT == ENABLED)
+
+static const uint32_t crc32Table[256] =
+{
+   0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
+   0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
+   0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
+   0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
+   0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
+   0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+   0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
+   0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
+   0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
+   0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
+   0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
+   0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+   0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
+   0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
+   0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
+   0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
+   0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
+   0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+   0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
+   0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
+   0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+   0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
+   0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
+   0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+   0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
+   0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
+   0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
+   0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
+   0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
+   0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+   0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
+   0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
+   0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
+   0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
+   0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
+   0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+   0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
+   0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
+   0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+   0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
+   0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
+   0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+   0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
+   0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
+   0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
+   0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
+   0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
+   0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+   0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
+   0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
+   0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
+   0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
+   0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
+   0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+   0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
+   0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
+   0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
+   0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
+   0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
+   0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+   0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
+   0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
+   0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+   0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+};
+
+#endif
+
+
+/**
+ * @brief Ethernet related initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ethInit(NetInterface *interface)
+{
+   //Clear the MAC filter table contents
+   memset(interface->macMulticastFilter, 0,
+      sizeof(interface->macMulticastFilter));
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process an incoming Ethernet frame
+ * @param[in] interface Underlying network interface
+ * @param[in] ethFrame Incoming Ethernet frame to process
+ * @param[in] length Total frame length
+ **/
+
+void ethProcessFrame(NetInterface *interface, EthHeader *ethFrame, size_t length)
+{
+#if (IPV6_SUPPORT == ENABLED)
+   NetBuffer1 buffer;
+#endif
+   uint32_t crc;
+
+   //Total number of octets received on the interface
+   MIB2_INC_COUNTER32(interface->mibIfEntry->ifInOctets, length);
+
+   //Ensure the length of the incoming frame is valid
+   if(length < sizeof(EthHeader))
+   {
+      //Number of inbound packets that contained errors
+      MIB2_INC_COUNTER32(interface->mibIfEntry->ifInErrors, 1);
+      //Discard the received frame
+      return;
+   }
+
+   //Debug message
+   TRACE_DEBUG("Ethernet frame received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump Ethernet header contents for debugging purpose
+   ethDumpHeader(ethFrame);
+
+   //Check whether the CRC is included in the received frame
+   if(!interface->nicDriver->autoCrcStrip)
+   {
+      //Ensure the length of the incoming frame is valid
+      if(length < (sizeof(EthHeader) + ETH_CRC_SIZE))
+      {
+         //Number of inbound packets that contained errors
+         MIB2_INC_COUNTER32(interface->mibIfEntry->ifInErrors, 1);
+         //Discard the received frame
+         return;
+      }
+
+      //CRC verification not supported by hardware?
+      if(!interface->nicDriver->autoCrcVerif)
+      {
+         //The value of the residue is 0x2144DF1C when no CRC errors are detected
+         if(ethCalcCrc(ethFrame, length) != 0x2144DF1C)
+         {
+            //Debug message
+            TRACE_WARNING("Wrong CRC detected!\r\n");
+            //Number of inbound packets that contained errors
+            MIB2_INC_COUNTER32(interface->mibIfEntry->ifInErrors, 1);
+            //Discard the received frame
+            return;
+         }
+      }
+
+      //Retrieve CRC value
+      memcpy(&crc, (uint8_t *) ethFrame + length - ETH_CRC_SIZE, ETH_CRC_SIZE);
+      //Reseed the pseudo-random number generator
+      netInitRand(crc);
+   }
+
+   //Frame filtering based on destination MAC address
+   if(ethCheckDestAddr(interface, &ethFrame->destAddr))
+   {
+      //Number of inbound packets which were chosen to be discarded
+      //even though no errors had been detected
+      MIB2_INC_COUNTER32(interface->mibIfEntry->ifInDiscards, 1);
+      //Discard the received frame
+      return;
+   }
+
+   //Check whether the destination address is a group address
+   if(macIsMulticastAddr(&ethFrame->destAddr))
+   {
+      //Number of non-unicast packets delivered to a higher-layer protocol
+      MIB2_INC_COUNTER32(interface->mibIfEntry->ifInNUcastPkts, 1);
+   }
+   else
+   {
+      //Number of unicast packets delivered to a higher-layer protocol
+      MIB2_INC_COUNTER32(interface->mibIfEntry->ifInUcastPkts, 1);
+   }
+
+#if (RAW_SOCKET_SUPPORT == ENABLED)
+   //Allow raw sockets to process Ethernet packets
+   rawSocketProcessEthPacket(interface, ethFrame, length - ETH_CRC_SIZE);
+#endif
+
+   //Calculate the length of the data payload
+   length -= sizeof(EthHeader);
+
+   //Check whether the CRC is included in the received frame
+   if(!interface->nicDriver->autoCrcStrip)
+      length -= ETH_CRC_SIZE;
+
+   //Check Ethernet type field
+   switch(ntohs(ethFrame->type))
+   {
+#if (IPV4_SUPPORT == ENABLED)
+   //ARP packet received?
+   case ETH_TYPE_ARP:
+      //Process incoming ARP packet
+      arpProcessPacket(interface, (ArpPacket *) ethFrame->data, length);
+      break;
+   //IPv4 packet received?
+   case ETH_TYPE_IPV4:
+      //Process incoming IPv4 packet
+      ipv4ProcessPacket(interface, (Ipv4Header *) ethFrame->data, length);
+      break;
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 packet received?
+   case ETH_TYPE_IPV6:
+      //The incoming Ethernet frame fits in a single chunk
+      buffer.chunkCount = 1;
+      buffer.maxChunkCount = 1;
+      buffer.chunk[0].address = ethFrame->data;
+      buffer.chunk[0].length = length;
+      buffer.chunk[0].size = 0;
+
+      //Process incoming IPv6 packet
+      ipv6ProcessPacket(interface, (NetBuffer *) &buffer, 0);
+      break;
+#endif
+
+   //Unknown packet received?
+   default:
+      //Debug message
+      TRACE_WARNING("Unknown Ethernet type!\r\n");
+
+      //Number of packets received via the interface which were
+      //discarded because of an unknown or unsupported protocol
+      MIB2_INC_COUNTER32(interface->mibIfEntry->ifInUnknownProtos , 1);
+      break;
+   }
+}
+
+
+/**
+ * @brief Send an Ethernet frame
+ * @param[in] interface Underlying network interface
+ * @param[in] destAddr MAC address of the destination host
+ * @param[in] buffer Multi-part buffer containing the payload
+ * @param[in] offset Offset to the first payload byte
+ * @param[in] type Ethernet type
+ * @return Error code
+ **/
+
+error_t ethSendFrame(NetInterface *interface, const MacAddr *destAddr,
+   NetBuffer *buffer, size_t offset, uint16_t type)
+{
+   error_t error;
+   size_t length;
+   EthHeader *header;
+
+   //Is there enough space for the Ethernet header?
+   if(offset < sizeof(EthHeader))
+      return ERROR_INVALID_PARAMETER;
+
+   //Make room for the Ethernet header
+   offset -= sizeof(EthHeader);
+   //Retrieve the length of the frame
+   length = netBufferGetLength(buffer) - offset;
+
+   //Position to the beginning of the frame
+   header = netBufferAt(buffer, offset);
+
+   //Format Ethernet header
+   header->destAddr = *destAddr;
+   header->srcAddr = interface->macAddr;
+   header->type = htons(type);
+
+   //Automatic padding not supported by hardware?
+   if(!interface->nicDriver->autoPadding)
+   {
+      //The host controller should manually add padding
+      //to the packet before transmitting it
+      if(length < (ETH_MIN_FRAME_SIZE - ETH_CRC_SIZE))
+      {
+         size_t n;
+
+         //Add padding as necessary
+         n = (ETH_MIN_FRAME_SIZE - ETH_CRC_SIZE) - length;
+
+         //Append padding bytes
+         error = netBufferAppend(buffer, ethPadding, n);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //Adjust frame length
+         length += n;
+      }
+   }
+
+   //CRC calculation not supported by hardware?
+   if(!interface->nicDriver->autoCrcCalc)
+   {
+      uint32_t crc;
+
+      //Compute CRC over the header and payload
+      crc = ethCalcCrcEx(buffer, offset, length);
+      //Convert from host byte order to little-endian byte order
+      crc = htole32(crc);
+
+      //Append the calculated CRC value
+      error = netBufferAppend(buffer, &crc, sizeof(crc));
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Adjust frame length
+      length += sizeof(crc);
+   }
+
+   //Total number of octets transmitted out of the interface
+   MIB2_INC_COUNTER32(interface->mibIfEntry->ifOutOctets, length);
+
+   //Check whether the destination address is a group address
+   if(macIsMulticastAddr(&header->destAddr))
+   {
+      //Total number of non-unicast packets that higher-level protocols
+      //requested be transmitted
+      MIB2_INC_COUNTER32(interface->mibIfEntry->ifOutNUcastPkts, 1);
+   }
+   else
+   {
+      //Total number of unicast packets that higher-level protocols
+      //requested be transmitted
+      MIB2_INC_COUNTER32(interface->mibIfEntry->ifOutUcastPkts, 1);
+   }
+
+   //Debug message
+   TRACE_DEBUG("Sending Ethernet frame (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump Ethernet header contents for debugging purpose
+   ethDumpHeader(header);
+
+   //Send the resulting packet over the specified link
+   error = nicSendPacket(interface, buffer, offset);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Destination MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @param[in] macAddr Destination MAC address to be checked
+ * @return Error code
+ **/
+
+error_t ethCheckDestAddr(NetInterface *interface, const MacAddr *macAddr)
+{
+   uint_t i;
+   MacFilterEntry *entry;
+
+   //Filter out any invalid addresses
+   error_t error = ERROR_INVALID_ADDRESS;
+
+   //Host address?
+   if(macCompAddr(macAddr, &interface->macAddr))
+   {
+      error = NO_ERROR;
+   }
+   //Broadcast address?
+   else if(macCompAddr(macAddr, &MAC_BROADCAST_ADDR))
+   {
+      error = NO_ERROR;
+   }
+   //Multicast address?
+   else if(macIsMulticastAddr(macAddr))
+   {
+      //Go through the multicast filter table
+      for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+      {
+         //Point to the current entry
+         entry = &interface->macMulticastFilter[i];
+
+         //Valid entry?
+         if(entry->refCount > 0)
+         {
+            //Check whether the destination MAC address matches
+            //a relevant multicast address
+            if(macCompAddr(&entry->addr, macAddr))
+            {
+               //The MAC address is acceptable
+               error = NO_ERROR;
+               //Stop immediately
+               break;
+            }
+         }
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Add a multicast address to the MAC filter table
+ * @param[in] interface Underlying network interface
+ * @param[in] macAddr Multicast MAC address to accept
+ * @return Error code
+ **/
+
+error_t ethAcceptMulticastAddr(NetInterface *interface, const MacAddr *macAddr)
+{
+   uint_t i;
+   MacFilterEntry *entry;
+   MacFilterEntry *firstFreeEntry;
+
+   //Keep track of the first free entry
+   firstFreeEntry = NULL;
+
+   //Go through the multicast filter table
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Check whether the table already contains the specified MAC address
+         if(macCompAddr(&entry->addr, macAddr))
+         {
+            //Increment the reference count
+            entry->refCount++;
+            //No error to report
+            return NO_ERROR;
+         }
+      }
+      else
+      {
+         //Keep track of the first free entry
+         if(firstFreeEntry == NULL)
+            firstFreeEntry = entry;
+      }
+   }
+
+   //Check whether the multicast filter table is full
+   if(firstFreeEntry == NULL)
+   {
+      //A new entry cannot be added
+      return ERROR_FAILURE;
+   }
+
+   //Add a new entry to the table
+   firstFreeEntry->addr = *macAddr;
+   //Initialize the reference count
+   firstFreeEntry->refCount = 1;
+
+   //Force the network interface controller to add the current
+   //entry to its MAC filter table
+   firstFreeEntry->addFlag = TRUE;
+   firstFreeEntry->deleteFlag = FALSE;
+
+   //Update the MAC filter table
+   nicSetMulticastFilter(interface);
+
+   //Clear the flag
+   firstFreeEntry->addFlag = FALSE;
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Remove a multicast address from the MAC filter table
+ * @param[in] interface Underlying network interface
+ * @param[in] macAddr Multicast MAC address to drop
+ * @return Error code
+ **/
+
+error_t ethDropMulticastAddr(NetInterface *interface, const MacAddr *macAddr)
+{
+   uint_t i;
+   MacFilterEntry *entry;
+
+   //Go through the multicast filter table
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Specified MAC address found?
+         if(macCompAddr(&entry->addr, macAddr))
+         {
+            //Decrement the reference count
+            entry->refCount--;
+
+            //Remove the entry if the reference count drops to zero
+            if(entry->refCount == 0)
+            {
+               //Force the network interface controller to remove the current
+               //entry from its MAC filter table
+               entry->deleteFlag = TRUE;
+
+               //Update the MAC filter table
+               nicSetMulticastFilter(interface);
+
+               //Clear the flag
+               entry->deleteFlag = FALSE;
+               //Remove the multicast address from the list
+               entry->addr = MAC_UNSPECIFIED_ADDR;
+            }
+
+            //No error to report
+            return NO_ERROR;
+         }
+      }
+   }
+
+   //The specified MAC address does not exist
+   return ERROR_ADDRESS_NOT_FOUND;
+}
+
+
+/**
+ * @brief Ethernet CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t ethCalcCrc(const void *data, size_t length)
+{
+//A lookup table can be used to speed up CRC calculation
+#if (ETH_FAST_CRC_SUPPORT == ENABLED)
+   uint_t i;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed byte by byte
+      crc = (crc >> 8) ^ crc32Table[(crc & 0xFF) ^ p[i]];
+   }
+
+   //Return 1's complement value
+   return ~crc;
+
+//Bit by bit CRC calculation
+#else
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //Update CRC value
+      crc ^= p[i];
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         if(crc & 0x00000001)
+            crc = (crc >> 1) ^ 0xEDB88320;
+         else
+            crc = crc >> 1;
+      }
+   }
+
+   //Return 1's complement value
+   return ~crc;
+#endif
+}
+
+
+/**
+ * @brief Calculate CRC over a multi-part buffer
+ * @param[in] buffer Pointer to the multi-part buffer
+ * @param[in] offset Offset from the beginning of the buffer
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t ethCalcCrcEx(const NetBuffer *buffer, size_t offset, size_t length)
+{
+   uint_t i;
+   uint_t n;
+   uint32_t crc;
+   uint8_t *p;
+#if (ETH_FAST_CRC_SUPPORT == DISABLED)
+   uint_t k;
+#endif
+
+   //CRC preset value
+   crc = 0xFFFFFFFF;
+
+   //Loop through data chunks
+   for(i = 0; i < buffer->chunkCount && length > 0; i++)
+   {
+      //Is there any data to process in the current chunk?
+      if(offset < buffer->chunk[i].length)
+      {
+         //Point to the first data byte
+         p = (uint8_t *) buffer->chunk[i].address + offset;
+         //Compute the number of bytes to process
+         n = MIN(buffer->chunk[i].length - offset, length);
+         //Adjust byte counter
+         length -= n;
+
+         //Process current chunk
+         while(n > 0)
+         {
+#if (ETH_FAST_CRC_SUPPORT == ENABLED)
+            //The message is processed byte by byte
+            crc = (crc >> 8) ^ crc32Table[(crc & 0xFF) ^ *p];
+#else
+            //Update CRC value
+            crc ^= *p;
+
+            //The message is processed bit by bit
+            for(k = 0; k < 8; k++)
+            {
+               if(crc & 0x00000001)
+                  crc = (crc >> 1) ^ 0xEDB88320;
+               else
+                  crc = crc >> 1;
+            }
+#endif
+            //Next byte
+            p++;
+            n--;
+         }
+
+         //Process the next block from the start
+         offset = 0;
+      }
+      else
+      {
+         //Skip the current chunk
+         offset -= buffer->chunk[i].length;
+      }
+   }
+
+   //Return 1's complement value
+   return ~crc;
+}
+
+
+/**
+ * @brief Allocate a buffer to hold an Ethernet frame
+ * @param[in] length Desired payload length
+ * @param[out] offset Offset to the first byte of the payload
+ * @return The function returns a pointer to the newly allocated
+ *   buffer. If the system is out of resources, NULL is returned
+ **/
+
+NetBuffer *ethAllocBuffer(size_t length, size_t *offset)
+{
+   NetBuffer *buffer;
+
+   //Allocate a buffer to hold the Ethernet header and the payload
+   buffer = netBufferAlloc(length + sizeof(EthHeader));
+   //Failed to allocate buffer?
+   if(buffer == NULL)
+      return NULL;
+
+   //Offset to the first byte of the payload
+   *offset = sizeof(EthHeader);
+
+   //Return a pointer to the freshly allocated buffer
+   return buffer;
+}
+
+
+/**
+ * @brief Convert a string representation of a MAC address to a binary MAC address
+ * @param[in] str NULL-terminated string representing the MAC address
+ * @param[out] macAddr Binary representation of the MAC address
+ * @return Error code
+ **/
+
+error_t macStringToAddr(const char_t *str, MacAddr *macAddr)
+{
+   error_t error;
+   int_t i = 0;
+   int_t value = -1;
+
+   //Parse input string
+   while(1)
+   {
+      //Hexadecimal digit found?
+      if(isxdigit((uint8_t) *str))
+      {
+         //First digit to be decoded?
+         if(value < 0) value = 0;
+         //Update the value of the current byte
+         if(isdigit((uint8_t) *str))
+            value = (value * 16) + (*str - '0');
+         else if(isupper((uint8_t) *str))
+            value = (value * 16) + (*str - 'A' + 10);
+         else
+            value = (value * 16) + (*str - 'a' + 10);
+         //Check resulting value
+         if(value > 0xFF)
+         {
+            //The conversion failed
+            error = ERROR_INVALID_SYNTAX;
+            break;
+         }
+      }
+      //Dash or colon separator found?
+      else if((*str == '-' || *str == ':') && i < 6)
+      {
+         //Each separator must be preceded by a valid number
+         if(value < 0)
+         {
+            //The conversion failed
+            error = ERROR_INVALID_SYNTAX;
+            break;
+         }
+
+         //Save the current byte
+         macAddr->b[i++] = value;
+         //Prepare to decode the next byte
+         value = -1;
+      }
+      //End of string detected?
+      else if(*str == '\0' && i == 5)
+      {
+         //The NULL character must be preceded by a valid number
+         if(value < 0)
+         {
+            //The conversion failed
+            error = ERROR_INVALID_SYNTAX;
+         }
+         else
+         {
+            //Save the last byte of the MAC address
+            macAddr->b[i] = value;
+            //The conversion succeeded
+            error = NO_ERROR;
+         }
+
+         //We are done
+         break;
+      }
+      //Invalid character...
+      else
+      {
+         //The conversion failed
+         error = ERROR_INVALID_SYNTAX;
+         break;
+      }
+
+      //Point to the next character
+      str++;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Convert a MAC address to a dash delimited string
+ * @param[in] macAddr Pointer to the MAC address
+ * @param[out] str NULL-terminated string representing the MAC address
+ * @return Pointer to the formatted string
+ **/
+
+char_t *macAddrToString(const MacAddr *macAddr, char_t *str)
+{
+   static char_t buffer[18];
+
+   //The str parameter is optional
+   if(str == NULL)
+      str = buffer;
+
+   //Format MAC address
+   sprintf(str, "%02" PRIX8 "-%02" PRIX8 "-%02" PRIX8 "-%02" PRIX8 "-%02" PRIX8 "-%02" PRIX8,
+      macAddr->b[0], macAddr->b[1], macAddr->b[2], macAddr->b[3], macAddr->b[4], macAddr->b[5]);
+
+   //Return a pointer to the formatted string
+   return str;
+}
+
+
+/**
+ * @brief Convert a string representation of an EUI-64 address to a binary EUI-64 address
+ * @param[in] str NULL-terminated string representing the EUI-64 address
+ * @param[out] eui64 Binary representation of the EUI-64 address
+ * @return Error code
+ **/
+
+error_t eui64StringToAddr(const char_t *str, Eui64 *eui64)
+{
+   error_t error;
+   int_t i = 0;
+   int_t value = -1;
+
+   //Parse input string
+   while(1)
+   {
+      //Hexadecimal digit found?
+      if(isxdigit((uint8_t) *str))
+      {
+         //First digit to be decoded?
+         if(value < 0) value = 0;
+         //Update the value of the current byte
+         if(isdigit((uint8_t) *str))
+            value = (value * 16) + (*str - '0');
+         else if(isupper((uint8_t) *str))
+            value = (value * 16) + (*str - 'A' + 10);
+         else
+            value = (value * 16) + (*str - 'a' + 10);
+         //Check resulting value
+         if(value > 0xFF)
+         {
+            //The conversion failed
+            error = ERROR_INVALID_SYNTAX;
+            break;
+         }
+      }
+      //Dash or colon separator found?
+      else if((*str == '-' || *str == ':') && i < 8)
+      {
+         //Each separator must be preceded by a valid number
+         if(value < 0)
+         {
+            //The conversion failed
+            error = ERROR_INVALID_SYNTAX;
+            break;
+         }
+
+         //Save the current byte
+         eui64->b[i++] = value;
+         //Prepare to decode the next byte
+         value = -1;
+      }
+      //End of string detected?
+      else if(*str == '\0' && i == 7)
+      {
+         //The NULL character must be preceded by a valid number
+         if(value < 0)
+         {
+            //The conversion failed
+            error = ERROR_INVALID_SYNTAX;
+         }
+         else
+         {
+            //Save the last byte of the EUI-64 address
+            eui64->b[i] = value;
+            //The conversion succeeded
+            error = NO_ERROR;
+         }
+
+         //We are done
+         break;
+      }
+      //Invalid character...
+      else
+      {
+         //The conversion failed
+         error = ERROR_INVALID_SYNTAX;
+         break;
+      }
+
+      //Point to the next character
+      str++;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Convert an EUI-64 address to a dash delimited string
+ * @param[in] eui64 Pointer to the EUI-64 address
+ * @param[out] str NULL-terminated string representing the EUI-64 address
+ * @return Pointer to the formatted string
+ **/
+
+char_t *eui64AddrToString(const Eui64 *eui64, char_t *str)
+{
+   static char_t buffer[24];
+
+   //The str parameter is optional
+   if(str == NULL)
+      str = buffer;
+
+   //Format EUI-64 identifier
+   sprintf(str, "%02" PRIX8 "-%02" PRIX8 "-%02" PRIX8 "-%02" PRIX8
+      "-%02" PRIX8 "-%02" PRIX8 "-%02" PRIX8 "-%02" PRIX8,
+      eui64->b[0], eui64->b[1], eui64->b[2], eui64->b[3],
+      eui64->b[4], eui64->b[5], eui64->b[6], eui64->b[7]);
+
+   //Return a pointer to the formatted string
+   return str;
+}
+
+
+/**
+ * @brief Map a MAC address to the IPv6 modified EUI-64 identifier
+ * @param[in] macAddr Host MAC address
+ * @param[out] interfaceId IPv6 modified EUI-64 identifier
+ **/
+
+void macAddrToEui64(const MacAddr *macAddr, Eui64 *interfaceId)
+{
+   //Copy the Organization Unique Identifier (OUI)
+   interfaceId->b[0] = macAddr->b[0];
+   interfaceId->b[1] = macAddr->b[1];
+   interfaceId->b[2] = macAddr->b[2];
+
+   //The middle 16 bits are given the value 0xFFFE
+   interfaceId->b[3] = 0xFF;
+   interfaceId->b[4] = 0xFE;
+
+   //Copy the right-most 24 bits of the MAC address
+   interfaceId->b[5] = macAddr->b[3];
+   interfaceId->b[6] = macAddr->b[4];
+   interfaceId->b[7] = macAddr->b[5];
+
+   //Modified EUI-64 format interface identifiers are
+   //formed by inverting the Universal/Local bit
+   interfaceId->b[0] ^= MAC_ADDR_FLAG_LOCAL;
+}
+
+
+/**
+ * @brief Dump Ethernet header for debugging purpose
+ * @param[in] ethHeader Pointer to the Ethernet header
+ **/
+
+void ethDumpHeader(const EthHeader *ethHeader)
+{
+   //Dump Ethernet header contents
+   TRACE_DEBUG("  Dest Addr = %s\r\n", macAddrToString(&ethHeader->destAddr, NULL));
+   TRACE_DEBUG("  Src Addr = %s\r\n", macAddrToString(&ethHeader->srcAddr, NULL));
+   TRACE_DEBUG("  Type = 0x%04" PRIX16 "\r\n", ntohs(ethHeader->type));
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/ethernet.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,206 @@
+/**
+ * @file ethernet.h
+ * @brief Ethernet
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _ETHERNET_H
+#define _ETHERNET_H
+
+//Dependencies
+#include "core/net.h"
+
+//Ethernet support
+#ifndef ETH_SUPPORT
+   #define ETH_SUPPORT ENABLED
+#elif (ETH_SUPPORT != ENABLED && ETH_SUPPORT != DISABLED)
+   #error ETH_SUPPORT parameter is not valid
+#endif
+
+//Size of the multicast MAC filter
+#ifndef MAC_MULTICAST_FILTER_SIZE
+   #define MAC_MULTICAST_FILTER_SIZE 12
+#elif (MAC_MULTICAST_FILTER_SIZE < 1)
+   #error MAC_MULTICAST_FILTER_SIZE parameter is not valid
+#endif
+
+//CRC32 calculation using a pre-calculated lookup table
+#ifndef ETH_FAST_CRC_SUPPORT
+   #define ETH_FAST_CRC_SUPPORT DISABLED
+#elif (ETH_FAST_CRC_SUPPORT != ENABLED && ETH_FAST_CRC_SUPPORT != DISABLED)
+   #error ETH_FAST_CRC_SUPPORT parameter is not valid
+#endif
+
+//Minimum Ethernet frame size
+#define ETH_MIN_FRAME_SIZE 64
+//Maximum Ethernet frame size
+#define ETH_MAX_FRAME_SIZE 1518
+//Ethernet maximum transmission unit
+#define ETH_MTU 1500
+//Ethernet CRC field size
+#define ETH_CRC_SIZE 4
+
+//Copy MAC address
+#define macCopyAddr(destMacAddr, srcMacAddr) memcpy(destMacAddr, srcMacAddr, sizeof(MacAddr))
+
+//Compare MAC addresses
+#define macCompAddr(macAddr1, macAddr2) (!memcmp(macAddr1, macAddr2, sizeof(MacAddr)))
+
+//Determine whether a MAC address is a group address
+#define macIsMulticastAddr(macAddr) ((macAddr)->b[0] & 0x01)
+
+//Copy EUI-64 address
+#define eui64CopyAddr(destEui64Addr, srcEui64Addr) memcpy(destEui64Addr, srcEui64Addr, sizeof(Eui64))
+
+//Compare EUI-64 addresses
+#define eui64CompAddr(eui64Addr1, eui64Addr2) (!memcmp(eui64Addr1, eui64Addr2, sizeof(Eui64)))
+
+
+/**
+ * @brief MAC address flags
+ **/
+
+typedef enum
+{
+   MAC_ADDR_FLAG_MULTICAST = 0x01,
+   MAC_ADDR_FLAG_LOCAL     = 0x02
+} MacAddrFlags;
+
+
+/**
+ * @brief Ethernet Type field
+ **/
+
+typedef enum
+{
+   ETH_TYPE_IPV4 = 0x0800,
+   ETH_TYPE_ARP  = 0x0806,
+   ETH_TYPE_RARP = 0x8035,
+   ETH_TYPE_IPV6 = 0x86DD
+} EthType;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief MAC address
+ **/
+
+typedef __start_packed struct
+{
+   __start_packed union
+   {
+      uint8_t b[6];
+      uint16_t w[3];
+   };
+} __end_packed MacAddr;
+
+
+/**
+ * @brief EUI-64 identifier
+ **/
+
+typedef __start_packed struct
+{
+   __start_packed union
+   {
+      uint8_t b[8];
+      uint16_t w[4];
+      uint32_t dw[2];
+   };
+} __end_packed Eui64;
+
+
+/**
+ * @brief Ethernet frame header
+ **/
+
+typedef __start_packed struct
+{
+   MacAddr destAddr; //0-5
+   MacAddr srcAddr;  //6-11
+   uint16_t type;    //12-13
+   uint8_t data[];   //14
+} __end_packed EthHeader;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+/**
+ * @brief MAC filter table entry
+ **/
+
+typedef struct
+{
+   MacAddr addr;    ///<MAC address
+   uint_t refCount; ///<Reference count for the current entry
+   bool_t addFlag;
+   bool_t deleteFlag;
+} MacFilterEntry;
+
+
+//Ethernet related constants
+extern const MacAddr MAC_UNSPECIFIED_ADDR;
+extern const MacAddr MAC_BROADCAST_ADDR;
+extern const Eui64 EUI64_UNSPECIFIED_ADDR;
+extern const uint8_t ethPadding[64];
+
+//Ethernet related functions
+error_t ethInit(NetInterface *interface);
+
+void ethProcessFrame(NetInterface *interface, EthHeader *ethFrame, size_t length);
+
+error_t ethSendFrame(NetInterface *interface, const MacAddr *destAddr,
+   NetBuffer *buffer, size_t offset, uint16_t type);
+
+error_t ethCheckDestAddr(NetInterface *interface, const MacAddr *macAddr);
+error_t ethAcceptMulticastAddr(NetInterface *interface, const MacAddr *macAddr);
+error_t ethDropMulticastAddr(NetInterface *interface, const MacAddr *macAddr);
+
+uint32_t ethCalcCrc(const void *data, size_t length);
+uint32_t ethCalcCrcEx(const NetBuffer *buffer, size_t offset, size_t length);
+
+NetBuffer *ethAllocBuffer(size_t length, size_t *offset);
+
+error_t macStringToAddr(const char_t *str, MacAddr *macAddr);
+char_t *macAddrToString(const MacAddr *macAddr, char_t *str);
+
+error_t eui64StringToAddr(const char_t *str, Eui64 *eui64);
+char_t *eui64AddrToString(const Eui64 *eui64, char_t *str);
+
+void macAddrToEui64(const MacAddr *macAddr, Eui64 *interfaceId);
+
+void ethDumpHeader(const EthHeader *ethHeader);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/ip.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,683 @@
+/**
+ * @file ip.c
+ * @brief IPv4 and IPv6 common routines
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL IP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "core/ethernet.h"
+#include "core/ip.h"
+#include "ipv4/ipv4.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_misc.h"
+#include "debug.h"
+
+//Special IP address
+const IpAddr IP_ADDR_ANY = {0};
+const IpAddr IP_ADDR_UNSPECIFIED = {0};
+
+
+/**
+ * @brief Send an IP datagram
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IP pseudo header
+ * @param[in] buffer Multi-part buffer containing the payload
+ * @param[in] offset Offset to the first payload byte
+ * @param[in] ttl TTL value. Default Time-To-Live is used when this parameter is zero
+ * @return Error code
+ **/
+
+error_t ipSendDatagram(NetInterface *interface, IpPseudoHeader *pseudoHeader,
+   NetBuffer *buffer, size_t offset, uint8_t ttl)
+{
+   error_t error;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //Destination address is an IPv4 address?
+   if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
+   {
+      //Form an IPv4 packet and send it
+      error = ipv4SendDatagram(interface, &pseudoHeader->ipv4Data,
+         buffer, offset, ttl);
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //Destination address is an IPv6 address?
+   if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
+   {
+      //Form an IPv6 packet and send it
+      error = ipv6SendDatagram(interface, &pseudoHeader->ipv6Data,
+         buffer, offset, ttl);
+   }
+   else
+#endif
+   //Destination address is invalid
+   {
+      //Report an error
+      error = ERROR_INVALID_ADDRESS;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief IP source address selection
+ *
+ * This function selects the source address and the relevant network interface
+ * to be used in order to join the specified destination address
+ *
+ * @param[in,out] interface A pointer to a valid network interface may be provided as
+ *   a hint. The function returns a pointer identifying the interface to be used
+ * @param[in] destAddr Destination IP address
+ * @param[out] srcAddr Local IP address to be used
+ * @return Error code
+ **/
+
+error_t ipSelectSourceAddr(NetInterface **interface,
+   const IpAddr *destAddr, IpAddr *srcAddr)
+{
+#if (IPV4_SUPPORT == ENABLED)
+   //The destination address is an IPv4 address?
+   if(destAddr->length == sizeof(Ipv4Addr))
+   {
+      //An IPv4 address is expected
+      srcAddr->length = sizeof(Ipv4Addr);
+      //Get the most appropriate source address to use
+      return ipv4SelectSourceAddr(interface, destAddr->ipv4Addr, &srcAddr->ipv4Addr);
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //The destination address is an IPv6 address?
+   if(destAddr->length == sizeof(Ipv6Addr))
+   {
+      //An IPv6 address is expected
+      srcAddr->length = sizeof(Ipv6Addr);
+      //Get the most appropriate source address to use
+      return ipv6SelectSourceAddr(interface, &destAddr->ipv6Addr, &srcAddr->ipv6Addr);
+   }
+   else
+#endif
+   //The destination address is not valid?
+   {
+      //Report an error
+      return ERROR_INVALID_ADDRESS;
+   }
+}
+
+
+/**
+ * @brief IP checksum calculation
+ * @param[in] data Pointer to the data over which to calculate the IP checksum
+ * @param[in] length Number of bytes to process
+ * @return Checksum value
+ **/
+
+uint16_t ipCalcChecksum(const void *data, size_t length)
+{
+   //Checksum preset value
+   uint32_t checksum = 0x0000;
+
+   //Process all the data
+   while(length > 1)
+   {
+      //Update checksum value
+      checksum += *((uint16_t *) data);
+      //Point to the next 16-bit word
+      data = (uint16_t *) data + 1;
+      //Adjust the number of remaining words to process
+      length -= 2;
+   }
+
+   //Add left-over byte, if any
+   if(length > 0)
+      checksum += *((uint8_t *) data);
+
+   //Fold 32-bit sum to 16 bits
+   while(checksum >> 16)
+      checksum = (checksum & 0xFFFF) + (checksum >> 16);
+
+   //Return 1's complement value
+   return checksum ^ 0xFFFF;
+}
+
+
+/**
+ * @brief Calculate IP checksum over a multi-part buffer
+ * @param[in] buffer Pointer to the multi-part buffer
+ * @param[in] offset Offset from the beginning of the buffer
+ * @param[in] length Number of bytes to process
+ * @return Checksum value
+ **/
+
+uint16_t ipCalcChecksumEx(const NetBuffer *buffer, size_t offset, size_t length)
+{
+   uint_t i;
+   uint_t m;
+   uint_t n;
+   bool_t odd;
+   uint8_t *data;
+   uint32_t checksum;
+
+   //Checksum preset value
+   checksum = 0x0000;
+   //Total number of bytes processed
+   n = 0;
+
+   //Loop through data chunks
+   for(i = 0; i < buffer->chunkCount && n < length; i++)
+   {
+      //Is there any data to process in the current chunk?
+      if(offset < buffer->chunk[i].length)
+      {
+         //Check whether the total number of bytes already processed is odd
+         odd = (n & 1) ? TRUE : FALSE;
+
+         //Point to the first data byte
+         data = (uint8_t *) buffer->chunk[i].address + offset;
+
+         //Number of bytes available in the current chunk
+         m = buffer->chunk[i].length - offset;
+         //Limit the number of byte to process
+         m = MIN(m, length - n);
+
+         //Now adjust the total length
+         n += m;
+
+         //Data buffer is not aligned on 16-bit boundaries?
+         if((uint_t) data & 1)
+         {
+            //The total number of bytes is even?
+            if(!odd)
+            {
+               //Fold 32-bit sum to 16 bits
+               while(checksum >> 16)
+                  checksum = (checksum & 0xFFFF) + (checksum >> 16);
+               //Swap checksum value
+               checksum = ((checksum >> 8) | (checksum << 8)) & 0xFFFF;
+            }
+
+            //Restore the alignment on 16-bit boundaries
+            if(m > 0)
+            {
+#ifdef _CPU_BIG_ENDIAN
+               //Update checksum value
+               checksum += *data;
+#else
+               //Update checksum value
+               checksum += *data << 8;
+#endif
+               //Point to the next byte
+               data += 1;
+               m -= 1;
+            }
+
+            //Process the data 2 bytes at a time
+            while(m > 1)
+            {
+               //Update checksum value
+               checksum += *((uint16_t *) data);
+               //Point to the next 16-bit word
+               data += 2;
+               m -= 2;
+            }
+
+            //Add left-over byte, if any
+            if(m > 0)
+            {
+#ifdef _CPU_BIG_ENDIAN
+               //Update checksum value
+               checksum += *data << 8;
+#else
+               //Update checksum value
+               checksum += *data;
+#endif
+            }
+
+            //Restore checksum endianness
+            if(!odd)
+            {
+               //Fold 32-bit sum to 16 bits
+               while(checksum >> 16)
+                  checksum = (checksum & 0xFFFF) + (checksum >> 16);
+               //Swap checksum value
+               checksum = ((checksum >> 8) | (checksum << 8)) & 0xFFFF;
+            }
+         }
+         //Data buffer is aligned on 16-bit boundaries?
+         else
+         {
+            //The total number of bytes is odd?
+            if(odd)
+            {
+               //Fold 32-bit sum to 16 bits
+               while(checksum >> 16)
+                  checksum = (checksum & 0xFFFF) + (checksum >> 16);
+               //Swap checksum value
+               checksum = ((checksum >> 8) | (checksum << 8)) & 0xFFFF;
+            }
+
+            //Process the data 2 bytes at a time
+            while(m > 1)
+            {
+               //Update checksum value
+               checksum += *((uint16_t *) data);
+               //Point to the next 16-bit word
+               data += 2;
+               m -= 2;
+            }
+
+            //Add left-over byte, if any
+            if(m > 0)
+            {
+#ifdef _CPU_BIG_ENDIAN
+               //Update checksum value
+               checksum += *data << 8;
+#else
+               //Update checksum value
+               checksum += *data;
+#endif
+            }
+
+            //Restore checksum endianness
+            if(odd)
+            {
+               //Fold 32-bit sum to 16 bits
+               while(checksum >> 16)
+                  checksum = (checksum & 0xFFFF) + (checksum >> 16);
+               //Swap checksum value
+               checksum = ((checksum >> 8) | (checksum << 8)) & 0xFFFF;
+            }
+         }
+
+         //Process the next block from the start
+         offset = 0;
+      }
+      else
+      {
+         //Skip the current chunk
+         offset -= buffer->chunk[i].length;
+      }
+   }
+
+   //Fold 32-bit sum to 16 bits
+   while(checksum >> 16)
+      checksum = (checksum & 0xFFFF) + (checksum >> 16);
+
+   //Return 1's complement value
+   return checksum ^ 0xFFFF;
+}
+
+
+/**
+ * @brief Calculate IP upper-layer checksum
+ * @param[in] pseudoHeader Pointer to the pseudo header
+ * @param[in] pseudoHeaderLength Pseudo header length
+ * @param[in] data Pointer to the upper-layer data
+ * @param[in] dataLength Upper-layer data length
+ * @return Checksum value
+ **/
+
+uint16_t ipCalcUpperLayerChecksum(const void *pseudoHeader,
+   size_t pseudoHeaderLength, const void *data, size_t dataLength)
+{
+   //Checksum preset value
+   uint32_t checksum = 0x0000;
+
+   //Process pseudo header
+   while(pseudoHeaderLength > 1)
+   {
+      //Update checksum value
+      checksum += *((uint16_t *) pseudoHeader);
+      //Point to the next 16-bit word
+      pseudoHeader = (uint16_t *) pseudoHeader + 1;
+      //Adjust the number of remaining words to process
+      pseudoHeaderLength -= 2;
+   }
+
+   //Process upper-layer data
+   while(dataLength > 1)
+   {
+      //Update checksum value
+      checksum += *((uint16_t *) data);
+      //Point to the next 16-bit word
+      data = (uint16_t *) data + 1;
+      //Adjust the number of remaining words to process
+      dataLength -= 2;
+   }
+
+   //Add left-over byte, if any
+   if(dataLength > 0)
+   {
+#ifdef _CPU_BIG_ENDIAN
+      //Update checksum value
+      checksum += *((uint8_t *) data) << 8;
+#else
+      //Update checksum value
+      checksum += *((uint8_t *) data);
+#endif
+   }
+
+   //Fold 32-bit sum to 16 bits
+   while(checksum >> 16)
+      checksum = (checksum & 0xFFFF) + (checksum >> 16);
+
+   //Return 1's complement value
+   return checksum ^ 0xFFFF;
+}
+
+
+/**
+ * @brief Calculate IP upper-layer checksum over a multi-part buffer
+ * @param[in] pseudoHeader Pointer to the pseudo header
+ * @param[in] pseudoHeaderLength Pseudo header length
+ * @param[in] buffer Multi-part buffer containing the upper-layer data
+ * @param[in] offset Offset from the first data byte to process
+ * @param[in] length Number of data bytes to process
+ * @return Checksum value
+ **/
+
+uint16_t ipCalcUpperLayerChecksumEx(const void *pseudoHeader,
+   size_t pseudoHeaderLength, const NetBuffer *buffer, size_t offset, size_t length)
+{
+   uint32_t checksum;
+
+   //Process upper-layer data
+   checksum = ipCalcChecksumEx(buffer, offset, length);
+   //Calculate 1's complement value
+   checksum = checksum ^ 0xFFFF;
+
+   //Process pseudo header
+   while(pseudoHeaderLength > 1)
+   {
+      //Update checksum value
+      checksum += *((uint16_t *) pseudoHeader);
+      //Point to the next 16-bit word
+      pseudoHeader = (uint16_t *) pseudoHeader + 1;
+      //Adjust the number of remaining words to process
+      pseudoHeaderLength -= 2;
+   }
+
+   //Fold 32-bit sum to 16 bits
+   while(checksum >> 16)
+      checksum = (checksum & 0xFFFF) + (checksum >> 16);
+
+   //Return 1's complement value
+   return checksum ^ 0xFFFF;
+}
+
+
+/**
+ * @brief Allocate a buffer to hold an IP packet
+ * @param[in] length Desired payload length
+ * @param[out] offset Offset to the first byte of the payload
+ * @return The function returns a pointer to the newly allocated
+ *   buffer. If the system is out of resources, NULL is returned
+ **/
+
+NetBuffer *ipAllocBuffer(size_t length, size_t *offset)
+{
+   size_t headerLength;
+   NetBuffer *buffer;
+
+#if (IPV6_SUPPORT == ENABLED)
+   //Maximum overhead when using IPv6
+   headerLength = sizeof(Ipv6Header) + sizeof(Ipv6FragmentHeader);
+#else
+   //Maximum overhead when using IPv4
+   headerLength = sizeof(Ipv4Header);
+#endif
+
+#if (ETH_SUPPORT == ENABLED)
+   //Allocate a buffer to hold the Ethernet header and the IP packet
+   buffer = ethAllocBuffer(length + headerLength, offset);
+#elif (PPP_SUPPORT == ENABLED)
+   //Allocate a buffer to hold the PPP header and the IP packet
+   buffer = pppAllocBuffer(length + headerLength, offset);
+#else
+   //Allocate a buffer to hold the IP packet
+   buffer = netBufferAlloc(length + headerLength);
+   //Clear offset value
+   *offset = 0;
+#endif
+
+   //Successful memory allocation?
+   if(buffer != NULL)
+   {
+      //Offset to the first byte of the payload
+      *offset += headerLength;
+   }
+
+   //Return a pointer to the freshly allocated buffer
+   return buffer;
+}
+
+
+/**
+ * @brief Join the specified host group
+ * @param[in] interface Underlying network interface (optional parameter)
+ * @param[in] groupAddr IP address identifying the host group to join
+ * @return Error code
+ **/
+
+error_t ipJoinMulticastGroup(NetInterface *interface, const IpAddr *groupAddr)
+{
+   error_t error;
+
+   //Use default network interface?
+   if(interface == NULL)
+      interface = netGetDefaultInterface();
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+#if (IPV4_SUPPORT == ENABLED)
+   //IPv4 multicast address?
+   if(groupAddr->length == sizeof(Ipv4Addr))
+   {
+      //Join the specified host group
+      error = ipv4JoinMulticastGroup(interface, groupAddr->ipv4Addr);
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 multicast address?
+   if(groupAddr->length == sizeof(Ipv6Addr))
+   {
+      //Join the specified host group
+      error = ipv6JoinMulticastGroup(interface, &groupAddr->ipv6Addr);
+   }
+   else
+#endif
+   //Invalid IP address?
+   {
+      //Report an error
+      error = ERROR_INVALID_ADDRESS;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Leave the specified host group
+ * @param[in] interface Underlying network interface (optional parameter)
+ * @param[in] groupAddr IP address identifying the host group to leave
+ * @return Error code
+ **/
+
+error_t ipLeaveMulticastGroup(NetInterface *interface, const IpAddr *groupAddr)
+{
+   //Use default network interface?
+   if(interface == NULL)
+      interface = netGetDefaultInterface();
+
+#if (IPV4_SUPPORT == ENABLED)
+   //IPv4 multicast address?
+   if(groupAddr->length == sizeof(Ipv4Addr))
+   {
+      //Drop membership
+      return ipv4LeaveMulticastGroup(interface, groupAddr->ipv4Addr);
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 multicast address?
+   if(groupAddr->length == sizeof(Ipv6Addr))
+   {
+      //Drop membership
+      return ipv6LeaveMulticastGroup(interface, &groupAddr->ipv6Addr);
+   }
+   else
+#endif
+   //Invalid IP address?
+   {
+      return ERROR_INVALID_ADDRESS;
+   }
+}
+
+
+/**
+ * @brief Compare an IP address against the unspecified address
+ * @param[in] ipAddr IP address
+ * @return TRUE if the IP address is unspecified, else FALSE
+ **/
+
+bool_t ipIsUnspecifiedAddr(const IpAddr *ipAddr)
+{
+#if (IPV4_SUPPORT == ENABLED)
+   //IPv4 address?
+   if(ipAddr->length == sizeof(Ipv4Addr))
+   {
+      //Compare IPv4 address
+      return (ipAddr->ipv4Addr == IPV4_UNSPECIFIED_ADDR) ? TRUE : FALSE;
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 address?
+   if(ipAddr->length == sizeof(Ipv6Addr))
+   {
+      //Compare IPv6 address
+      return ipv6CompAddr(&ipAddr->ipv6Addr, &IPV6_UNSPECIFIED_ADDR);
+   }
+   else
+#endif
+   //Invalid IP address?
+   {
+      return FALSE;
+   }
+}
+
+
+/**
+ * @brief Convert a string representation of an IP address to a binary IP address
+ * @param[in] str NULL-terminated string representing the IP address
+ * @param[out] ipAddr Binary representation of the IP address
+ * @return Error code
+ **/
+
+error_t ipStringToAddr(const char_t *str, IpAddr *ipAddr)
+{
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 address?
+   if(strchr(str, ':'))
+   {
+      //IPv6 addresses are 16-byte long
+      ipAddr->length = sizeof(Ipv6Addr);
+      //Convert the string to IPv6 address
+      return ipv6StringToAddr(str, &ipAddr->ipv6Addr);
+   }
+   else
+#endif
+#if (IPV4_SUPPORT == ENABLED)
+   //IPv4 address?
+   if(strchr(str, '.'))
+   {
+      //IPv4 addresses are 4-byte long
+      ipAddr->length = sizeof(Ipv4Addr);
+      //Convert the string to IPv4 address
+      return ipv4StringToAddr(str, &ipAddr->ipv4Addr);
+   }
+   else
+#endif
+   //Invalid IP address?
+   {
+      //Report an error
+      return ERROR_FAILURE;
+   }
+}
+
+
+/**
+ * @brief Convert a binary IP address to a string representation
+ * @param[in] ipAddr Binary representation of the IP address
+ * @param[out] str NULL-terminated string representing the IP address
+ * @return Pointer to the formatted string
+ **/
+
+char_t *ipAddrToString(const IpAddr *ipAddr, char_t *str)
+{
+#if (IPV4_SUPPORT == ENABLED)
+   //IPv4 address?
+   if(ipAddr->length == sizeof(Ipv4Addr))
+   {
+      //Convert IPv4 address to string representation
+      return ipv4AddrToString(ipAddr->ipv4Addr, str);
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 address?
+   if(ipAddr->length == sizeof(Ipv6Addr))
+   {
+      //Convert IPv6 address to string representation
+      return ipv6AddrToString(&ipAddr->ipv6Addr, str);
+   }
+   else
+#endif
+   //Invalid IP address?
+   {
+      static char_t c;
+      //The str parameter is optional
+      if(!str) str = &c;
+      //Properly terminate the string
+      str[0] = '\0';
+      //Return an empty string
+      return str;
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/ip.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,118 @@
+/**
+ * @file ip.h
+ * @brief IPv4 and IPv6 common routines
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _IP_H
+#define _IP_H
+
+//Dependencies
+#include "ipv4/ipv4.h"
+#include "ipv6/ipv6.h"
+
+
+/**
+ * @brief IP supported protocols
+ **/
+
+typedef enum
+{
+   IP_PROTOCOL_TCP  = 6,
+   IP_PROTOCOL_UDP  = 17
+} IpProtocol;
+
+
+/**
+ * @brief IP network address
+ **/
+
+typedef struct
+{
+   size_t length;
+   union
+   {
+#if (IPV4_SUPPORT == ENABLED)
+      Ipv4Addr ipv4Addr;
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+      Ipv6Addr ipv6Addr;
+#endif
+   };
+} IpAddr;
+
+
+/**
+ * @brief IP pseudo header
+ **/
+
+typedef struct
+{
+   size_t length;
+   union
+   {
+#if (IPV4_SUPPORT == ENABLED)
+      Ipv4PseudoHeader ipv4Data;
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+      Ipv6PseudoHeader ipv6Data;
+#endif
+      uint8_t data[4];
+   };
+} IpPseudoHeader;
+
+
+//IP related constants
+extern const IpAddr IP_ADDR_ANY;
+extern const IpAddr IP_ADDR_UNSPECIFIED;
+
+//IP related functions
+error_t ipSendDatagram(NetInterface *interface, IpPseudoHeader *pseudoHeader,
+   NetBuffer *buffer, size_t offset, uint8_t ttl);
+
+error_t ipSelectSourceAddr(NetInterface **interface,
+   const IpAddr *destAddr, IpAddr *srcAddr);
+
+uint16_t ipCalcChecksum(const void *data, size_t length);
+uint16_t ipCalcChecksumEx(const NetBuffer *buffer, size_t offset, size_t length);
+
+uint16_t ipCalcUpperLayerChecksum(const void *pseudoHeader,
+   size_t pseudoHeaderLength, const void *data, size_t dataLength);
+
+uint16_t ipCalcUpperLayerChecksumEx(const void *pseudoHeader,
+   size_t pseudoHeaderLength, const NetBuffer *buffer, size_t offset, size_t length);
+
+NetBuffer *ipAllocBuffer(size_t length, size_t *offset);
+
+error_t ipJoinMulticastGroup(NetInterface *interface, const IpAddr *groupAddr);
+error_t ipLeaveMulticastGroup(NetInterface *interface, const IpAddr *groupAddr);
+
+bool_t ipIsUnspecifiedAddr(const IpAddr *ipAddr);
+
+error_t ipStringToAddr(const char_t *str, IpAddr *ipAddr);
+char_t *ipAddrToString(const IpAddr *ipAddr, char_t *str);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/net.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1550 @@
+/**
+ * @file net.c
+ * @brief TCP/IP stack core
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL ETH_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include "core/net.h"
+#include "core/socket.h"
+#include "core/tcp_timer.h"
+#include "core/ethernet.h"
+#include "ipv4/arp.h"
+#include "ipv4/ipv4.h"
+#include "ipv4/ipv4_routing.h"
+#include "ipv4/igmp.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_routing.h"
+#include "ipv6/mld.h"
+#include "ipv6/ndp.h"
+#include "ipv6/ndp_router_adv.h"
+#include "dhcp/dhcp_client.h"
+#include "dhcp/dhcp_server.h"
+#include "dns/dns_cache.h"
+#include "dns/dns_client.h"
+#include "mdns/mdns_client.h"
+#include "mdns/mdns_responder.h"
+#include "mdns/mdns_common.h"
+#include "dns_sd/dns_sd.h"
+#include "netbios/nbns_client.h"
+#include "netbios/nbns_responder.h"
+#include "netbios/nbns_common.h"
+#include "dns_sd/dns_sd.h"
+#include "mibs/mib2_module.h"
+#include "str.h"
+#include "debug.h"
+
+#if (WEB_SOCKET_SUPPORT == ENABLED)
+   #include "web_socket/web_socket.h"
+#endif
+
+//TCP/IP stack handle
+OsTask *netTaskHandle;
+//Mutex preventing simultaneous access to the TCP/IP stack
+OsMutex netMutex;
+//Event object to receive notifications from device drivers
+OsEvent netEvent;
+//Network interfaces
+NetInterface netInterface[NET_INTERFACE_COUNT];
+
+//TCP/IP process state
+static bool_t netTaskRunning;
+//Timestamp
+static systime_t netTimestamp;
+//Pseudo-random number generator state
+static uint32_t prngState = 0;
+
+//Mutex to prevent simultaneous access to the callback table
+static OsMutex callbackTableMutex;
+//Table that holds the registered user callbacks
+static LinkChangeCallbackDesc callbackTable[NET_CALLBACK_TABLE_SIZE];
+
+//Check TCP/IP stack configuration
+#if (NET_STATIC_OS_RESOURCES == ENABLED)
+
+//Task responsible for handling TCP/IP events
+static OsTask netTaskInstance;
+static uint_t netTaskStack[NET_TASK_STACK_SIZE];
+
+#endif
+
+
+/**
+ * @brief TCP/IP stack initialization
+ * @return Error code
+ **/
+
+error_t netInit(void)
+{
+   error_t error;
+   uint_t i;
+   NetInterface *interface;
+
+   //The TCP/IP process is currently suspended
+   netTaskRunning = FALSE;
+   //Get current time
+   netTimestamp = osGetSystemTime();
+
+   //Create a mutex to prevent simultaneous access to the TCP/IP stack
+   if(!osCreateMutex(&netMutex))
+   {
+      //Failed to create mutex
+      return ERROR_OUT_OF_RESOURCES;
+   }
+
+   //Create a event object to receive notifications from device drivers
+   if(!osCreateEvent(&netEvent))
+   {
+      //Failed to create mutex
+      return ERROR_OUT_OF_RESOURCES;
+   }
+
+   //Memory pool initialization
+   error = memPoolInit();
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Clear configuration data for each interface
+   memset(netInterface, 0, sizeof(netInterface));
+
+   //Save the number of interfaces
+   MIB2_SET_INTEGER(mib2Base.ifGroup.ifNumber, NET_INTERFACE_COUNT);
+
+   //Loop through network interfaces
+   for(i = 0; i < NET_INTERFACE_COUNT; i++)
+   {
+      //Point to the current interface
+      interface = &netInterface[i];
+
+      //Default interface name
+      sprintf(interface->name, "eth%u", i);
+      //Default interface identifier
+      interface->id = i;
+      //Default PHY address
+      interface->phyAddr = UINT8_MAX;
+
+#if (MIB2_SUPPORT == ENABLED)
+      //MIB ifEntry object
+      interface->mibIfEntry = &mib2Base.ifGroup.ifTable[i];
+#endif
+
+      //Interface identifier
+      MIB2_SET_INTEGER(interface->mibIfEntry->ifIndex, i + 1);
+      //Interface name
+      MIB2_SET_OCTET_STRING(interface->mibIfEntry->ifDescr, interface->name, strlen(interface->name));
+      MIB2_SET_OCTET_STRING_LEN(interface->mibIfEntry->ifDescrLen, strlen(interface->name));
+      //Default interface type
+      MIB2_SET_INTEGER(interface->mibIfEntry->ifType, MIB2_IF_TYPE_OTHER);
+      //Default interface state
+      MIB2_SET_INTEGER(interface->mibIfEntry->ifAdminStatus, MIB2_IF_ADMIN_STATUS_DOWN);
+      MIB2_SET_INTEGER(interface->mibIfEntry->ifOperStatus, MIB2_IF_OPER_STATUS_DOWN);
+   }
+
+   //Create a mutex to prevent simultaneous access to the callback table
+   if(!osCreateMutex(&callbackTableMutex))
+   {
+      //Failed to create mutex
+      return ERROR_OUT_OF_RESOURCES;
+   }
+
+   //Initialize callback table
+   memset(callbackTable, 0, sizeof(callbackTable));
+
+   //Socket related initialization
+   error = socketInit();
+   //Any error to report?
+   if(error)
+      return error;
+
+#if (WEB_SOCKET_SUPPORT == ENABLED)
+   //WebSocket related initialization
+   webSocketInit();
+#endif
+
+#if (IPV4_SUPPORT == ENABLED && IPV4_ROUTING_SUPPORT == ENABLED)
+   //Initialize IPv4 routing table
+   error = ipv4InitRouting();
+   //Any error to report?
+   if(error)
+      return error;
+#endif
+
+#if (IPV6_SUPPORT == ENABLED && IPV6_ROUTING_SUPPORT == ENABLED)
+   //Initialize IPv6 routing table
+   error = ipv6InitRouting();
+   //Any error to report?
+   if(error)
+      return error;
+#endif
+
+#if (UDP_SUPPORT == ENABLED)
+   //UDP related initialization
+   error = udpInit();
+   //Any error to report?
+   if(error)
+      return error;
+#endif
+
+#if (TCP_SUPPORT == ENABLED)
+   //TCP related initialization
+   error = tcpInit();
+   //Any error to report?
+   if(error)
+      return error;
+#endif
+
+#if (DNS_CLIENT_SUPPORT == ENABLED || MDNS_CLIENT_SUPPORT == ENABLED || \
+   NBNS_CLIENT_SUPPORT == ENABLED)
+   //DNS cache initialization
+   error = dnsInit();
+   //Any error to report?
+   if(error)
+      return error;
+#endif
+
+   //Initialize tick counters
+   nicTickCounter = 0;
+
+#if (PPP_SUPPORT == ENABLED)
+   pppTickCounter = 0;
+#endif
+#if (IPV4_SUPPORT == ENABLED && ETH_SUPPORT == ENABLED)
+   arpTickCounter = 0;
+#endif
+#if (IPV4_SUPPORT == ENABLED && IPV4_FRAG_SUPPORT == ENABLED)
+   ipv4FragTickCounter = 0;
+#endif
+#if (IPV4_SUPPORT == ENABLED && IGMP_SUPPORT == ENABLED)
+   igmpTickCounter = 0;
+#endif
+#if (IPV4_SUPPORT == ENABLED && AUTO_IP_SUPPORT == ENABLED)
+   autoIpTickCounter = 0;
+#endif
+#if (IPV4_SUPPORT == ENABLED && DHCP_CLIENT_SUPPORT == ENABLED)
+   dhcpClientTickCounter = 0;
+#endif
+#if (IPV4_SUPPORT == ENABLED && DHCP_SERVER_SUPPORT == ENABLED)
+   dhcpServerTickCounter = 0;
+#endif
+#if (IPV6_SUPPORT == ENABLED && IPV6_FRAG_SUPPORT == ENABLED)
+   ipv6FragTickCounter = 0;
+#endif
+#if (IPV6_SUPPORT == ENABLED && MLD_SUPPORT == ENABLED)
+   mldTickCounter = 0;
+#endif
+#if (IPV6_SUPPORT == ENABLED && NDP_SUPPORT == ENABLED)
+   ndpTickCounter = 0;
+#endif
+#if (IPV6_SUPPORT == ENABLED && NDP_ROUTER_ADV_SUPPORT == ENABLED)
+   ndpRouterAdvTickCounter = 0;
+#endif
+#if (IPV6_SUPPORT == ENABLED && DHCPV6_CLIENT_SUPPORT == ENABLED)
+   dhcpv6ClientTickCounter = 0;
+#endif
+#if (TCP_SUPPORT == ENABLED)
+   tcpTickCounter = 0;
+#endif
+#if (DNS_CLIENT_SUPPORT == ENABLED || MDNS_CLIENT_SUPPORT == ENABLED || \
+   NBNS_CLIENT_SUPPORT == ENABLED)
+   dnsTickCounter = 0;
+#endif
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+   mdnsResponderTickCounter = 0;
+#endif
+#if (DNS_SD_SUPPORT == ENABLED)
+   dnsSdTickCounter = 0;
+#endif
+
+#if (NET_STATIC_OS_RESOURCES == ENABLED)
+   //Create a task to handle TCP/IP events
+   osCreateStaticTask(&netTaskInstance, "TCP/IP Stack", (OsTaskCode) netTask,
+      NULL, netTaskStack, NET_TASK_STACK_SIZE, NET_TASK_PRIORITY);
+#else
+   //Create a task to handle TCP/IP events
+   netTaskHandle = osCreateTask("TCP/IP Stack", (OsTaskCode) netTask,
+      NULL, NET_TASK_STACK_SIZE, NET_TASK_PRIORITY);
+
+   //Unable to create the task?
+   if(netTaskHandle == OS_INVALID_HANDLE)
+      return ERROR_OUT_OF_RESOURCES;
+#endif
+
+#if (NET_RTOS_SUPPORT == DISABLED)
+   //The TCP/IP process is now running
+   netTaskRunning = TRUE;
+#endif
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set MAC address
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] macAddr MAC address
+ * @return Error code
+ **/
+
+error_t netSetMacAddr(NetInterface *interface, const MacAddr *macAddr)
+{
+   //Check parameters
+   if(interface == NULL || macAddr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (ETH_SUPPORT == ENABLED)
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Set MAC address
+   interface->macAddr = *macAddr;
+
+   //Generate the 64-bit interface identifier
+   macAddrToEui64(macAddr, &interface->eui64);
+
+   //Interface's physical address
+   MIB2_SET_OCTET_STRING(interface->mibIfEntry->ifPhysAddress, &interface->macAddr, 6);
+   MIB2_SET_OCTET_STRING_LEN(interface->mibIfEntry->ifPhysAddressLen, 6);
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+#endif
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Retrieve MAC address
+ * @param[in] interface Pointer to the desired network interface
+ * @param[out] macAddr MAC address
+ * @return Error code
+ **/
+
+error_t netGetMacAddr(NetInterface *interface, MacAddr *macAddr)
+{
+   //Check parameters
+   if(interface == NULL || macAddr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (ETH_SUPPORT == ENABLED)
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Get MAC address
+   *macAddr = interface->macAddr;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+#endif
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set EUI-64 interface identifier
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] eui64 Interface identifier
+ * @return Error code
+ **/
+
+error_t netSetEui64(NetInterface *interface, const Eui64 *eui64)
+{
+   //Check parameters
+   if(interface == NULL || eui64 == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Set interface identifier
+   interface->eui64 = *eui64;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Retrieve EUI-64 interface identifier
+ * @param[in] interface Pointer to the desired network interface
+ * @param[out] eui64 Interface identifier
+ * @return Error code
+ **/
+
+error_t netGetEui64(NetInterface *interface, Eui64 *eui64)
+{
+   //Check parameters
+   if(interface == NULL || eui64 == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Get interface identifier
+   *eui64 = interface->eui64;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set interface identifier
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] id Unique number identifying the interface
+ * @return Error code
+ **/
+
+error_t netSetInterfaceId(NetInterface *interface, uint32_t id)
+{
+   //Check parameters
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Set interface identifier
+   interface->id = id;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set interface name
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] name NULL-terminated string that contains the interface name
+ * @return Error code
+ **/
+
+error_t netSetInterfaceName(NetInterface *interface, const char_t *name)
+{
+#if (MIB2_SUPPORT == ENABLED)
+   size_t n;
+#endif
+
+   //Check parameters
+   if(interface == NULL || name == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Set interface name
+   strSafeCopy(interface->name, name, NET_MAX_IF_NAME_LEN);
+
+#if (MIB2_SUPPORT == ENABLED)
+   //Get the length of the string
+   n = strlen(interface->name);
+
+   //Text string containing information about the interface
+   MIB2_SET_OCTET_STRING(interface->mibIfEntry->ifDescr, interface->name, n);
+   MIB2_SET_OCTET_STRING_LEN(interface->mibIfEntry->ifDescrLen, n);
+#endif
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set host name
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] name NULL-terminated string that contains the host name
+ * @return Error code
+ **/
+
+error_t netSetHostname(NetInterface *interface, const char_t *name)
+{
+   //Check parameters
+   if(interface == NULL || name == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Set host name
+   strSafeCopy(interface->hostname, name, NET_MAX_HOSTNAME_LEN);
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set proxy server
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] name Proxy server name
+ * @param[in] port Proxy server port
+ * @return Error code
+ **/
+
+error_t netSetProxy(NetInterface *interface, const char_t *name, uint16_t port)
+{
+   //Check parameters
+   if(interface == NULL || name == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Set proxy server name
+   strSafeCopy(interface->proxyName, name, NET_MAX_PROXY_NAME_LEN);
+   //Set proxy server port
+   interface->proxyPort = port;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set Ethernet MAC driver
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] driver Ethernet MAC driver
+ * @return Error code
+ **/
+
+error_t netSetDriver(NetInterface *interface, const NicDriver *driver)
+{
+   //Check parameters
+   if(interface == NULL || driver == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Set Ethernet MAC driver
+   interface->nicDriver = driver;
+
+   //Set interface type
+   if(driver->type == NIC_TYPE_ETHERNET)
+   {
+      //Ethernet interface
+      MIB2_SET_INTEGER(interface->mibIfEntry->ifType, MIB2_IF_TYPE_ETHERNET_CSMACD);
+   }
+   else if(driver->type == NIC_TYPE_PPP)
+   {
+      //PPP interface
+      MIB2_SET_INTEGER(interface->mibIfEntry->ifType, MIB2_IF_TYPE_PPP);
+   }
+   else if(driver->type == NIC_TYPE_6LOWPAN)
+   {
+      //IEEE 802.15.4 WPAN interface
+      MIB2_SET_INTEGER(interface->mibIfEntry->ifType, MIB2_IF_TYPE_IEEE_802_15_4);
+   }
+   else
+   {
+      //Unknown interface type
+      MIB2_SET_INTEGER(interface->mibIfEntry->ifType, MIB2_IF_TYPE_OTHER);
+   }
+
+   //Set interface MTU
+   MIB2_SET_INTEGER(interface->mibIfEntry->ifMtu, driver->mtu);
+   //Update interface state
+   MIB2_SET_INTEGER(interface->mibIfEntry->ifAdminStatus, MIB2_IF_ADMIN_STATUS_UP);
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set Ethernet PHY driver
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] driver Ethernet PHY driver (can be NULL for MAC + PHY controller)
+ * @return Error code
+ **/
+
+error_t netSetPhyDriver(NetInterface *interface, const PhyDriver *driver)
+{
+   //Check parameters
+   if(interface == NULL || driver == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Set Ethernet PHY driver
+   interface->phyDriver = driver;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set Ethernet PHY address
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] phyAddr PHY address
+ * @return Error code
+ **/
+
+error_t netSetPhyAddr(NetInterface *interface, uint8_t phyAddr)
+{
+   //Make sure the network interface is valid
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure the PHY address is valid
+   if(phyAddr >= 32)
+      return ERROR_OUT_OF_RANGE;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Set PHY address
+   interface->phyAddr = phyAddr;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set SPI driver
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] driver Underlying SPI driver
+ * @return Error code
+ **/
+
+error_t netSetSpiDriver(NetInterface *interface, const SpiDriver *driver)
+{
+   //Check parameters
+   if(interface == NULL || driver == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Set SPI driver
+   interface->spiDriver = driver;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set UART driver
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] driver Underlying UART driver
+ * @return Error code
+ **/
+
+error_t netSetUartDriver(NetInterface *interface, const UartDriver *driver)
+{
+   //Check parameters
+   if(interface == NULL || driver == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Set UART driver
+   interface->uartDriver = driver;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set external interrupt line driver
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] driver Underlying SPI driver
+ * @return Error code
+ **/
+
+error_t netSetExtIntDriver(NetInterface *interface, const ExtIntDriver *driver)
+{
+   //Check parameters
+   if(interface == NULL || driver == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Set external interrupt line driver
+   interface->extIntDriver = driver;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set link state (for virtual drivers only)
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] linkState Link state
+ * @return Error code
+ **/
+
+error_t netSetLinkState(NetInterface *interface, NicLinkState linkState)
+{
+   //Make sure the network interface is valid
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Link state changed?
+   if(linkState != interface->linkState)
+   {
+      //Update link state
+      interface->linkState = linkState;
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Get link state
+ * @param[in] interface Pointer to the desired network interface
+ * @return Link state
+ **/
+
+bool_t netGetLinkState(NetInterface *interface)
+{
+   bool_t linkState;
+
+   //Make sure the network interface is valid
+   if(interface == NULL)
+      return FALSE;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Retrieve link state
+   linkState = interface->linkState;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return link state
+   return linkState;
+}
+
+
+/**
+ * @brief Configure network interface
+ * @param[in] interface Network interface to configure
+ * @return Error code
+ **/
+
+error_t netConfigInterface(NetInterface *interface)
+{
+   error_t error;
+
+   //Make sure the network interface is valid
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Disable hardware interrupts
+   interface->nicDriver->disableIrq(interface);
+
+   //Start of exception handling block
+   do
+   {
+      //Receive notifications when the transmitter is ready to send
+      if(!osCreateEvent(&interface->nicTxEvent))
+      {
+         //Failed to create event object
+         error = ERROR_OUT_OF_RESOURCES;
+         //Stop immediately
+         break;
+      }
+
+      //Network controller initialization
+      error = interface->nicDriver->init(interface);
+      //Any error to report?
+      if(error)
+         break;
+
+#if (ETH_SUPPORT == ENABLED)
+      //Ethernet related initialization
+      error = ethInit(interface);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Interface's physical address
+      MIB2_SET_OCTET_STRING(interface->mibIfEntry->ifPhysAddress, &interface->macAddr, 6);
+      MIB2_SET_OCTET_STRING_LEN(interface->mibIfEntry->ifPhysAddressLen, 6);
+#endif
+
+#if (IPV4_SUPPORT == ENABLED)
+      //IPv4 initialization
+      error = ipv4Init(interface);
+      //Any error to report?
+      if(error)
+         break;
+
+#if (ETH_SUPPORT == ENABLED)
+      //ARP cache initialization
+      error = arpInit(interface);
+      //Any error to report?
+      if(error)
+         break;
+#endif
+
+#if (IGMP_SUPPORT == ENABLED)
+      //IGMP related initialization
+      error = igmpInit(interface);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Join the all-systems group
+      error = ipv4JoinMulticastGroup(interface, IGMP_ALL_SYSTEMS_ADDR);
+      //Any error to report?
+      if(error)
+         break;
+#endif
+
+#if (NBNS_CLIENT_SUPPORT == ENABLED || NBNS_RESPONDER_SUPPORT == ENABLED)
+      //NetBIOS Name Service related initialization
+      error = nbnsInit(interface);
+      //Any error to report?
+      if(error)
+         break;
+#endif
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+      //IPv6 initialization
+      error = ipv6Init(interface);
+      //Any error to report?
+      if(error)
+         break;
+
+#if (NDP_SUPPORT == ENABLED)
+      //NDP related initialization
+      error = ndpInit(interface);
+      //Any error to report?
+      if(error)
+         break;
+#endif
+
+#if (MLD_SUPPORT == ENABLED)
+      //MLD related initialization
+      error = mldInit(interface);
+      //Any error to report?
+      if(error)
+         break;
+#endif
+
+      //Join the All-Nodes multicast address
+      error = ipv6JoinMulticastGroup(interface, &IPV6_LINK_LOCAL_ALL_NODES_ADDR);
+      //Any error to report?
+      if(error)
+         break;
+#endif
+
+#if (MDNS_CLIENT_SUPPORT == ENABLED || MDNS_RESPONDER_SUPPORT == ENABLED)
+      //mDNS related initialization
+      error = mdnsInit(interface);
+      //Any error to report?
+      if(error)
+         break;
+#endif
+
+      //End of exception handling block
+   } while(0);
+
+   //Check status code
+   if(!error)
+   {
+      //The network interface is now fully configured
+      interface->configured = TRUE;
+
+      //Check whether the TCP/IP process is running
+      if(netTaskRunning)
+      {
+         //Interrupts can be safely enabled
+         interface->nicDriver->enableIrq(interface);
+      }
+   }
+   else
+   {
+      //Clean up side effects before returning
+      osDeleteEvent(&interface->nicTxEvent);
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief TCP/IP events handling
+ **/
+
+void netTask(void)
+{
+   uint_t i;
+   bool_t status;
+   systime_t time;
+   systime_t timeout;
+   NetInterface *interface;
+
+#if (NET_RTOS_SUPPORT == ENABLED)
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //The TCP/IP process is now running
+   netTaskRunning = TRUE;
+
+   //Loop through network interfaces
+   for(i = 0; i < NET_INTERFACE_COUNT; i++)
+   {
+      //Point to the current network interface
+      interface = &netInterface[i];
+
+      //Check whether the interface is fully configured
+      if(interface->configured)
+      {
+         //Interrupts can be safely enabled
+         interface->nicDriver->enableIrq(interface);
+      }
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Main loop
+   while(1)
+   {
+#endif
+      //Get current time
+      time = osGetSystemTime();
+
+      //Compute the maximum blocking time when waiting for an event
+      if(timeCompare(time, netTimestamp) < 0)
+         timeout = netTimestamp - time;
+      else
+         timeout = 0;
+
+      //Receive notifications when a frame has been received, or the
+      //link state of any network interfaces has changed
+      status = osWaitForEvent(&netEvent, timeout);
+
+      //Check whether the specified event is in signaled state
+      if(status)
+      {
+         //Get exclusive access
+         osAcquireMutex(&netMutex);
+
+         //Process events
+         for(i = 0; i < NET_INTERFACE_COUNT; i++)
+         {
+            //Point to the current network interface
+            interface = &netInterface[i];
+
+            //Check whether a NIC event is pending
+            if(interface->nicEvent)
+            {
+               //Acknowledge the event by clearing the flag
+               interface->nicEvent = FALSE;
+
+               //Disable hardware interrupts
+               interface->nicDriver->disableIrq(interface);
+               //Handle NIC events
+               interface->nicDriver->eventHandler(interface);
+               //Re-enable hardware interrupts
+               interface->nicDriver->enableIrq(interface);
+            }
+
+            //Check whether a PHY event is pending
+            if(interface->phyEvent)
+            {
+               //Acknowledge the event by clearing the flag
+               interface->phyEvent = FALSE;
+
+               //Disable hardware interrupts
+               interface->nicDriver->disableIrq(interface);
+               //Handle PHY events
+               interface->phyDriver->eventHandler(interface);
+               //Re-enable hardware interrupts
+               interface->nicDriver->enableIrq(interface);
+            }
+         }
+
+         //Release exclusive access
+         osReleaseMutex(&netMutex);
+      }
+
+      //Check current time
+      if(timeCompare(time, netTimestamp) > 0)
+      {
+         //Get exclusive access
+         osAcquireMutex(&netMutex);
+         //Handle periodic operations
+         netTick();
+         //Release exclusive access
+         osReleaseMutex(&netMutex);
+
+         //Next event
+         netTimestamp = time + NET_TICK_INTERVAL;
+      }
+#if (NET_RTOS_SUPPORT == ENABLED)
+   }
+#endif
+}
+
+
+/**
+ * @brief Manage TCP/IP timers
+ **/
+
+void netTick(void)
+{
+   uint_t i;
+
+   //Increment tick counter
+   nicTickCounter += NET_TICK_INTERVAL;
+
+   //Handle periodic operations such as polling the link state
+   if(nicTickCounter >= NIC_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+      {
+         //Make sure the interface has been properly configured
+         if(netInterface[i].configured)
+            nicTick(&netInterface[i]);
+      }
+
+      //Reset tick counter
+      nicTickCounter = 0;
+   }
+
+#if (PPP_SUPPORT == ENABLED)
+   //Increment tick counter
+   pppTickCounter += NET_TICK_INTERVAL;
+
+   //Manage PPP related timers
+   if(pppTickCounter >= PPP_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+      {
+         //Make sure the interface has been properly configured
+         if(netInterface[i].configured)
+            pppTick(&netInterface[i]);
+      }
+
+      //Reset tick counter
+      pppTickCounter = 0;
+   }
+#endif
+
+#if (IPV4_SUPPORT == ENABLED && ETH_SUPPORT == ENABLED)
+   //Increment tick counter
+   arpTickCounter += NET_TICK_INTERVAL;
+
+   //Manage ARP cache
+   if(arpTickCounter >= ARP_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+      {
+         //Make sure the interface has been properly configured
+         if(netInterface[i].configured)
+            arpTick(&netInterface[i]);
+      }
+
+      //Reset tick counter
+      arpTickCounter = 0;
+   }
+#endif
+
+#if (IPV4_SUPPORT == ENABLED && IPV4_FRAG_SUPPORT == ENABLED)
+   //Increment tick counter
+   ipv4FragTickCounter += NET_TICK_INTERVAL;
+
+   //Handle IPv4 fragment reassembly timeout
+   if(ipv4FragTickCounter >= IPV4_FRAG_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+      {
+         //Make sure the interface has been properly configured
+         if(netInterface[i].configured)
+            ipv4FragTick(&netInterface[i]);
+      }
+
+      //Reset tick counter
+      ipv4FragTickCounter = 0;
+   }
+#endif
+
+#if (IPV4_SUPPORT == ENABLED && IGMP_SUPPORT == ENABLED)
+   //Increment tick counter
+   igmpTickCounter += NET_TICK_INTERVAL;
+
+   //Handle IGMP related timers
+   if(igmpTickCounter >= IGMP_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+      {
+         //Make sure the interface has been properly configured
+         if(netInterface[i].configured)
+            igmpTick(&netInterface[i]);
+      }
+
+      //Reset tick counter
+      igmpTickCounter = 0;
+   }
+#endif
+
+#if (IPV4_SUPPORT == ENABLED && AUTO_IP_SUPPORT == ENABLED)
+   //Increment tick counter
+   autoIpTickCounter += NET_TICK_INTERVAL;
+
+   //Handle Auto-IP related timers
+   if(autoIpTickCounter >= AUTO_IP_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+         autoIpTick(netInterface[i].autoIpContext);
+
+      //Reset tick counter
+      autoIpTickCounter = 0;
+   }
+#endif
+
+#if (IPV4_SUPPORT == ENABLED && DHCP_CLIENT_SUPPORT == ENABLED)
+   //Increment tick counter
+   dhcpClientTickCounter += NET_TICK_INTERVAL;
+
+   //Handle DHCP client related timers
+   if(dhcpClientTickCounter >= DHCP_CLIENT_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+         dhcpClientTick(netInterface[i].dhcpClientContext);
+
+      //Reset tick counter
+      dhcpClientTickCounter = 0;
+   }
+#endif
+
+#if (IPV4_SUPPORT == ENABLED && DHCP_SERVER_SUPPORT == ENABLED)
+   //Increment tick counter
+   dhcpServerTickCounter += NET_TICK_INTERVAL;
+
+   //Handle DHCP server related timers
+   if(dhcpServerTickCounter >= DHCP_SERVER_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+         dhcpServerTick(netInterface[i].dhcpServerContext);
+
+      //Reset tick counter
+      dhcpServerTickCounter = 0;
+   }
+#endif
+
+#if (IPV6_SUPPORT == ENABLED && IPV6_FRAG_SUPPORT == ENABLED)
+   //Increment tick counter
+   ipv6FragTickCounter += NET_TICK_INTERVAL;
+
+   //Handle IPv6 fragment reassembly timeout
+   if(ipv6FragTickCounter >= IPV6_FRAG_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+      {
+         //Make sure the interface has been properly configured
+         if(netInterface[i].configured)
+            ipv6FragTick(&netInterface[i]);
+      }
+
+      //Reset tick counter
+      ipv6FragTickCounter = 0;
+   }
+#endif
+
+#if (IPV6_SUPPORT == ENABLED && MLD_SUPPORT == ENABLED)
+   //Increment tick counter
+   mldTickCounter += NET_TICK_INTERVAL;
+
+   //Handle MLD related timers
+   if(mldTickCounter >= MLD_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+      {
+         //Make sure the interface has been properly configured
+         if(netInterface[i].configured)
+            mldTick(&netInterface[i]);
+      }
+
+      //Reset tick counter
+      mldTickCounter = 0;
+   }
+#endif
+
+#if (IPV6_SUPPORT == ENABLED && NDP_SUPPORT == ENABLED)
+   //Increment tick counter
+   ndpTickCounter += NET_TICK_INTERVAL;
+
+   //Handle NDP related timers
+   if(ndpTickCounter >= NDP_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+      {
+         //Make sure the interface has been properly configured
+         if(netInterface[i].configured)
+            ndpTick(&netInterface[i]);
+      }
+
+      //Reset tick counter
+      ndpTickCounter = 0;
+   }
+#endif
+
+#if (IPV6_SUPPORT == ENABLED && NDP_ROUTER_ADV_SUPPORT == ENABLED)
+   //Increment tick counter
+   ndpRouterAdvTickCounter += NET_TICK_INTERVAL;
+
+   //Handle RA service related timers
+   if(ndpRouterAdvTickCounter >= NDP_ROUTER_ADV_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+         ndpRouterAdvTick(netInterface[i].ndpRouterAdvContext);
+
+      //Reset tick counter
+      ndpRouterAdvTickCounter = 0;
+   }
+#endif
+
+#if (IPV6_SUPPORT == ENABLED && DHCPV6_CLIENT_SUPPORT == ENABLED)
+   //Increment tick counter
+   dhcpv6ClientTickCounter += NET_TICK_INTERVAL;
+
+   //Handle DHCPv6 client related timers
+   if(dhcpv6ClientTickCounter >= DHCPV6_CLIENT_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+         dhcpv6ClientTick(netInterface[i].dhcpv6ClientContext);
+
+      //Reset tick counter
+      dhcpv6ClientTickCounter = 0;
+   }
+#endif
+
+#if (TCP_SUPPORT == ENABLED)
+   //Increment tick counter
+   tcpTickCounter += NET_TICK_INTERVAL;
+
+   //Manage TCP related timers
+   if(tcpTickCounter >= TCP_TICK_INTERVAL)
+   {
+      //TCP timer handler
+      tcpTick();
+      //Reset tick counter
+      tcpTickCounter = 0;
+   }
+#endif
+
+#if (DNS_CLIENT_SUPPORT == ENABLED || MDNS_CLIENT_SUPPORT == ENABLED || \
+NBNS_CLIENT_SUPPORT == ENABLED)
+   //Increment tick counter
+   dnsTickCounter += NET_TICK_INTERVAL;
+
+   //Manage DNS cache
+   if(dnsTickCounter >= DNS_TICK_INTERVAL)
+   {
+      //DNS timer handler
+      dnsTick();
+      //Reset tick counter
+      dnsTickCounter = 0;
+   }
+#endif
+
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+   //Increment tick counter
+   mdnsResponderTickCounter += NET_TICK_INTERVAL;
+
+   //Manage mDNS probing and announcing
+   if(mdnsResponderTickCounter >= MDNS_RESPONDER_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+         mdnsResponderTick(netInterface[i].mdnsResponderContext);
+
+      //Reset tick counter
+      mdnsResponderTickCounter = 0;
+   }
+#endif
+
+#if (DNS_SD_SUPPORT == ENABLED)
+   //Increment tick counter
+   dnsSdTickCounter += NET_TICK_INTERVAL;
+
+   //Manage DNS-SD probing and announcing
+   if(dnsSdTickCounter >= DNS_SD_TICK_INTERVAL)
+   {
+      //Loop through network interfaces
+      for(i = 0; i < NET_INTERFACE_COUNT; i++)
+         dnsSdTick(netInterface[i].dnsSdContext);
+
+      //Reset tick counter
+      dnsSdTickCounter = 0;
+   }
+#endif
+}
+
+
+/**
+ * @brief Get default network interface
+ * @return Pointer to the default network interface to be used
+ **/
+
+NetInterface *netGetDefaultInterface(void)
+{
+   //Default network interface
+   return &netInterface[0];
+}
+
+
+/**
+ * @brief Seed pseudo-random number generator
+ * @param[in] seed An integer value to be used as seed by the pseudo-random number generator
+ * @return Error code
+ **/
+
+error_t netInitRand(uint32_t seed)
+{
+   //Seed the pseudo-random number generator
+   prngState += seed;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Get a random value
+ * @return Error code
+ **/
+
+uint32_t netGetRand(void)
+{
+   uint32_t result;
+
+   //Use a linear congruential generator (LCG) to update the state of the PRNG
+   prngState *= 1103515245;
+   prngState += 12345;
+   result = (prngState >> 16) & 0x07FF;
+
+   prngState *= 1103515245;
+   prngState += 12345;
+   result <<= 10;
+   result |= (prngState >> 16) & 0x03FF;
+
+   prngState *= 1103515245;
+   prngState += 12345;
+   result <<= 10;
+   result |= (prngState >> 16) & 0x03FF;
+
+   //Return the resulting value
+   return result;
+}
+
+
+/**
+ * @brief Get a random value in the specified range
+ * @param[in] min Lower bound
+ * @param[in] max Upper bound
+ * @return Random value in the specified range
+ **/
+
+int32_t netGetRandRange(int32_t min, int32_t max)
+{
+   //Return a random value in the given range
+   return min + netGetRand() % (max - min + 1);
+}
+
+
+/**
+ * @brief Register link change callback
+ * @param[in] interface Underlying network interface
+ * @param[in] callback Callback function to be called when the link state changed
+ * @param[in] params Callback function parameter (optional)
+ * @param[out] cookie Identifier that can be used to unregister the callback function
+ * @return Error code
+ **/
+
+error_t netAttachLinkChangeCallback(NetInterface *interface,
+   LinkChangeCallback callback, void *params, uint_t *cookie)
+{
+   uint_t i;
+   LinkChangeCallbackDesc *entry;
+
+   //Acquire exclusive access to the callback table
+   osAcquireMutex(&callbackTableMutex);
+
+   //Loop through the table
+   for(i = 0; i < NET_CALLBACK_TABLE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &callbackTable[i];
+
+      //Check whether the entry is currently in used
+      if(entry->callback == NULL)
+      {
+         //Create a new entry
+         entry->interface = interface;
+         entry->callback = callback;
+         entry->params = params;
+         //We are done
+         break;
+      }
+   }
+
+   //Release exclusive access to the callback table
+   osReleaseMutex(&callbackTableMutex);
+
+   //Failed to attach the specified user callback?
+   if(i >= NET_CALLBACK_TABLE_SIZE)
+      return ERROR_OUT_OF_RESOURCES;
+
+   //Return a cookie that can be used later to unregister the callback
+   if(cookie != NULL)
+      *cookie = i;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Unregister link change callback
+ * @param[in] cookie Identifier specifying the callback to be unregistered
+ * @return Error code
+ **/
+
+error_t netDetachLinkChangeCallback(uint_t cookie)
+{
+   //Make sure the cookie is valid
+   if(cookie >= NET_CALLBACK_TABLE_SIZE)
+      return ERROR_INVALID_PARAMETER;
+
+   //Acquire exclusive access to the callback table
+   osAcquireMutex(&callbackTableMutex);
+   //Unregister user callback
+   callbackTable[cookie].callback = NULL;
+   //Release exclusive access to the callback table
+   osReleaseMutex(&callbackTableMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Invoke link change callback
+ * @param[in] interface Underlying network interface
+ * @param[in] linkState Link state
+ **/
+
+void netInvokeLinkChangeCallback(NetInterface *interface, bool_t linkState)
+{
+   uint_t i;
+   LinkChangeCallbackDesc *entry;
+
+   //Acquire exclusive access to the callback table
+   osAcquireMutex(&callbackTableMutex);
+
+   //Loop through the table
+   for(i = 0; i < NET_CALLBACK_TABLE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &callbackTable[i];
+
+      //Any registered callback?
+      if(entry->callback != NULL)
+      {
+         //Check whether the network interface matches the current entry
+         if(entry->interface == NULL || entry->interface == interface)
+         {
+            //Invoke user callback function
+            entry->callback(interface, linkState, entry->params);
+         }
+      }
+   }
+
+   //Release exclusive access to the callback table
+   osReleaseMutex(&callbackTableMutex);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/net.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,296 @@
+/**
+ * @file net.h
+ * @brief TCP/IP stack core
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _NET_H
+#define _NET_H
+
+//Forward declaration of NetInterface structure
+struct _NetInterface;
+#define NetInterface struct _NetInterface
+
+//Dependencies
+#include "os_port.h"
+#include "net_config.h"
+#include "core/net_legacy.h"
+#include "core/net_mem.h"
+#include "core/nic.h"
+#include "core/ethernet.h"
+#include "ipv4/ipv4.h"
+#include "ipv4/ipv4_frag.h"
+#include "ipv4/auto_ip.h"
+#include "ipv6/ipv6.h"
+#include "ipv4/arp.h"
+#include "ipv6/ndp.h"
+#include "ipv6/ndp_router_adv.h"
+#include "ipv6/slaac.h"
+#include "ppp/ppp.h"
+#include "dhcp/dhcp_client.h"
+#include "dhcp/dhcp_server.h"
+#include "dhcpv6/dhcpv6_client.h"
+#include "dns/dns_client.h"
+#include "mdns/mdns_responder.h"
+#include "mdns/mdns_common.h"
+#include "dns_sd/dns_sd.h"
+#include "mibs/mib2_module.h"
+#include "cpu_endian.h"
+#include "error.h"
+
+//Version string
+#define NET_VERSION_STRING "1.7.6"
+//Major version
+#define NET_MAJOR_VERSION 1
+//Minor version
+#define NET_MINOR_VERSION 7
+//Revision number
+#define NET_REV_NUMBER 6
+
+//RTOS support
+#ifndef NET_RTOS_SUPPORT
+   #define NET_RTOS_SUPPORT ENABLED
+#elif (NET_RTOS_SUPPORT != ENABLED && NET_RTOS_SUPPORT != DISABLED)
+   #error NET_RTOS_SUPPORT parameter is not valid
+#endif
+
+//Number of network adapters
+#ifndef NET_INTERFACE_COUNT
+   #define NET_INTERFACE_COUNT 1
+#elif (NET_INTERFACE_COUNT < 1)
+   #error NET_INTERFACE_COUNT parameter is not valid
+#endif
+
+//Maximum number of callback functions that can be registered
+//to monitor link changes
+#ifndef NET_CALLBACK_TABLE_SIZE
+   #define NET_CALLBACK_TABLE_SIZE 6
+#elif (NET_CALLBACK_TABLE_SIZE < 1)
+   #error NET_CALLBACK_TABLE_SIZE parameter is not valid
+#endif
+
+//Maximum length of interface name
+#ifndef NET_MAX_IF_NAME_LEN
+   #define NET_MAX_IF_NAME_LEN 8
+#elif (NET_MAX_IF_NAME_LEN < 1)
+   #error NET_MAX_IF_NAME_LEN parameter is not valid
+#endif
+
+//Maximum length of host name
+#ifndef NET_MAX_HOSTNAME_LEN
+   #define NET_MAX_HOSTNAME_LEN 16
+#elif (NET_MAX_HOSTNAME_LEN < 1)
+   #error NET_MAX_HOSTNAME_LEN parameter is not valid
+#endif
+
+//Maximum length of proxy server name
+#ifndef NET_MAX_PROXY_NAME_LEN
+   #define NET_MAX_PROXY_NAME_LEN 16
+#elif (NET_MAX_PROXY_NAME_LEN < 1)
+   #error NET_MAX_PROXY_NAME_LEN parameter is not valid
+#endif
+
+//OS resources are statically allocated at compile time
+#ifndef NET_STATIC_OS_RESOURCES
+   #define NET_STATIC_OS_RESOURCES DISABLED
+#elif (NET_STATIC_OS_RESOURCES != ENABLED && NET_STATIC_OS_RESOURCES != DISABLED)
+   #error NET_STATIC_OS_RESOURCES parameter is not valid
+#endif
+
+//Stack size required to run the TCP/IP task
+#ifndef NET_TASK_STACK_SIZE
+   #define NET_TASK_STACK_SIZE 650
+#elif (NET_TASK_STACK_SIZE < 1)
+   #error NET_TASK_STACK_SIZE parameter is not valid
+#endif
+
+//Priority at which the TCP/IP task should run
+#ifndef NET_TASK_PRIORITY
+   #define NET_TASK_PRIORITY OS_TASK_PRIORITY_HIGH
+#endif
+
+//TCP/IP stack tick interval
+#ifndef NET_TICK_INTERVAL
+   #define NET_TICK_INTERVAL 100
+#elif (NET_TICK_INTERVAL < 10)
+   #error NET_TICK_INTERVAL parameter is not valid
+#endif
+
+
+/**
+ * @brief Structure describing a network interface
+ **/
+
+struct _NetInterface
+{
+   uint32_t id;                                   ///<A unique number identifying the interface
+   Eui64 eui64;                                   ///<EUI-64 interface identifier
+   char_t name[NET_MAX_IF_NAME_LEN + 1];          ///<A unique name identifying the interface
+   char_t hostname[NET_MAX_HOSTNAME_LEN + 1];     ///<Host name
+   char_t proxyName[NET_MAX_PROXY_NAME_LEN + 1];  ///<Proxy server name
+   uint16_t proxyPort;                            ///<Proxy server port
+   const NicDriver *nicDriver;                    ///<NIC driver
+   const PhyDriver *phyDriver;                    ///<PHY driver
+   uint8_t phyAddr;                               ///<PHY address
+   const SpiDriver *spiDriver;                    ///<Underlying SPI driver
+   const UartDriver *uartDriver;                  ///<Underlying UART driver
+   const ExtIntDriver *extIntDriver;              ///<External interrupt line driver
+   uint8_t nicContext[NIC_CONTEXT_SIZE];          ///<Driver specific context
+   OsEvent nicTxEvent;                            ///<Network controller TX event
+   bool_t nicEvent;                               ///<A NIC event is pending
+   bool_t phyEvent;                               ///<A PHY event is pending
+   bool_t linkState;                              ///<Link state
+   uint32_t linkSpeed;                            ///<Link speed
+   NicDuplexMode duplexMode;                      ///<Duplex mode
+   bool_t configured;                             ///<Configuration done
+
+#if (ETH_SUPPORT == ENABLED)
+   MacAddr macAddr;                               ///<Link-layer address
+   MacFilterEntry macMulticastFilter[MAC_MULTICAST_FILTER_SIZE]; ///<Multicast MAC filter
+#endif
+
+#if (IPV4_SUPPORT == ENABLED)
+   Ipv4Context ipv4Context;                       ///<IPv4 context
+   ArpCacheEntry arpCache[ARP_CACHE_SIZE];        ///<ARP cache
+#if (IGMP_SUPPORT == ENABLED)
+   systime_t igmpv1RouterPresentTimer;            ///<IGMPv1 router present timer
+   bool_t igmpv1RouterPresent;                    ///<An IGMPv1 query has been recently heard
+#endif
+#if (AUTO_IP_SUPPORT == ENABLED)
+   AutoIpContext *autoIpContext;                  ///<Auto-IP context
+#endif
+#if (DHCP_CLIENT_SUPPORT == ENABLED)
+   DhcpClientContext *dhcpClientContext;          ///<DHCP client context
+#endif
+#if (DHCP_SERVER_SUPPORT == ENABLED)
+   DhcpServerContext *dhcpServerContext;          ///<DHCP server context
+#endif
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+   Ipv6Context ipv6Context;                       ///<IPv6 context
+#if (NDP_SUPPORT == ENABLED)
+   NdpContext ndpContext;                         ///<NDP context
+#endif
+#if (NDP_ROUTER_ADV_SUPPORT == ENABLED)
+   NdpRouterAdvContext *ndpRouterAdvContext;      ///<RA service context
+#endif
+#if (SLAAC_SUPPORT == ENABLED)
+   SlaacContext *slaacContext;                    ///<SLAAC context
+#endif
+#if (DHCPV6_CLIENT_SUPPORT == ENABLED)
+   Dhcpv6ClientContext *dhcpv6ClientContext;      ///<DHCPv6 client context
+#endif
+#endif
+
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+   MdnsResponderContext *mdnsResponderContext;    ///<mDNS responder context
+#endif
+
+#if (DNS_SD_SUPPORT == ENABLED)
+   DnsSdContext *dnsSdContext;                    ///DNS-SD context
+#endif
+
+#if (PPP_SUPPORT == ENABLED)
+   PppContext *pppContext;                        ///<PPP context
+#endif
+
+#if (MIB2_SUPPORT == ENABLED)
+   Mib2IfEntry *mibIfEntry;
+#endif
+};
+
+
+/**
+ * @brief Link change callback
+ **/
+
+typedef void (*LinkChangeCallback)(NetInterface *interface, bool_t linkState, void *params);
+
+
+/**
+ * @brief Entry describing a user callback
+ **/
+
+typedef struct
+{
+   NetInterface *interface;
+   LinkChangeCallback callback;
+   void *params;
+} LinkChangeCallbackDesc;
+
+
+//Global variables
+extern OsTask *netTaskHandle;
+extern OsMutex netMutex;
+extern OsEvent netEvent;
+extern NetInterface netInterface[NET_INTERFACE_COUNT];
+
+//TCP/IP stack related functions
+error_t netInit(void);
+
+error_t netSetMacAddr(NetInterface *interface, const MacAddr *macAddr);
+error_t netGetMacAddr(NetInterface *interface, MacAddr *macAddr);
+
+error_t netSetEui64(NetInterface *interface, const Eui64 *eui64);
+error_t netGetEui64(NetInterface *interface, Eui64 *eui64);
+
+error_t netSetInterfaceId(NetInterface *interface, uint32_t id);
+error_t netSetInterfaceName(NetInterface *interface, const char_t *name);
+error_t netSetHostname(NetInterface *interface, const char_t *name);
+error_t netSetProxy(NetInterface *interface, const char_t *name, uint16_t port);
+
+error_t netSetDriver(NetInterface *interface, const NicDriver *driver);
+
+error_t netSetPhyDriver(NetInterface *interface, const PhyDriver *driver);
+error_t netSetPhyAddr(NetInterface *interface, uint8_t phyAddr);
+
+error_t netSetSpiDriver(NetInterface *interface, const SpiDriver *driver);
+error_t netSetUartDriver(NetInterface *interface, const UartDriver *driver);
+error_t netSetExtIntDriver(NetInterface *interface, const ExtIntDriver *driver);
+
+error_t netSetLinkState(NetInterface *interface, NicLinkState linkState);
+bool_t netGetLinkState(NetInterface *interface);
+
+error_t netConfigInterface(NetInterface *interface);
+
+void netTask(void);
+void netTick(void);
+
+NetInterface *netGetDefaultInterface(void);
+
+error_t netInitRand(uint32_t seed);
+uint32_t netGetRand(void);
+int32_t netGetRandRange(int32_t min, int32_t max);
+
+error_t netAttachLinkChangeCallback(NetInterface *interface,
+   LinkChangeCallback callback, void *params, uint_t *cookie);
+
+error_t netDetachLinkChangeCallback(uint_t cookie);
+
+void netInvokeLinkChangeCallback(NetInterface *interface, bool_t linkState);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/net_legacy.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,201 @@
+/**
+ * @file net_legacy.h
+ * @brief Legacy definitions
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _NET_LEGACY_H
+#define _NET_LEGACY_H
+
+//Check compiler
+#if !defined(_WIN32)
+
+//Deprecated properties
+#ifdef TCP_IP_CALLBACK_TABLE_SIZE
+   #warning TCP_IP_CALLBACK_TABLE_SIZE property is deprecated. NET_CALLBACK_TABLE_SIZE should be used instead.
+   #define NET_CALLBACK_TABLE_SIZE TCP_IP_CALLBACK_TABLE_SIZE
+#endif
+
+#ifdef TCP_IP_MAX_IF_NAME_LEN
+   #warning TCP_IP_MAX_IF_NAME_LEN property is deprecated. NET_MAX_IF_NAME_LEN should be used instead.
+   #define NET_MAX_IF_NAME_LEN TCP_IP_MAX_IF_NAME_LEN
+#endif
+
+#ifdef TCP_IP_MAX_HOSTNAME_LEN
+   #warning TCP_IP_MAX_HOSTNAME_LEN property is deprecated. NET_MAX_HOSTNAME_LEN should be used instead.
+   #define NET_MAX_HOSTNAME_LEN TCP_IP_MAX_HOSTNAME_LEN
+#endif
+
+#ifdef TCP_IP_MAX_PROXY_NAME_LEN
+   #warning TCP_IP_MAX_PROXY_NAME_LEN property is deprecated. NET_MAX_PROXY_NAME_LEN should be used instead.
+   #define NET_MAX_PROXY_NAME_LEN TCP_IP_MAX_PROXY_NAME_LEN
+#endif
+
+#ifdef TCP_IP_STATIC_OS_RESOURCES
+   #warning TCP_IP_STATIC_OS_RESOURCES property is deprecated. NET_STATIC_OS_RESOURCES should be used instead.
+   #define NET_STATIC_OS_RESOURCES TCP_IP_STATIC_OS_RESOURCES
+#endif
+
+#ifdef TCP_IP_TICK_STACK_SIZE
+   #warning TCP_IP_TICK_STACK_SIZE property is deprecated. NET_TICK_STACK_SIZE should be used instead.
+   #define NET_TICK_STACK_SIZE TCP_IP_TICK_STACK_SIZE
+#endif
+
+#ifdef TCP_IP_TICK_PRIORITY
+   #warning TCP_IP_TICK_PRIORITY property is deprecated. NET_TICK_PRIORITY should be used instead.
+   #define NET_TICK_PRIORITY TCP_IP_TICK_PRIORITY
+#endif
+
+#ifdef TCP_IP_TICK_INTERVAL
+   #warning TCP_IP_TICK_INTERVAL property is deprecated. NET_TICK_INTERVAL should be used instead.
+   #define NET_TICK_INTERVAL TCP_IP_TICK_INTERVAL
+#endif
+
+#ifdef TCP_IP_RX_STACK_SIZE
+   #warning TCP_IP_RX_STACK_SIZE property is deprecated. NET_RX_STACK_SIZE should be used instead.
+   #define NET_RX_STACK_SIZE TCP_IP_RX_STACK_SIZE
+#endif
+
+#ifdef TCP_IP_RX_PRIORITY
+   #warning TCP_IP_RX_PRIORITY property is deprecated. NET_RX_PRIORITY should be used instead.
+   #define NET_RX_PRIORITY TCP_IP_RX_PRIORITY
+#endif
+
+#ifdef TCP_SYN_QUEUE_SIZE
+   #warning TCP_SYN_QUEUE_SIZE property is deprecated. TCP_DEFAULT_SYN_QUEUE_SIZE should be used instead.
+   #define TCP_DEFAULT_SYN_QUEUE_SIZE TCP_SYN_QUEUE_SIZE
+#endif
+
+#ifdef MAC_FILTER_MAX_SIZE
+   //#warning MAC_FILTER_MAX_SIZE property is deprecated. MAC_MULTICAST_FILTER_SIZE should be used instead.
+   #define MAC_MULTICAST_FILTER_SIZE MAC_FILTER_MAX_SIZE
+#endif
+
+#ifdef IPV4_FILTER_MAX_SIZE
+   //#warning IPV4_FILTER_MAX_SIZE property is deprecated. IPV4_MULTICAST_FILTER_SIZE should be used instead.
+   #define IPV4_MULTICAST_FILTER_SIZE IPV4_FILTER_MAX_SIZE
+#endif
+
+#ifdef IPV6_FILTER_MAX_SIZE
+   //#warning IPV6_FILTER_MAX_SIZE property is deprecated. IPV6_MULTICAST_FILTER_SIZE should be used instead.
+   #define IPV6_MULTICAST_FILTER_SIZE IPV6_FILTER_MAX_SIZE
+#endif
+
+#ifdef IPV4_MAX_DNS_SERVERS
+   //#warning IPV4_MAX_DNS_SERVERS property is deprecated. IPV4_DNS_SERVER_LIST_SIZE should be used instead.
+   #define IPV4_DNS_SERVER_LIST_SIZE IPV4_MAX_DNS_SERVERS
+#endif
+
+#ifdef IPV6_MAX_DNS_SERVERS
+   //#warning IPV6_MAX_DNS_SERVERS property is deprecated. IPV6_DNS_SERVER_LIST_SIZE should be used instead.
+   #define IPV6_DNS_SERVER_LIST_SIZE IPV6_MAX_DNS_SERVERS
+#endif
+
+#ifdef NET_TICK_STACK_SIZE
+   #warning NET_TICK_STACK_SIZE property is deprecated and should be removed from net_config.h. The TCP/IP stack now uses a single task
+#endif
+
+#ifdef NET_TICK_PRIORITY
+   #warning NET_TICK_PRIORITY property is deprecated and should be removed from net_config.h. TCP/IP stack now uses a single task.
+#endif
+
+#ifdef NET_RX_STACK_SIZE
+   #warning NET_RX_STACK_SIZE property is deprecated since the TCP/IP stack now uses a single task. NET_TASK_STACK_SIZE should be used instead.
+   #define NET_TASK_STACK_SIZE NET_RX_STACK_SIZE
+#endif
+
+#ifdef NET_RX_PRIORITY
+   #warning NET_RX_PRIORITY property is deprecated since the TCP/IP stack now uses a single task. NET_TASK_PRIORITY should be used instead.
+   #define NET_TASK_PRIORITY NET_RX_PRIORITY
+#endif
+
+#endif
+
+//Legacy definitions
+#define SOCKET_TYPE_RAW SOCKET_TYPE_RAW_IP
+#define SOCKET_PROTOCOL_ICMP SOCKET_IP_PROTO_ICMP
+#define SOCKET_PROTOCOL_IGMP SOCKET_IP_PROTO_IGMP
+#define SOCKET_PROTOCOL_TCP SOCKET_IP_PROTO_TCP
+#define SOCKET_PROTOCOL_UDP SOCKET_IP_PROTO_UDP
+#define SOCKET_PROTOCOL_ICMPV6 SOCKET_IP_PROTO_ICMPV6
+
+//Deprecated functions
+#define tcpIpStackInit netInit
+#define tcpIpStackSetInterfaceName netSetInterfaceName
+#define tcpIpStackSetHostname netSetHostname
+#define tcpIpStackSetDriver netSetDriver
+#define tcpIpStackSetPhyDriver netSetPhyDriver
+#define tcpIpStackSetSpiDriver netSetSpiDriver
+#define tcpIpStackSetUartDriver netSetUartDriver
+#define tcpIpStackSetExtIntDriver netSetExtIntDriver
+#define tcpIpStackSetMacAddr netSetMacAddr
+#define tcpIpStackSetProxy netSetProxy
+#define tcpIpStackGetLinkState netGetLinkState
+#define tcpIpStackConfigInterface netConfigInterface
+#define tcpIpStackTickTask netTickTask
+#define tcpIpStackRxTask netRxTask
+#define tcpIpStackGetDefaultInterface netGetDefaultInterface
+#define tcpIpStackInitRand netInitRand
+#define tcpIpStackGetRand netGetRand
+#define tcpIpStackGetRandRange netGetRandRange
+#define tcpIpStackAttachLinkChangeCallback netAttachLinkChangeCallback
+#define tcpIpStackDetachLinkChangeCallback netDetachLinkChangeCallback
+#define tcpIpStackInvokeLinkChangeCallback netInvokeLinkChangeCallback
+
+#define ChunkedBuffer NetBuffer
+#define chunkedBufferGetLength netBufferGetLength
+#define chunkedBufferRead netBufferRead
+
+#define ipv4IsInLocalSubnet ipv4IsOnLocalSubnet
+
+#ifdef SMTP_DEFAULT_TIMEOUT
+   #define SMTP_CLIENT_DEFAULT_TIMEOUT SMTP_DEFAULT_TIMEOUT
+#endif
+
+#ifdef SMTP_MAX_LINE_LENGTH
+   #define SMTP_CLIENT_MAX_LINE_LENGTH SMTP_MAX_LINE_LENGTH
+#endif
+
+#ifdef SMTP_TLS_SUPPORT
+   #define SMTP_CLIENT_TLS_SUPPORT SMTP_TLS_SUPPORT
+#endif
+
+#ifdef SMTP_LOGIN_AUTH_SUPPORT
+   #define SMTP_CLIENT_LOGIN_AUTH_SUPPORT SMTP_LOGIN_AUTH_SUPPORT
+#endif
+
+#ifdef SMTP_PLAIN_AUTH_SUPPORT
+   #define SMTP_CLIENT_PLAIN_AUTH_SUPPORT SMTP_PLAIN_AUTH_SUPPORT
+#endif
+
+#ifdef SMTP_CRAM_MD5_AUTH_SUPPORT
+   #define SMTP_CLIENT_CRAM_MD5_AUTH_SUPPORT SMTP_CRAM_MD5_AUTH_SUPPORT
+#endif
+
+#define DhcpClientCtx DhcpClientContext
+#define Dhcpv6ClientCtx Dhcpv6ClientContext
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/net_mem.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,712 @@
+/**
+ * @file net_mem.c
+ * @brief Memory management
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL MEM_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "core/net_mem.h"
+#include "debug.h"
+
+//Maximum number of chunks for dynamically allocated buffers
+#if (IPV4_SUPPORT == ENABLED && IPV6_SUPPORT == ENABLED)
+   #define MAX_CHUNK_COUNT (N(MAX(IPV4_MAX_FRAG_DATAGRAM_SIZE, IPV6_MAX_FRAG_DATAGRAM_SIZE)) + 3)
+#elif (IPV4_SUPPORT == ENABLED)
+   #define MAX_CHUNK_COUNT (N(IPV4_MAX_FRAG_DATAGRAM_SIZE) + 3)
+#elif (IPV6_SUPPORT == ENABLED)
+   #define MAX_CHUNK_COUNT (N(IPV6_MAX_FRAG_DATAGRAM_SIZE) + 3)
+#endif
+
+//Use fixed-size blocks allocation?
+#if (NET_MEM_POOL_SUPPORT == ENABLED)
+
+//Mutex preventing simultaneous access to the memory pool
+static OsMutex memPoolMutex;
+//Memory pool
+static uint8_t memPool[NET_MEM_POOL_BUFFER_COUNT][NET_MEM_POOL_BUFFER_SIZE];
+//Allocation table
+static bool_t memPoolAllocTable[NET_MEM_POOL_BUFFER_COUNT];
+//Number of buffers currently allocated
+uint_t memPoolCurrentUsage;
+//Maximum number of buffers that have been allocated so far
+uint_t memPoolMaxUsage;
+
+#endif
+
+
+/**
+ * @brief Memory pool initialization
+ * @return Error code
+ **/
+
+error_t memPoolInit(void)
+{
+//Use fixed-size blocks allocation?
+#if (NET_MEM_POOL_SUPPORT == ENABLED)
+   //Create a mutex to prevent simultaneous access to the memory pool
+   if(!osCreateMutex(&memPoolMutex))
+   {
+      //Failed to create mutex
+      return ERROR_OUT_OF_RESOURCES;
+   }
+
+   //Clear allocation table
+   memset(memPoolAllocTable, 0, sizeof(memPoolAllocTable));
+
+   //Clear statistics
+   memPoolCurrentUsage = 0;
+   memPoolMaxUsage = 0;
+#endif
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Allocate a memory block
+ * @param[in] size Bytes to allocate
+ * @return Pointer to the allocated space or NULL if there is insufficient memory available
+ **/
+
+void *memPoolAlloc(size_t size)
+{
+#if (NET_MEM_POOL_SUPPORT == ENABLED)
+   uint_t i;
+#endif
+
+   //Pointer to the allocated memory block
+   void *p = NULL;
+
+   //Debug message
+   TRACE_DEBUG("Allocating %" PRIuSIZE " bytes...\r\n", size);
+
+//Use fixed-size blocks allocation?
+#if (NET_MEM_POOL_SUPPORT == ENABLED)
+   //Acquire exclusive access to the memory pool
+   osAcquireMutex(&memPoolMutex);
+
+   //Enforce block size
+   if(size <= NET_MEM_POOL_BUFFER_SIZE)
+   {
+      //Loop through allocation table
+      for(i = 0; i < NET_MEM_POOL_BUFFER_COUNT; i++)
+      {
+         //Check whether the current block is free
+         if(!memPoolAllocTable[i])
+         {
+            //Mark the current entry as used
+            memPoolAllocTable[i] = TRUE;
+            //Point to the corresponding memory block
+            p = memPool[i];
+
+            //Update statistics
+            memPoolCurrentUsage++;
+            //Maximum number of buffers that have been allocated so far
+            memPoolMaxUsage = MAX(memPoolCurrentUsage, memPoolMaxUsage);
+
+            //Exit immediately
+            break;
+         }
+      }
+   }
+
+   //Release exclusive access to the memory pool
+   osReleaseMutex(&memPoolMutex);
+#else
+   //Allocate a memory block
+   p = osAllocMem(size);
+#endif
+
+   //Failed to allocate memory?
+   if(!p)
+   {
+      //Debug message
+      TRACE_WARNING("Memory allocation failed!\r\n");
+   }
+
+   //Return a pointer to the allocated memory block
+   return p;
+}
+
+
+/**
+ * @brief Release a memory block
+ * @param[in] p Previously allocated memory block to be freed
+ **/
+
+void memPoolFree(void *p)
+{
+//Use fixed-size blocks allocation?
+#if (NET_MEM_POOL_SUPPORT == ENABLED)
+   uint_t i;
+
+   //Acquire exclusive access to the memory pool
+   osAcquireMutex(&memPoolMutex);
+
+   //Loop through allocation table
+   for(i = 0; i < NET_MEM_POOL_BUFFER_COUNT; i++)
+   {
+      if(memPool[i] == p)
+      {
+         //Mark the current block as free
+         memPoolAllocTable[i] = FALSE;
+
+         //Update statistics
+         memPoolCurrentUsage--;
+
+         //Exit immediately
+         break;
+      }
+   }
+
+   //Release exclusive access to the memory pool
+   osReleaseMutex(&memPoolMutex);
+#else
+   //Release memory block
+   osFreeMem(p);
+#endif
+}
+
+
+/**
+ * @brief Get memory pool usage
+ * @param[out] currentUsage Number of buffers currently allocated
+ * @param[out] maxUsage Maximum number of buffers that have been allocated so far
+ * @param[out] size Total number of buffers in the memory pool
+ **/
+
+void memPoolGetStats(uint_t *currentUsage, uint_t *maxUsage, uint_t *size)
+{
+//Use fixed-size blocks allocation?
+#if (NET_MEM_POOL_SUPPORT == ENABLED)
+   //Number of buffers currently allocated
+   if(currentUsage != NULL)
+      *currentUsage = memPoolCurrentUsage;
+
+   //Maximum number of buffers that have been allocated so far
+   if(maxUsage != NULL)
+      *maxUsage = memPoolMaxUsage;
+
+   //Total number of buffers in the memory pool
+   if(size != NULL)
+      *size = NET_MEM_POOL_BUFFER_COUNT;
+#else
+   //Memory pool is not used...
+   if(currentUsage != NULL)
+      *currentUsage = 0;
+
+   if(maxUsage != NULL)
+      *maxUsage = 0;
+
+   if(size != NULL)
+      *size = 0;
+#endif
+}
+
+
+/**
+ * @brief Allocate a multi-part buffer
+ * @param[in] length Desired length
+ * @return Pointer to the allocated buffer or NULL if there is
+ *   insufficient memory available
+ **/
+
+NetBuffer *netBufferAlloc(size_t length)
+{
+   error_t error;
+   NetBuffer *buffer;
+
+   //Allocate memory to hold the multi-part buffer
+   buffer = memPoolAlloc(NET_MEM_POOL_BUFFER_SIZE);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return NULL;
+
+   //The multi-part buffer consists of a single chunk
+   buffer->chunkCount = 1;
+   buffer->maxChunkCount = MAX_CHUNK_COUNT;
+   buffer->chunk[0].address = (uint8_t *) buffer + CHUNKED_BUFFER_HEADER_SIZE;
+   buffer->chunk[0].length = NET_MEM_POOL_BUFFER_SIZE - CHUNKED_BUFFER_HEADER_SIZE;
+   buffer->chunk[0].size = 0;
+
+   //Adjust the length of the buffer
+   error = netBufferSetLength(buffer, length);
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects
+      netBufferFree(buffer);
+      //Report an failure
+      return NULL;
+   }
+
+   //Successful memory allocation
+   return buffer;
+}
+
+
+/**
+ * @brief Dispose a multi-part buffer
+ * @param[in] buffer Pointer to the multi-part buffer to be released
+ **/
+
+void netBufferFree(NetBuffer *buffer)
+{
+   //Properly dispose data chunks
+   netBufferSetLength(buffer, 0);
+   //Release multi-part buffer
+   memPoolFree(buffer);
+}
+
+
+/**
+ * @brief Get the actual length of a multi-part buffer
+ * @param[in] buffer Pointer to a multi-part buffer
+ * @return Actual length in bytes
+ **/
+
+size_t netBufferGetLength(const NetBuffer *buffer)
+{
+   uint_t i;
+
+   //Total length
+   size_t length = 0;
+
+   //Loop through data chunks
+   for(i = 0; i < buffer->chunkCount; i++)
+      length += buffer->chunk[i].length;
+
+   //Return total length
+   return length;
+}
+
+
+/**
+ * @brief Adjust the length of a multi-part buffer
+ * @param[in] buffer Pointer to the multi-part buffer whose length is to be changed
+ * @param[in] length Desired length
+ * @return Error code
+ **/
+
+error_t netBufferSetLength(NetBuffer *buffer, size_t length)
+{
+   uint_t i;
+   uint_t chunkCount;
+   ChunkDesc *chunk;
+
+   //Get the actual number of chunks
+   chunkCount = buffer->chunkCount;
+
+   //Loop through data chunks
+   for(i = 0; i < chunkCount && length > 0; i++)
+   {
+      //Point to the chunk descriptor;
+      chunk = &buffer->chunk[i];
+
+      //Adjust the length of the current chunk when possible
+      if(length <= chunk->length)
+      {
+         chunk->length = length;
+      }
+      else if(chunk->size > 0 && i == (chunkCount - 1))
+      {
+         chunk->length = MIN(length, chunk->size);
+      }
+
+      //Prepare to process next chunk
+      length -= chunk->length;
+   }
+
+   //The size of the buffer should be decreased?
+   if(!length)
+   {
+      //Adjust the number of chunks
+      buffer->chunkCount = i;
+
+      //Delete unnecessary data chunks
+      while(i < chunkCount)
+      {
+         //Point to the chunk descriptor;
+         chunk = &buffer->chunk[i];
+
+         //Release previously allocated memory
+         if(chunk->size > 0)
+            memPoolFree(chunk->address);
+
+         //Mark the current chunk as free
+         chunk->address = NULL;
+         chunk->length = 0;
+         chunk->size = 0;
+
+         //Next chunk
+         i++;
+      }
+   }
+   //The size of the buffer should be increased?
+   else
+   {
+      //Add as many chunks as necessary
+      while(i < buffer->maxChunkCount && length > 0)
+      {
+         //Point to the chunk descriptor;
+         chunk = &buffer->chunk[i];
+
+         //Allocate memory to hold a new chunk
+         chunk->address = memPoolAlloc(NET_MEM_POOL_BUFFER_SIZE);
+         //Failed to allocate memory?
+         if(!chunk->address)
+            return ERROR_OUT_OF_MEMORY;
+
+         //Allocated memory
+         chunk->size = NET_MEM_POOL_BUFFER_SIZE;
+         //Actual length of the data chunk
+         chunk->length = MIN(length, NET_MEM_POOL_BUFFER_SIZE);
+
+         //Prepare to process next chunk
+         length -= chunk->length;
+         buffer->chunkCount++;
+         i++;
+      }
+   }
+
+   //Return status code
+   return (length > 0) ? ERROR_OUT_OF_RESOURCES : NO_ERROR;
+}
+
+
+/**
+ * @brief Returns a pointer to the data at the specified position
+ * @param[in] buffer Pointer to a multi-part buffer
+ * @param[in] offset Offset from the beginning of the buffer
+ * @return Pointer the data at the specified position
+ **/
+
+void *netBufferAt(const NetBuffer *buffer, size_t offset)
+{
+   uint_t i;
+
+   //Loop through data chunks
+   for(i = 0; i < buffer->chunkCount; i++)
+   {
+      //The data at the specified offset resides in the current chunk?
+      if(offset < buffer->chunk[i].length)
+         return (uint8_t *) buffer->chunk[i].address + offset;
+
+      //Jump to the next chunk
+      offset -= buffer->chunk[i].length;
+   }
+
+   //Invalid offset...
+   return NULL;
+}
+
+
+/**
+ * @brief Concatenate two multi-part buffers
+ * @param[out] dest Pointer to the destination buffer
+ * @param[in] src Pointer to the source buffer
+ * @param[in] srcOffset Read offset
+ * @param[in] length Number of bytes to read from the source buffer
+ * @return Error code
+ **/
+
+error_t netBufferConcat(NetBuffer *dest,
+   const NetBuffer *src, size_t srcOffset, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Skip the beginning of the source data
+   for(j = 0; j < src->chunkCount; j++)
+   {
+      //The data at the specified offset resides in the current chunk?
+      if(srcOffset < src->chunk[j].length)
+         break;
+
+      //Jump to the next chunk
+      srcOffset -= src->chunk[j].length;
+   }
+
+   //Invalid offset?
+   if(j >= src->chunkCount)
+      return ERROR_INVALID_PARAMETER;
+
+   //Position to the end of the destination data
+   i = dest->chunkCount;
+
+   //Copy data blocks
+   while(length > 0 && i < dest->maxChunkCount && j < src->chunkCount)
+   {
+      //Copy current block
+      dest->chunk[i].address = (uint8_t *) src->chunk[j].address + srcOffset;
+      dest->chunk[i].length = src->chunk[j].length - srcOffset;
+      dest->chunk[i].size = 0;
+
+      //Limit the number of bytes to copy
+      if(length < dest->chunk[i].length)
+         dest->chunk[i].length = length;
+
+      //Decrement the number of remaining bytes
+      length -= dest->chunk[i].length;
+      //Increment the number of chunks
+      dest->chunkCount++;
+
+      //Adjust variables
+      srcOffset = 0;
+      i++;
+      j++;
+   }
+
+   //Return status code
+   return (length > 0) ? ERROR_FAILURE : NO_ERROR;
+}
+
+
+/**
+ * @brief Copy data between multi-part buffers
+ * @param[out] dest Pointer to the destination buffer
+ * @param[in] destOffset Write offset
+ * @param[in] src Pointer to the source buffer
+ * @param[in] srcOffset Read offset
+ * @param[in] length Number of bytes to be copied
+ * @return Error code
+ **/
+
+error_t netBufferCopy(NetBuffer *dest, size_t destOffset,
+   const NetBuffer *src, size_t srcOffset, size_t length)
+{
+   uint_t i;
+   uint_t j;
+   uint_t n;
+   uint8_t *p;
+   uint8_t *q;
+
+   //Skip the beginning of the source data
+   for(i = 0; i < dest->chunkCount; i++)
+   {
+      //The data at the specified offset resides in the current chunk?
+      if(destOffset < dest->chunk[i].length)
+         break;
+
+      //Jump to the next chunk
+      destOffset -= dest->chunk[i].length;
+   }
+
+   //Invalid offset?
+   if(i >= dest->chunkCount)
+      return ERROR_INVALID_PARAMETER;
+
+   //Skip the beginning of the source data
+   for(j = 0; j < src->chunkCount; j++)
+   {
+      //The data at the specified offset resides in the current chunk?
+      if(srcOffset < src->chunk[j].length)
+         break;
+
+      //Jump to the next chunk
+      srcOffset -= src->chunk[j].length;
+   }
+
+   //Invalid offset?
+   if(j >= src->chunkCount)
+      return ERROR_INVALID_PARAMETER;
+
+   while(length > 0 && i < dest->chunkCount && j < src->chunkCount)
+   {
+      //Point to the first data byte
+      p = (uint8_t *) dest->chunk[i].address + destOffset;
+      q = (uint8_t *) src->chunk[j].address + srcOffset;
+
+      //Compute the number of bytes to copy
+      n = MIN(length, dest->chunk[i].length - destOffset);
+      n = MIN(n, src->chunk[j].length - srcOffset);
+
+      //Copy data
+      memcpy(p, q, n);
+
+      destOffset += n;
+      srcOffset += n;
+      length -= n;
+
+      if(destOffset >= dest->chunk[i].length)
+      {
+         destOffset = 0;
+         i++;
+      }
+
+      if(srcOffset >= src->chunk[j].length)
+      {
+         srcOffset = 0;
+         j++;
+      }
+   }
+
+   //Return status code
+   return (length > 0) ? ERROR_FAILURE : NO_ERROR;
+}
+
+
+/**
+ * @brief Append data a multi-part buffer
+ * @param[out] dest Pointer to a multi-part buffer
+ * @param[in] src User buffer containing the data to be appended
+ * @param[in] length Number of bytes in the user buffer
+ * @return Error code
+ **/
+
+error_t netBufferAppend(NetBuffer *dest, const void *src, size_t length)
+{
+   uint_t i;
+
+   //Make sure there is enough space to add an extra chunk
+   if(dest->chunkCount >= dest->maxChunkCount)
+      return ERROR_FAILURE;
+
+   //Position to the end of the buffer
+   i = dest->chunkCount;
+
+   //Insert a new chunk at the end of the list
+   dest->chunk[i].address = (void *) src;
+   dest->chunk[i].length = length;
+   dest->chunk[i].size = 0;
+
+   //Increment the number of chunks
+   dest->chunkCount++;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write data to a multi-part buffer
+ * @param[out] dest Pointer to a multi-part buffer
+ * @param[in] destOffset Offset from the beginning of the multi-part buffer
+ * @param[in] src User buffer containing the data to be written
+ * @param[in] length Number of bytes to copy
+ * @return Actual number of bytes copied
+ **/
+
+size_t netBufferWrite(NetBuffer *dest,
+   size_t destOffset, const void *src, size_t length)
+{
+   uint_t i;
+   uint_t n;
+   size_t totalLength;
+   uint8_t *p;
+
+   //Total number of bytes written
+   totalLength = 0;
+
+   //Loop through data chunks
+   for(i = 0; i < dest->chunkCount && totalLength < length; i++)
+   {
+      //Is there any data to copy in the current chunk?
+      if(destOffset < dest->chunk[i].length)
+      {
+         //Point to the first byte to be written
+         p = (uint8_t *) dest->chunk[i].address + destOffset;
+         //Compute the number of bytes to copy at a time
+         n = MIN(length - totalLength, dest->chunk[i].length - destOffset);
+
+         //Copy data
+         memcpy(p, src, n);
+
+         //Advance read pointer
+         src = (uint8_t *) src + n;
+         //Total number of bytes written
+         totalLength += n;
+         //Process the next block from the start
+         destOffset = 0;
+      }
+      else
+      {
+         //Skip the current chunk
+         destOffset -= dest->chunk[i].length;
+      }
+   }
+
+   //Return the actual number of bytes written
+   return totalLength;
+}
+
+
+/**
+ * @brief Read data from a multi-part buffer
+ * @param[out] dest Pointer to the buffer where to return the data
+ * @param[in] src Pointer to a multi-part buffer
+ * @param[in] srcOffset Offset from the beginning of the multi-part buffer
+ * @param[in] length Number of bytes to copy
+ * @return Actual number of bytes copied
+ **/
+
+size_t netBufferRead(void *dest, const NetBuffer *src,
+   size_t srcOffset, size_t length)
+{
+   uint_t i;
+   uint_t n;
+   size_t totalLength;
+   uint8_t *p;
+
+   //Total number of bytes copied
+   totalLength = 0;
+
+   //Loop through data chunks
+   for(i = 0; i < src->chunkCount && totalLength < length; i++)
+   {
+      //Is there any data to copy from the current chunk?
+      if(srcOffset < src->chunk[i].length)
+      {
+         //Point to the first byte to be read
+         p = (uint8_t *) src->chunk[i].address + srcOffset;
+         //Compute the number of bytes to copy at a time
+         n = MIN(length - totalLength, src->chunk[i].length - srcOffset);
+
+         //Copy data
+         memcpy(dest, p, n);
+
+         //Advance write pointer
+         dest = (uint8_t *) dest + n;
+         //Total number of bytes copied
+         totalLength += n;
+         //Process the next block from the start
+         srcOffset = 0;
+      }
+      else
+      {
+         //Skip the current chunk
+         srcOffset -= src->chunk[i].length;
+      }
+   }
+
+   //Return the actual number of bytes copied
+   return totalLength;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/net_mem.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,142 @@
+/**
+ * @file net_mem.h
+ * @brief Memory management
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _NET_MEM_H
+#define _NET_MEM_H
+
+//Dependencies
+#include "net_config.h"
+#include "os_port.h"
+#include "error.h"
+
+//Deprecated properties
+#ifdef MEM_POOL_SUPPORT
+   #warning MEM_POOL_SUPPORT property is deprecated. NET_MEM_POOL_SUPPORT should be used instead.
+   #define NET_MEM_POOL_SUPPORT MEM_POOL_SUPPORT
+#endif
+
+#ifdef MEM_POOL_BUFFER_COUNT
+   #warning MEM_POOL_BUFFER_COUNT property is deprecated. NET_MEM_POOL_BUFFER_COUNT should be used instead.
+   #define NET_MEM_POOL_BUFFER_COUNT MEM_POOL_BUFFER_COUNT
+#endif
+
+#ifdef MEM_POOL_BUFFER_SIZE
+   #warning MEM_POOL_BUFFER_SIZE property is deprecated. NET_MEM_POOL_BUFFER_SIZE should be used instead.
+   #define NET_MEM_POOL_BUFFER_SIZE MEM_POOL_BUFFER_SIZE
+#endif
+
+//Use fixed-size blocks allocation?
+#ifndef NET_MEM_POOL_SUPPORT
+   #define NET_MEM_POOL_SUPPORT DISABLED
+#elif (NET_MEM_POOL_SUPPORT != ENABLED && NET_MEM_POOL_SUPPORT != DISABLED)
+   #error NET_MEM_POOL_SUPPORT parameter is not valid
+#endif
+
+//Number of buffers available
+#ifndef NET_MEM_POOL_BUFFER_COUNT
+   #define NET_MEM_POOL_BUFFER_COUNT 32
+#elif (NET_MEM_POOL_BUFFER_COUNT < 1)
+   #error NET_MEM_POOL_BUFFER_COUNT parameter is not valid
+#endif
+
+//Size of the buffers
+#ifndef NET_MEM_POOL_BUFFER_SIZE
+   #define NET_MEM_POOL_BUFFER_SIZE 1536
+#elif (NET_MEM_POOL_BUFFER_SIZE < 128)
+   #error NET_MEM_POOL_BUFFER_SIZE parameter is not valid
+#endif
+
+//Size of the header part of the buffer
+#define CHUNKED_BUFFER_HEADER_SIZE (sizeof(NetBuffer) + MAX_CHUNK_COUNT * sizeof(ChunkDesc))
+
+//Helper macro for defining a buffer
+#define N(size) (((size) + NET_MEM_POOL_BUFFER_SIZE - 1) / NET_MEM_POOL_BUFFER_SIZE)
+
+
+/**
+ * @brief Structure describing a chunk of data
+ **/
+
+typedef struct
+{
+   void *address;
+   uint16_t length;
+   uint16_t size;
+} ChunkDesc;
+
+
+/**
+ * @brief Structure describing a buffer that spans multiple chunks
+ **/
+
+typedef struct
+{
+   uint_t chunkCount;
+   uint_t maxChunkCount;
+   ChunkDesc chunk[];
+} NetBuffer;
+
+
+typedef struct
+{
+   uint_t chunkCount;
+   uint_t maxChunkCount;
+   ChunkDesc chunk[1];
+} NetBuffer1;
+
+
+//Memory management functions
+error_t memPoolInit(void);
+void *memPoolAlloc(size_t size);
+void memPoolFree(void *p);
+void memPoolGetStats(uint_t *currentUsage, uint_t *maxUsage, uint_t *size);
+
+NetBuffer *netBufferAlloc(size_t length);
+void netBufferFree(NetBuffer *buffer);
+
+size_t netBufferGetLength(const NetBuffer *buffer);
+error_t netBufferSetLength(NetBuffer *buffer, size_t length);
+
+void *netBufferAt(const NetBuffer *buffer, size_t offset);
+
+error_t netBufferConcat(NetBuffer *dest,
+   const NetBuffer *src, size_t srcOffset, size_t length);
+
+error_t netBufferCopy(NetBuffer *dest, size_t destOffset,
+   const NetBuffer *src, size_t srcOffset, size_t length);
+
+error_t netBufferAppend(NetBuffer *dest, const void *src, size_t length);
+
+size_t netBufferWrite(NetBuffer *dest,
+   size_t destOffset, const void *src, size_t length);
+
+size_t netBufferRead(void *dest, const NetBuffer *src,
+   size_t srcOffset, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/nic.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,345 @@
+/**
+ * @file nic.c
+ * @brief Network interface controller abstraction layer
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "core/nic.h"
+#include "core/socket.h"
+#include "core/raw_socket.h"
+#include "core/tcp_misc.h"
+#include "core/udp.h"
+#include "ipv4/ipv4.h"
+#include "ipv6/ipv6.h"
+#include "dns/dns_cache.h"
+#include "dns/dns_client.h"
+#include "mdns/mdns_client.h"
+#include "mdns/mdns_responder.h"
+#include "dns_sd/dns_sd.h"
+#include "mibs/mib2_module.h"
+#include "debug.h"
+
+//Tick counter to handle periodic operations
+systime_t nicTickCounter;
+
+
+/**
+ * @brief Network controller timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void nicTick(NetInterface *interface)
+{
+   //Disable interrupts
+   interface->nicDriver->disableIrq(interface);
+
+   //Handle periodic operations
+   interface->nicDriver->tick(interface);
+
+   //Re-enable interrupts if necessary
+   if(interface->configured)
+      interface->nicDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Send a packet to the network controller
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t nicSendPacket(NetInterface *interface, const NetBuffer *buffer, size_t offset)
+{
+   error_t error;
+   bool_t status;
+
+#if (TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+   //Retrieve the length of the packet
+   size_t length = netBufferGetLength(buffer) - offset;
+
+   //Debug message
+   TRACE_DEBUG("Sending packet (%" PRIuSIZE " bytes)...\r\n", length);
+   TRACE_DEBUG_NET_BUFFER("  ", buffer, offset, length);
+#endif
+
+   //Wait for the transmitter to be ready to send
+   status = osWaitForEvent(&interface->nicTxEvent, NIC_MAX_BLOCKING_TIME);
+
+   //Check whether the specified event is in signaled state
+   if(status)
+   {
+      //Disable interrupts
+      interface->nicDriver->disableIrq(interface);
+
+      //Send Ethernet frame
+      error = interface->nicDriver->sendPacket(interface, buffer, offset);
+
+      //Re-enable interrupts if necessary
+      if(interface->configured)
+         interface->nicDriver->enableIrq(interface);
+   }
+   else
+   {
+      //The transmitter is busy...
+      return ERROR_TRANSMITTER_BUSY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t nicSetMulticastFilter(NetInterface *interface)
+{
+   error_t error;
+
+   //Disable interrupts
+   interface->nicDriver->disableIrq(interface);
+
+   //Update MAC filter table
+   error = interface->nicDriver->setMulticastFilter(interface);
+
+   //Re-enable interrupts if necessary
+   if(interface->configured)
+      interface->nicDriver->enableIrq(interface);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Handle a packet received by the network controller
+ * @param[in] interface Underlying network interface
+ * @param[in] packet Incoming packet to process
+ * @param[in] length Total packet length
+ **/
+
+void nicProcessPacket(NetInterface *interface, void *packet, size_t length)
+{
+   NicType type;
+
+   //Re-enable interrupts if necessary
+   if(interface->configured)
+      interface->nicDriver->enableIrq(interface);
+
+   //Debug message
+   TRACE_DEBUG("Packet received (%" PRIuSIZE " bytes)...\r\n", length);
+   TRACE_DEBUG_ARRAY("  ", packet, length);
+
+   //Retrieve network interface type
+   type = interface->nicDriver->type;
+
+   //Ethernet interface?
+   if(type == NIC_TYPE_ETHERNET)
+   {
+#if (ETH_SUPPORT == ENABLED)
+      //Process incoming Ethernet frame
+      ethProcessFrame(interface, packet, length);
+#endif
+   }
+   //PPP interface?
+   else if(type == NIC_TYPE_PPP)
+   {
+#if (PPP_SUPPORT == ENABLED)
+      //Process incoming PPP frame
+      pppProcessFrame(interface, packet, length);
+#endif
+   }
+   //6LoWPAN interface?
+   else if(type == NIC_TYPE_6LOWPAN)
+   {
+#if (IPV6_SUPPORT == ENABLED)
+      NetBuffer1 buffer;
+
+      //The incoming packet fits in a single chunk
+      buffer.chunkCount = 1;
+      buffer.maxChunkCount = 1;
+      buffer.chunk[0].address = packet;
+      buffer.chunk[0].length = length;
+      buffer.chunk[0].size = 0;
+
+      //Process incoming IPv6 packet
+      ipv6ProcessPacket(interface, (NetBuffer *) &buffer, 0);
+#endif
+   }
+
+   //Disable interrupts
+   interface->nicDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief Process link state change event
+ * @param[in] interface Underlying network interface
+ **/
+
+void nicNotifyLinkChange(NetInterface *interface)
+{
+   uint_t i;
+   Socket *socket;
+
+   //Re-enable interrupts if necessary
+   if(interface->configured)
+      interface->nicDriver->enableIrq(interface);
+
+   //Check link state
+   if(interface->linkState)
+   {
+      //Display link state
+      TRACE_INFO("Link is up (%s)...\r\n", interface->name);
+
+      //Display link speed
+      if(interface->linkSpeed == NIC_LINK_SPEED_1GBPS)
+      {
+         //1000BASE-T
+         TRACE_INFO("  Link speed = 1000 Mbps\r\n");
+      }
+      else if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      {
+         //100BASE-TX
+         TRACE_INFO("  Link speed = 100 Mbps\r\n");
+      }
+      else if(interface->linkSpeed == NIC_LINK_SPEED_10MBPS)
+      {
+         //10BASE-T
+         TRACE_INFO("  Link speed = 10 Mbps\r\n");
+      }
+      else if(interface->linkSpeed != NIC_LINK_SPEED_UNKNOWN)
+      {
+         //10BASE-T
+         TRACE_INFO("  Link speed = %" PRIu32 " bps\r\n", interface->linkSpeed);
+      }
+
+      //Display duplex mode
+      if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      {
+         //1000BASE-T
+         TRACE_INFO("  Duplex mode = Full-Duplex\r\n");
+      }
+      else if(interface->duplexMode == NIC_HALF_DUPLEX_MODE)
+      {
+         //100BASE-TX
+         TRACE_INFO("  Duplex mode = Half-Duplex\r\n");
+      }
+   }
+   else
+   {
+      //Display link state
+      TRACE_INFO("Link is down (%s)...\r\n", interface->name);
+   }
+
+   //Interface's current bandwidth
+   MIB2_SET_GAUGE32(interface->mibIfEntry->ifSpeed, interface->linkSpeed);
+
+   //The current operational state of the interface
+   if(interface->linkState)
+      MIB2_SET_INTEGER(interface->mibIfEntry->ifOperStatus, MIB2_IF_OPER_STATUS_UP);
+   else
+      MIB2_SET_INTEGER(interface->mibIfEntry->ifOperStatus, MIB2_IF_OPER_STATUS_DOWN);
+
+   //The time at which the interface entered its current operational state
+   MIB2_SET_TIME_TICKS(interface->mibIfEntry->ifLastChange, osGetSystemTime() / 10);
+
+#if (IPV4_SUPPORT == ENABLED)
+   //Notify IPv4 of link state changes
+   ipv4LinkChangeEvent(interface);
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+   //Notify IPv6 of link state changes
+   ipv6LinkChangeEvent(interface);
+#endif
+
+#if (DNS_CLIENT_SUPPORT == ENABLED || MDNS_CLIENT_SUPPORT == ENABLED || \
+   NBNS_CLIENT_SUPPORT == ENABLED)
+   //Flush DNS cache
+   dnsFlushCache(interface);
+#endif
+
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+   //Perform probing and announcing
+   mdnsResponderLinkChangeEvent(interface->mdnsResponderContext);
+#endif
+
+#if (DNS_SD_SUPPORT == ENABLED)
+   //Perform probing and announcing
+   dnsSdLinkChangeEvent(interface->dnsSdContext);
+#endif
+
+   //Notify registered users of link state changes
+   netInvokeLinkChangeCallback(interface, interface->linkState);
+
+   //Loop through opened sockets
+   for(i = 0; i < SOCKET_MAX_COUNT; i++)
+   {
+      //Point to the current socket
+      socket = socketTable + i;
+
+#if (TCP_SUPPORT == ENABLED)
+      //Connection-oriented socket?
+      if(socket->type == SOCKET_TYPE_STREAM)
+      {
+         tcpUpdateEvents(socket);
+      }
+#endif
+#if (UDP_SUPPORT == ENABLED)
+      //Connectionless socket?
+      if(socket->type == SOCKET_TYPE_DGRAM)
+      {
+         udpUpdateEvents(socket);
+      }
+#endif
+#if (RAW_SOCKET_SUPPORT == ENABLED)
+      //Raw socket?
+      if(socket->type == SOCKET_TYPE_RAW_IP ||
+         socket->type == SOCKET_TYPE_RAW_ETH)
+      {
+         rawSocketUpdateEvents(socket);
+      }
+#endif
+   }
+
+   //Disable interrupts
+   interface->nicDriver->disableIrq(interface);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/nic.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,247 @@
+/**
+ * @file nic.h
+ * @brief Network interface controller abstraction layer
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _NIC_H
+#define _NIC_H
+
+//Dependencies
+#include "core/net.h"
+
+//Tick interval to handle NIC periodic operations
+#ifndef NIC_TICK_INTERVAL
+   #define NIC_TICK_INTERVAL 1000
+#elif (NIC_TICK_INTERVAL < 10)
+   #error NIC_TICK_INTERVAL parameter is not valid
+#endif
+
+//Maximum duration a write operation may block
+#ifndef NIC_MAX_BLOCKING_TIME
+   #define NIC_MAX_BLOCKING_TIME INFINITE_DELAY
+#elif (NIC_MAX_BLOCKING_TIME < 0)
+   #error NIC_MAX_BLOCKING_TIME parameter is not valid
+#endif
+
+//Size of the NIC driver context
+#ifndef NIC_CONTEXT_SIZE
+   #define NIC_CONTEXT_SIZE 16
+#elif (NIC_CONTEXT_SIZE < 1)
+   #error NIC_CONTEXT_SIZE parameter is not valid
+#endif
+
+
+/**
+ * @brief NIC types
+ **/
+
+typedef enum
+{
+   NIC_TYPE_ETHERNET = 0, ///<Ethernet interface
+   NIC_TYPE_PPP      = 1, ///<PPP interface
+   NIC_TYPE_6LOWPAN  = 2  ///<6LoWPAN interface
+} NicType;
+
+
+/**
+ * @brief Link state
+ **/
+
+typedef enum
+{
+   NIC_LINK_STATE_DOWN = 0,
+   NIC_LINK_STATE_UP   = 1,
+   NIC_LINK_STATE_AUTO = 2
+} NicLinkState;
+
+
+/**
+ * @brief Link speed
+ **/
+
+typedef enum
+{
+   NIC_LINK_SPEED_UNKNOWN = 0,
+   NIC_LINK_SPEED_10MBPS  = 10000000,
+   NIC_LINK_SPEED_100MBPS = 100000000,
+   NIC_LINK_SPEED_1GBPS   = 1000000000
+} NicLinkSpeed;
+
+
+/**
+ * @brief Duplex mode
+ **/
+
+typedef enum
+{
+   NIC_UNKNOWN_DUPLEX_MODE = 0,
+   NIC_HALF_DUPLEX_MODE    = 1,
+   NIC_FULL_DUPLEX_MODE    = 2
+} NicDuplexMode;
+
+
+//NIC abstraction layer
+typedef error_t (*NicInit)(NetInterface *interface);
+typedef void (*NicTick)(NetInterface *interface);
+typedef void (*NicEnableIrq)(NetInterface *interface);
+typedef void (*NicDisableIrq)(NetInterface *interface);
+typedef void (*NicEventHandler)(NetInterface *interface);
+typedef error_t (*NicSendPacket)(NetInterface *interface, const NetBuffer *buffer, size_t offset);
+typedef error_t (*NicSetMulticastFilter)(NetInterface *interface);
+typedef error_t (*NicUpdateMacConfig)(NetInterface *interface);
+typedef void (*NicWritePhyReg)(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+typedef uint16_t (*NicReadPhyReg)(uint8_t phyAddr, uint8_t regAddr);
+
+//PHY abstraction layer
+typedef error_t (*PhyInit)(NetInterface *interface);
+typedef void (*PhyTick)(NetInterface *interface);
+typedef void (*PhyEnableIrq)(NetInterface *interface);
+typedef void (*PhyDisableIrq)(NetInterface *interface);
+typedef void (*PhyEventHandler)(NetInterface *interface);
+
+//SPI abstraction layer
+typedef error_t (*SpiInit)(void);
+typedef error_t (*SpiSetMode)(uint_t mode);
+typedef error_t (*SpiSetBitrate)(uint_t bitrate);
+typedef void (*SpiAssertCs)(void);
+typedef void (*SpiDeassertCs)(void);
+typedef uint8_t (*SpiTransfer)(uint8_t data);
+
+//UART abstraction layer
+typedef error_t (*UartInit)(void);
+typedef void (*UartEnableIrq)(void);
+typedef void (*UartDisableIrq)(void);
+typedef void (*UartStartTx)(void);
+
+//External interrupt line abstraction layer
+typedef error_t (*ExtIntInit)(void);
+typedef void (*ExtIntEnableIrq)(void);
+typedef void (*ExtIntDisableIrq)(void);
+
+
+/**
+ * @brief NIC driver
+ **/
+
+typedef struct
+{
+   NicType type;
+   size_t mtu;
+   NicInit init;
+   NicTick tick;
+   NicEnableIrq enableIrq;
+   NicDisableIrq disableIrq;
+   NicEventHandler eventHandler;
+   NicSendPacket sendPacket;
+   NicSetMulticastFilter setMulticastFilter;
+   NicUpdateMacConfig updateMacConfig;
+   NicWritePhyReg writePhyReg;
+   NicReadPhyReg readPhyReg;
+   bool_t autoPadding;
+   bool_t autoCrcCalc;
+   bool_t autoCrcVerif;
+   bool_t autoCrcStrip;
+   //bool_t autoIpv4ChecksumCalc;
+   //bool_t autoIpv4ChecksumVerif;
+   //bool_t autoIpv6ChecksumCalc;
+   //bool_t autoIpv6ChecksumVerif;
+   //bool_t autoIcmpChecksumCalc;
+   //bool_t autoIcmpChecksumVerif;
+   //bool_t autoTcpChecksumCalc;
+   //bool_t autoTcpChecksumVerif;
+   //bool_t autoUdpChecksumCalc;
+   //bool_t autoUdpChecksumVerif;
+} NicDriver;
+
+
+/**
+ * @brief PHY driver
+ **/
+
+typedef struct
+{
+   PhyInit init;
+   PhyTick tick;
+   PhyEnableIrq enableIrq;
+   PhyDisableIrq disableIrq;
+   PhyEventHandler eventHandler;
+} PhyDriver;
+
+
+/**
+ * @brief SPI driver
+ **/
+
+typedef struct
+{
+   SpiInit init;
+   SpiSetMode setMode;
+   SpiSetBitrate setBitrate;
+   SpiAssertCs assertCs;
+   SpiDeassertCs deassertCs;
+   SpiTransfer transfer;
+} SpiDriver;
+
+
+/**
+ * @brief UART driver
+ **/
+
+typedef struct
+{
+   UartInit init;
+   UartEnableIrq enableIrq;
+   UartDisableIrq disableIrq;
+   UartStartTx startTx;
+} UartDriver;
+
+
+/**
+ * @brief External interrupt line driver
+ **/
+
+typedef struct
+{
+   ExtIntInit init;
+   ExtIntEnableIrq enableIrq;
+   ExtIntDisableIrq disableIrq;
+} ExtIntDriver;
+
+
+//Tick counter to handle periodic operations
+extern systime_t nicTickCounter;
+
+//NIC abstraction layer
+void nicTick(NetInterface *interface);
+
+error_t nicSendPacket(NetInterface *interface, const NetBuffer *buffer, size_t offset);
+error_t nicSetMulticastFilter(NetInterface *interface);
+
+void nicProcessPacket(NetInterface *interface, void *packet, size_t length);
+void nicNotifyLinkChange(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/ping.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,561 @@
+/**
+ * @file ping.c
+ * @brief Ping utility
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL PING_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "core/ping.h"
+#include "core/ip.h"
+#include "ipv4/ipv4.h"
+#include "ipv4/icmp.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_misc.h"
+#include "ipv6/icmpv6.h"
+#include "core/socket.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (PING_SUPPORT == ENABLED)
+
+//Sequence number field
+static uint16_t pingSequenceNumber = 0;
+
+
+/**
+ * @brief Test the reachability of a host
+ *
+ * Ping operates by sending an ICMP Echo Request message to the
+ * target host and waiting for an ICMP Echo Reply message
+ *
+ * @param[in] interface Underlying network interface (optional parameter)
+ * @param[in] targetIpAddr IP address of the host to reach
+ * @param[in] size Size of the data payload in bytes
+ * @param[in] ttl Time-To-Live value to be used
+ * @param[in] timeout Maximum time to wait before giving up
+ * @param[out] rtt Round-trip time (optional parameter)
+ * @return Error code
+ **/
+
+error_t ping(NetInterface *interface, const IpAddr *targetIpAddr,
+   size_t size, uint8_t ttl, systime_t timeout, systime_t *rtt)
+{
+   error_t error;
+   PingContext context;
+
+   //Check parameters
+   if(targetIpAddr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Initialize context
+   pingInit(&context);
+
+   //Start of exception handling block
+   do
+   {
+      //Select the specified network interface
+      error = pingBindToInterface(&context, interface);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Set timeout value
+      error = pingSetTimeout(&context, timeout);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Send an ICMP Echo Request message
+      error = pingSendRequest(&context, targetIpAddr, size, ttl);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Wait for a matching Echo Reply message
+      error = pingWaitForReply(&context, NULL, rtt);
+      //Any error to report?
+      if(error)
+         break;
+
+      //End of exception handling block
+   } while(0);
+
+   //Release resources
+   pingRelease(&context);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Initialize ping context
+ * @param[in] context Pointer to the ping context
+ **/
+
+void pingInit(PingContext *context)
+{
+   //Make sure the context is valid
+   if(context != NULL)
+   {
+      //Initialize context
+      memset(context, 0, sizeof(PingContext));
+
+      //Set the default timeout to be used
+      context->timeout = PING_DEFAULT_TIMEOUT;
+   }
+}
+
+
+/**
+ * @brief Set timeout value
+ * @param[in] context Pointer to the ping context
+ * @param[in] timeout Maximum time to wait
+ * @return Error code
+ **/
+
+error_t pingSetTimeout(PingContext *context, systime_t timeout)
+{
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save timeout value
+   context->timeout = timeout;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Select a particular network interface
+ * @param[in] context Pointer to the ping context
+ * @param[in] interface Network interface to be used
+ * @return Error code
+ **/
+
+error_t pingBindToInterface(PingContext *context, NetInterface *interface)
+{
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Select the specified network interface
+   context->interface = interface;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Send an ICMP Echo Request message
+ * @param[in] context Pointer to the ping context
+ * @param[in] targetIpAddr IP address of the host to reach
+ * @param[in] size Size of the data payload, in bytes
+ * @param[in] ttl Time-To-Live value to be used
+ * @return Error code
+ **/
+
+error_t pingSendRequest(PingContext *context,
+   const IpAddr *targetIpAddr, size_t size, uint8_t ttl)
+{
+   error_t error;
+   size_t i;
+   size_t length;
+   NetInterface *interface;
+   IcmpEchoMessage *message;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Limit the size of the data payload
+   context->dataPayloadSize = MIN (size, PING_MAX_DATA_SIZE);
+
+   //Close existing socket, if necessary
+   if(context->socket != NULL)
+   {
+      socketClose(context->socket);
+      context->socket = NULL;
+   }
+
+   //Identifier field is used to help matching requests and replies
+   context->identifier = netGetRand();
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Sequence Number field is increment each time an Echo Request is sent
+   context->sequenceNumber = pingSequenceNumber++;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Point to the buffer where to format the ICMP message
+   message = (IcmpEchoMessage *) context->buffer;
+
+   //Format ICMP Echo Request message
+   message->type = ICMP_TYPE_ECHO_REQUEST;
+   message->code = 0;
+   message->checksum = 0;
+   message->identifier = context->identifier;
+   message->sequenceNumber = context->sequenceNumber;
+
+   //Initialize data payload
+   for(i = 0; i < context->dataPayloadSize; i++)
+      message->data[i] = i & 0xFF;
+
+   //Length of the complete ICMP message including header and data
+   length = sizeof(IcmpEchoMessage) + context->dataPayloadSize;
+
+   //Select the relevant network interface
+   interface = context->interface;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //Is target address an IPv4 address?
+   if(targetIpAddr->length == sizeof(Ipv4Addr))
+   {
+      Ipv4Addr srcIpAddr;
+
+      //Select the source IPv4 address and the relevant network
+      //interface to use when pinging the specified host
+      error = ipv4SelectSourceAddr(&interface, targetIpAddr->ipv4Addr,
+         &srcIpAddr);
+
+      //Any error to report?
+      if(error)
+         return error;
+
+      //ICMP Echo Request message
+      message->type = ICMP_TYPE_ECHO_REQUEST;
+      //Message checksum calculation
+      message->checksum = ipCalcChecksum(message, length);
+
+      //Open a raw socket
+      context->socket = socketOpen(SOCKET_TYPE_RAW_IP, SOCKET_IP_PROTO_ICMP);
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //Is target address an IPv6 address?
+   if(targetIpAddr->length == sizeof(Ipv6Addr))
+   {
+      Ipv6PseudoHeader pseudoHeader;
+
+      //Select the source IPv6 address and the relevant network
+      //interface to use when pinging the specified host
+      error = ipv6SelectSourceAddr(&interface, &targetIpAddr->ipv6Addr,
+         &pseudoHeader.srcAddr);
+
+      //Any error to report?
+      if(error)
+         return error;
+
+      //ICMPv6 Echo Request message
+      message->type = ICMPV6_TYPE_ECHO_REQUEST;
+
+      //Format IPv6 pseudo header
+      pseudoHeader.destAddr = targetIpAddr->ipv6Addr;
+      pseudoHeader.length = htonl(length);
+      pseudoHeader.reserved = 0;
+      pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER;
+
+      //Message checksum calculation
+      message->checksum = ipCalcUpperLayerChecksum(&pseudoHeader,
+         sizeof(Ipv6PseudoHeader), message, length);
+
+      //Open a raw socket
+      context->socket = socketOpen(SOCKET_TYPE_RAW_IP, SOCKET_IP_PROTO_ICMPV6);
+   }
+   else
+#endif
+   //Invalid target address?
+   {
+      //Report an error
+      return ERROR_INVALID_ADDRESS;
+   }
+
+   //Failed to open socket?
+   if(context->socket == NULL)
+      return ERROR_OPEN_FAILED;
+
+   //Set the TTL value to be used
+   context->socket->ttl = ttl;
+
+   //Start of exception handling block
+   do
+   {
+      //Associate the newly created socket with the relevant interface
+      error = socketBindToInterface(context->socket, interface);
+      //Unable to bind the socket to the desired interface?
+      if(error)
+         break;
+
+      //Debug message
+      TRACE_INFO("Sending ICMP echo request to %s (%" PRIuSIZE " bytes)...\r\n",
+         ipAddrToString(targetIpAddr, NULL), length);
+
+      //Send Echo Request message
+      error = socketSendTo(context->socket, targetIpAddr, 0,
+         message, length, NULL, 0);
+      //Failed to send message ?
+      if(error)
+         break;
+
+      //Save the time at which the request was sent
+      context->timestamp = osGetSystemTime();
+
+      //End of exception handling block
+   } while(0);
+
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects
+      socketClose(context->socket);
+      context->socket = NULL;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Check whether an incoming ICMP message is acceptable
+ * @param[in] context Pointer to the ping context
+ * @param[in] srcIpAddr Source IP address
+ * @param[in] destIpAddr Destination IP address
+ * @param[in] message Pointer to the incoming ICMP message
+ * @param[in] length Length of the message, in bytes
+ * @return Error code
+ **/
+
+error_t pingCheckReply(PingContext *context, const IpAddr *srcIpAddr,
+   const IpAddr *destIpAddr, const IcmpEchoMessage *message, size_t length)
+{
+   size_t i;
+
+   //Check message length
+   if(length != (sizeof(IcmpEchoMessage) + context->dataPayloadSize))
+      return ERROR_INVALID_MESSAGE;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //Is target address an IPv4 address?
+   if(context->socket->protocol == SOCKET_IP_PROTO_ICMP)
+   {
+      //Check address type
+      if(destIpAddr->length != sizeof(Ipv4Addr))
+         return ERROR_INVALID_MESSAGE;
+
+      //Check message type
+      if(message->type != ICMP_TYPE_ECHO_REPLY)
+         return ERROR_INVALID_MESSAGE;
+
+      //Verify checksum value
+      if(ipCalcChecksum(message, length) != 0x0000)
+         return ERROR_INVALID_MESSAGE;
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //Is target address an IPv6 address?
+   if(context->socket->protocol == SOCKET_IP_PROTO_ICMPV6)
+   {
+      Ipv6PseudoHeader pseudoHeader;
+
+      //Check address type
+      if(destIpAddr->length != sizeof(Ipv6Addr))
+         return ERROR_INVALID_MESSAGE;
+
+      //Check message type
+      if(message->type != ICMPV6_TYPE_ECHO_REPLY)
+         return ERROR_INVALID_MESSAGE;
+
+      //Format IPv6 pseudo header
+      pseudoHeader.srcAddr = srcIpAddr->ipv6Addr;
+      pseudoHeader.destAddr = destIpAddr->ipv6Addr;
+      pseudoHeader.length = htonl(length);
+      pseudoHeader.reserved = 0;
+      pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER;
+
+      //Verify checksum value
+      if(ipCalcUpperLayerChecksum(&pseudoHeader,
+         sizeof(Ipv6PseudoHeader), message, length) != 0x0000)
+      {
+         //The checksum is not valid
+         return ERROR_INVALID_MESSAGE;
+      }
+   }
+   else
+#endif
+   //Invalid target address?
+   {
+      //Report an error
+      return ERROR_INVALID_ADDRESS;
+   }
+
+   //Make sure the response identifier matches the request identifier
+   if(message->identifier != context->identifier)
+      return ERROR_INVALID_MESSAGE;
+   //Make sure the sequence number is correct
+   if(message->sequenceNumber != context->sequenceNumber)
+      return ERROR_INVALID_MESSAGE;
+
+   //Verify data payload
+   for(i = 0; i < context->dataPayloadSize; i++)
+   {
+      //Compare received data against expected data pattern
+      if(message->data[i] != (i & 0xFF))
+         return ERROR_INVALID_MESSAGE;
+   }
+
+   //The ICMP Echo Reply message is acceptable
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Wait for a matching ICMP Echo Reply message
+ * @param[in] context Pointer to the ping context
+ * @param[out] targetIpAddr IP address of the remote host (optional parameter)
+ * @param[out] rtt Round-trip time (optional parameter)
+ * @return Error code
+ **/
+
+error_t pingWaitForReply(PingContext *context,
+   IpAddr *targetIpAddr, systime_t *rtt)
+{
+   error_t error;
+   size_t length;
+   systime_t time;
+   systime_t timeout;
+   IpAddr srcIpAddr;
+   IpAddr destIpAddr;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Wait for an ICMP Echo Reply message
+   do
+   {
+      //Get current time
+      time = osGetSystemTime();
+
+      //Compute the timeout to be used
+      if(timeCompare(time, context->timestamp + context->timeout) < 0)
+         timeout = context->timestamp + context->timeout - time;
+      else
+         timeout = 0;
+
+      //Adjust receive timeout
+      error = socketSetTimeout(context->socket, timeout);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Wait for an incoming ICMP message
+      error = socketReceiveEx(context->socket, &srcIpAddr, NULL,
+         &destIpAddr, context->buffer, PING_BUFFER_SIZE, &length, 0);
+
+#if (NET_RTOS_SUPPORT == DISABLED)
+      //Catch timeout exception
+      if(error == ERROR_TIMEOUT)
+         error = ERROR_WOULD_BLOCK;
+#endif
+
+      //Get current time
+      time = osGetSystemTime();
+
+      //Check status code
+      if(!error)
+      {
+         //Check whether the incoming ICMP message is acceptable
+         error = pingCheckReply(context, &srcIpAddr, &destIpAddr,
+            (IcmpEchoMessage *) context->buffer, length);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Calculate round-trip time
+         context->rtt = time - context->timestamp;
+
+         //Debug message
+         TRACE_INFO("ICMP echo reply received from %s (%" PRIu32 " ms)...\r\n",
+            ipAddrToString(&srcIpAddr, NULL), context->rtt);
+
+         //Return the IP address of the host
+         if(targetIpAddr != NULL)
+            *targetIpAddr = srcIpAddr;
+
+         //Return the round-trip time
+         if(rtt != NULL)
+            *rtt = context->rtt;
+      }
+      else
+      {
+         //Timeout value exceeded?
+         if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+         {
+            //Report an error
+            error = ERROR_TIMEOUT;
+         }
+      }
+
+      //Wait for the next incoming ICMP message
+   } while(error == ERROR_INVALID_MESSAGE);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Release ping context
+ * @param[in] context Pointer to the ping context
+ **/
+
+void pingRelease(PingContext *context)
+{
+   //Make sure the context is valid
+   if(context != NULL)
+   {
+      //Close underlying socket
+      if(context->socket != NULL)
+      {
+         socketClose(context->socket);
+         context->socket = NULL;
+      }
+   }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/ping.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,97 @@
+/**
+ * @file ping.h
+ * @brief Ping utility
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _PING_H
+#define _PING_H
+
+//Dependencies
+#include "core/net.h"
+#include "ipv4/icmp.h"
+#include "ipv6/icmpv6.h"
+
+//Ping utility support
+#ifndef PING_SUPPORT
+   #define PING_SUPPORT ENABLED
+#elif (PING_SUPPORT != ENABLED && PING_SUPPORT != DISABLED)
+   #error PING_SUPPORT parameter is not valid
+#endif
+
+//Default timeout value
+#ifndef PING_DEFAULT_TIMEOUT
+   #define PING_DEFAULT_TIMEOUT 1000
+#elif (PING_DEFAULT_TIMEOUT < 0)
+   #error PING_DEFAULT_TIMEOUT parameter is not valid
+#endif
+
+//Maximum size of the data payload
+#ifndef PING_MAX_DATA_SIZE
+   #define PING_MAX_DATA_SIZE 32
+#elif (PING_MAX_DATA_SIZE < 0)
+   #error PING_MAX_DATA_SIZE parameter is not valid
+#endif
+
+//Size of the internal buffer
+#define PING_BUFFER_SIZE (sizeof(IcmpEchoMessage) + PING_MAX_DATA_SIZE)
+
+
+/**
+ * @brief Ping context
+ **/
+
+typedef struct
+{
+   NetInterface *interface;
+   Socket *socket;
+   size_t dataPayloadSize;
+   uint16_t identifier;
+   uint16_t sequenceNumber;
+   systime_t timestamp;
+   systime_t timeout;
+   systime_t rtt;
+   uint8_t buffer[PING_BUFFER_SIZE];
+} PingContext;
+
+
+//Ping related functions
+error_t ping(NetInterface *interface, const IpAddr *targetIpAddr,
+   size_t size, uint8_t ttl, systime_t timeout, systime_t *rtt);
+
+void pingInit(PingContext *context);
+error_t pingSetTimeout(PingContext *context, systime_t timeout);
+error_t pingBindToInterface(PingContext *context, NetInterface *interface);
+
+error_t pingSendRequest(PingContext *context,
+   const IpAddr *targetIpAddr, size_t size, uint8_t ttl);
+
+error_t pingWaitForReply(PingContext *context,
+   IpAddr *targetIpAddr, systime_t *rtt);
+
+void pingRelease(PingContext *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/raw_socket.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,778 @@
+/**
+ * @file raw_socket.c
+ * @brief TCP/IP raw sockets
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * A raw socket is a type of socket that allows access to the
+ * underlying transport provider
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL RAW_SOCKET_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "core/net.h"
+#include "ipv4/ipv4.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_misc.h"
+#include "core/raw_socket.h"
+#include "core/socket.h"
+#include "core/ethernet.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (RAW_SOCKET_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Process incoming IP packet
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IPv4 or IPv6 pseudo header
+ * @param[in] buffer Multi-part buffer containing the IP packet
+ * @param[in] offset Offset to the first byte of the IP packet
+ * @return Error code
+ **/
+
+error_t rawSocketProcessIpPacket(NetInterface *interface,
+   IpPseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset)
+{
+   uint_t i;
+   size_t length;
+   Socket *socket;
+   SocketQueueItem *queueItem;
+   NetBuffer *p;
+
+   //Retrieve the length of the raw IP packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Loop through opened sockets
+   for(i = 0; i < SOCKET_MAX_COUNT; i++)
+   {
+      //Point to the current socket
+      socket = socketTable + i;
+
+      //Raw socket found?
+      if(socket->type != SOCKET_TYPE_RAW_IP)
+         continue;
+      //Check whether the socket is bound to a particular interface
+      if(socket->interface && socket->interface != interface)
+         continue;
+
+#if (IPV4_SUPPORT == ENABLED)
+      //An IPv4 packet was received?
+      if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
+      {
+         //Check protocol field
+         if(socket->protocol != pseudoHeader->ipv4Data.protocol)
+            continue;
+         //Destination IP address filtering
+         if(socket->localIpAddr.length)
+         {
+            //An IPv4 address is expected
+            if(socket->localIpAddr.length != sizeof(Ipv4Addr))
+               continue;
+            //Filter out non-matching addresses
+            if(socket->localIpAddr.ipv4Addr != pseudoHeader->ipv4Data.destAddr)
+               continue;
+         }
+         //Source IP address filtering
+         if(socket->remoteIpAddr.length)
+         {
+            //An IPv4 address is expected
+            if(socket->remoteIpAddr.length != sizeof(Ipv4Addr))
+               continue;
+            //Filter out non-matching addresses
+            if(socket->remoteIpAddr.ipv4Addr != pseudoHeader->ipv4Data.srcAddr)
+               continue;
+         }
+      }
+      else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+      //An IPv6 packet was received?
+      if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
+      {
+         //Check protocol field
+         if(socket->protocol != pseudoHeader->ipv6Data.nextHeader)
+            continue;
+
+         //Destination IP address filtering
+         if(socket->localIpAddr.length)
+         {
+            //An IPv6 address is expected
+            if(socket->localIpAddr.length != sizeof(Ipv6Addr))
+               continue;
+            //Filter out non-matching addresses
+            if(!ipv6CompAddr(&socket->localIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.destAddr))
+               continue;
+         }
+
+         //Source IP address filtering
+         if(socket->remoteIpAddr.length)
+         {
+            //An IPv6 address is expected
+            if(socket->remoteIpAddr.length != sizeof(Ipv6Addr))
+               continue;
+            //Filter out non-matching addresses
+            if(!ipv6CompAddr(&socket->remoteIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.srcAddr))
+               continue;
+         }
+      }
+      else
+#endif
+      //An invalid packet was received?
+      {
+         //This should never occur...
+         continue;
+      }
+
+      //The current socket meets all the criteria
+      break;
+   }
+
+   //Drop incoming packet if no matching socket was found
+   if(i >= SOCKET_MAX_COUNT)
+      return ERROR_PROTOCOL_UNREACHABLE;
+
+   //Empty receive queue?
+   if(!socket->receiveQueue)
+   {
+      //Allocate a memory buffer to hold the data and the associated descriptor
+      p = netBufferAlloc(sizeof(SocketQueueItem) + length);
+
+      //Successful memory allocation?
+      if(p != NULL)
+      {
+         //Point to the newly created item
+         queueItem = netBufferAt(p, 0);
+         queueItem->buffer = p;
+         //Add the newly created item to the queue
+         socket->receiveQueue = queueItem;
+      }
+      else
+      {
+         //Memory allocation failed
+         queueItem = NULL;
+      }
+   }
+   else
+   {
+      //Point to the very first item
+      queueItem = socket->receiveQueue;
+      //Reach the last item in the receive queue
+      for(i = 1; queueItem->next; i++)
+         queueItem = queueItem->next;
+
+      //Make sure the receive queue is not full
+      if(i >= RAW_SOCKET_RX_QUEUE_SIZE)
+         return ERROR_RECEIVE_QUEUE_FULL;
+
+      //Allocate a memory buffer to hold the data and the associated descriptor
+      p = netBufferAlloc(sizeof(SocketQueueItem) + length);
+
+      //Successful memory allocation?
+      if(p != NULL)
+      {
+         //Add the newly created item to the queue
+         queueItem->next = netBufferAt(p, 0);
+         //Point to the newly created item
+         queueItem = queueItem->next;
+         queueItem->buffer = p;
+      }
+      else
+      {
+         //Memory allocation failed
+         queueItem = NULL;
+      }
+   }
+
+   //Failed to allocate memory?
+   if(queueItem == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize next field
+   queueItem->next = NULL;
+   //Port number is unused
+   queueItem->srcPort = 0;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //IPv4 remote address?
+   if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
+   {
+      //Save the source IPv4 address
+      queueItem->srcIpAddr.length = sizeof(Ipv4Addr);
+      queueItem->srcIpAddr.ipv4Addr = pseudoHeader->ipv4Data.srcAddr;
+      //Save the destination IPv4 address
+      queueItem->destIpAddr.length = sizeof(Ipv4Addr);
+      queueItem->destIpAddr.ipv4Addr = pseudoHeader->ipv4Data.destAddr;
+   }
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 remote address?
+   if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
+   {
+      //Save the source IPv6 address
+      queueItem->srcIpAddr.length = sizeof(Ipv6Addr);
+      queueItem->srcIpAddr.ipv6Addr = pseudoHeader->ipv6Data.srcAddr;
+      //Save the destination IPv6 address
+      queueItem->destIpAddr.length = sizeof(Ipv6Addr);
+      queueItem->destIpAddr.ipv6Addr = pseudoHeader->ipv6Data.destAddr;
+   }
+#endif
+
+   //Offset to the raw IP packet
+   queueItem->offset = sizeof(SocketQueueItem);
+   //Copy the raw data
+   netBufferCopy(queueItem->buffer, queueItem->offset, buffer, offset, length);
+
+   //Notify user that data is available
+   rawSocketUpdateEvents(socket);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process incoming Ethernet packet
+ * @param[in] interface Underlying network interface
+ * @param[in] ethFrame Incoming Ethernet frame to process
+ * @param[in] length Total frame length
+ **/
+
+void rawSocketProcessEthPacket(NetInterface *interface,
+   EthHeader *ethFrame, size_t length)
+{
+   uint_t i;
+   Socket *socket;
+   SocketQueueItem *queueItem;
+   NetBuffer *p;
+
+   //Loop through opened sockets
+   for(i = 0; i < SOCKET_MAX_COUNT; i++)
+   {
+      //Point to the current socket
+      socket = socketTable + i;
+
+      //Raw socket found?
+      if(socket->type != SOCKET_TYPE_RAW_ETH)
+         continue;
+      //Check whether the socket is bound to a particular interface
+      if(socket->interface && socket->interface != interface)
+         continue;
+      //Check protocol field
+      if(socket->protocol != SOCKET_ETH_PROTO_ALL && socket->protocol != ntohs(ethFrame->type))
+         continue;
+
+      //The current socket meets all the criteria
+      break;
+   }
+
+   //Drop incoming packet if no matching socket was found
+   if(i >= SOCKET_MAX_COUNT)
+      return;
+
+   //Empty receive queue?
+   if(!socket->receiveQueue)
+   {
+      //Allocate a memory buffer to hold the data and the associated descriptor
+      p = netBufferAlloc(sizeof(SocketQueueItem) + length);
+
+      //Successful memory allocation?
+      if(p != NULL)
+      {
+         //Point to the newly created item
+         queueItem = netBufferAt(p, 0);
+         queueItem->buffer = p;
+         //Add the newly created item to the queue
+         socket->receiveQueue = queueItem;
+      }
+      else
+      {
+         //Memory allocation failed
+         queueItem = NULL;
+      }
+   }
+   else
+   {
+      //Point to the very first item
+      queueItem = socket->receiveQueue;
+      //Reach the last item in the receive queue
+      for(i = 1; queueItem->next; i++)
+         queueItem = queueItem->next;
+
+      //Make sure the receive queue is not full
+      if(i >= RAW_SOCKET_RX_QUEUE_SIZE)
+         return;
+
+      //Allocate a memory buffer to hold the data and the associated descriptor
+      p = netBufferAlloc(sizeof(SocketQueueItem) + length);
+
+      //Successful memory allocation?
+      if(p != NULL)
+      {
+         //Add the newly created item to the queue
+         queueItem->next = netBufferAt(p, 0);
+         //Point to the newly created item
+         queueItem = queueItem->next;
+         queueItem->buffer = p;
+      }
+      else
+      {
+         //Memory allocation failed
+         queueItem = NULL;
+      }
+   }
+
+   //Failed to allocate memory?
+   if(queueItem == NULL)
+      return;
+
+   //Initialize next field
+   queueItem->next = NULL;
+   //Other fields are meaningless
+   queueItem->srcPort = 0;
+   queueItem->srcIpAddr = IP_ADDR_ANY;
+   queueItem->destIpAddr = IP_ADDR_ANY;
+
+   //Offset to the raw datagram
+   queueItem->offset = sizeof(SocketQueueItem);
+   //Copy the raw data
+   netBufferWrite(queueItem->buffer, queueItem->offset, ethFrame, length);
+
+   //Notify user that data is available
+   rawSocketUpdateEvents(socket);
+}
+
+
+/**
+ * @brief Send an raw IP packet
+ * @param[in] socket Handle referencing the socket
+ * @param[in] destIpAddr IP address of the target host
+ * @param[in] data Pointer to raw data
+ * @param[in] length Length of the raw data
+ * @param[out] written Actual number of bytes written (optional parameter)
+ * @return Error code
+ **/
+
+error_t rawSocketSendIpPacket(Socket *socket, const IpAddr *destIpAddr,
+   const void *data, size_t length, size_t *written)
+{
+   error_t error;
+   size_t offset;
+   NetBuffer *buffer;
+   NetInterface *interface;
+   IpPseudoHeader pseudoHeader;
+
+   //The socket may be bound to a particular network interface
+   interface = socket->interface;
+
+   //Allocate a buffer memory to hold the raw IP datagram
+   buffer = ipAllocBuffer(0, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Start of exception handling block
+   do
+   {
+      //Copy the raw data
+      error = netBufferAppend(buffer, data, length);
+      //Any error to report?
+      if(error)
+         break;
+
+#if (IPV4_SUPPORT == ENABLED)
+      //Destination address is an IPv4 address?
+      if(destIpAddr->length == sizeof(Ipv4Addr))
+      {
+         Ipv4Addr srcIpAddr;
+
+         //Select the source IPv4 address and the relevant network interface
+         //to use when sending data to the specified destination host
+         error = ipv4SelectSourceAddr(&interface, destIpAddr->ipv4Addr, &srcIpAddr);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Format IPv4 pseudo header
+         pseudoHeader.length = sizeof(Ipv4PseudoHeader);
+         pseudoHeader.ipv4Data.srcAddr = srcIpAddr;
+         pseudoHeader.ipv4Data.destAddr = destIpAddr->ipv4Addr;
+         pseudoHeader.ipv4Data.reserved = 0;
+         pseudoHeader.ipv4Data.protocol = socket->protocol;
+         pseudoHeader.ipv4Data.length = htons(length);
+      }
+      else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+      //Destination address is an IPv6 address?
+      if(destIpAddr->length == sizeof(Ipv6Addr))
+      {
+         //Select the source IPv6 address and the relevant network interface
+         //to use when sending data to the specified destination host
+         error = ipv6SelectSourceAddr(&interface,
+            &destIpAddr->ipv6Addr, &pseudoHeader.ipv6Data.srcAddr);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Format IPv6 pseudo header
+         pseudoHeader.length = sizeof(Ipv6PseudoHeader);
+         pseudoHeader.ipv6Data.destAddr = destIpAddr->ipv6Addr;
+         pseudoHeader.ipv6Data.length = htonl(length);
+         pseudoHeader.ipv6Data.reserved = 0;
+         pseudoHeader.ipv6Data.nextHeader = socket->protocol;
+      }
+      else
+#endif
+      //Invalid destination address?
+      {
+         //An internal error has occurred
+         error = ERROR_FAILURE;
+         //Exit immediately
+         break;
+      }
+
+      //Send raw IP datagram
+      error = ipSendDatagram(interface, &pseudoHeader, buffer, offset, socket->ttl);
+      //Failed to send data?
+      if(error)
+         break;
+
+      //Total number of bytes successfully transmitted
+      if(written != NULL)
+         *written = length;
+
+      //End of exception handling block
+   } while(0);
+
+   //Free previously allocated memory block
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send an raw Ethernet packet
+ * @param[in] socket Handle referencing the socket
+ * @param[in] data Pointer to raw data
+ * @param[in] length Length of the raw data
+ * @param[out] written Actual number of bytes written (optional parameter)
+ * @return Error code
+ **/
+
+error_t rawSocketSendEthPacket(Socket *socket,
+   const void *data, size_t length, size_t *written)
+{
+   error_t error;
+
+#if (ETH_SUPPORT == ENABLED)
+   NetBuffer *buffer;
+   NetInterface *interface;
+
+   //Select the relevant network interface
+   if(!socket->interface)
+      interface = netGetDefaultInterface();
+   else
+      interface = socket->interface;
+
+   //Ethernet interface?
+   if(interface->nicDriver->type == NIC_TYPE_ETHERNET)
+   {
+      //Allocate a buffer memory to hold the raw Ethernet packet
+      buffer = netBufferAlloc(0);
+      //Failed to allocate buffer?
+      if(buffer == NULL)
+         return ERROR_OUT_OF_MEMORY;
+
+      //Copy the raw data
+      error = netBufferAppend(buffer, data, length);
+
+      //Successful processing?
+      if(!error)
+      {
+         //Automatic padding not supported by hardware?
+         if(!interface->nicDriver->autoPadding)
+         {
+            //The host controller should manually add padding
+            //to the packet before transmitting it
+            if(length < (ETH_MIN_FRAME_SIZE - ETH_CRC_SIZE))
+            {
+               size_t n;
+
+               //Add padding as necessary
+               n = (ETH_MIN_FRAME_SIZE - ETH_CRC_SIZE) - length;
+
+               //Append padding bytes
+               error = netBufferAppend(buffer, ethPadding, n);
+               //Any error to report?
+               if(error)
+                  return error;
+
+               //Adjust frame length
+               length += n;
+            }
+         }
+
+         //CRC calculation not supported by hardware?
+         if(!interface->nicDriver->autoCrcCalc)
+         {
+            uint32_t crc;
+
+            //Compute CRC over the header and payload
+            crc = ethCalcCrcEx(buffer, 0, length);
+            //Convert from host byte order to little-endian byte order
+            crc = htole32(crc);
+
+            //Append the calculated CRC value
+            error = netBufferAppend(buffer, &crc, sizeof(crc));
+            //Any error to report?
+            if(error)
+               return error;
+
+            //Adjust frame length
+            length += sizeof(crc);
+         }
+
+         //Debug message
+         TRACE_DEBUG("Sending raw Ethernet frame (%" PRIuSIZE " bytes)...\r\n", length);
+
+         //Send the resulting packet over the specified link
+         error = nicSendPacket(interface, buffer, 0);
+      }
+
+      //Free previously allocated memory block
+      netBufferFree(buffer);
+   }
+   else
+#endif
+   //Unknown interface type?
+   {
+      //Report an error
+      error = ERROR_INVALID_INTERFACE;
+   }
+
+   //Successful processing?
+   if(!error)
+   {
+      //Total number of bytes successfully transmitted
+      if(written != NULL)
+         *written = length;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Receive an IP packet from a raw socket
+ * @param[in] socket Handle referencing the socket
+ * @param[out] srcIpAddr Source IP address (optional)
+ * @param[out] destIpAddr Destination IP address (optional)
+ * @param[out] data Buffer where to store the incoming data
+ * @param[in] size Maximum number of bytes that can be received
+ * @param[out] received Number of bytes that have been received
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t rawSocketReceiveIpPacket(Socket *socket, IpAddr *srcIpAddr,
+   IpAddr *destIpAddr, void *data, size_t size, size_t *received, uint_t flags)
+{
+   SocketQueueItem *queueItem;
+
+   //The SOCKET_FLAG_DONT_WAIT enables non-blocking operation
+   if(!(flags & SOCKET_FLAG_DONT_WAIT))
+   {
+      //The receive queue is empty?
+      if(!socket->receiveQueue)
+      {
+         //Set the events the application is interested in
+         socket->eventMask = SOCKET_EVENT_RX_READY;
+         //Reset the event object
+         osResetEvent(&socket->event);
+
+         //Release exclusive access
+         osReleaseMutex(&netMutex);
+         //Wait until an event is triggered
+         osWaitForEvent(&socket->event, socket->timeout);
+         //Get exclusive access
+         osAcquireMutex(&netMutex);
+      }
+   }
+
+   //Check whether the read operation timed out
+   if(!socket->receiveQueue)
+   {
+      //No data can be read
+      *received = 0;
+      //Report a timeout error
+      return ERROR_TIMEOUT;
+   }
+
+   //Point to the first item in the receive queue
+   queueItem = socket->receiveQueue;
+   //Copy data to user buffer
+   *received = netBufferRead(data, queueItem->buffer, queueItem->offset, size);
+
+   //Save the source IP address
+   if(srcIpAddr)
+      *srcIpAddr = queueItem->srcIpAddr;
+   //Save the destination IP address
+   if(destIpAddr)
+      *destIpAddr = queueItem->destIpAddr;
+
+   //If the SOCKET_FLAG_PEEK flag is set, the data is copied
+   //into the buffer but is not removed from the input queue
+   if(!(flags & SOCKET_FLAG_PEEK))
+   {
+      //Remove the item from the receive queue
+      socket->receiveQueue = queueItem->next;
+      //Deallocate memory buffer
+      netBufferFree(queueItem->buffer);
+   }
+
+   //Update the state of events
+   rawSocketUpdateEvents(socket);
+
+   //Successful read operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive an Ethernet packet from a raw socket
+ * @param[in] socket Handle referencing the socket
+ * @param[out] data Buffer where to store the incoming data
+ * @param[in] size Maximum number of bytes that can be received
+ * @param[out] received Number of bytes that have been received
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t rawSocketReceiveEthPacket(Socket *socket,
+   void *data, size_t size, size_t *received, uint_t flags)
+{
+   SocketQueueItem *queueItem;
+
+   //The SOCKET_FLAG_DONT_WAIT enables non-blocking operation
+   if(!(flags & SOCKET_FLAG_DONT_WAIT))
+   {
+      //The receive queue is empty?
+      if(!socket->receiveQueue)
+      {
+         //Set the events the application is interested in
+         socket->eventMask = SOCKET_EVENT_RX_READY;
+         //Reset the event object
+         osResetEvent(&socket->event);
+
+         //Release exclusive access
+         osReleaseMutex(&netMutex);
+         //Wait until an event is triggered
+         osWaitForEvent(&socket->event, socket->timeout);
+         //Get exclusive access
+         osAcquireMutex(&netMutex);
+      }
+   }
+
+   //Check whether the read operation timed out
+   if(!socket->receiveQueue)
+   {
+      //No data can be read
+      *received = 0;
+      //Report a timeout error
+      return ERROR_TIMEOUT;
+   }
+
+   //Point to the first item in the receive queue
+   queueItem = socket->receiveQueue;
+   //Copy data to user buffer
+   *received = netBufferRead(data, queueItem->buffer, queueItem->offset, size);
+
+   //If the SOCKET_FLAG_PEEK flag is set, the data is copied
+   //into the buffer but is not removed from the input queue
+   if(!(flags & SOCKET_FLAG_PEEK))
+   {
+      //Remove the item from the receive queue
+      socket->receiveQueue = queueItem->next;
+      //Deallocate memory buffer
+      netBufferFree(queueItem->buffer);
+   }
+
+   //Update the state of events
+   rawSocketUpdateEvents(socket);
+
+   //Successful read operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Update event state for raw sockets
+ * @param[in] socket Handle referencing the socket
+ **/
+
+void rawSocketUpdateEvents(Socket *socket)
+{
+   //Clear event flags
+   socket->eventFlags = 0;
+
+   //The socket is marked as readable if a datagram is pending in the queue
+   if(socket->receiveQueue)
+      socket->eventFlags |= SOCKET_EVENT_RX_READY;
+
+   //Check whether the socket is bound to a particular network interface
+   if(socket->interface != NULL)
+   {
+      //Handle link up and link down events
+      if(socket->interface->linkState)
+         socket->eventFlags |= SOCKET_EVENT_LINK_UP;
+      else
+         socket->eventFlags |= SOCKET_EVENT_LINK_DOWN;
+   }
+
+   //Mask unused events
+   socket->eventFlags &= socket->eventMask;
+
+   //Any event to signal?
+   if(socket->eventFlags)
+   {
+      //Unblock I/O operations currently in waiting state
+      osSetEvent(&socket->event);
+
+      //Set user event to signaled state if necessary
+      if(socket->userEvent != NULL)
+         osSetEvent(socket->userEvent);
+   }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/raw_socket.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,73 @@
+/**
+ * @file raw_socket.h
+ * @brief TCP/IP raw sockets
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _RAW_SOCKET_H
+#define _RAW_SOCKET_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/ip.h"
+#include "core/socket.h"
+
+//Raw socket support
+#ifndef RAW_SOCKET_SUPPORT
+   #define RAW_SOCKET_SUPPORT DISABLED
+#elif (RAW_SOCKET_SUPPORT != ENABLED && RAW_SOCKET_SUPPORT != DISABLED)
+   #error RAW_SOCKET_SUPPORT parameter is not valid
+#endif
+
+//Receive queue depth for raw sockets
+#ifndef RAW_SOCKET_RX_QUEUE_SIZE
+   #define RAW_SOCKET_RX_QUEUE_SIZE 4
+#elif (RAW_SOCKET_RX_QUEUE_SIZE < 1)
+   #error RAW_SOCKET_RX_QUEUE_SIZE parameter is not valid
+#endif
+
+//Raw socket related functions
+error_t rawSocketProcessIpPacket(NetInterface *interface,
+   IpPseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset);
+
+void rawSocketProcessEthPacket(NetInterface *interface,
+   EthHeader *ethFrame, size_t length);
+
+error_t rawSocketSendIpPacket(Socket *socket, const IpAddr *destIpAddr,
+   const void *data, size_t length, size_t *written);
+
+error_t rawSocketSendEthPacket(Socket *socket,
+   const void *data, size_t length, size_t *written);
+
+error_t rawSocketReceiveIpPacket(Socket *socket, IpAddr *srcIpAddr,
+   IpAddr *destIpAddr, void *data, size_t size, size_t *received, uint_t flags);
+
+error_t rawSocketReceiveEthPacket(Socket *socket,
+   void *data, size_t size, size_t *received, uint_t flags);
+
+void rawSocketUpdateEvents(Socket *socket);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/socket.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1169 @@
+/**
+ * @file socket.c
+ * @brief Socket API
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL SOCKET_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "core/net.h"
+#include "core/socket.h"
+#include "core/raw_socket.h"
+#include "core/udp.h"
+#include "core/tcp.h"
+#include "core/tcp_misc.h"
+#include "dns/dns_client.h"
+#include "mdns/mdns_client.h"
+#include "netbios/nbns_client.h"
+#include "debug.h"
+
+//Socket table
+Socket socketTable[SOCKET_MAX_COUNT];
+
+
+/**
+ * @brief Socket related initialization
+ * @return Error code
+ **/
+
+error_t socketInit(void)
+{
+   uint_t i;
+   uint_t j;
+
+   //Initialize socket descriptors
+   memset(socketTable, 0, sizeof(socketTable));
+
+   //Loop through socket descriptors
+   for(i = 0; i < SOCKET_MAX_COUNT; i++)
+   {
+      //Set socket identifier
+      socketTable[i].descriptor = i;
+
+      //Create an event object to track socket events
+      if(!osCreateEvent(&socketTable[i].event))
+      {
+         //Clean up side effects
+         for(j = 0; j < i; j++)
+            osDeleteEvent(&socketTable[j].event);
+
+         //Report an error
+         return ERROR_OUT_OF_RESOURCES;
+      }
+   }
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Create a socket (UDP or TCP)
+ * @param[in] type Type specification for the new socket
+ * @param[in] protocol Protocol to be used
+ * @return Handle referencing the new socket
+ **/
+
+Socket *socketOpen(uint_t type, uint_t protocol)
+{
+   error_t error;
+   uint_t i;
+   uint16_t port;
+   Socket *socket;
+   OsEvent event;
+
+   //Initialize socket handle
+   socket = NULL;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+#if (TCP_SUPPORT == ENABLED)
+   //Connection-oriented socket?
+   if(type == SOCKET_TYPE_STREAM)
+   {
+      //Always use TCP as underlying transport protocol
+      protocol = SOCKET_IP_PROTO_TCP;
+      //Get an ephemeral port number
+      port = tcpGetDynamicPort();
+      //Continue processing
+      error = NO_ERROR;
+   }
+   else
+#endif
+#if (UDP_SUPPORT == ENABLED)
+   //Connectionless socket?
+   if(type == SOCKET_TYPE_DGRAM)
+   {
+      //Always use UDP as underlying transport protocol
+      protocol = SOCKET_IP_PROTO_UDP;
+      //Get an ephemeral port number
+      port = udpGetDynamicPort();
+      //Continue processing
+      error = NO_ERROR;
+   }
+   else
+#endif
+#if (RAW_SOCKET_SUPPORT == ENABLED)
+   //Raw socket?
+   if(type == SOCKET_TYPE_RAW_IP || type == SOCKET_TYPE_RAW_ETH)
+   {
+      //Port numbers are not relevant for raw sockets
+      port = 0;
+      //Continue processing
+      error = NO_ERROR;
+   }
+   else
+#endif
+   {
+      //The socket type is not supported
+      error = ERROR_INVALID_PARAMETER;
+   }
+
+   //Check status code
+   if(!error)
+   {
+      //Loop through socket descriptors
+      for(i = 0; i < SOCKET_MAX_COUNT; i++)
+      {
+         //Unused socket found?
+         if(socketTable[i].type == SOCKET_TYPE_UNUSED)
+         {
+            //Save socket handle
+            socket = &socketTable[i];
+            //We are done
+            break;
+         }
+      }
+
+#if (TCP_SUPPORT == ENABLED)
+      //No more sockets available?
+      if(socket == NULL)
+      {
+         //Kill the oldest connection in the TIME-WAIT state
+         //whenever the socket table runs out of space
+         socket = tcpKillOldestConnection();
+      }
+#endif
+
+      //Check whether the current entry is free
+      if(socket != NULL)
+      {
+         //Save socket descriptor
+         i = socket->descriptor;
+         //Save event object instance
+         memcpy(&event, &socket->event, sizeof(OsEvent));
+
+         //Clear associated structure
+         memset(socket, 0, sizeof(Socket));
+         //Reuse event objects and avoid recreating them whenever possible
+         memcpy(&socket->event, &event, sizeof(OsEvent));
+
+         //Save socket characteristics
+         socket->descriptor = i;
+         socket->type = type;
+         socket->protocol = protocol;
+         socket->localPort = port;
+         socket->timeout = INFINITE_DELAY;
+
+#if (TCP_SUPPORT == ENABLED)
+         socket->txBufferSize = MIN(TCP_DEFAULT_TX_BUFFER_SIZE, TCP_MAX_TX_BUFFER_SIZE);
+         socket->rxBufferSize = MIN(TCP_DEFAULT_RX_BUFFER_SIZE, TCP_MAX_RX_BUFFER_SIZE);
+#endif
+      }
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return a handle to the freshly created socket
+   return socket;
+}
+
+
+/**
+ * @brief Set timeout value for blocking operations
+ * @param[in] socket Handle to a socket
+ * @param[in] timeout Maximum time to wait
+ * @return Error code
+ **/
+
+error_t socketSetTimeout(Socket *socket, systime_t timeout)
+{
+   //Make sure the socket handle is valid
+   if(socket == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Record timeout value
+   socket->timeout = timeout;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Specify the size of the send buffer
+ * @param[in] socket Handle to a socket
+ * @param[in] size Desired buffer size in bytes
+ * @return Error code
+ **/
+
+error_t socketSetTxBufferSize(Socket *socket, size_t size)
+{
+#if (TCP_SUPPORT == ENABLED)
+   //Make sure the socket handle is valid
+   if(socket == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Check parameter value
+   if(size < 1 || size > TCP_MAX_TX_BUFFER_SIZE)
+      return ERROR_INVALID_PARAMETER;
+
+   //This function shall be used with connection-oriented socket types
+   if(socket->type != SOCKET_TYPE_STREAM)
+      return ERROR_INVALID_SOCKET;
+   //The buffer size cannot be changed when the connection is established
+   if(tcpGetState(socket) != TCP_STATE_CLOSED)
+      return ERROR_INVALID_SOCKET;
+
+   //Use the specified buffer size
+   socket->txBufferSize = size;
+   //No error to report
+   return NO_ERROR;
+#else
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Specify the size of the receive buffer
+ * @param[in] socket Handle to a socket
+ * @param[in] size Desired buffer size in bytes
+ * @return Error code
+ **/
+
+error_t socketSetRxBufferSize(Socket *socket, size_t size)
+{
+#if (TCP_SUPPORT == ENABLED)
+   //Make sure the socket handle is valid
+   if(socket == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Check parameter value
+   if(size < 1 || size > TCP_MAX_RX_BUFFER_SIZE)
+      return ERROR_INVALID_PARAMETER;
+
+   //This function shall be used with connection-oriented socket types
+   if(socket->type != SOCKET_TYPE_STREAM)
+      return ERROR_INVALID_SOCKET;
+   //The buffer size cannot be changed when the connection is established
+   if(tcpGetState(socket) != TCP_STATE_CLOSED)
+      return ERROR_INVALID_SOCKET;
+
+   //Use the specified buffer size
+   socket->rxBufferSize = size;
+   //No error to report
+   return NO_ERROR;
+#else
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Bind a socket to a particular network interface
+ * @param[in] socket Handle to a socket
+ * @param[in] interface Network interface to be used
+ * @return Error code
+ **/
+
+error_t socketBindToInterface(Socket *socket, NetInterface *interface)
+{
+   //Make sure the socket handle is valid
+   if(socket == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Explicitly associate the socket with the specified interface
+   socket->interface = interface;
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Associate a local address with a socket
+ * @param[in] socket Handle to a socket
+ * @param[in] localIpAddr Local address to assign to the bound socket
+ * @param[in] localPort Local port number to assign to the bound socket
+ * @return Error code
+ **/
+
+error_t socketBind(Socket *socket, const IpAddr *localIpAddr, uint16_t localPort)
+{
+   //Check input parameters
+   if(!socket || !localIpAddr)
+      return ERROR_INVALID_PARAMETER;
+   //Make sure the socket type is correct
+   if(socket->type != SOCKET_TYPE_STREAM && socket->type != SOCKET_TYPE_DGRAM)
+      return ERROR_INVALID_SOCKET;
+
+   //Associate the specified IP address and port number
+   socket->localIpAddr = *localIpAddr;
+   socket->localPort = localPort;
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Establish a connection to a specified socket
+ * @param[in] socket Handle to an unconnected socket
+ * @param[in] remoteIpAddr IP address of the remote host
+ * @param[in] remotePort Remote port number that will be used to establish the connection
+ * @return Error code
+ **/
+
+error_t socketConnect(Socket *socket, const IpAddr *remoteIpAddr, uint16_t remotePort)
+{
+   error_t error;
+
+   //Check input parameters
+   if(!socket || !remoteIpAddr)
+      return ERROR_INVALID_PARAMETER;
+
+#if (TCP_SUPPORT == ENABLED)
+   //Connection-oriented socket?
+   if(socket->type == SOCKET_TYPE_STREAM)
+   {
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+
+      //Establish TCP connection
+      error = tcpConnect(socket, remoteIpAddr, remotePort);
+
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+   }
+   else
+#endif
+   //Connectionless socket?
+   if(socket->type == SOCKET_TYPE_DGRAM)
+   {
+      //Save port number and IP address of the remote host
+      socket->remoteIpAddr = *remoteIpAddr;
+      socket->remotePort = remotePort;
+      //No error to report
+      error = NO_ERROR;
+   }
+   //Raw socket?
+   else if(socket->type == SOCKET_TYPE_RAW_IP)
+   {
+      //Save the IP address of the remote host
+      socket->remoteIpAddr = *remoteIpAddr;
+      //No error to report
+      error = NO_ERROR;
+   }
+   //Socket type not supported...
+   else
+   {
+      //Invalid socket type
+      error = ERROR_INVALID_SOCKET;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Place a socket in the listening state
+ *
+ * Place a socket in a state in which it is listening for an incoming connection
+ *
+ * @param[in] socket Socket to place in the listening state
+ * @param[in] backlog backlog The maximum length of the pending connection queue.
+ *   If this parameter is zero, then the default backlog value is used instead
+ * @return Error code
+ **/
+
+error_t socketListen(Socket *socket, uint_t backlog)
+{
+#if (TCP_SUPPORT == ENABLED)
+   error_t error;
+
+   //Make sure the socket handle is valid
+   if(socket == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //This function shall be used with connection-oriented socket types
+   if(socket->type != SOCKET_TYPE_STREAM)
+      return ERROR_INVALID_SOCKET;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Start listening for an incoming connection
+   error = tcpListen(socket, backlog);
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return status code
+   return error;
+#else
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Permit an incoming connection attempt on a socket
+ * @param[in] socket Handle to a socket previously placed in a listening state
+ * @param[out] clientIpAddr IP address of the client
+ * @param[out] clientPort Port number used by the client
+ * @return Handle to the socket in which the actual connection is made
+ **/
+
+Socket *socketAccept(Socket *socket, IpAddr *clientIpAddr, uint16_t *clientPort)
+{
+#if (TCP_SUPPORT == ENABLED)
+   Socket *newSocket;
+
+   //Make sure the socket handle is valid
+   if(socket == NULL)
+      return NULL;
+   //This function shall be used with connection-oriented socket types
+   if(socket->type != SOCKET_TYPE_STREAM)
+      return NULL;
+
+   //Accept an incoming connection attempt
+   newSocket = tcpAccept(socket, clientIpAddr, clientPort);
+
+   //Return a handle to the newly created socket
+   return newSocket;
+#else
+   return NULL;
+#endif
+}
+
+
+/**
+ * @brief Send data to a connected socket
+ * @param[in] socket Handle that identifies a connected socket
+ * @param[in] data Pointer to a buffer containing the data to be transmitted
+ * @param[in] length Number of data bytes to send
+ * @param[out] written Actual number of bytes written (optional parameter)
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t socketSend(Socket *socket, const void *data,
+   size_t length, size_t *written, uint_t flags)
+{
+   //Use default remote IP address for connectionless or raw sockets
+   return socketSendTo(socket, &socket->remoteIpAddr,
+      socket->remotePort, data, length, written, flags);
+}
+
+
+/**
+ * @brief Send a datagram to a specific destination
+ * @param[in] socket Handle that identifies a socket
+ * @param[in] destIpAddr IP address of the target host
+ * @param[in] destPort Target port number
+ * @param[in] data Pointer to a buffer containing the data to be transmitted
+ * @param[in] length Number of data bytes to send
+ * @param[out] written Actual number of bytes written (optional parameter)
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t socketSendTo(Socket *socket, const IpAddr *destIpAddr, uint16_t destPort,
+   const void *data, size_t length, size_t *written, uint_t flags)
+{
+   error_t error;
+
+   //No data has been transmitted yet
+   if(written)
+      *written = 0;
+
+   //Make sure the socket handle is valid
+   if(socket == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+#if (TCP_SUPPORT == ENABLED)
+   //Connection-oriented socket?
+   if(socket->type == SOCKET_TYPE_STREAM)
+   {
+      //For connection-oriented sockets, target address is ignored
+      error = tcpSend(socket, data, length, written, flags);
+   }
+   else
+#endif
+#if (UDP_SUPPORT == ENABLED)
+   //Connectionless socket?
+   if(socket->type == SOCKET_TYPE_DGRAM)
+   {
+      //Send UDP datagram
+      error = udpSendDatagram(socket, destIpAddr,
+         destPort, data, length, written);
+   }
+   else
+#endif
+#if (RAW_SOCKET_SUPPORT == ENABLED)
+   //Raw socket?
+   if(socket->type == SOCKET_TYPE_RAW_IP)
+   {
+      //Send a raw IP packet
+      error = rawSocketSendIpPacket(socket, destIpAddr, data, length, written);
+   }
+   else if(socket->type == SOCKET_TYPE_RAW_ETH)
+   {
+      //Send a raw Ethernet packet
+      error = rawSocketSendEthPacket(socket, data, length, written);
+   }
+   else
+#endif
+   //Socket type not supported...
+   {
+      //Invalid socket type
+      error = ERROR_INVALID_SOCKET;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Receive data from a connected socket
+ * @param[in] socket Handle that identifies a connected socket
+ * @param[out] data Buffer where to store the incoming data
+ * @param[in] size Maximum number of bytes that can be received
+ * @param[out] received Number of bytes that have been received
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t socketReceive(Socket *socket, void *data,
+   size_t size, size_t *received, uint_t flags)
+{
+   //For connection-oriented sockets, source and destination addresses are no use
+   return socketReceiveEx(socket, NULL, NULL, NULL, data, size, received, flags);
+}
+
+
+/**
+ * @brief Receive a datagram from a connectionless socket
+ * @param[in] socket Handle that identifies a socket
+ * @param[out] srcIpAddr Source IP address (optional)
+ * @param[out] srcPort Source port number (optional)
+ * @param[out] data Buffer where to store the incoming data
+ * @param[in] size Maximum number of bytes that can be received
+ * @param[out] received Number of bytes that have been received
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t socketReceiveFrom(Socket *socket, IpAddr *srcIpAddr, uint16_t *srcPort,
+   void *data, size_t size, size_t *received, uint_t flags)
+{
+   //Destination address is no use
+   return socketReceiveEx(socket, srcIpAddr, srcPort, NULL, data, size, received, flags);
+}
+
+
+/**
+ * @brief Receive a datagram
+ * @param[in] socket Handle that identifies a socket
+ * @param[out] srcIpAddr Source IP address (optional)
+ * @param[out] srcPort Source port number (optional)
+ * @param[out] destIpAddr Destination IP address (optional)
+ * @param[out] data Buffer where to store the incoming data
+ * @param[in] size Maximum number of bytes that can be received
+ * @param[out] received Number of bytes that have been received
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t socketReceiveEx(Socket *socket, IpAddr *srcIpAddr, uint16_t *srcPort,
+   IpAddr *destIpAddr, void *data, size_t size, size_t *received, uint_t flags)
+{
+   error_t error;
+
+   //Make sure the socket handle is valid
+   if(socket == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+#if (TCP_SUPPORT == ENABLED)
+   //Connection-oriented socket?
+   if(socket->type == SOCKET_TYPE_STREAM)
+   {
+      //Receive data
+      error = tcpReceive(socket, data, size, received, flags);
+
+      //Output parameters
+      if(srcIpAddr)
+         *srcIpAddr = socket->remoteIpAddr;
+      if(srcPort)
+         *srcPort = socket->remotePort;
+      if(destIpAddr)
+         *destIpAddr = socket->localIpAddr;
+   }
+   else
+#endif
+#if (UDP_SUPPORT == ENABLED)
+   //Connectionless socket?
+   if(socket->type == SOCKET_TYPE_DGRAM)
+   {
+      //Receive UDP datagram
+      error = udpReceiveDatagram(socket, srcIpAddr,
+         srcPort, destIpAddr, data, size, received, flags);
+   }
+   else
+#endif
+#if (RAW_SOCKET_SUPPORT == ENABLED)
+   //Raw socket?
+   if(socket->type == SOCKET_TYPE_RAW_IP)
+   {
+      //Receive a raw IP packet
+      error = rawSocketReceiveIpPacket(socket,
+         srcIpAddr, destIpAddr, data, size, received, flags);
+   }
+   else if(socket->type == SOCKET_TYPE_RAW_ETH)
+   {
+      //Receive a raw Ethernet packet
+      error = rawSocketReceiveEthPacket(socket, data, size, received, flags);
+   }
+   else
+#endif
+   //Socket type not supported...
+   {
+      //No data can be read
+      *received = 0;
+      //Invalid socket type
+      error = ERROR_INVALID_SOCKET;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Retrieve the local address for a given socket
+ * @param[in] socket Handle that identifies a socket
+ * @param[out] localIpAddr Local IP address (optional)
+ * @param[out] localPort Local port number (optional)
+ * @return Error code
+ **/
+
+error_t socketGetLocalAddr(Socket *socket, IpAddr *localIpAddr, uint16_t *localPort)
+{
+   //Make sure the socket handle is valid
+   if(socket == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Retrieve local IP address
+   if(localIpAddr != NULL)
+      *localIpAddr = socket->localIpAddr;
+
+   //Retrieve local port number
+   if(localPort != NULL)
+      *localPort = socket->localPort;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Retrieve the address of the peer to which a socket is connected
+ * @param[in] socket Handle that identifies a socket
+ * @param[out] remoteIpAddr IP address of the remote host (optional)
+ * @param[out] remotePort Remote port number (optional)
+ * @return Error code
+ **/
+
+error_t socketGetRemoteAddr(Socket *socket, IpAddr *remoteIpAddr, uint16_t *remotePort)
+{
+   //Make sure the socket handle is valid
+   if(socket == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Retrieve local IP address
+   if(remoteIpAddr != NULL)
+      *remoteIpAddr = socket->remoteIpAddr;
+
+   //Retrieve local port number
+   if(remotePort != NULL)
+      *remotePort = socket->remotePort;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Disable reception, transmission, or both
+ *
+ * Note that socketShutdown() does not close the socket, and resources attached
+ * to the socket will not be freed until socketClose() is invoked
+ *
+ * @param[in] socket Handle to a socket
+ * @param[in] how Flag that describes what types of operation will no longer be allowed
+ * @return Error code
+ **/
+
+error_t socketShutdown(Socket *socket, uint_t how)
+{
+#if (TCP_SUPPORT == ENABLED)
+   error_t error;
+
+   //Make sure the socket handle is valid
+   if(socket == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Make sure the socket type is correct
+   if(socket->type != SOCKET_TYPE_STREAM)
+      return ERROR_INVALID_SOCKET;
+   //Check flags
+   if((how != SOCKET_SD_SEND) && (how != SOCKET_SD_RECEIVE) && (how != SOCKET_SD_BOTH))
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Graceful shutdown
+   error = tcpShutdown(socket, how);
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return status code
+   return error;
+#else
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Close an existing socket
+ * @param[in] socket Handle identifying the socket to close
+ **/
+
+void socketClose(Socket *socket)
+{
+   //Make sure the socket handle is valid
+   if(socket == NULL)
+      return;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+#if (TCP_SUPPORT == ENABLED)
+   //Connection-oriented socket?
+   if(socket->type == SOCKET_TYPE_STREAM)
+   {
+      //Abort the current TCP connection
+      tcpAbort(socket);
+   }
+#endif
+#if (UDP_SUPPORT == ENABLED || RAW_SOCKET_SUPPORT == ENABLED)
+   //Connectionless socket or raw socket?
+   if(socket->type == SOCKET_TYPE_DGRAM ||
+      socket->type == SOCKET_TYPE_RAW_IP ||
+      socket->type == SOCKET_TYPE_RAW_ETH)
+   {
+      //Point to the first item in the receive queue
+      SocketQueueItem *queueItem = socket->receiveQueue;
+
+      //Purge the receive queue
+      while(queueItem)
+      {
+         //Keep track of the next item in the queue
+         SocketQueueItem *nextQueueItem = queueItem->next;
+         //Free previously allocated memory
+         memPoolFree(queueItem->buffer);
+         //Point to the next item
+         queueItem = nextQueueItem;
+      }
+
+      //Mark the socket as closed
+      socket->type = SOCKET_TYPE_UNUSED;
+   }
+#endif
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+}
+
+
+/**
+ * @brief Wait for one of a set of sockets to become ready to perform I/O
+ *
+ * The socketPoll function determines the status of one or more sockets,
+ * waiting if necessary, to perform synchronous I/O
+ *
+ * @param[in,out] eventDesc Set of entries specifying the events the user is interested in
+ * @param[in] size Number of entries in the descriptor set
+ * @param[in] extEvent External event that can abort the wait if necessary (optional)
+ * @param[in] timeout Maximum time to wait before returning
+ * @return Error code
+ **/
+
+error_t socketPoll(SocketEventDesc *eventDesc, uint_t size, OsEvent *extEvent, systime_t timeout)
+{
+   uint_t i;
+   bool_t status;
+   OsEvent *event;
+   OsEvent eventObject;
+
+   //Check parameters
+   if(!eventDesc || !size)
+      return ERROR_INVALID_PARAMETER;
+
+   //Try to use the supplied event object to receive notifications
+   if(!extEvent)
+   {
+      //Create an event object only if necessary
+      if(!osCreateEvent(&eventObject))
+      {
+         //Report an error
+         return ERROR_OUT_OF_RESOURCES;
+      }
+
+      //Reference to the newly created event
+      event = &eventObject;
+   }
+   else
+   {
+      //Reference to the external event
+      event = extEvent;
+   }
+
+   //Loop through descriptors
+   for(i = 0; i < size; i++)
+   {
+      //Clear event flags
+      eventDesc[i].eventFlags = 0;
+      //Subscribe to the requested events
+      socketRegisterEvents(eventDesc[i].socket, event, eventDesc[i].eventMask);
+   }
+
+   //Block the current task until an event occurs
+   status = osWaitForEvent(event, timeout);
+
+   //Any socket event is in the signaled state?
+   if(status)
+   {
+      //Loop through descriptors
+      for(i = 0; i < size; i++)
+      {
+         //Retrieve event flags for the current socket
+         socketGetEvents(eventDesc[i].socket, &eventDesc[i].eventFlags);
+         //Clear unnecessary flags
+         eventDesc[i].eventFlags &= eventDesc[i].eventMask;
+      }
+   }
+
+   //Unsubscribe previously registered events
+   for(i = 0; i < size; i++)
+      socketUnregisterEvents(eventDesc[i].socket);
+
+   //Reset event object before exiting...
+   osResetEvent(event);
+
+   //Release previously allocated resources
+   if(!extEvent)
+      osDeleteEvent(&eventObject);
+
+   //Return status code
+   return status ? NO_ERROR : ERROR_TIMEOUT;
+}
+
+
+/**
+ * @brief Subscribe to the specified socket events
+ * @param[in] socket Handle that identifies a socket
+ * @param[in] event Event object used to receive notifications
+ * @param[in] eventMask Logic OR of the requested socket events
+ * @return Error code
+ **/
+
+error_t socketRegisterEvents(Socket *socket, OsEvent *event, uint_t eventMask)
+{
+   //Make sure the socket handle is valid
+   if(socket == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //An user event may have been previously registered...
+   if(socket->userEvent != NULL)
+      socket->eventMask |= eventMask;
+   else
+      socket->eventMask = eventMask;
+
+   //Suscribe to get notified of events
+   socket->userEvent = event;
+
+#if (TCP_SUPPORT == ENABLED)
+   //Handle TCP specific events
+   if(socket->type == SOCKET_TYPE_STREAM)
+   {
+      tcpUpdateEvents(socket);
+   }
+#endif
+#if (UDP_SUPPORT == ENABLED)
+   //Handle UDP specific events
+   if(socket->type == SOCKET_TYPE_DGRAM)
+   {
+      udpUpdateEvents(socket);
+   }
+#endif
+#if (RAW_SOCKET_SUPPORT == ENABLED)
+   //Handle events that are specific to raw sockets
+   if(socket->type == SOCKET_TYPE_RAW_IP ||
+      socket->type == SOCKET_TYPE_RAW_ETH)
+   {
+      rawSocketUpdateEvents(socket);
+   }
+#endif
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Unsubscribe previously registered events
+ * @param[in] socket Handle that identifies a socket
+ * @return Error code
+ **/
+
+error_t socketUnregisterEvents(Socket *socket)
+{
+   //Make sure the socket handle is valid
+   if(socket == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Unsuscribe socket events
+   socket->userEvent = NULL;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Retrieve event flags for a specified socket
+ * @param[in] socket Handle that identifies a socket
+ * @param[out] eventFlags Logic OR of events in the signaled state
+ * @return Error code
+ **/
+
+error_t socketGetEvents(Socket *socket, uint_t *eventFlags)
+{
+   //Make sure the socket handle is valid
+   if(socket == NULL)
+   {
+      //Always return a valid value
+      *eventFlags = 0;
+      //Report an error
+      return ERROR_INVALID_PARAMETER;
+   }
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Read event flags for the specified socket
+   *eventFlags = socket->eventFlags;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Resolve a host name into an IP address
+ * @param[in] interface Underlying network interface (optional parameter)
+ * @param[in] name Name of the host to be resolved
+ * @param[out] ipAddr IP address corresponding to the specified host name
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t getHostByName(NetInterface *interface,
+   const char_t *name, IpAddr *ipAddr, uint_t flags)
+{
+   error_t error;
+
+   //Default address type depends on TCP/IP stack configuration
+#if (IPV4_SUPPORT == ENABLED)
+   HostType type = HOST_TYPE_IPV4;
+#elif (IPV6_SUPPORT == ENABLED)
+   HostType type = HOST_TYPE_IPV6;
+#else
+   HostType type = HOST_TYPE_ANY;
+#endif
+
+   //Default name resolution protocol depends on TCP/IP stack configuration
+#if (DNS_CLIENT_SUPPORT == ENABLED)
+   HostnameResolver protocol = HOST_NAME_RESOLVER_DNS;
+#elif (MDNS_CLIENT_SUPPORT == ENABLED)
+   HostnameResolver protocol = HOST_NAME_RESOLVER_MDNS;
+#elif (NBNS_CLIENT_SUPPORT == ENABLED)
+   HostnameResolver protocol = HOST_NAME_RESOLVER_NBNS;
+#else
+   HostnameResolver protocol = HOST_NAME_RESOLVER_ANY;
+#endif
+
+   //Check parameters
+   if(name == NULL || ipAddr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Use default network interface?
+   if(interface == NULL)
+      interface = netGetDefaultInterface();
+
+   //The specified name can be either an IP or a host name
+   error = ipStringToAddr(name, ipAddr);
+
+   //Perform name resolution if necessary
+   if(error)
+   {
+      //The user may provide a hint to choose between IPv4 and IPv6
+      if(flags & HOST_TYPE_IPV4)
+         type = HOST_TYPE_IPV4;
+      else if(flags & HOST_TYPE_IPV6)
+         type = HOST_TYPE_IPV6;
+
+      //The user may provide a hint to to select the desired protocol to be used
+      if(flags & HOST_NAME_RESOLVER_DNS)
+      {
+         //Use DNS to resolve the specified host name
+         protocol = HOST_NAME_RESOLVER_DNS;
+      }
+      else if(flags & HOST_NAME_RESOLVER_MDNS)
+      {
+         //Use mDNS to resolve the specified host name
+         protocol = HOST_NAME_RESOLVER_MDNS;
+      }
+      else if(flags & HOST_NAME_RESOLVER_NBNS)
+      {
+         //Use NBNS to resolve the specified host name
+         protocol = HOST_NAME_RESOLVER_NBNS;
+      }
+      else
+      {
+         //Retrieve the length of the host name to be resolved
+         size_t n = strlen(name);
+
+         //Select the most suitable protocol
+         if(n >= 6 && !strcasecmp(name + n - 6, ".local"))
+         {
+#if (MDNS_CLIENT_SUPPORT == ENABLED)
+            //Use mDNS to resolve the specified host name
+            protocol = HOST_NAME_RESOLVER_MDNS;
+#endif
+         }
+         else if(n <= 15 && !strchr(name, '.') && type == HOST_TYPE_IPV4)
+         {
+#if (NBNS_CLIENT_SUPPORT == ENABLED)
+            //Use NetBIOS Name Service to resolve the specified host name
+            protocol = HOST_NAME_RESOLVER_NBNS;
+#endif
+         }
+      }
+
+#if (DNS_CLIENT_SUPPORT == ENABLED)
+      //Use DNS protocol?
+      if(protocol == HOST_NAME_RESOLVER_DNS)
+      {
+         //Perform host name resolution
+         error = dnsResolve(interface, name, type, ipAddr);
+      }
+      else
+#endif
+#if (MDNS_CLIENT_SUPPORT == ENABLED)
+      //Use mDNS protocol?
+      if(protocol == HOST_NAME_RESOLVER_MDNS)
+      {
+         //Perform host name resolution
+         error = mdnsClientResolve(interface, name, type, ipAddr);
+      }
+      else
+#endif
+#if (NBNS_CLIENT_SUPPORT == ENABLED && IPV4_SUPPORT == ENABLED)
+      //Use NetBIOS Name Service protocol?
+      if(protocol == HOST_NAME_RESOLVER_NBNS)
+      {
+         //Perform host name resolution
+         error = nbnsResolve(interface, name, ipAddr);
+      }
+      else
+#endif
+      //Invalid protocol?
+      {
+         //Report an error
+         error = ERROR_INVALID_PARAMETER;
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/socket.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,354 @@
+/**
+ * @file socket.h
+ * @brief Socket API
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SOCKET_H
+#define _SOCKET_H
+
+//Forward declaration of Socket structure
+struct _Socket;
+#define Socket struct _Socket
+
+//Dependencies
+#include "core/net.h"
+#include "core/ip.h"
+#include "core/tcp.h"
+
+//Number of sockets that can be opened simultaneously
+#ifndef SOCKET_MAX_COUNT
+   #define SOCKET_MAX_COUNT 16
+#elif (SOCKET_MAX_COUNT < 1)
+   #error SOCKET_MAX_COUNT parameter is not valid
+#endif
+
+//Dynamic port range (lower limit)
+#ifndef SOCKET_EPHEMERAL_PORT_MIN
+   #define SOCKET_EPHEMERAL_PORT_MIN 49152
+#elif (SOCKET_EPHEMERAL_PORT_MIN < 1024)
+   #error SOCKET_EPHEMERAL_PORT_MIN parameter is not valid
+#endif
+
+//Dynamic port range (upper limit)
+#ifndef SOCKET_EPHEMERAL_PORT_MAX
+   #define SOCKET_EPHEMERAL_PORT_MAX 65535
+#elif (SOCKET_EPHEMERAL_PORT_MAX <= SOCKET_EPHEMERAL_PORT_MIN || SOCKET_EPHEMERAL_PORT_MAX > 65535)
+   #error SOCKET_EPHEMERAL_PORT_MAX parameter is not valid
+#endif
+
+
+/**
+ * @brief Socket types
+ **/
+
+typedef enum
+{
+   SOCKET_TYPE_UNUSED  = 0,
+   SOCKET_TYPE_STREAM  = 1,
+   SOCKET_TYPE_DGRAM   = 2,
+   SOCKET_TYPE_RAW_IP  = 3,
+   SOCKET_TYPE_RAW_ETH = 4
+} SocketType;
+
+
+/**
+ * @brief IP protocols
+ **/
+
+typedef enum
+{
+   SOCKET_IP_PROTO_ICMP   = 1,
+   SOCKET_IP_PROTO_IGMP   = 2,
+   SOCKET_IP_PROTO_TCP    = 6,
+   SOCKET_IP_PROTO_UDP    = 17,
+   SOCKET_IP_PROTO_ICMPV6 = 58
+} SocketIpProtocol;
+
+
+/**
+ * @brief Ethernet protocols
+ **/
+
+typedef enum
+{
+   SOCKET_ETH_PROTO_ALL  = 0x0000,
+   SOCKET_ETH_PROTO_IPV4 = 0x0800,
+   SOCKET_ETH_PROTO_ARP  = 0x0806,
+   SOCKET_ETH_PROTO_IPV6 = 0x86DD
+} SocketEthProtocol;
+
+
+/**
+ * @brief Flags used by I/O functions
+ **/
+
+typedef enum
+{
+   SOCKET_FLAG_PEEK       = 0x0200,
+   SOCKET_FLAG_DONT_ROUTE = 0x0400,
+   SOCKET_FLAG_WAIT_ALL   = 0x0800,
+   SOCKET_FLAG_DONT_WAIT  = 0x0100,
+   SOCKET_FLAG_BREAK_CHAR = 0x1000,
+   SOCKET_FLAG_BREAK_CRLF = 0x100A,
+   SOCKET_FLAG_WAIT_ACK   = 0x2000,
+   SOCKET_FLAG_NO_DELAY   = 0x4000,
+   SOCKET_FLAG_DELAY      = 0x8000
+} SocketFlags;
+
+
+//The SOCKET_FLAG_BREAK macro causes the I/O functions to stop reading
+//data whenever the specified break character is encountered
+#define SOCKET_FLAG_BREAK(c) (SOCKET_FLAG_BREAK_CHAR | LSB(c))
+
+
+/**
+ * @brief Flags used by shutdown function
+ **/
+
+typedef enum
+{
+   SOCKET_SD_RECEIVE = 0,
+   SOCKET_SD_SEND    = 1,
+   SOCKET_SD_BOTH    = 2
+} SocketShutdownFlags;
+
+
+/**
+ * @brief Socket events
+ **/
+
+typedef enum
+{
+   SOCKET_EVENT_TIMEOUT      = 0x0000,
+   SOCKET_EVENT_CONNECTED    = 0x0001,
+   SOCKET_EVENT_CLOSED       = 0x0002,
+   SOCKET_EVENT_TX_READY     = 0x0004,
+   SOCKET_EVENT_TX_DONE      = 0x0008,
+   SOCKET_EVENT_TX_ACKED     = 0x0010,
+   SOCKET_EVENT_TX_SHUTDOWN  = 0x0020,
+   SOCKET_EVENT_RX_READY     = 0x0040,
+   SOCKET_EVENT_RX_SHUTDOWN  = 0x0080,
+   SOCKET_EVENT_LINK_UP      = 0x0100,
+   SOCKET_EVENT_LINK_DOWN    = 0x0200
+} SocketEvent;
+
+
+/**
+ * @brief Host types
+ **/
+
+typedef enum
+{
+   HOST_TYPE_ANY  = 0,
+   HOST_TYPE_IPV4 = 16,
+   HOST_TYPE_IPV6 = 32
+} HostType;
+
+
+/**
+ * @brief Name resolution protocols
+ **/
+
+typedef enum
+{
+   HOST_NAME_RESOLVER_ANY   = 0,
+   HOST_NAME_RESOLVER_DNS   = 1,
+   HOST_NAME_RESOLVER_MDNS  = 2,
+   HOST_NAME_RESOLVER_NBNS  = 4,
+   HOST_NAME_RESOLVER_LLMNR = 8
+} HostnameResolver;
+
+
+/**
+ * @brief Receive queue item
+ **/
+
+typedef struct _SocketQueueItem
+{
+   struct _SocketQueueItem *next;
+   IpAddr srcIpAddr;
+   uint16_t srcPort;
+   IpAddr destIpAddr;
+   NetBuffer *buffer;
+   size_t offset;
+} SocketQueueItem;
+
+
+/**
+ * @brief Structure describing a socket
+ **/
+
+struct _Socket
+{
+   uint_t descriptor;
+   uint_t type;
+   uint_t protocol;
+   NetInterface *interface;
+   IpAddr localIpAddr;
+   uint16_t localPort;
+   IpAddr remoteIpAddr;
+   uint16_t remotePort;
+   systime_t timeout;
+   uint8_t ttl;
+   int_t errnoCode;
+   OsEvent event;
+   uint_t eventMask;
+   uint_t eventFlags;
+   OsEvent *userEvent;
+
+//TCP specific variables
+#if (TCP_SUPPORT == ENABLED)
+   TcpState state;                ///<Current state of the TCP finite state machine
+   bool_t ownedFlag;              ///<The user is the owner of the TCP socket
+   bool_t closedFlag;             ///<The connection has been closed properly
+   bool_t resetFlag;              ///<The connection has been reset
+
+   uint16_t smss;                 ///<Sender maximum segment size
+   uint16_t rmss;                 ///<Receiver maximum segment size
+   uint32_t iss;                  ///<Initial send sequence number
+   uint32_t irs;                  ///<Initial receive sequence number
+
+   uint32_t sndUna;               ///<Data that have been sent but not yet acknowledged
+   uint32_t sndNxt;               ///<Sequence number of the next byte to be sent
+   uint16_t sndUser;              ///<Amount of data buffered but not yet sent
+   uint16_t sndWnd;               ///<Size of the send window
+   uint16_t maxSndWnd;            ///<Maximum send window it has seen so far on the connection
+   uint32_t sndWl1;               ///<Segment sequence number used for last window update
+   uint32_t sndWl2;               ///<Segment acknowledgment number used for last window update
+
+   uint32_t rcvNxt;               ///<Receive next sequence number
+   uint16_t rcvUser;              ///<Number of data received but not yet consumed
+   uint16_t rcvWnd;               ///<Receive window
+
+   bool_t rttBusy;                ///<RTT measurement is being performed
+   uint32_t rttSeqNum;            ///<Sequence number identifying a TCP segment
+   systime_t rttStartTime;        ///<Round-trip start time
+   systime_t srtt;                ///<Smoothed round-trip time
+   systime_t rttvar;              ///<Round-trip time variation
+   systime_t rto;                 ///<Retransmission timeout
+
+#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
+   TcpCongestState congestState;  ///<Congestion state
+   uint16_t cwnd;                 ///<Congestion window
+   uint16_t ssthresh;             ///<Slow start threshold
+   uint_t dupAckCount;            ///<Number of consecutive duplicate ACKs
+   uint_t n;                      ///<Number of bytes acknowledged during the whole round-trip
+   uint32_t recover;              ///<NewReno modification to TCP's fast recovery algorithm
+#endif
+
+   TcpTxBuffer txBuffer;          ///<Send buffer
+   size_t txBufferSize;           ///<Size of the send buffer
+   TcpRxBuffer rxBuffer;          ///<Receive buffer
+   size_t rxBufferSize;           ///<Size of the receive buffer
+
+   TcpQueueItem *retransmitQueue; ///<Retransmission queue
+   TcpTimer retransmitTimer;      ///<Retransmission timer
+   uint_t retransmitCount;        ///<Number of retransmissions
+
+   TcpSynQueueItem *synQueue;     ///<SYN queue for listening sockets
+   uint_t synQueueSize;           ///<Maximum number of pending connections for listening sockets
+
+   uint_t wndProbeCount;          ///<Zero window probe counter
+   systime_t wndProbeInterval;    ///<Interval between successive probes
+
+   TcpTimer persistTimer;         ///<Persist timer
+   TcpTimer overrideTimer;        ///<Override timer
+   TcpTimer finWait2Timer;        ///<FIN-WAIT-2 timer
+   TcpTimer timeWaitTimer;        ///<2MSL timer
+
+   bool_t sackPermitted;                        ///<SACK Permitted option received
+   TcpSackBlock sackBlock[TCP_MAX_SACK_BLOCKS]; ///<List of non-contiguous blocks that have been received
+   uint_t sackBlockCount;                       ///<Number of non-contiguous blocks that have been received
+#endif
+
+//UDP specific variables
+#if (UDP_SUPPORT == ENABLED || RAW_SOCKET_SUPPORT == ENABLED)
+   SocketQueueItem *receiveQueue;
+#endif
+};
+
+
+/**
+ * @brief Structure describing socket events
+ **/
+
+typedef struct
+{
+   Socket *socket;    ///<Handle to a socket to monitor
+   uint_t eventMask;  ///<Requested events
+   uint_t eventFlags; ///<Returned events
+} SocketEventDesc;
+
+
+//Global variables
+extern Socket socketTable[SOCKET_MAX_COUNT];
+
+//Socket related functions
+error_t socketInit(void);
+
+Socket *socketOpen(uint_t type, uint_t protocol);
+
+error_t socketSetTimeout(Socket *socket, systime_t timeout);
+error_t socketSetTxBufferSize(Socket *socket, size_t size);
+error_t socketSetRxBufferSize(Socket *socket, size_t size);
+
+error_t socketBindToInterface(Socket *socket, NetInterface *interface);
+error_t socketBind(Socket *socket, const IpAddr *localIpAddr, uint16_t localPort);
+error_t socketConnect(Socket *socket, const IpAddr *remoteIpAddr, uint16_t remotePort);
+error_t socketListen(Socket *socket, uint_t backlog);
+Socket *socketAccept(Socket *socket, IpAddr *clientIpAddr, uint16_t *clientPort);
+
+error_t socketSend(Socket *socket, const void *data,
+   size_t length, size_t *written, uint_t flags);
+
+error_t socketSendTo(Socket *socket, const IpAddr *destIpAddr, uint16_t destPort,
+   const void *data, size_t length, size_t *written, uint_t flags);
+
+error_t socketReceive(Socket *socket, void *data,
+   size_t size, size_t *received, uint_t flags);
+
+error_t socketReceiveFrom(Socket *socket, IpAddr *srcIpAddr, uint16_t *srcPort,
+   void *data, size_t size, size_t *received, uint_t flags);
+
+error_t socketReceiveEx(Socket *socket, IpAddr *srcIpAddr, uint16_t *srcPort,
+   IpAddr *destIpAddr, void *data, size_t size, size_t *received, uint_t flags);
+
+error_t socketGetLocalAddr(Socket *socket, IpAddr *localIpAddr, uint16_t *localPort);
+error_t socketGetRemoteAddr(Socket *socket, IpAddr *remoteIpAddr, uint16_t *remotePort);
+
+error_t socketShutdown(Socket *socket, uint_t how);
+void socketClose(Socket *socket);
+
+error_t socketPoll(SocketEventDesc *eventDesc, uint_t size, OsEvent *extEvent, systime_t timeout);
+error_t socketRegisterEvents(Socket *socket, OsEvent *event, uint_t eventMask);
+error_t socketUnregisterEvents(Socket *socket);
+error_t socketGetEvents(Socket *socket, uint_t *eventFlags);
+
+error_t getHostByName(NetInterface *interface,
+   const char_t *name, IpAddr *ipAddr, uint_t flags);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/tcp.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1008 @@
+/**
+ * @file tcp.c
+ * @brief TCP (Transmission Control Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TCP_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "core/net.h"
+#include "core/socket.h"
+#include "core/tcp.h"
+#include "core/tcp_misc.h"
+#include "core/tcp_timer.h"
+#include "mibs/mib2_module.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (TCP_SUPPORT == ENABLED)
+
+//Tick counter to handle periodic operations
+systime_t tcpTickCounter;
+
+//Ephemeral ports are used for dynamic port assignment
+static uint16_t tcpDynamicPort;
+
+
+/**
+ * @brief TCP related initialization
+ * @return Error code
+ **/
+
+error_t tcpInit(void)
+{
+   //Reset ephemeral port number
+   tcpDynamicPort = 0;
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Get an ephemeral port number
+ * @return Ephemeral port
+ **/
+
+uint16_t tcpGetDynamicPort(void)
+{
+   uint_t port;
+
+   //Retrieve current port number
+   port = tcpDynamicPort;
+
+   //Invalid port number?
+   if(port < SOCKET_EPHEMERAL_PORT_MIN || port > SOCKET_EPHEMERAL_PORT_MAX)
+   {
+      //Generate a random port number
+      port = SOCKET_EPHEMERAL_PORT_MIN + netGetRand() %
+         (SOCKET_EPHEMERAL_PORT_MAX - SOCKET_EPHEMERAL_PORT_MIN + 1);
+   }
+
+   //Next dynamic port to use
+   if(port < SOCKET_EPHEMERAL_PORT_MAX)
+   {
+      //Increment port number
+      tcpDynamicPort = port + 1;
+   }
+   else
+   {
+      //Wrap around if necessary
+      tcpDynamicPort = SOCKET_EPHEMERAL_PORT_MIN;
+   }
+
+   //Return an ephemeral port number
+   return port;
+}
+
+
+/**
+ * @brief Establish a TCP connection
+ * @param[in] socket Handle to an unconnected socket
+ * @param[in] remoteIpAddr IP address of the remote host
+ * @param[in] remotePort Remote port number that will be used to establish the connection
+ * @return Error code
+ **/
+
+error_t tcpConnect(Socket *socket, const IpAddr *remoteIpAddr, uint16_t remotePort)
+{
+   error_t error;
+   uint_t event;
+
+   //Check current TCP state
+   if(socket->state == TCP_STATE_CLOSED)
+   {
+      //Save port number and IP address of the remote host
+      socket->remoteIpAddr = *remoteIpAddr;
+      socket->remotePort = remotePort;
+
+      //Select the source address and the relevant network interface
+      //to use when establishing the connection
+      error = ipSelectSourceAddr(&socket->interface,
+         &socket->remoteIpAddr, &socket->localIpAddr);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Make sure the source address is valid
+      if(ipIsUnspecifiedAddr(&socket->localIpAddr))
+         return ERROR_NOT_CONFIGURED;
+
+      //The user owns the socket
+      socket->ownedFlag = TRUE;
+
+      //Number of chunks that comprise the TX and the RX buffers
+      socket->txBuffer.maxChunkCount = arraysize(socket->txBuffer.chunk);
+      socket->rxBuffer.maxChunkCount = arraysize(socket->rxBuffer.chunk);
+
+      //Allocate transmit buffer
+      error = netBufferSetLength((NetBuffer *) &socket->txBuffer,
+         socket->txBufferSize);
+
+      //Allocate receive buffer
+      if(!error)
+      {
+         error = netBufferSetLength((NetBuffer *) &socket->rxBuffer,
+            socket->rxBufferSize);
+      }
+
+      //Failed to allocate memory?
+      if(error)
+      {
+         //Free any previously allocated memory
+         tcpDeleteControlBlock(socket);
+         //Report an error to the caller
+         return error;
+      }
+
+      //The SMSS is the size of the largest segment that the sender can transmit
+      socket->smss = MIN(TCP_DEFAULT_MSS, TCP_MAX_MSS);
+      //The RMSS is the size of the largest segment the receiver is willing to accept
+      socket->rmss = MIN(socket->rxBufferSize, TCP_MAX_MSS);
+
+      //An initial send sequence number is selected
+      socket->iss = netGetRand();
+
+      //Initialize TCP control block
+      socket->sndUna = socket->iss;
+      socket->sndNxt = socket->iss + 1;
+      socket->rcvUser = 0;
+      socket->rcvWnd = socket->rxBufferSize;
+
+      //Default retransmission timeout
+      socket->rto = TCP_INITIAL_RTO;
+
+#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
+      //Default congestion state
+      socket->congestState = TCP_CONGEST_STATE_IDLE;
+      //Initial congestion window
+      socket->cwnd = MIN(TCP_INITIAL_WINDOW * socket->smss, socket->txBufferSize);
+      //Slow start threshold should be set arbitrarily high
+      socket->ssthresh = UINT16_MAX;
+      //Recover is set to the initial send sequence number
+      socket->recover = socket->iss;
+#endif
+
+      //Send a SYN segment
+      error = tcpSendSegment(socket, TCP_FLAG_SYN, socket->iss, 0, 0, TRUE);
+      //Failed to send TCP segment?
+      if(error)
+         return error;
+
+      //Switch to the SYN-SENT state
+      tcpChangeState(socket, TCP_STATE_SYN_SENT);
+
+      //Number of times TCP connections have made a direct transition to
+      //the SYN-SENT state from the CLOSED state
+      MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpActiveOpens, 1);
+   }
+
+   //Wait for the connection to be established
+   event = tcpWaitForEvents(socket, SOCKET_EVENT_CONNECTED |
+      SOCKET_EVENT_CLOSED, socket->timeout);
+
+   //Connection successfully established?
+   if(event == SOCKET_EVENT_CONNECTED)
+      return NO_ERROR;
+   //Failed to establish connection?
+   else if(event == SOCKET_EVENT_CLOSED)
+      return ERROR_CONNECTION_FAILED;
+   //Timeout exception?
+   else
+      return ERROR_TIMEOUT;
+}
+
+
+/**
+ * @brief Place a socket in the listening state
+ *
+ * Place a socket in a state in which it is listening for an incoming connection
+ *
+ * @param[in] socket Socket to place in the listening state
+ * @param[in] backlog backlog The maximum length of the pending connection queue.
+ *   If this parameter is zero, then the default backlog value is used instead
+ * @return Error code
+ **/
+
+error_t tcpListen(Socket *socket, uint_t backlog)
+{
+   //Socket already connected?
+   if(socket->state != TCP_STATE_CLOSED)
+      return ERROR_ALREADY_CONNECTED;
+
+   //Set the size of the SYN queue
+   socket->synQueueSize = (backlog > 0) ? backlog : TCP_DEFAULT_SYN_QUEUE_SIZE;
+   //Limit the number of pending connections
+   socket->synQueueSize = MIN(socket->synQueueSize, TCP_MAX_SYN_QUEUE_SIZE);
+
+   //Place the socket in the listening state
+   tcpChangeState(socket, TCP_STATE_LISTEN);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Permit an incoming connection attempt on a TCP socket
+ * @param[in] socket Handle to a socket previously placed in a listening state
+ * @param[out] clientIpAddr IP address of the client
+ * @param[out] clientPort Port number used by the client
+ * @return Handle to the socket in which the actual connection is made
+ **/
+
+Socket *tcpAccept(Socket *socket, IpAddr *clientIpAddr, uint16_t *clientPort)
+{
+   error_t error;
+   Socket *newSocket;
+   TcpSynQueueItem *queueItem;
+
+   //Ensure the socket was previously placed in the listening state
+   if(tcpGetState(socket) != TCP_STATE_LISTEN)
+      return NULL;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Wait for an connection attempt
+   while(1)
+   {
+      //The SYN queue is empty?
+      if(!socket->synQueue)
+      {
+         //Set the events the application is interested in
+         socket->eventMask = SOCKET_EVENT_RX_READY;
+         //Reset the event object
+         osResetEvent(&socket->event);
+
+         //Release exclusive access
+         osReleaseMutex(&netMutex);
+         //Wait until a SYN message is received from a client
+         osWaitForEvent(&socket->event, socket->timeout);
+         //Get exclusive access
+         osAcquireMutex(&netMutex);
+      }
+
+      //Check whether the queue is still empty
+      if(!socket->synQueue)
+      {
+         //Timeout error
+         newSocket = NULL;
+         //Exit immediately
+         break;
+      }
+
+      //Point to the first item in the receive queue
+      queueItem = socket->synQueue;
+
+      //Return the client IP address and port number
+      if(clientIpAddr)
+         *clientIpAddr = queueItem->srcAddr;
+      if(clientPort)
+         *clientPort = queueItem->srcPort;
+
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+      //Create a new socket to handle the incoming connection request
+      newSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+
+      //Socket successfully created?
+      if(newSocket != NULL)
+      {
+         //The user owns the socket
+         newSocket->ownedFlag = TRUE;
+
+         //Inherit settings from the listening socket
+         newSocket->txBufferSize = socket->txBufferSize;
+         newSocket->rxBufferSize = socket->rxBufferSize;
+
+         //Number of chunks that comprise the TX and the RX buffers
+         newSocket->txBuffer.maxChunkCount = arraysize(newSocket->txBuffer.chunk);
+         newSocket->rxBuffer.maxChunkCount = arraysize(newSocket->rxBuffer.chunk);
+
+         //Allocate transmit buffer
+         error = netBufferSetLength((NetBuffer *) &newSocket->txBuffer, newSocket->txBufferSize);
+
+         //Check status code
+         if(!error)
+         {
+            //Allocate receive buffer
+            error = netBufferSetLength((NetBuffer *) &newSocket->rxBuffer, newSocket->rxBufferSize);
+         }
+
+         //Transmit and receive buffers successfully allocated?
+         if(!error)
+         {
+            //Bind the newly created socket to the appropriate interface
+            newSocket->interface = queueItem->interface;
+
+            //Bind the socket to the specified address
+            newSocket->localIpAddr = queueItem->destAddr;
+            newSocket->localPort = socket->localPort;
+            //Save the port number and the IP address of the remote host
+            newSocket->remoteIpAddr = queueItem->srcAddr;
+            newSocket->remotePort = queueItem->srcPort;
+
+            //The SMSS is the size of the largest segment that the sender
+            //can transmit
+            newSocket->smss = queueItem->mss;
+
+            //The RMSS is the size of the largest segment the receiver is
+            //willing to accept
+            newSocket->rmss = MIN(newSocket->rxBufferSize, TCP_MAX_MSS);
+
+            //Initialize TCP control block
+            newSocket->iss = netGetRand();
+            newSocket->irs = queueItem->isn;
+            newSocket->sndUna = newSocket->iss;
+            newSocket->sndNxt = newSocket->iss + 1;
+            newSocket->rcvNxt = newSocket->irs + 1;
+            newSocket->rcvUser = 0;
+            newSocket->rcvWnd = newSocket->rxBufferSize;
+
+            //Default retransmission timeout
+            newSocket->rto = TCP_INITIAL_RTO;
+
+#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
+            //Default congestion state
+            socket->congestState = TCP_CONGEST_STATE_IDLE;
+            //Initial congestion window
+            newSocket->cwnd = MIN(TCP_INITIAL_WINDOW * newSocket->smss, newSocket->txBufferSize);
+            //Slow start threshold should be set arbitrarily high
+            newSocket->ssthresh = UINT16_MAX;
+            //Recover is set to the initial send sequence number
+            newSocket->recover = newSocket->iss;
+#endif
+            //Send a SYN ACK control segment
+            error = tcpSendSegment(newSocket, TCP_FLAG_SYN | TCP_FLAG_ACK,
+               newSocket->iss, newSocket->rcvNxt, 0, TRUE);
+
+            //TCP segment successfully sent?
+            if(!error)
+            {
+               //Remove the item from the SYN queue
+               socket->synQueue = queueItem->next;
+               //Deallocate memory buffer
+               memPoolFree(queueItem);
+               //Update the state of events
+               tcpUpdateEvents(socket);
+
+               //The connection state should be changed to SYN-RECEIVED
+               tcpChangeState(newSocket, TCP_STATE_SYN_RECEIVED);
+
+               //Number of times TCP connections have made a direct transition to
+               //the SYN-RECEIVED state from the LISTEN state
+               MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpPassiveOpens, 1);
+
+               //We are done...
+               break;
+            }
+         }
+
+         //Dispose the socket
+         tcpAbort(newSocket);
+      }
+
+      //Debug message
+      TRACE_WARNING("Cannot accept TCP connection!\r\n");
+
+      //Remove the item from the SYN queue
+      socket->synQueue = queueItem->next;
+      //Deallocate memory buffer
+      memPoolFree(queueItem);
+
+      //Wait for the next connection attempt
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return a handle to the newly created socket
+   return newSocket;
+}
+
+
+/**
+ * @brief Send data to a connected socket
+ * @param[in] socket Handle that identifies a connected socket
+ * @param[in] data Pointer to a buffer containing the data to be transmitted
+ * @param[in] length Number of bytes to be transmitted
+ * @param[out] written Actual number of bytes written (optional parameter)
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t tcpSend(Socket *socket, const uint8_t *data,
+   size_t length, size_t *written, uint_t flags)
+{
+   uint_t n;
+   uint_t totalLength;
+   uint_t event;
+
+   //Check whether the socket is in the listening state
+   if(socket->state == TCP_STATE_LISTEN)
+      return ERROR_NOT_CONNECTED;
+
+   //Actual number of bytes written
+   totalLength = 0;
+
+   //Send as much data as possible
+   do
+   {
+      //Wait until there is more room in the send buffer
+      event = tcpWaitForEvents(socket, SOCKET_EVENT_TX_READY, socket->timeout);
+
+      //A timeout exception occurred?
+      if(event != SOCKET_EVENT_TX_READY)
+         return ERROR_TIMEOUT;
+
+      //Check current TCP state
+      switch(socket->state)
+      {
+      //ESTABLISHED or CLOSE-WAIT state?
+      case TCP_STATE_ESTABLISHED:
+      case TCP_STATE_CLOSE_WAIT:
+         //The send buffer is now available for writing
+         break;
+
+      //LAST-ACK, FIN-WAIT-1, FIN-WAIT-2, CLOSING or TIME-WAIT state?
+      case TCP_STATE_LAST_ACK:
+      case TCP_STATE_FIN_WAIT_1:
+      case TCP_STATE_FIN_WAIT_2:
+      case TCP_STATE_CLOSING:
+      case TCP_STATE_TIME_WAIT:
+         //The connection is being closed
+         return ERROR_CONNECTION_CLOSING;
+
+      //CLOSED state?
+      default:
+         //The connection was reset by remote side?
+         return (socket->resetFlag) ? ERROR_CONNECTION_RESET : ERROR_NOT_CONNECTED;
+      }
+
+      //Determine the actual number of bytes in the send buffer
+      n = socket->sndUser + socket->sndNxt - socket->sndUna;
+      //Exit immediately if the transmission buffer is full (sanity check)
+      if(n >= socket->txBufferSize)
+         return ERROR_FAILURE;
+
+      //Number of bytes available for writing
+      n = socket->txBufferSize - n;
+      //Calculate the number of bytes to copy at a time
+      n = MIN(n, length - totalLength);
+
+      //Any data to copy?
+      if(n > 0)
+      {
+         //Copy user data to send buffer
+         tcpWriteTxBuffer(socket, socket->sndNxt + socket->sndUser, data, n);
+
+         //Update the number of data buffered but not yet sent
+         socket->sndUser += n;
+         //Advance data pointer
+         data += n;
+         //Update byte counter
+         totalLength += n;
+
+         //Total number of data that have been written
+         if(written != NULL)
+            *written = totalLength;
+
+         //Update TX events
+         tcpUpdateEvents(socket);
+
+         //To avoid a deadlock, it is necessary to have a timeout to force
+         //transmission of data, overriding the SWS avoidance algorithm. In
+         //practice, this timeout should seldom occur (see RFC 1122 4.2.3.4)
+         if(socket->sndUser == n)
+            tcpTimerStart(&socket->overrideTimer, TCP_OVERRIDE_TIMEOUT);
+      }
+
+      //The Nagle algorithm should be implemented to coalesce
+      //short segments (refer to RFC 1122 4.2.3.4)
+      tcpNagleAlgo(socket, flags);
+
+      //Send as much data as possible
+   } while(totalLength < length);
+
+   //The SOCKET_FLAG_WAIT_ACK flag causes the function to
+   //wait for acknowledgment from the remote side
+   if(flags & SOCKET_FLAG_WAIT_ACK)
+   {
+      //Wait for the data to be acknowledged
+      event = tcpWaitForEvents(socket, SOCKET_EVENT_TX_ACKED, socket->timeout);
+
+      //A timeout exception occurred?
+      if(event != SOCKET_EVENT_TX_ACKED)
+         return ERROR_TIMEOUT;
+
+      //The connection was closed before an acknowledgment was received?
+      if(socket->state != TCP_STATE_ESTABLISHED && socket->state != TCP_STATE_CLOSE_WAIT)
+         return ERROR_NOT_CONNECTED;
+   }
+
+   //Successful write operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive data from a connected socket
+ * @param[in] socket Handle that identifies a connected socket
+ * @param[out] data Buffer where to store the incoming data
+ * @param[in] size Maximum number of bytes that can be received
+ * @param[out] received Number of bytes that have been received
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t tcpReceive(Socket *socket, uint8_t *data,
+   size_t size, size_t *received, uint_t flags)
+{
+   uint_t i;
+   uint_t n;
+   uint_t event;
+   uint32_t seqNum;
+   systime_t timeout;
+
+   //Retrieve the break character code
+   char_t c = LSB(flags);
+   //No data has been read yet
+   *received = 0;
+
+   //Check whether the socket is in the listening state
+   if(socket->state == TCP_STATE_LISTEN)
+      return ERROR_NOT_CONNECTED;
+
+   //Read as much data as possible
+   while(*received < size)
+   {
+      //The SOCKET_FLAG_DONT_WAIT enables non-blocking operation
+      timeout = (flags & SOCKET_FLAG_DONT_WAIT) ? 0 : socket->timeout;
+      //Wait for data to be available for reading
+      event = tcpWaitForEvents(socket, SOCKET_EVENT_RX_READY, timeout);
+
+      //A timeout exception occurred?
+      if(event != SOCKET_EVENT_RX_READY)
+         return ERROR_TIMEOUT;
+
+      //Check current TCP state
+      switch(socket->state)
+      {
+      //ESTABLISHED, FIN-WAIT-1 or FIN-WAIT-2 state?
+      case TCP_STATE_ESTABLISHED:
+      case TCP_STATE_FIN_WAIT_1:
+      case TCP_STATE_FIN_WAIT_2:
+         //Sequence number of the first byte to read
+         seqNum = socket->rcvNxt - socket->rcvUser;
+         //Data is available in the receive buffer
+         break;
+
+      //CLOSE-WAIT, LAST-ACK, CLOSING or TIME-WAIT state?
+      case TCP_STATE_CLOSE_WAIT:
+      case TCP_STATE_LAST_ACK:
+      case TCP_STATE_CLOSING:
+      case TCP_STATE_TIME_WAIT:
+         //The user must be satisfied with data already on hand
+         if(!socket->rcvUser)
+         {
+            if(*received > 0)
+               return NO_ERROR;
+            else
+               return ERROR_END_OF_STREAM;
+         }
+
+         //Sequence number of the first byte to read
+         seqNum = (socket->rcvNxt - 1) - socket->rcvUser;
+         //Data is available in the receive buffer
+         break;
+
+      //CLOSED state?
+      default:
+         //The connection was reset by remote side?
+         if(socket->resetFlag)
+            return ERROR_CONNECTION_RESET;
+         //The connection has not yet been established?
+         if(!socket->closedFlag)
+            return ERROR_NOT_CONNECTED;
+
+         //The user must be satisfied with data already on hand
+         if(!socket->rcvUser)
+         {
+            if(*received > 0)
+               return NO_ERROR;
+            else
+               return ERROR_END_OF_STREAM;
+         }
+
+         //Sequence number of the first byte to read
+         seqNum = (socket->rcvNxt - 1) - socket->rcvUser;
+         //Data is available in the receive buffer
+         break;
+      }
+
+      //Sanity check
+      if(!socket->rcvUser)
+         return ERROR_FAILURE;
+
+      //Calculate the number of bytes to read at a time
+      n = MIN(socket->rcvUser, size - *received);
+      //Copy data from circular buffer
+      tcpReadRxBuffer(socket, seqNum, data, n);
+
+      //Read data until a break character is encountered?
+      if(flags & SOCKET_FLAG_BREAK_CHAR)
+      {
+         //Search for the specified break character
+         for(i = 0; i < n && data[i] != c; i++);
+         //Adjust the number of data to read
+         n = MIN(n, i + 1);
+      }
+
+      //Total number of data that have been read
+      *received += n;
+      //Remaining data still available in the receive buffer
+      socket->rcvUser -= n;
+
+      //Update the receive window
+      tcpUpdateReceiveWindow(socket);
+      //Update RX event state
+      tcpUpdateEvents(socket);
+
+      //The SOCKET_FLAG_BREAK_CHAR flag causes the function to stop reading
+      //data as soon as the specified break character is encountered
+      if(flags & SOCKET_FLAG_BREAK_CHAR)
+      {
+         //Check whether a break character has been found
+         if(data[n - 1] == c)
+            break;
+      }
+      //The SOCKET_FLAG_WAIT_ALL flag causes the function to return
+      //only when the requested number of bytes have been read
+      else if(!(flags & SOCKET_FLAG_WAIT_ALL))
+      {
+         break;
+      }
+
+      //Advance data pointer
+      data += n;
+   }
+
+   //Successful read operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Shutdown gracefully reception, transmission, or both
+ *
+ * Note that socketShutdown() does not close the socket, and resources attached
+ * to the socket will not be freed until socketClose() is invoked
+ *
+ * @param[in] socket Handle to a socket
+ * @param[in] how Flag that describes what types of operation will no longer be allowed
+ * @return Error code
+ **/
+
+error_t tcpShutdown(Socket *socket, uint_t how)
+{
+   error_t error;
+   uint_t event;
+
+   //Disable transmission?
+   if(how == SOCKET_SD_SEND || how == SOCKET_SD_BOTH)
+   {
+      //Check current state
+      switch(socket->state)
+      {
+      //CLOSED or LISTEN state?
+      case TCP_STATE_CLOSED:
+      case TCP_STATE_LISTEN:
+         //Connection does not exist
+         return ERROR_NOT_CONNECTED;
+
+      //SYN-RECEIVED or ESTABLISHED state?
+      case TCP_STATE_SYN_RECEIVED:
+      case TCP_STATE_ESTABLISHED:
+         //Flush the send buffer
+         error = tcpSend(socket, NULL, 0, NULL, SOCKET_FLAG_NO_DELAY);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //Make sure all the data has been sent out
+         event = tcpWaitForEvents(socket, SOCKET_EVENT_TX_DONE, socket->timeout);
+         //Timeout error?
+         if(event != SOCKET_EVENT_TX_DONE)
+            return ERROR_TIMEOUT;
+
+         //Send a FIN segment
+         error = tcpSendSegment(socket, TCP_FLAG_FIN | TCP_FLAG_ACK,
+            socket->sndNxt, socket->rcvNxt, 0, TRUE);
+         //Failed to send FIN segment?
+         if(error)
+            return error;
+
+         //Sequence number expected to be received
+         socket->sndNxt++;
+         //Switch to the FIN-WAIT1 state
+         tcpChangeState(socket, TCP_STATE_FIN_WAIT_1);
+
+         //Wait for the FIN to be acknowledged
+         event = tcpWaitForEvents(socket, SOCKET_EVENT_TX_SHUTDOWN, socket->timeout);
+         //Timeout interval elapsed?
+         if(event != SOCKET_EVENT_TX_SHUTDOWN)
+            return ERROR_TIMEOUT;
+
+         //Continue processing...
+         break;
+
+      //CLOSE-WAIT state?
+      case TCP_STATE_CLOSE_WAIT:
+         //Flush the send buffer
+         error = tcpSend(socket, NULL, 0, NULL, SOCKET_FLAG_NO_DELAY);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //Make sure all the data has been sent out
+         event = tcpWaitForEvents(socket, SOCKET_EVENT_TX_DONE, socket->timeout);
+         //Timeout error?
+         if(event != SOCKET_EVENT_TX_DONE)
+            return ERROR_TIMEOUT;
+
+         //Send a FIN segment
+         error = tcpSendSegment(socket, TCP_FLAG_FIN | TCP_FLAG_ACK,
+            socket->sndNxt, socket->rcvNxt, 0, TRUE);
+         //Failed to send FIN segment?
+         if(error)
+            return error;
+
+         //Sequence number expected to be received
+         socket->sndNxt++;
+         //Switch to the LAST-ACK state
+         tcpChangeState(socket, TCP_STATE_LAST_ACK);
+
+         //Wait for the FIN to be acknowledged
+         event = tcpWaitForEvents(socket, SOCKET_EVENT_TX_SHUTDOWN, socket->timeout);
+         //Timeout interval elapsed?
+         if(event != SOCKET_EVENT_TX_SHUTDOWN)
+            return ERROR_TIMEOUT;
+
+         //Continue processing...
+         break;
+
+      //FIN-WAIT-1, CLOSING or LAST-ACK state?
+      case TCP_STATE_FIN_WAIT_1:
+      case TCP_STATE_CLOSING:
+      case TCP_STATE_LAST_ACK:
+         //Wait for the FIN to be acknowledged
+         event = tcpWaitForEvents(socket, SOCKET_EVENT_TX_SHUTDOWN, socket->timeout);
+         //Timeout interval elapsed?
+         if(event != SOCKET_EVENT_TX_SHUTDOWN)
+            return ERROR_TIMEOUT;
+
+         //Continue processing...
+         break;
+
+      //SYN-SENT, FIN-WAIT-2 or TIME-WAIT state?
+      default:
+         //Continue processing...
+         break;
+      }
+   }
+
+   //Disable reception?
+   if(how == SOCKET_SD_RECEIVE || how == SOCKET_SD_BOTH)
+   {
+      //Check current state
+      switch(socket->state)
+      {
+      //CLOSED or LISTEN state?
+      case TCP_STATE_CLOSED:
+      case TCP_STATE_LISTEN:
+         //Connection does not exist
+         return ERROR_NOT_CONNECTED;
+
+      //SYN-SENT, SYN-RECEIVED, ESTABLISHED, FIN-WAIT-1 or FIN-WAIT-2 state?
+      case TCP_STATE_SYN_SENT:
+      case TCP_STATE_SYN_RECEIVED:
+      case TCP_STATE_ESTABLISHED:
+      case TCP_STATE_FIN_WAIT_1:
+      case TCP_STATE_FIN_WAIT_2:
+         //Wait for a FIN to be received
+         event = tcpWaitForEvents(socket, SOCKET_EVENT_RX_SHUTDOWN, socket->timeout);
+         //Timeout interval elapsed?
+         if(event != SOCKET_EVENT_RX_SHUTDOWN)
+            return ERROR_TIMEOUT;
+         //A FIN segment has been received
+         break;
+
+      //CLOSING, TIME-WAIT, CLOSE-WAIT or LAST-ACK state?
+      default:
+         //A FIN segment has already been received
+         break;
+      }
+   }
+
+   //Successful operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Abort an existing TCP connection
+ * @param[in] socket Handle identifying the socket to close
+ * @return Error code
+ **/
+
+error_t tcpAbort(Socket *socket)
+{
+   error_t error;
+
+   //Check current state
+   switch(socket->state)
+   {
+   //SYN-RECEIVED, ESTABLISHED, FIN-WAIT-1
+   //FIN-WAIT-2 or CLOSE-WAIT state?
+   case TCP_STATE_SYN_RECEIVED:
+   case TCP_STATE_ESTABLISHED:
+   case TCP_STATE_FIN_WAIT_1:
+   case TCP_STATE_FIN_WAIT_2:
+   case TCP_STATE_CLOSE_WAIT:
+      //Send a reset segment
+      error = tcpSendSegment(socket, TCP_FLAG_RST, socket->sndNxt, 0, 0, FALSE);
+      //Enter CLOSED state
+      tcpChangeState(socket, TCP_STATE_CLOSED);
+      //Delete TCB
+      tcpDeleteControlBlock(socket);
+      //Mark the socket as closed
+      socket->type = SOCKET_TYPE_UNUSED;
+      //Return status code
+      return error;
+
+   //TIME-WAIT state?
+   case TCP_STATE_TIME_WAIT:
+#if (TCP_2MSL_TIMER > 0)
+      //The user doe not own the socket anymore...
+      socket->ownedFlag = FALSE;
+      //TCB will be deleted and socket will be closed
+      //when the 2MSL timer will elapse
+      return NO_ERROR;
+#else
+      //Enter CLOSED state
+      tcpChangeState(socket, TCP_STATE_CLOSED);
+      //Delete TCB
+      tcpDeleteControlBlock(socket);
+      //Mark the socket as closed
+      socket->type = SOCKET_TYPE_UNUSED;
+      //No error to report
+      return NO_ERROR;
+#endif
+
+   //Any other state?
+   default:
+      //Enter CLOSED state
+      tcpChangeState(socket, TCP_STATE_CLOSED);
+      //Delete TCB
+      tcpDeleteControlBlock(socket);
+      //Mark the socket as closed
+      socket->type = SOCKET_TYPE_UNUSED;
+      //No error to report
+      return NO_ERROR;
+   }
+}
+
+
+/**
+ * @brief Get the current state of the TCP FSM
+ * @param[in] socket Handle identifying the socket
+ * @return TCP FSM state
+ **/
+
+TcpState tcpGetState(Socket *socket)
+{
+   TcpState state;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Get TCP FSM current state
+   state = socket->state;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return current state
+   return state;
+}
+
+
+/**
+ * @brief Kill the oldest socket in the TIME-WAIT state
+ * @return Handle identifying the oldest TCP connection in the TIME-WAIT state.
+ *   NULL is returned if no socket is currently in the TIME-WAIT state
+ **/
+
+Socket *tcpKillOldestConnection(void)
+{
+   uint_t i;
+   Socket *socket;
+   Socket *oldestSocket;
+
+   //Keep track of the oldest socket in the TIME-WAIT state
+   oldestSocket = NULL;
+
+   //Loop through socket descriptors
+   for(i = 0; i < SOCKET_MAX_COUNT; i++)
+   {
+      //Point to the current socket descriptor
+      socket = &socketTable[i];
+
+      //TCP connection found?
+      if(socket->type == SOCKET_TYPE_STREAM)
+      {
+         //Check current state
+         if(socket->state == TCP_STATE_TIME_WAIT)
+         {
+            //Keep track of the oldest socket in the TIME-WAIT state
+            if(oldestSocket == NULL)
+            {
+               //Save socket handle
+               oldestSocket = socket;
+            }
+            else if(timeCompare(socket->timeWaitTimer.startTime,
+               oldestSocket->timeWaitTimer.startTime) < 0)
+            {
+               //Save socket handle
+               oldestSocket = socket;
+            }
+         }
+      }
+   }
+
+   //Any connection in the TIME-WAIT state?
+   if(oldestSocket != NULL)
+   {
+      //Enter CLOSED state
+      tcpChangeState(oldestSocket, TCP_STATE_CLOSED);
+      //Delete TCB
+      tcpDeleteControlBlock(oldestSocket);
+      //Mark the socket as closed
+      oldestSocket->type = SOCKET_TYPE_UNUSED;
+   }
+
+   //The oldest connection in the TIME-WAIT state can be reused
+   //when the socket table runs out of space
+   return oldestSocket;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/tcp.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,437 @@
+/**
+ * @file tcp.h
+ * @brief TCP (Transmission Control Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TCP_H
+#define _TCP_H
+
+//Dependencies
+#include "net_config.h"
+#include "core/ip.h"
+
+//TCP support
+#ifndef TCP_SUPPORT
+   #define TCP_SUPPORT ENABLED
+#elif (TCP_SUPPORT != ENABLED && TCP_SUPPORT != DISABLED)
+   #error TCP_SUPPORT parameter is not valid
+#endif
+
+//TCP tick interval
+#ifndef TCP_TICK_INTERVAL
+   #define TCP_TICK_INTERVAL 100
+#elif (TCP_TICK_INTERVAL < 10)
+   #error TCP_TICK_INTERVAL parameter is not valid
+#endif
+
+//Maximum segment size
+#ifndef TCP_MAX_MSS
+   #define TCP_MAX_MSS 1430
+#elif (TCP_MAX_MSS < 536)
+   #error TCP_MAX_MSS parameter is not valid
+#endif
+
+//Mimimum acceptable segment size
+#ifndef TCP_MIN_MSS
+   #define TCP_MIN_MSS 64
+#elif (TCP_MIN_MSS < 1)
+   #error TCP_MIN_MSS parameter is not valid
+#endif
+
+//Default buffer size for transmission
+#ifndef TCP_DEFAULT_TX_BUFFER_SIZE
+   #define TCP_DEFAULT_TX_BUFFER_SIZE 2860
+#elif (TCP_DEFAULT_TX_BUFFER_SIZE < 536)
+   #error TCP_DEFAULT_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Maximum acceptable size for the send buffer
+#ifndef TCP_MAX_TX_BUFFER_SIZE
+   #define TCP_MAX_TX_BUFFER_SIZE 22880
+#elif (TCP_MAX_TX_BUFFER_SIZE < 536)
+   #error TCP_MAX_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Default buffer size for reception
+#ifndef TCP_DEFAULT_RX_BUFFER_SIZE
+   #define TCP_DEFAULT_RX_BUFFER_SIZE 2860
+#elif (TCP_DEFAULT_RX_BUFFER_SIZE < 536)
+   #error TCP_DEFAULT_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Maximum acceptable size for the receive buffer
+#ifndef TCP_MAX_RX_BUFFER_SIZE
+   #define TCP_MAX_RX_BUFFER_SIZE 22880
+#elif (TCP_MAX_RX_BUFFER_SIZE < 536)
+   #error TCP_MAX_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Default SYN queue size for listening sockets
+#ifndef TCP_DEFAULT_SYN_QUEUE_SIZE
+   #define TCP_DEFAULT_SYN_QUEUE_SIZE 4
+#elif (TCP_DEFAULT_SYN_QUEUE_SIZE < 1)
+   #error TCP_DEFAULT_SYN_QUEUE_SIZE parameter is not valid
+#endif
+
+//Maximum SYN queue size for listening sockets
+#ifndef TCP_MAX_SYN_QUEUE_SIZE
+   #define TCP_MAX_SYN_QUEUE_SIZE 16
+#elif (TCP_MAX_SYN_QUEUE_SIZE < 1)
+   #error TCP_MAX_SYN_QUEUE_SIZE parameter is not valid
+#endif
+
+//Maximum number of retransmissions
+#ifndef TCP_MAX_RETRIES
+   #define TCP_MAX_RETRIES 5
+#elif (TCP_MAX_RETRIES < 1)
+   #error TCP_MAX_RETRIES parameter is not valid
+#endif
+
+//Initial retransmission timeout
+#ifndef TCP_INITIAL_RTO
+   #define TCP_INITIAL_RTO 1000
+#elif (TCP_INITIAL_RTO < 100)
+   #error TCP_INITIAL_RTO parameter is not valid
+#endif
+
+//Minimum retransmission timeout
+#ifndef TCP_MIN_RTO
+   #define TCP_MIN_RTO 1000
+#elif (TCP_MIN_RTO < 100)
+   #error TCP_MIN_RTO parameter is not valid
+#endif
+
+//Maximum retransmission timeout
+#ifndef TCP_MAX_RTO
+   #define TCP_MAX_RTO 60000
+#elif (TCP_MAX_RTO < 1000)
+   #error TCP_MAX_RTO parameter is not valid
+#endif
+
+//TCP congestion control
+#ifndef TCP_CONGEST_CONTROL_SUPPORT
+   #define TCP_CONGEST_CONTROL_SUPPORT ENABLED
+#elif (TCP_CONGEST_CONTROL_SUPPORT != ENABLED && TCP_CONGEST_CONTROL_SUPPORT != DISABLED)
+   #error TCP_CONGEST_CONTROL_SUPPORT parameter is not valid
+#endif
+
+//Number of duplicate ACKs that triggers fast retransmit algorithm
+#ifndef TCP_FAST_RETRANSMIT_THRES
+   #define TCP_FAST_RETRANSMIT_THRES 3
+#elif (TCP_FAST_RETRANSMIT_THRES < 1)
+   #error TCP_FAST_RETRANSMIT_THRES parameter is not valid
+#endif
+
+//Size of the congestion window after the three-way handshake is completed
+#ifndef TCP_INITIAL_WINDOW
+   #define TCP_INITIAL_WINDOW 3
+#elif (TCP_INITIAL_WINDOW < 1)
+   #error TCP_INITIAL_WINDOW parameter is not valid
+#endif
+
+//Size of the congestion window after TCP detects loss using its retransmission timer
+#ifndef TCP_LOSS_WINDOW
+   #define TCP_LOSS_WINDOW 1
+#elif (TCP_LOSS_WINDOW < 1)
+   #error TCP_LOSS_WINDOW parameter is not valid
+#endif
+
+//Default interval between successive window probes
+#ifndef TCP_DEFAULT_PROBE_INTERVAL
+   #define TCP_DEFAULT_PROBE_INTERVAL 1000
+#elif (TCP_DEFAULT_PROBE_INTERVAL < 100)
+   #error TCP_DEFAULT_PROBE_INTERVAL parameter is not valid
+#endif
+
+//Maximum interval between successive window probes
+#ifndef TCP_MAX_PROBE_INTERVAL
+   #define TCP_MAX_PROBE_INTERVAL 60000
+#elif (TCP_MAX_PROBE_INTERVAL < 1000)
+   #error TCP_MAX_PROBE_INTERVAL parameter is not valid
+#endif
+
+//Override timeout (should be in the range 0.1 to 1 seconds)
+#ifndef TCP_OVERRIDE_TIMEOUT
+   #define TCP_OVERRIDE_TIMEOUT 500
+#elif (TCP_OVERRIDE_TIMEOUT < 100)
+   #error TCP_OVERRIDE_TIMEOUT parameter is not valid
+#endif
+
+//FIN-WAIT-2 timer
+#ifndef TCP_FIN_WAIT_2_TIMER
+   #define TCP_FIN_WAIT_2_TIMER 4000
+#elif (TCP_FIN_WAIT_2_TIMER < 1000)
+   #error TCP_FIN_WAIT_2_TIMER parameter is not valid
+#endif
+
+//TIME-WAIT timer
+#ifndef TCP_2MSL_TIMER
+   #define TCP_2MSL_TIMER 4000
+#elif (TCP_2MSL_TIMER < 0)
+   #error TCP_2MSL_TIMER parameter is not valid
+#endif
+
+//Selective acknowledgment support
+#ifndef TCP_SACK_SUPPORT
+   #define TCP_SACK_SUPPORT DISABLED
+#elif (TCP_SACK_SUPPORT != ENABLED && TCP_SACK_SUPPORT != DISABLED)
+   #error TCP_SACK_SUPPORT parameter is not valid
+#endif
+
+//Number of SACK blocks
+#ifndef TCP_MAX_SACK_BLOCKS
+   #define TCP_MAX_SACK_BLOCKS 4
+#elif (TCP_MAX_SACK_BLOCKS < 1)
+   #error TCP_MAX_SACK_BLOCKS parameter is not valid
+#endif
+
+//Maximum TCP header length
+#define TCP_MAX_HEADER_LENGTH 60
+//Default maximum segment size
+#define TCP_DEFAULT_MSS 536
+
+//Sequence number comparison macro
+#define TCP_CMP_SEQ(a, b) ((int32_t) ((a) - (b)))
+
+
+/**
+ * @brief TCP FSM states
+ **/
+
+typedef enum
+{
+   TCP_STATE_CLOSED       = 0,
+   TCP_STATE_LISTEN       = 1,
+   TCP_STATE_SYN_SENT     = 2,
+   TCP_STATE_SYN_RECEIVED = 3,
+   TCP_STATE_ESTABLISHED  = 4,
+   TCP_STATE_CLOSE_WAIT   = 5,
+   TCP_STATE_LAST_ACK     = 6,
+   TCP_STATE_FIN_WAIT_1   = 7,
+   TCP_STATE_FIN_WAIT_2   = 8,
+   TCP_STATE_CLOSING      = 9,
+   TCP_STATE_TIME_WAIT    = 10
+} TcpState;
+
+
+/**
+ * @brief TCP congestion states
+ **/
+
+typedef enum
+{
+   TCP_CONGEST_STATE_IDLE          = 0,
+   TCP_CONGEST_STATE_RECOVERY      = 1,
+   TCP_CONGEST_STATE_LOSS_RECOVERY = 2
+} TcpCongestState;
+
+
+/**
+ * @brief TCP control flags
+ **/
+
+typedef enum
+{
+   TCP_FLAG_FIN = 0x01,
+   TCP_FLAG_SYN = 0x02,
+   TCP_FLAG_RST = 0x04,
+   TCP_FLAG_PSH = 0x08,
+   TCP_FLAG_ACK = 0x10,
+   TCP_FLAG_URG = 0x20
+} TcpFlags;
+
+
+/**
+ * @brief TCP option types
+ **/
+
+typedef enum
+{
+   TCP_OPTION_END                 = 0,
+   TCP_OPTION_NOP                 = 1,
+   TCP_OPTION_MAX_SEGMENT_SIZE    = 2,
+   TCP_OPTION_WINDOW_SCALE_FACTOR = 3,
+   TCP_OPTION_SACK_PERMITTED      = 4,
+   TCP_OPTION_SACK                = 5,
+   TCP_OPTION_TIMESTAMP           = 8
+} TcpOptionKind;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief TCP header
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t srcPort;       //0-1
+   uint16_t destPort;      //2-3
+   uint32_t seqNum;        //4-7
+   uint32_t ackNum;        //8-11
+#ifdef _CPU_BIG_ENDIAN
+   uint8_t dataOffset : 4; //12
+   uint8_t reserved1 : 4;
+   uint8_t reserved2 : 2;  //13
+   uint8_t flags : 6;
+#else
+   uint8_t reserved1 : 4;  //12
+   uint8_t dataOffset : 4;
+   uint8_t flags : 6;      //13
+   uint8_t reserved2 : 2;
+#endif
+   uint16_t window;        //14-15
+   uint16_t checksum;      //16-17
+   uint16_t urgentPointer; //18-19
+   uint8_t options[];      //20
+} __end_packed TcpHeader;
+
+
+/**
+ * @brief TCP option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t kind;
+   uint8_t length;
+   uint8_t value[];
+} __end_packed TcpOption;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+/**
+ * @brief TCP timer
+ **/
+
+typedef struct
+{
+   bool_t running;
+   systime_t startTime;
+   systime_t interval;
+} TcpTimer;
+
+
+/**
+ * @brief Retransmission queue item
+ **/
+
+typedef struct _TcpQueueItem
+{
+   struct _TcpQueueItem *next;
+   uint_t length;
+   uint_t sacked;
+   IpPseudoHeader pseudoHeader;
+   uint8_t header[TCP_MAX_HEADER_LENGTH];
+} TcpQueueItem;
+
+
+/**
+ * @brief SYN queue item
+ **/
+
+typedef struct _TcpSynQueueItem
+{
+   struct _TcpSynQueueItem *next;
+   NetInterface *interface;
+   IpAddr srcAddr;
+   uint16_t srcPort;
+   IpAddr destAddr;
+   uint32_t isn;
+   uint16_t mss;
+} TcpSynQueueItem;
+
+
+/**
+ * @brief SACK block
+ **/
+
+typedef struct
+{
+   uint32_t leftEdge;
+   uint32_t rightEdge;
+} TcpSackBlock;
+
+
+/**
+ * @brief Transmit buffer
+ **/
+
+typedef struct
+{
+   uint_t chunkCount;
+   uint_t maxChunkCount;
+   ChunkDesc chunk[N(TCP_MAX_TX_BUFFER_SIZE)];
+} TcpTxBuffer;
+
+
+/**
+ * @brief Receive buffer
+ **/
+
+typedef struct
+{
+   uint_t chunkCount;
+   uint_t maxChunkCount;
+   ChunkDesc chunk[N(TCP_MAX_RX_BUFFER_SIZE)];
+} TcpRxBuffer;
+
+
+//Tick counter to handle periodic operations
+extern systime_t tcpTickCounter;
+
+//TCP related functions
+error_t tcpInit(void);
+uint16_t tcpGetDynamicPort(void);
+
+error_t tcpConnect(Socket *socket, const IpAddr *remoteIpAddr, uint16_t remotePort);
+error_t tcpListen(Socket *socket, uint_t backlog);
+Socket *tcpAccept(Socket *socket, IpAddr *clientIpAddr, uint16_t *clientPort);
+
+error_t tcpSend(Socket *socket, const uint8_t *data,
+   size_t length, size_t *written, uint_t flags);
+
+error_t tcpReceive(Socket *socket, uint8_t *data,
+   size_t size, size_t *received, uint_t flags);
+
+error_t tcpShutdown(Socket *socket, uint_t how);
+error_t tcpAbort(Socket *socket);
+
+TcpState tcpGetState(Socket *socket);
+
+Socket *tcpKillOldestConnection(void);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/tcp_fsm.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1162 @@
+/**
+ * @file tcp_fsm.c
+ * @brief TCP finite state machine
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The TCP state machine progresses from one state to another in response to
+ * events (user calls, incoming segments and timeouts). This file describes
+ * the state transitions caused by incoming segments. Refer to the
+ * following RFCs for complete details:
+ * - RFC 793: Transmission Control Protocol
+ * - RFC 1122: Requirements for Internet Hosts - Communication Layers
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TCP_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include <string.h>
+#include "core/net.h"
+#include "core/ip.h"
+#include "core/socket.h"
+#include "core/tcp.h"
+#include "core/tcp_fsm.h"
+#include "core/tcp_misc.h"
+#include "core/tcp_timer.h"
+#include "ipv4/ipv4.h"
+#include "ipv6/ipv6.h"
+#include "mibs/mib2_module.h"
+#include "date_time.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (TCP_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Incoming TCP segment processing
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader TCP pseudo header
+ * @param[in] buffer Multi-part buffer that holds the incoming TCP segment
+ * @param[in] offset Offset to the first byte of the TCP header
+ **/
+
+void tcpProcessSegment(NetInterface *interface,
+   IpPseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset)
+{
+   uint_t i;
+   size_t length;
+   Socket *socket;
+   Socket *passiveSocket;
+   TcpHeader *segment;
+
+   //Total number of segments received, including those received in error
+   MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInSegs, 1);
+   MIB2_INC_COUNTER64(mib2Base.tcpGroup.tcpHCInSegs, 1);
+
+   //A TCP implementation must silently discard an incoming
+   //segment that is addressed to a broadcast or multicast
+   //address (see RFC 1122 4.2.3.10)
+#if (IPV4_SUPPORT == ENABLED)
+   if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
+   {
+      //Ensure the destination address is not a broadcast address
+      if(ipv4IsBroadcastAddr(interface, pseudoHeader->ipv4Data.destAddr))
+         return;
+      //Ensure the destination address is not a multicast address
+      if(ipv4IsMulticastAddr(pseudoHeader->ipv4Data.destAddr))
+         return;
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
+   {
+      //Ensure the destination address is not a multicast address
+      if(ipv6IsMulticastAddr(&pseudoHeader->ipv6Data.destAddr))
+         return;
+   }
+   else
+#endif
+   {
+      //This should never occur...
+      return;
+   }
+
+   //Retrieve the length of the TCP segment
+   length = netBufferGetLength(buffer) - offset;
+
+   //Point to the TCP header
+   segment = netBufferAt(buffer, offset);
+   //Sanity check
+   if(segment == NULL)
+      return;
+
+   //Ensure the TCP header is valid
+   if(length < sizeof(TcpHeader))
+   {
+      //Debug message
+      TRACE_WARNING("TCP segment length is invalid!\r\n");
+      //Total number of segments received in error
+      MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInErrs, 1);
+      //Exit immediately
+      return;
+   }
+
+   //Check header length
+   if(segment->dataOffset < 5 || ((size_t) segment->dataOffset * 4) > length)
+   {
+      //Debug message
+      TRACE_WARNING("TCP header length is invalid!\r\n");
+      //Total number of segments received in error
+      MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInErrs, 1);
+      //Exit immediately
+      return;
+   }
+
+   //Verify TCP checksum
+   if(ipCalcUpperLayerChecksumEx(pseudoHeader->data,
+      pseudoHeader->length, buffer, offset, length) != 0x0000)
+   {
+      //Debug message
+      TRACE_WARNING("Wrong TCP header checksum!\r\n");
+      //Total number of segments received in error
+      MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpInErrs, 1);
+      //Exit immediately
+      return;
+   }
+
+   //No matching socket in the LISTEN state for the moment
+   passiveSocket = NULL;
+
+   //Look through opened sockets
+   for(i = 0; i < SOCKET_MAX_COUNT; i++)
+   {
+      //Point to the current socket
+      socket = socketTable + i;
+
+      //TCP socket found?
+      if(socket->type != SOCKET_TYPE_STREAM)
+         continue;
+      //Check whether the socket is bound to a particular interface
+      if(socket->interface && socket->interface != interface)
+         continue;
+      //Check destination port number
+      if(socket->localPort != ntohs(segment->destPort))
+         continue;
+
+#if (IPV4_SUPPORT == ENABLED)
+      //An IPv4 packet was received?
+      if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
+      {
+         //Destination IP address filtering
+         if(socket->localIpAddr.length)
+         {
+            //An IPv4 address is expected
+            if(socket->localIpAddr.length != sizeof(Ipv4Addr))
+               continue;
+            //Filter out non-matching addresses
+            if(socket->localIpAddr.ipv4Addr != pseudoHeader->ipv4Data.destAddr)
+               continue;
+         }
+         //Source IP address filtering
+         if(socket->remoteIpAddr.length)
+         {
+            //An IPv4 address is expected
+            if(socket->remoteIpAddr.length != sizeof(Ipv4Addr))
+               continue;
+            //Filter out non-matching addresses
+            if(socket->remoteIpAddr.ipv4Addr != pseudoHeader->ipv4Data.srcAddr)
+               continue;
+         }
+      }
+      else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+      //An IPv6 packet was received?
+      if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
+      {
+         //Destination IP address filtering
+         if(socket->localIpAddr.length)
+         {
+            //An IPv6 address is expected
+            if(socket->localIpAddr.length != sizeof(Ipv6Addr))
+               continue;
+            //Filter out non-matching addresses
+            if(!ipv6CompAddr(&socket->localIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.destAddr))
+               continue;
+         }
+         //Source IP address filtering
+         if(socket->remoteIpAddr.length)
+         {
+            //An IPv6 address is expected
+            if(socket->remoteIpAddr.length != sizeof(Ipv6Addr))
+               continue;
+            //Filter out non-matching addresses
+            if(!ipv6CompAddr(&socket->remoteIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.srcAddr))
+               continue;
+         }
+      }
+      else
+#endif
+      //An invalid packet was received?
+      {
+         //This should never occur...
+         continue;
+      }
+
+      //Keep track of the first matching socket in the LISTEN state
+      if(socket->state == TCP_STATE_LISTEN && !passiveSocket)
+         passiveSocket = socket;
+      //Source port filtering
+      if(socket->remotePort != ntohs(segment->srcPort))
+         continue;
+
+      //A matching socket has been found
+      break;
+   }
+
+   //If no matching socket has been found then try to
+   //use the first matching socket in the LISTEN state
+   if(i >= SOCKET_MAX_COUNT) socket = passiveSocket;
+
+   //Offset to the first data byte
+   offset += segment->dataOffset * 4;
+   //Calculate the length of the data
+   length -= segment->dataOffset * 4;
+
+   //Debug message
+   TRACE_DEBUG("%s: TCP segment received (%" PRIuSIZE " data bytes)...\r\n",
+      formatSystemTime(osGetSystemTime(), NULL), length);
+
+   //Dump TCP header contents for debugging purpose
+   if(socket == NULL)
+      tcpDumpHeader(segment, length, 0, 0);
+   else
+      tcpDumpHeader(segment, length, socket->irs, socket->iss);
+
+   //Convert from network byte order to host byte order
+   segment->srcPort = ntohs(segment->srcPort);
+   segment->destPort = ntohs(segment->destPort);
+   segment->seqNum = ntohl(segment->seqNum);
+   segment->ackNum = ntohl(segment->ackNum);
+   segment->window = ntohs(segment->window);
+   segment->urgentPointer = ntohs(segment->urgentPointer);
+
+   //Specified port is unreachable?
+   if(socket == NULL)
+   {
+      //An incoming segment not containing a RST causes
+      //a reset to be sent in response
+      if(!(segment->flags & TCP_FLAG_RST))
+         tcpSendResetSegment(interface, pseudoHeader, segment, length);
+
+      //Return immediately
+      return;
+   }
+
+   //Check current state
+   switch(socket->state)
+   {
+   //Process CLOSED state
+   case TCP_STATE_CLOSED:
+      //This is the default state that each connection starts in before
+      //the process of establishing it begins
+      tcpStateClosed(interface, pseudoHeader, segment, length);
+      break;
+   //Process LISTEN state
+   case TCP_STATE_LISTEN:
+      //A device (normally a server) is waiting to receive a synchronize (SYN)
+      //message from a client. It has not yet sent its own SYN message
+      tcpStateListen(socket, interface, pseudoHeader, segment, length);
+      break;
+   //Process SYN_SENT state
+   case TCP_STATE_SYN_SENT:
+      //The device (normally a client) has sent a synchronize (SYN) message and
+      //is waiting for a matching SYN from the other device (usually a server)
+      tcpStateSynSent(socket, segment, length);
+      break;
+   //Process SYN_RECEIVED state
+   case TCP_STATE_SYN_RECEIVED:
+      //The device has both received a SYN from its partner and sent its own SYN.
+      //It is now waiting for an ACK to its SYN to finish connection setup
+      tcpStateSynReceived(socket, segment, buffer, offset, length);
+      break;
+   //Process ESTABLISHED state
+   case TCP_STATE_ESTABLISHED:
+      //Data can be exchanged freely once both devices in the connection enter
+      //this state. This will continue until the connection is closed
+      tcpStateEstablished(socket, segment, buffer, offset, length);
+      break;
+   //Process CLOSE_WAIT state
+   case TCP_STATE_CLOSE_WAIT:
+      //The device has received a close request (FIN) from the other device. It
+      //must now wait for the application to acknowledge this request and
+      //generate a matching request
+      tcpStateCloseWait(socket, segment, length);
+      break;
+   //Process LAST_ACK state
+   case TCP_STATE_LAST_ACK:
+      //A device that has already received a close request and acknowledged it,
+      //has sent its own FIN and is waiting for an ACK to this request
+      tcpStateLastAck(socket, segment, length);
+      break;
+   //Process FIN_WAIT_1 state
+   case TCP_STATE_FIN_WAIT_1:
+      //A device in this state is waiting for an ACK for a FIN it has sent, or
+      //is waiting for a connection termination request from the other device
+      tcpStateFinWait1(socket, segment, buffer, offset, length);
+      break;
+   //Process FIN_WAIT_2 state
+   case TCP_STATE_FIN_WAIT_2:
+      //A device in this state has received an ACK for its request to terminate the
+      //connection and is now waiting for a matching FIN from the other device
+      tcpStateFinWait2(socket, segment, buffer, offset, length);
+      break;
+   //Process CLOSING state
+   case TCP_STATE_CLOSING:
+      //The device has received a FIN from the other device and sent an ACK for
+      //it, but not yet received an ACK for its own FIN message
+      tcpStateClosing(socket, segment, length);
+      break;
+   //Process TIME_WAIT state
+   case TCP_STATE_TIME_WAIT:
+      //The device has now received a FIN from the other device and acknowledged
+      //it, and sent its own FIN and received an ACK for it. We are done, except
+      //for waiting to ensure the ACK is received and prevent potential overlap
+      //with new connections
+      tcpStateTimeWait(socket, segment, length);
+      break;
+   //Invalid state...
+   default:
+      //Back to the CLOSED state
+      tcpChangeState(socket, TCP_STATE_CLOSED);
+      //Silently discard incoming packet
+      break;
+   }
+}
+
+
+/**
+ * @brief CLOSED state
+ *
+ * This is the default state that each connection starts in before
+ * the process of establishing it begins
+ *
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader TCP pseudo header
+ * @param[in] segment Incoming TCP segment
+ * @param[in] length Length of the segment data
+ **/
+
+void tcpStateClosed(NetInterface *interface,
+   IpPseudoHeader *pseudoHeader, TcpHeader *segment, size_t length)
+{
+   //Debug message
+   TRACE_DEBUG("TCP FSM: CLOSED state\r\n");
+
+   //An incoming segment not containing a RST causes
+   //a reset to be sent in response
+   if(!(segment->flags & TCP_FLAG_RST))
+      tcpSendResetSegment(interface, pseudoHeader, segment, length);
+}
+
+
+/**
+ * @brief LISTEN state
+ *
+ * A device (normally a server) is waiting to receive a synchronize (SYN)
+ * message from a client. It has not yet sent its own SYN message
+ *
+ * @param[in] socket Handle referencing the current socket
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader TCP pseudo header
+ * @param[in] segment Incoming TCP segment
+ * @param[in] length Length of the segment data
+ **/
+
+void tcpStateListen(Socket *socket, NetInterface *interface,
+   IpPseudoHeader *pseudoHeader, TcpHeader *segment, size_t length)
+{
+   uint_t i;
+   TcpOption *option;
+   TcpSynQueueItem *queueItem;
+
+   //Debug message
+   TRACE_DEBUG("TCP FSM: LISTEN state\r\n");
+
+   //An incoming RST should be ignored
+   if(segment->flags & TCP_FLAG_RST)
+      return;
+
+   //Any acknowledgment is bad if it arrives on a connection
+   //still in the LISTEN state
+   if(segment->flags & TCP_FLAG_ACK)
+   {
+      //A reset segment should be formed for any arriving ACK-bearing segment
+      tcpSendResetSegment(interface, pseudoHeader, segment, length);
+      //Return immediately
+      return;
+   }
+
+   //Check the SYN bit
+   if(segment->flags & TCP_FLAG_SYN)
+   {
+      //The SYN queue is empty?
+      if(!socket->synQueue)
+      {
+         //Allocate memory to save incoming data
+         queueItem = memPoolAlloc(sizeof(TcpSynQueueItem));
+         //Add the newly created item to the queue
+         socket->synQueue = queueItem;
+      }
+      else
+      {
+         //Point to the very first item
+         queueItem = socket->synQueue;
+
+         //Reach the last item in the receive queue
+         for(i = 1; queueItem->next; i++)
+            queueItem = queueItem->next;
+
+         //Make sure the receive queue is not full
+         if(i >= socket->synQueueSize)
+            return;
+
+         //Allocate memory to save incoming data
+         queueItem->next = memPoolAlloc(sizeof(TcpSynQueueItem));
+         //Point to the newly created item
+         queueItem = queueItem->next;
+      }
+
+      //Failed to allocate memory?
+      if(queueItem == NULL)
+         return;
+
+#if (IPV4_SUPPORT == ENABLED)
+      //IPv4 is currently used?
+      if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
+      {
+         //Save the source IPv4 address
+         queueItem->srcAddr.length = sizeof(Ipv4Addr);
+         queueItem->srcAddr.ipv4Addr = pseudoHeader->ipv4Data.srcAddr;
+         //Save the destination IPv4 address
+         queueItem->destAddr.length = sizeof(Ipv4Addr);
+         queueItem->destAddr.ipv4Addr = pseudoHeader->ipv4Data.destAddr;
+      }
+      else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+      //IPv6 is currently used?
+      if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
+      {
+         //Save the source IPv6 address
+         queueItem->srcAddr.length = sizeof(Ipv6Addr);
+         queueItem->srcAddr.ipv6Addr = pseudoHeader->ipv6Data.srcAddr;
+         //Save the destination IPv6 address
+         queueItem->destAddr.length = sizeof(Ipv6Addr);
+         queueItem->destAddr.ipv6Addr = pseudoHeader->ipv6Data.destAddr;
+      }
+      else
+#endif
+      //Invalid pseudo header?
+      {
+         //This should never occur...
+         return;
+      }
+
+      //Initialize next field
+      queueItem->next = NULL;
+      //Underlying network interface
+      queueItem->interface = interface;
+      //Save the port number of the client
+      queueItem->srcPort = segment->srcPort;
+      //Save the initial sequence number
+      queueItem->isn = segment->seqNum;
+      //Default MSS value
+      queueItem->mss = MIN(TCP_DEFAULT_MSS, TCP_MAX_MSS);
+
+      //Get the maximum segment size
+      option = tcpGetOption(segment, TCP_OPTION_MAX_SEGMENT_SIZE);
+      //Specified option found?
+      if(option != NULL && option->length == 4)
+      {
+         //Retrieve MSS value
+         memcpy(&queueItem->mss, option->value, 2);
+         //Convert from network byte order to host byte order
+         queueItem->mss = ntohs(queueItem->mss);
+
+         //Debug message
+         TRACE_DEBUG("Remote host MSS = %" PRIu16 "\r\n", queueItem->mss);
+
+         //Make sure that the MSS advertised by the peer is acceptable
+         queueItem->mss = MIN(queueItem->mss, TCP_MAX_MSS);
+         queueItem->mss = MAX(queueItem->mss, TCP_MIN_MSS);
+      }
+
+      //Notify user that a connection request is pending
+      tcpUpdateEvents(socket);
+
+      //The rest of the processing described in RFC 793 will be done
+      //asynchronously when socketAccept() function is called
+   }
+}
+
+
+/**
+ * @brief SYN-SENT state
+ *
+ * The device (normally a client) has sent a synchronize (SYN) message and
+ * is waiting for a matching SYN from the other device (usually a server)
+ *
+ * @param[in] socket Handle referencing the current socket
+ * @param[in] segment Incoming TCP segment
+ * @param[in] length Length of the segment data
+ **/
+
+void tcpStateSynSent(Socket *socket, TcpHeader *segment, size_t length)
+{
+   TcpOption *option;
+
+   //Debug message
+   TRACE_DEBUG("TCP FSM: SYN-SENT state\r\n");
+
+   //Check the ACK bit
+   if(segment->flags & TCP_FLAG_ACK)
+   {
+      //Make sure the acknowledgment number is valid
+      if(segment->ackNum != socket->sndNxt)
+      {
+         //Send a reset segment unless the RST bit is set
+         if(!(segment->flags & TCP_FLAG_RST))
+            tcpSendSegment(socket, TCP_FLAG_RST, segment->ackNum, 0, 0, FALSE);
+
+         //Drop the segment and return
+         return;
+      }
+   }
+
+   //Check the RST bit
+   if(segment->flags & TCP_FLAG_RST)
+   {
+      //Make sure the ACK is acceptable
+      if(segment->flags & TCP_FLAG_ACK)
+      {
+         //Enter CLOSED state
+         tcpChangeState(socket, TCP_STATE_CLOSED);
+
+         //Number of times TCP connections have made a direct transition to the
+         //CLOSED state from either the SYN-SENT state or the SYN-RECEIVED state
+         MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpAttemptFails, 1);
+      }
+
+      //Drop the segment and return
+      return;
+   }
+
+   //Check the SYN bit
+   if(segment->flags & TCP_FLAG_SYN)
+   {
+      //Save initial receive sequence number
+      socket->irs = segment->seqNum;
+      //Initialize RCV.NXT pointer
+      socket->rcvNxt = segment->seqNum + 1;
+
+      //If there is an ACK, SND.UNA should be advanced to equal SEG.ACK
+      if(segment->flags & TCP_FLAG_ACK)
+         socket->sndUna = segment->ackNum;
+
+      //Compute retransmission timeout
+      tcpComputeRto(socket);
+
+      //Any segments on the retransmission queue which are thereby
+      //acknowledged should be removed
+      tcpUpdateRetransmitQueue(socket);
+
+      //Get the maximum segment size
+      option = tcpGetOption(segment, TCP_OPTION_MAX_SEGMENT_SIZE);
+
+      //Specified option found?
+      if(option != NULL && option->length == 4)
+      {
+         //Retrieve MSS value
+         memcpy(&socket->smss, option->value, 2);
+         //Convert from network byte order to host byte order
+         socket->smss = ntohs(socket->smss);
+
+         //Debug message
+         TRACE_DEBUG("Remote host MSS = %" PRIu16 "\r\n", socket->smss);
+
+         //Make sure that the MSS advertised by the peer is acceptable
+         socket->smss = MIN(socket->smss, TCP_MAX_MSS);
+         socket->smss = MAX(socket->smss, TCP_MIN_MSS);
+      }
+
+#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
+      //Initial congestion window
+      socket->cwnd = MIN(TCP_INITIAL_WINDOW * socket->smss, socket->txBufferSize);
+#endif
+
+      //Check whether our SYN has been acknowledged (SND.UNA > ISS)
+      if(TCP_CMP_SEQ(socket->sndUna, socket->iss) > 0)
+      {
+         //Update the send window before entering ESTABLISHED state (see RFC 1122 4.2.2.20)
+         socket->sndWnd = segment->window;
+         socket->sndWl1 = segment->seqNum;
+         socket->sndWl2 = segment->ackNum;
+
+         //Maximum send window it has seen so far on the connection
+         socket->maxSndWnd = segment->window;
+
+         //Form an ACK segment and send it
+         tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt, socket->rcvNxt, 0, FALSE);
+         //Switch to the ESTABLISHED state
+         tcpChangeState(socket, TCP_STATE_ESTABLISHED);
+      }
+      else
+      {
+         //Form an SYN ACK segment and send it
+         tcpSendSegment(socket, TCP_FLAG_SYN | TCP_FLAG_ACK, socket->iss, socket->rcvNxt, 0, TRUE);
+         //Enter SYN-RECEIVED state
+         tcpChangeState(socket, TCP_STATE_SYN_RECEIVED);
+      }
+   }
+}
+
+
+/**
+ * @brief SYN-RECEIVED state
+ *
+ * The device has both received a SYN from its partner and sent its own SYN.
+ * It is now waiting for an ACK to its SYN to finish connection setup
+ *
+ * @param[in] socket Handle referencing the current socket
+ * @param[in] segment Incoming TCP segment
+ * @param[in] buffer Multi-part buffer containing the incoming TCP segment
+ * @param[in] offset Offset to the first data byte
+ * @param[in] length Length of the segment data
+ **/
+
+void tcpStateSynReceived(Socket *socket, TcpHeader *segment,
+   const NetBuffer *buffer, size_t offset, size_t length)
+{
+   //Debug message
+   TRACE_DEBUG("TCP FSM: SYN-RECEIVED state\r\n");
+
+   //First check sequence number
+   if(tcpCheckSequenceNumber(socket, segment, length))
+      return;
+
+   //Check the RST bit
+   if(segment->flags & TCP_FLAG_RST)
+   {
+      //Return to CLOSED state
+      tcpChangeState(socket, TCP_STATE_CLOSED);
+
+      //Number of times TCP connections have made a direct transition to the
+      //CLOSED state from either the SYN-SENT state or the SYN-RECEIVED state
+      MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpAttemptFails, 1);
+
+      //Return immediately
+      return;
+   }
+
+   //Check the SYN bit
+   if(tcpCheckSyn(socket, segment, length))
+      return;
+
+   //If the ACK bit is off drop the segment and return
+   if(!(segment->flags & TCP_FLAG_ACK))
+      return;
+
+   //Make sure the acknowledgment number is valid
+   if(segment->ackNum != socket->sndNxt)
+   {
+      //If the segment acknowledgment is not acceptable, form a reset
+      //segment and send it
+      tcpSendSegment(socket, TCP_FLAG_RST, segment->ackNum, 0, 0, FALSE);
+
+      //Drop the segment and return
+      return;
+   }
+
+   //Update the send window before entering ESTABLISHED state (see RFC 1122 4.2.2.20)
+   socket->sndWnd = segment->window;
+   socket->sndWl1 = segment->seqNum;
+   socket->sndWl2 = segment->ackNum;
+
+   //Maximum send window it has seen so far on the connection
+   socket->maxSndWnd = segment->window;
+
+   //Enter ESTABLISHED state
+   tcpChangeState(socket, TCP_STATE_ESTABLISHED);
+   //And continue processing...
+   tcpStateEstablished(socket, segment, buffer, offset, length);
+}
+
+
+/**
+ * @brief ESTABLISHED state
+ *
+ * Data can be exchanged freely once both devices in the connection enter
+ * this state. This will continue until the connection is closed
+ *
+ * @param[in] socket Handle referencing the current socket
+ * @param[in] segment Incoming TCP segment
+ * @param[in] buffer Multi-part buffer containing the incoming TCP segment
+ * @param[in] offset Offset to the first data byte
+ * @param[in] length Length of the segment data
+ **/
+
+void tcpStateEstablished(Socket *socket, TcpHeader *segment,
+   const NetBuffer *buffer, size_t offset, size_t length)
+{
+   uint_t flags = 0;
+
+   //Debug message
+   TRACE_DEBUG("TCP FSM: ESTABLISHED state\r\n");
+
+   //First check sequence number
+   if(tcpCheckSequenceNumber(socket, segment, length))
+      return;
+
+   //Check the RST bit
+   if(segment->flags & TCP_FLAG_RST)
+   {
+      //Switch to the CLOSED state
+      tcpChangeState(socket, TCP_STATE_CLOSED);
+
+      //Number of times TCP connections have made a direct transition to the
+      //CLOSED state from either the ESTABLISHED state or the CLOSE-WAIT state
+      MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpEstabResets, 1);
+
+      //Return immediately
+      return;
+   }
+
+   //Check the SYN bit
+   if(tcpCheckSyn(socket, segment, length))
+      return;
+   //Check the ACK field
+   if(tcpCheckAck(socket, segment, length))
+      return;
+   //Process the segment text
+   if(length > 0)
+      tcpProcessSegmentData(socket, segment, buffer, offset, length);
+
+   //Check the FIN bit
+   if(segment->flags & TCP_FLAG_FIN)
+   {
+      //The FIN can only be acknowledged if all the segment data
+      //has been successfully transferred to the receive buffer
+      if(socket->rcvNxt == (segment->seqNum + length))
+      {
+         //Advance RCV.NXT over the FIN
+         socket->rcvNxt++;
+         //Send an acknowledgment for the FIN
+         tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt, socket->rcvNxt, 0, FALSE);
+         //Switch to the CLOSE-WAIT state
+         tcpChangeState(socket, TCP_STATE_CLOSE_WAIT);
+      }
+   }
+
+#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
+   //Duplicate AK received?
+   if(socket->dupAckCount > 0)
+      flags = SOCKET_FLAG_NO_DELAY;
+#endif
+
+   //The Nagle algorithm should be implemented to coalesce
+   //short segments (refer to RFC 1122 4.2.3.4)
+   tcpNagleAlgo(socket, flags);
+}
+
+
+/**
+ * @brief CLOSE-WAIT state
+ *
+ * The device has received a close request (FIN) from the other device. It
+ * must now wait for the application to acknowledge this request and
+ * generate a matching request
+ *
+ * @param[in] socket Handle referencing the current socket
+ * @param[in] segment Incoming TCP segment
+ * @param[in] length Length of the segment data
+ **/
+
+void tcpStateCloseWait(Socket *socket, TcpHeader *segment, size_t length)
+{
+   uint_t flags = 0;
+
+   //Debug message
+   TRACE_DEBUG("TCP FSM: CLOSE-WAIT state\r\n");
+
+   //First check sequence number
+   if(tcpCheckSequenceNumber(socket, segment, length))
+      return;
+
+   //Check the RST bit
+   if(segment->flags & TCP_FLAG_RST)
+   {
+      //Switch to the CLOSED state
+      tcpChangeState(socket, TCP_STATE_CLOSED);
+
+      //Number of times TCP connections have made a direct transition to the
+      //CLOSED state from either the ESTABLISHED state or the CLOSE-WAIT state
+      MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpEstabResets, 1);
+
+      //Return immediately
+      return;
+   }
+
+   //Check the SYN bit
+   if(tcpCheckSyn(socket, segment, length))
+      return;
+   //Check the ACK field
+   if(tcpCheckAck(socket, segment, length))
+      return;
+
+#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
+   //Duplicate AK received?
+   if(socket->dupAckCount > 0)
+      flags = SOCKET_FLAG_NO_DELAY;
+#endif
+
+   //The Nagle algorithm should be implemented to coalesce
+   //short segments (refer to RFC 1122 4.2.3.4)
+   tcpNagleAlgo(socket, flags);
+}
+
+
+/**
+ * @brief LAST-ACK state
+ *
+ * A device that has already received a close request and acknowledged it,
+ * has sent its own FIN and is waiting for an ACK to this request
+ *
+ * @param[in] socket Handle referencing the current socket
+ * @param[in] segment Incoming TCP segment
+ * @param[in] length Length of the segment data
+ **/
+
+void tcpStateLastAck(Socket *socket, TcpHeader *segment, size_t length)
+{
+   //Debug message
+   TRACE_DEBUG("TCP FSM: LAST-ACK state\r\n");
+
+   //First check sequence number
+   if(tcpCheckSequenceNumber(socket, segment, length))
+      return;
+
+   //Check the RST bit
+   if(segment->flags & TCP_FLAG_RST)
+   {
+      //Enter CLOSED state
+      tcpChangeState(socket, TCP_STATE_CLOSED);
+      //Return immediately
+      return;
+   }
+
+   //Check the SYN bit
+   if(tcpCheckSyn(socket, segment, length))
+      return;
+   //If the ACK bit is off drop the segment and return
+   if(!(segment->flags & TCP_FLAG_ACK))
+      return;
+
+   //The only thing that can arrive in this state is an
+   //acknowledgment of our FIN
+   if(segment->ackNum == socket->sndNxt)
+   {
+      //Enter CLOSED state
+      tcpChangeState(socket, TCP_STATE_CLOSED);
+   }
+}
+
+
+/**
+ * @brief FIN-WAIT-1 state
+ *
+ * A device in this state is waiting for an ACK for a FIN it has sent, or
+ * is waiting for a connection termination request from the other device
+ *
+ * @param[in] socket Handle referencing the current socket
+ * @param[in] segment Incoming TCP segment
+ * @param[in] buffer Multi-part buffer containing the incoming TCP segment
+ * @param[in] offset Offset to the first data byte
+ * @param[in] length Length of the segment data
+ **/
+
+void tcpStateFinWait1(Socket *socket, TcpHeader *segment,
+   const NetBuffer *buffer, size_t offset, size_t length)
+{
+   //Debug message
+   TRACE_DEBUG("TCP FSM: FIN-WAIT-1 state\r\n");
+
+   //First check sequence number
+   if(tcpCheckSequenceNumber(socket, segment, length))
+      return;
+
+   //Check the RST bit
+   if(segment->flags & TCP_FLAG_RST)
+   {
+      //Switch to the CLOSED state
+      tcpChangeState(socket, TCP_STATE_CLOSED);
+      //Return immediately
+      return;
+   }
+
+   //Check the SYN bit
+   if(tcpCheckSyn(socket, segment, length))
+      return;
+   //Check the ACK field
+   if(tcpCheckAck(socket, segment, length))
+      return;
+
+   //Check whether our FIN is now acknowledged
+   if(segment->ackNum == socket->sndNxt)
+   {
+      //Start the FIN-WAIT-2 timer to prevent the connection
+      //from staying in the FIN-WAIT-2 state forever
+      tcpTimerStart(&socket->finWait2Timer, TCP_FIN_WAIT_2_TIMER);
+      //enter FIN-WAIT-2 and continue processing in that state
+      tcpChangeState(socket, TCP_STATE_FIN_WAIT_2);
+   }
+
+   //Process the segment text
+   if(length > 0)
+      tcpProcessSegmentData(socket, segment, buffer, offset, length);
+
+   //Check the FIN bit
+   if(segment->flags & TCP_FLAG_FIN)
+   {
+      //The FIN can only be acknowledged if all the segment data
+      //has been successfully transferred to the receive buffer
+      if(socket->rcvNxt == (segment->seqNum + length))
+      {
+         //Advance RCV.NXT over the FIN
+         socket->rcvNxt++;
+         //Send an acknowledgment for the FIN
+         tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt, socket->rcvNxt, 0, FALSE);
+
+         //Check if our FIN has been acknowledged
+         if(segment->ackNum == socket->sndNxt)
+         {
+            //Release previously allocated resources
+            tcpDeleteControlBlock(socket);
+            //Start the 2MSL timer
+            tcpTimerStart(&socket->timeWaitTimer, TCP_2MSL_TIMER);
+            //Switch to the TIME-WAIT state
+            tcpChangeState(socket, TCP_STATE_TIME_WAIT);
+         }
+         else
+         {
+            //If our FIN has not been acknowledged, then enter CLOSING state
+            tcpChangeState(socket, TCP_STATE_CLOSING);
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief FIN-WAIT-2 state
+ *
+ * A device in this state has received an ACK for its request to terminate the
+ * connection and is now waiting for a matching FIN from the other device
+ *
+ * @param[in] socket Handle referencing the current socket
+ * @param[in] segment Incoming TCP segment
+ * @param[in] buffer Multi-part buffer containing the incoming TCP segment
+ * @param[in] offset Offset to the first data byte
+ * @param[in] length Length of the segment data
+ **/
+
+void tcpStateFinWait2(Socket *socket, TcpHeader *segment,
+   const NetBuffer *buffer, size_t offset, size_t length)
+{
+   //Debug message
+   TRACE_DEBUG("TCP FSM: FIN-WAIT-2 state\r\n");
+
+   //First check sequence number
+   if(tcpCheckSequenceNumber(socket, segment, length))
+      return;
+
+   //Check the RST bit
+   if(segment->flags & TCP_FLAG_RST)
+   {
+      //Switch to the CLOSED state
+      tcpChangeState(socket, TCP_STATE_CLOSED);
+      //Return immediately
+      return;
+   }
+
+   //Check the SYN bit
+   if(tcpCheckSyn(socket, segment, length))
+      return;
+   //Check the ACK field
+   if(tcpCheckAck(socket, segment, length))
+      return;
+   //Process the segment text
+   if(length > 0)
+      tcpProcessSegmentData(socket, segment, buffer, offset, length);
+
+   //Check the FIN bit
+   if(segment->flags & TCP_FLAG_FIN)
+   {
+      //The FIN can only be acknowledged if all the segment data
+      //has been successfully transferred to the receive buffer
+      if(socket->rcvNxt == (segment->seqNum + length))
+      {
+         //Advance RCV.NXT over the FIN
+         socket->rcvNxt++;
+         //Send an acknowledgment for the FIN
+         tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt, socket->rcvNxt, 0, FALSE);
+
+         //Release previously allocated resources
+         tcpDeleteControlBlock(socket);
+         //Start the 2MSL timer
+         tcpTimerStart(&socket->timeWaitTimer, TCP_2MSL_TIMER);
+         //Switch to the TIME_WAIT state
+         tcpChangeState(socket, TCP_STATE_TIME_WAIT);
+      }
+   }
+}
+
+
+/**
+ * @brief CLOSING state
+ *
+ * The device has received a FIN from the other device and sent an ACK for
+ * it, but not yet received an ACK for its own FIN message
+ *
+ * @param[in] socket Handle referencing the current socket
+ * @param[in] segment Incoming TCP segment
+ * @param[in] length Length of the segment data
+ **/
+
+void tcpStateClosing(Socket *socket, TcpHeader *segment, size_t length)
+{
+   //Debug message
+   TRACE_DEBUG("TCP FSM: CLOSING state\r\n");
+
+   //First check sequence number
+   if(tcpCheckSequenceNumber(socket, segment, length))
+      return;
+
+   //Check the RST bit
+   if(segment->flags & TCP_FLAG_RST)
+   {
+      //Enter CLOSED state
+      tcpChangeState(socket, TCP_STATE_CLOSED);
+      //Return immediately
+      return;
+   }
+
+   //Check the SYN bit
+   if(tcpCheckSyn(socket, segment, length))
+      return;
+   //Check the ACK field
+   if(tcpCheckAck(socket, segment, length))
+      return;
+
+   //If the ACK acknowledges our FIN then enter the TIME-WAIT
+   //state, otherwise ignore the segment
+   if(segment->ackNum == socket->sndNxt)
+   {
+      //Release previously allocated resources
+      tcpDeleteControlBlock(socket);
+      //Start the 2MSL timer
+      tcpTimerStart(&socket->timeWaitTimer, TCP_2MSL_TIMER);
+      //Switch to the TIME-WAIT state
+      tcpChangeState(socket, TCP_STATE_TIME_WAIT);
+   }
+}
+
+
+/**
+ * @brief TIME-WAIT state
+ *
+ * The device has now received a FIN from the other device and acknowledged
+ * it, and sent its own FIN and received an ACK for it. We are done, except
+ * for waiting to ensure the ACK is received and prevent potential overlap
+ * with new connections
+ *
+ * @param[in] socket Handle referencing the current socket
+ * @param[in] segment Incoming TCP segment
+ * @param[in] length Length of the segment data
+ **/
+
+void tcpStateTimeWait(Socket *socket, TcpHeader *segment, size_t length)
+{
+   //Debug message
+   TRACE_DEBUG("TCP FSM: TIME-WAIT state\r\n");
+
+   //First check sequence number
+   if(tcpCheckSequenceNumber(socket, segment, length))
+      return;
+
+   //Check the RST bit
+   if(segment->flags & TCP_FLAG_RST)
+   {
+      //Enter CLOSED state
+      tcpChangeState(socket, TCP_STATE_CLOSED);
+
+      //Dispose the socket if the user does not have the ownership anymore
+      if(!socket->ownedFlag)
+      {
+         //Delete the TCB
+         tcpDeleteControlBlock(socket);
+         //Mark the socket as closed
+         socket->type = SOCKET_TYPE_UNUSED;
+      }
+
+      //Return immediately
+      return;
+   }
+
+   //Check the SYN bit
+   if(tcpCheckSyn(socket, segment, length))
+      return;
+   //If the ACK bit is off drop the segment and return
+   if(!(segment->flags & TCP_FLAG_ACK))
+      return;
+
+   //The only thing that can arrive in this state is a retransmission
+   //of the remote FIN. Acknowledge it and restart the 2 MSL timeout
+   if(segment->flags & TCP_FLAG_FIN)
+   {
+      //Send an acknowledgment for the FIN
+      tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt, socket->rcvNxt, 0, FALSE);
+      //Restart the 2MSL timer
+      tcpTimerStart(&socket->timeWaitTimer, TCP_2MSL_TIMER);
+   }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/tcp_fsm.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,68 @@
+/**
+ * @file tcp_fsm.h
+ * @brief TCP finite state machine
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TCP_FSM_H
+#define _TCP_FSM_H
+
+//Dependencies
+#include "core/tcp.h"
+
+//TCP FSM related functions
+void tcpProcessSegment(NetInterface *interface,
+   IpPseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset);
+
+void tcpStateClosed(NetInterface *interface,
+   IpPseudoHeader *pseudoHeader, TcpHeader *segment, size_t length);
+
+void tcpStateListen(Socket *socket, NetInterface *interface,
+   IpPseudoHeader *pseudoHeader, TcpHeader *segment, size_t length);
+
+void tcpStateSynSent(Socket *socket, TcpHeader *segment, size_t length);
+
+void tcpStateSynReceived(Socket *socket, TcpHeader *segment,
+   const NetBuffer *buffer, size_t offset, size_t length);
+
+void tcpStateEstablished(Socket *socket, TcpHeader *segment,
+   const NetBuffer *buffer, size_t offset, size_t length);
+
+void tcpStateCloseWait(Socket *socket, TcpHeader *segment, size_t length);
+
+void tcpStateLastAck(Socket *socket, TcpHeader *segment, size_t length);
+
+void tcpStateFinWait1(Socket *socket, TcpHeader *segment,
+   const NetBuffer *buffer, size_t offset, size_t length);
+
+void tcpStateFinWait2(Socket *socket, TcpHeader *segment,
+   const NetBuffer *buffer, size_t offset, size_t length);
+
+void tcpStateClosing(Socket *socket, TcpHeader *segment, size_t length);
+
+void tcpStateTimeWait(Socket *socket, TcpHeader *segment, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/tcp_misc.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1989 @@
+/**
+ * @file tcp_misc.c
+ * @brief Helper functions for TCP
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TCP_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include <string.h>
+#include "core/net.h"
+#include "core/socket.h"
+#include "core/tcp.h"
+#include "core/tcp_misc.h"
+#include "core/tcp_timer.h"
+#include "core/ip.h"
+#include "ipv4/ipv4.h"
+#include "ipv6/ipv6.h"
+#include "mibs/mib2_module.h"
+#include "date_time.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (TCP_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Send a TCP segment
+ * @param[in] socket Handle referencing a socket
+ * @param[in] flags Value that contains bitwise OR of flags (see #TcpFlags enumeration)
+ * @param[in] seqNum Sequence number
+ * @param[in] ackNum Acknowledgment number
+ * @param[in] length Length of the segment data
+ * @param[in] addToQueue Add the segment to retransmission queue
+ * @return Error code
+ **/
+
+error_t tcpSendSegment(Socket *socket, uint8_t flags, uint32_t seqNum,
+   uint32_t ackNum, size_t length, bool_t addToQueue)
+{
+   error_t error;
+   size_t offset;
+   size_t totalLength;
+   NetBuffer *buffer;
+   TcpHeader *segment;
+   TcpQueueItem *queueItem;
+   IpPseudoHeader pseudoHeader;
+
+   //Maximum segment size
+   uint16_t mss = HTONS(socket->rmss);
+
+   //Allocate a memory buffer to hold the TCP segment
+   buffer = ipAllocBuffer(TCP_MAX_HEADER_LENGTH, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the TCP segment
+   segment = netBufferAt(buffer, offset);
+
+   //Format TCP header
+   segment->srcPort = htons(socket->localPort);
+   segment->destPort = htons(socket->remotePort);
+   segment->seqNum = htonl(seqNum);
+   segment->ackNum = (flags & TCP_FLAG_ACK) ? htonl(ackNum) : 0;
+   segment->reserved1 = 0;
+   segment->dataOffset = 5;
+   segment->flags = flags;
+   segment->reserved2 = 0;
+   segment->window = htons(socket->rcvWnd);
+   segment->checksum = 0;
+   segment->urgentPointer = 0;
+
+   //SYN flag set?
+   if(flags & TCP_FLAG_SYN)
+   {
+      //Append MSS option
+      tcpAddOption(segment, TCP_OPTION_MAX_SEGMENT_SIZE, &mss, sizeof(mss));
+
+#if (TCP_SACK_SUPPORT == ENABLED)
+      //Append SACK Permitted option
+      tcpAddOption(segment, TCP_OPTION_SACK_PERMITTED, NULL, 0);
+#endif
+   }
+
+   //Adjust the length of the multi-part buffer
+   netBufferSetLength(buffer, offset + segment->dataOffset * 4);
+
+   //Any data to send?
+   if(length > 0)
+   {
+      //Copy data
+      error = tcpReadTxBuffer(socket, seqNum, buffer, length);
+      //Any error to report?
+      if(error)
+      {
+         //Clean up side effects
+         netBufferFree(buffer);
+         //Exit immediately
+         return error;
+      }
+   }
+
+   //Calculate the length of the complete TCP segment
+   totalLength = segment->dataOffset * 4 + length;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //Destination address is an IPv4 address?
+   if(socket->remoteIpAddr.length == sizeof(Ipv4Addr))
+   {
+      //Format IPv4 pseudo header
+      pseudoHeader.length = sizeof(Ipv4PseudoHeader);
+      pseudoHeader.ipv4Data.srcAddr = socket->localIpAddr.ipv4Addr;
+      pseudoHeader.ipv4Data.destAddr = socket->remoteIpAddr.ipv4Addr;
+      pseudoHeader.ipv4Data.reserved = 0;
+      pseudoHeader.ipv4Data.protocol = IPV4_PROTOCOL_TCP;
+      pseudoHeader.ipv4Data.length = htons(totalLength);
+
+      //Calculate TCP header checksum
+      segment->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader.ipv4Data,
+         sizeof(Ipv4PseudoHeader), buffer, offset, totalLength);
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //Destination address is an IPv6 address?
+   if(socket->remoteIpAddr.length == sizeof(Ipv6Addr))
+   {
+      //Format IPv6 pseudo header
+      pseudoHeader.length = sizeof(Ipv6PseudoHeader);
+      pseudoHeader.ipv6Data.srcAddr = socket->localIpAddr.ipv6Addr;
+      pseudoHeader.ipv6Data.destAddr = socket->remoteIpAddr.ipv6Addr;
+      pseudoHeader.ipv6Data.length = htonl(totalLength);
+      pseudoHeader.ipv6Data.reserved = 0;
+      pseudoHeader.ipv6Data.nextHeader = IPV6_TCP_HEADER;
+
+      //Calculate TCP header checksum
+      segment->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader.ipv6Data,
+         sizeof(Ipv6PseudoHeader), buffer, offset, totalLength);
+   }
+   else
+#endif
+   //Destination address is not valid?
+   {
+      //Free previously allocated memory
+      netBufferFree(buffer);
+      //This should never occur...
+      return ERROR_INVALID_ADDRESS;
+   }
+
+   //Add current segment to retransmission queue?
+   if(addToQueue)
+   {
+      //Empty retransmission queue?
+      if(!socket->retransmitQueue)
+      {
+         //Create a new item
+         queueItem = memPoolAlloc(sizeof(TcpQueueItem));
+         //Add the newly created item to the queue
+         socket->retransmitQueue = queueItem;
+      }
+      else
+      {
+         //Point to the very first item
+         queueItem = socket->retransmitQueue;
+         //Reach the last item of the retransmission queue
+         while(queueItem->next) queueItem = queueItem->next;
+         //Create a new item
+         queueItem->next = memPoolAlloc(sizeof(TcpQueueItem));
+         //Point to the newly created item
+         queueItem = queueItem->next;
+      }
+
+      //Failed to allocate memory?
+      if(queueItem == NULL)
+      {
+         //Free previously allocated memory
+         netBufferFree(buffer);
+         //Return status
+         return ERROR_OUT_OF_MEMORY;
+      }
+
+      //Retransmission mechanism requires additional information
+      queueItem->next = NULL;
+      queueItem->length = length;
+      queueItem->sacked = FALSE;
+      //Save TCP header
+      memcpy(queueItem->header, segment, segment->dataOffset * 4);
+      //Save pseudo header
+      queueItem->pseudoHeader = pseudoHeader;
+
+      //Take one RTT measurement at a time
+      if(!socket->rttBusy)
+      {
+         //Save round-trip start time
+         socket->rttStartTime = osGetSystemTime();
+         //Record current sequence number
+         socket->rttSeqNum = ntohl(segment->seqNum);
+         //Wait for an acknowledgment that covers that sequence number...
+         socket->rttBusy = TRUE;
+
+#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
+         //Reset the byte counter
+         socket->n = 0;
+#endif
+      }
+
+      //Check whether the RTO timer is already running
+      if(!tcpTimerRunning(&socket->retransmitTimer))
+      {
+         //If the timer is not running, start it running so that
+         //it will expire after RTO seconds
+         tcpTimerStart(&socket->retransmitTimer, socket->rto);
+         //Reset retransmission counter
+         socket->retransmitCount = 0;
+      }
+   }
+
+   //Total number of segments sent
+   MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpOutSegs, 1);
+   MIB2_INC_COUNTER64(mib2Base.tcpGroup.tcpHCOutSegs, 1);
+
+   //RST flag set?
+   if(flags & TCP_FLAG_RST)
+   {
+      //Number of TCP segments sent containing the RST flag
+      MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpOutRsts, 1);
+   }
+
+   //Debug message
+   TRACE_DEBUG("%s: Sending TCP segment (%" PRIuSIZE " data bytes)...\r\n",
+      formatSystemTime(osGetSystemTime(), NULL), length);
+
+   //Dump TCP header contents for debugging purpose
+   tcpDumpHeader(segment, length, socket->iss, socket->irs);
+
+   //Send TCP segment
+   error = ipSendDatagram(socket->interface, &pseudoHeader, buffer, offset, 0);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return error code
+   return error;
+}
+
+
+/**
+ * @brief Send a TCP reset in response to an invalid segment
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader TCP pseudo header describing the incoming segment
+ * @param[in] segment Incoming TCP segment
+ * @param[in] length Length of the incoming segment data
+ * @return Error code
+ **/
+
+error_t tcpSendResetSegment(NetInterface *interface,
+   IpPseudoHeader *pseudoHeader, TcpHeader *segment, size_t length)
+{
+   error_t error;
+   size_t offset;
+   uint8_t flags;
+   uint32_t seqNum;
+   uint32_t ackNum;
+   NetBuffer *buffer;
+   TcpHeader *segment2;
+   IpPseudoHeader pseudoHeader2;
+
+   //Check whether the ACK bit is set
+   if(segment->flags & TCP_FLAG_ACK)
+   {
+      //If the incoming segment has an ACK field, the reset takes
+      //its sequence number from the ACK field of the segment
+      flags = TCP_FLAG_RST;
+      seqNum = segment->ackNum;
+      ackNum = 0;
+   }
+   else
+   {
+      //Otherwise the reset has sequence number zero and the ACK field is set to
+      //the sum of the sequence number and segment length of the incoming segment
+      flags = TCP_FLAG_RST | TCP_FLAG_ACK;
+      seqNum = 0;
+      ackNum = segment->seqNum + length;
+
+      //Advance the acknowledgment number over the SYN or the FIN
+      if(segment->flags & TCP_FLAG_SYN)
+         ackNum++;
+      if(segment->flags & TCP_FLAG_FIN)
+         ackNum++;
+   }
+
+   //Allocate a memory buffer to hold the reset segment
+   buffer = ipAllocBuffer(sizeof(TcpHeader), &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the TCP segment
+   segment2 = netBufferAt(buffer, offset);
+
+   //Format TCP header
+   segment2->srcPort = htons(segment->destPort);
+   segment2->destPort = htons(segment->srcPort);
+   segment2->seqNum = htonl(seqNum);
+   segment2->ackNum = htonl(ackNum);
+   segment2->reserved1 = 0;
+   segment2->dataOffset = 5;
+   segment2->flags = flags;
+   segment2->reserved2 = 0;
+   segment2->window = 0;
+   segment2->checksum = 0;
+   segment2->urgentPointer = 0;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //Destination address is an IPv4 address?
+   if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
+   {
+      //Format IPv4 pseudo header
+      pseudoHeader2.length = sizeof(Ipv4PseudoHeader);
+      pseudoHeader2.ipv4Data.srcAddr = pseudoHeader->ipv4Data.destAddr;
+      pseudoHeader2.ipv4Data.destAddr = pseudoHeader->ipv4Data.srcAddr;
+      pseudoHeader2.ipv4Data.reserved = 0;
+      pseudoHeader2.ipv4Data.protocol = IPV4_PROTOCOL_TCP;
+      pseudoHeader2.ipv4Data.length = HTONS(sizeof(TcpHeader));
+
+      //Calculate TCP header checksum
+      segment2->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader2.ipv4Data,
+         sizeof(Ipv4PseudoHeader), buffer, offset, sizeof(TcpHeader));
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //Destination address is an IPv6 address?
+   if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
+   {
+      //Format IPv6 pseudo header
+      pseudoHeader2.length = sizeof(Ipv6PseudoHeader);
+      pseudoHeader2.ipv6Data.srcAddr = pseudoHeader->ipv6Data.destAddr;
+      pseudoHeader2.ipv6Data.destAddr = pseudoHeader->ipv6Data.srcAddr;
+      pseudoHeader2.ipv6Data.length = HTONL(sizeof(TcpHeader));
+      pseudoHeader2.ipv6Data.reserved = 0;
+      pseudoHeader2.ipv6Data.nextHeader = IPV6_TCP_HEADER;
+
+      //Calculate TCP header checksum
+      segment2->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader2.ipv6Data,
+         sizeof(Ipv6PseudoHeader), buffer, offset, sizeof(TcpHeader));
+   }
+   else
+#endif
+   //Destination address is not valid?
+   {
+      //Free previously allocated memory
+      netBufferFree(buffer);
+      //This should never occur...
+      return ERROR_INVALID_ADDRESS;
+   }
+
+   //Total number of segments sent
+   MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpOutSegs, 1);
+   MIB2_INC_COUNTER64(mib2Base.tcpGroup.tcpHCOutSegs, 1);
+
+   //Number of TCP segments sent containing the RST flag
+   MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpOutRsts, 1);
+
+   //Debug message
+   TRACE_DEBUG("%s: Sending TCP reset segment...\r\n",
+      formatSystemTime(osGetSystemTime(), NULL));
+   //Dump TCP header contents for debugging purpose
+   tcpDumpHeader(segment2, length, 0, 0);
+
+   //Send TCP segment
+   error = ipSendDatagram(interface, &pseudoHeader2, buffer, offset, 0);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return error code
+   return error;
+}
+
+
+/**
+ * @brief Append an option to a TCP segment
+ * @param[in] segment Pointer to the TCP header
+ * @param[in] kind Option code
+ * @param[in] value Option value
+ * @param[in] length Length of the option value
+ * @return Error code
+ **/
+
+error_t tcpAddOption(TcpHeader *segment, uint8_t kind, const void *value, uint8_t length)
+{
+   uint_t i;
+   size_t paddingSize;
+   TcpOption *option;
+
+   //Length of the complete option field
+   length += sizeof(TcpOption);
+
+   //Make sure there is enough space to add the specified option
+   if((segment->dataOffset * 4 + length) > TCP_MAX_HEADER_LENGTH)
+      return ERROR_FAILURE;
+
+   //Index of the first available byte
+   i = segment->dataOffset * 4 - sizeof(TcpHeader);
+
+   //Calculate the number of padding bytes
+   paddingSize = (length % 4) ? 4 - (length % 4) : 0;
+   //Write padding bytes
+   while(paddingSize--)
+      segment->options[i++] = TCP_OPTION_NOP;
+
+   //Point to the current location
+   option = (TcpOption *) (segment->options + i);
+   //Write specified option
+   option->kind = kind;
+   option->length = length;
+   memcpy(option->value, value, length - sizeof(TcpOption));
+   //Adjust index value
+   i += length;
+
+   //Update TCP header length
+   segment->dataOffset = (sizeof(TcpHeader) + i) / 4;
+
+   //Option successfully added
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Find a specified option in a TCP segment
+ * @param[in] segment Pointer to the TCP header
+ * @param[in] kind Code of the option to find
+ * @return If the specified option is found, a pointer to the corresponding
+ *   option is returned. Otherwise NULL pointer is returned
+ **/
+
+TcpOption *tcpGetOption(TcpHeader *segment, uint8_t kind)
+{
+   size_t length;
+   uint_t i;
+   TcpOption *option;
+
+   //Make sure the TCP header is valid
+   if(segment->dataOffset < 5)
+      return NULL;
+
+   //Compute the length of the options field
+   length = segment->dataOffset * 4 - sizeof(TcpHeader);
+
+   //Point to the very first option
+   i = 0;
+
+   //Parse TCP options
+   while(i < length)
+   {
+      //Point to the current option
+      option = (TcpOption *) (segment->options + i);
+
+      //NOP option detected?
+      if(option->kind == TCP_OPTION_NOP)
+      {
+         i++;
+         continue;
+      }
+      //END option detected?
+      if(option->kind == TCP_OPTION_END)
+         break;
+      //Check option length
+      if((i + 1) >= length || (i + option->length) > length)
+         break;
+
+      //Current option kind match the specified one?
+      if(option->kind == kind)
+         return option;
+
+      //Jump to next the next option
+      i += option->length;
+   }
+
+   //Specified option code not found
+   return NULL;
+}
+
+
+/**
+ * @brief Test the sequence number of an incoming segment
+ * @param[in] socket Handle referencing the current socket
+ * @param[in] segment Pointer to the TCP segment to check
+ * @param[in] length Length of the segment data
+ * @return NO_ERROR if the incoming segment is acceptable, ERROR_FAILURE otherwise
+ **/
+
+error_t tcpCheckSequenceNumber(Socket *socket, TcpHeader *segment, size_t length)
+{
+   //Acceptability test for an incoming segment
+   bool_t acceptable = FALSE;
+
+   //Case where both segment length and receive window are zero
+   if(!length && !socket->rcvWnd)
+   {
+      //Make sure that SEG.SEQ = RCV.NXT
+      if(segment->seqNum == socket->rcvNxt)
+      {
+         acceptable = TRUE;
+      }
+   }
+   //Case where segment length is zero and receive window is non zero
+   else if(!length && socket->rcvWnd)
+   {
+      //Make sure that RCV.NXT <= SEG.SEQ < RCV.NXT+RCV.WND
+      if(TCP_CMP_SEQ(segment->seqNum, socket->rcvNxt) >= 0 &&
+         TCP_CMP_SEQ(segment->seqNum, socket->rcvNxt + socket->rcvWnd) < 0)
+      {
+         acceptable = TRUE;
+      }
+   }
+   //Case where both segment length and receive window are non zero
+   else if(length && socket->rcvWnd)
+   {
+      //Check whether RCV.NXT <= SEG.SEQ < RCV.NXT+RCV.WND
+      if(TCP_CMP_SEQ(segment->seqNum, socket->rcvNxt) >= 0 &&
+         TCP_CMP_SEQ(segment->seqNum, socket->rcvNxt + socket->rcvWnd) < 0)
+      {
+         acceptable = TRUE;
+      }
+      //or RCV.NXT <= SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND
+      else if(TCP_CMP_SEQ(segment->seqNum + length - 1, socket->rcvNxt) >= 0 &&
+         TCP_CMP_SEQ(segment->seqNum + length - 1, socket->rcvNxt + socket->rcvWnd) < 0)
+      {
+         acceptable = TRUE;
+      }
+   }
+
+   //Non acceptable sequence number?
+   if(!acceptable)
+   {
+      //Debug message
+      TRACE_WARNING("Sequence number is not acceptable!\r\n");
+
+      //If an incoming segment is not acceptable, an acknowledgment
+      //should be sent in reply (unless the RST bit is set)
+      if(!(segment->flags & TCP_FLAG_RST))
+         tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt, socket->rcvNxt, 0, FALSE);
+
+      //Return status code
+      return ERROR_FAILURE;
+   }
+
+   //Sequence number is acceptable
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Check the SYN bit of an incoming segment
+ * @param[in] socket Handle referencing the current socket
+ * @param[in] segment Pointer to the TCP segment to check
+ * @param[in] length Length of the segment data
+ * @return ERROR_FAILURE if the SYN is in the window, NO_ERROR otherwise
+ **/
+
+error_t tcpCheckSyn(Socket *socket, TcpHeader *segment, size_t length)
+{
+   //Check the SYN bit
+   if(segment->flags & TCP_FLAG_SYN)
+   {
+      //If this step is reached, the SYN is in the window.
+      //It is an error and a reset shall be sent in response
+      if(segment->flags & TCP_FLAG_ACK)
+         tcpSendSegment(socket, TCP_FLAG_RST, segment->ackNum, 0, 0, FALSE);
+      else
+         tcpSendSegment(socket, TCP_FLAG_RST | TCP_FLAG_ACK, 0, segment->seqNum + length + 1, 0, FALSE);
+
+      //Return immediately
+      return ERROR_FAILURE;
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Test the ACK field of an incoming segment
+ * @param[in] socket Handle referencing the current socket
+ * @param[in] segment Pointer to the TCP segment to check
+ * @param[in] length Length of the segment data
+ * @return NO_ERROR if the acknowledgment is acceptable, ERROR_FAILURE otherwise
+ **/
+
+error_t tcpCheckAck(Socket *socket, TcpHeader *segment, size_t length)
+{
+   uint_t n;
+   uint_t ownd;
+   uint_t thresh;
+   bool_t duplicateFlag;
+   bool_t updateFlag;
+
+   //If the ACK bit is off drop the segment and return
+   if(!(segment->flags & TCP_FLAG_ACK))
+      return ERROR_FAILURE;
+
+   //Test the case where SEG.ACK < SND.UNA
+   if(TCP_CMP_SEQ(segment->ackNum, socket->sndUna) < 0)
+   {
+      //An old duplicate ACK has been received
+      return NO_ERROR;
+   }
+   //Test the case where SEG.ACK > SND.NXT
+   else if(TCP_CMP_SEQ(segment->ackNum, socket->sndNxt) > 0)
+   {
+      //Send an ACK segment indicating the current send sequence number
+      //and the acknowledgment number expected to be received
+      tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt, socket->rcvNxt, 0, FALSE);
+
+      //The ACK segment acknowledges something not yet sent
+      return ERROR_FAILURE;
+   }
+
+   //Check whether the ACK is a duplicate
+   duplicateFlag = tcpIsDuplicateAck(socket, segment, length);
+
+   //The send window should be updated
+   tcpUpdateSendWindow(socket, segment);
+
+   //The incoming ACK segment acknowledges new data?
+   if(TCP_CMP_SEQ(segment->ackNum, socket->sndUna) > 0)
+   {
+#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
+      //Compute the number of bytes acknowledged by the incoming ACK
+      n = segment->ackNum - socket->sndUna;
+
+      //Check whether the ACK segment acknowledges our SYN
+      if(socket->sndUna == socket->iss)
+         n--;
+
+      //Total number of bytes acknowledged during the whole round-trip
+      socket->n += n;
+#endif
+      //Update SND.UNA pointer
+      socket->sndUna = segment->ackNum;
+
+      //Compute retransmission timeout
+      updateFlag = tcpComputeRto(socket);
+
+      //Any segments on the retransmission queue which are thereby
+      //entirely acknowledged are removed
+      tcpUpdateRetransmitQueue(socket);
+
+#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
+      //Check congestion state
+      if(socket->congestState == TCP_CONGEST_STATE_RECOVERY)
+      {
+         //Invoke fast recovery (refer to RFC 6582)
+         tcpFastRecovery(socket, segment, n);
+      }
+      else
+      {
+         //Reset duplicate ACK counter
+         socket->dupAckCount = 0;
+
+         //Check congestion state
+         if(socket->congestState == TCP_CONGEST_STATE_LOSS_RECOVERY)
+         {
+            //Invoke fast loss recovery
+            tcpFastLossRecovery(socket, segment);
+         }
+
+         //Slow start algorithm is used when cwnd is lower than ssthresh
+         if(socket->cwnd < socket->ssthresh)
+         {
+            //During slow start, TCP increments cwnd by at most SMSS bytes
+            //for each ACK received that cumulatively acknowledges new data
+            socket->cwnd += MIN(n, socket->smss);
+         }
+         //Congestion avoidance algorithm is used when cwnd exceeds ssthres
+         else
+         {
+            //Congestion window is updated once per RTT
+            if(updateFlag)
+            {
+               //TCP must not increment cwnd by more than SMSS bytes
+               socket->cwnd += MIN(socket->n, socket->smss);
+            }
+         }
+      }
+
+      //Limit the size of the congestion window
+      socket->cwnd = MIN(socket->cwnd, socket->txBufferSize);
+#endif
+   }
+   //The incoming ACK segment does not acknowledge new data?
+   else
+   {
+#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
+      //Check whether the acknowledgment is a duplicate
+      if(duplicateFlag)
+      {
+         //Increment duplicate ACK counter
+         socket->dupAckCount++;
+         //Debug message
+         TRACE_INFO("TCP duplicate ACK #%u\r\n", socket->dupAckCount);
+      }
+      else
+      {
+         //Reset duplicate ACK counter
+         socket->dupAckCount = 0;
+      }
+
+      //Check congestion state
+      if(socket->congestState == TCP_CONGEST_STATE_IDLE)
+      {
+         //Use default duplicate ACK threshold
+         thresh = TCP_FAST_RETRANSMIT_THRES;
+         //Amount of data sent but not yet acknowledged
+         ownd = socket->sndNxt - socket->sndUna;
+
+         //Test if there is either no unsent data ready for transmission at
+         //the sender, or the advertised receive window does not permit new
+         //segments to be transmitted (refer to RFC 5827 section 3.1)
+         if(socket->sndUser == 0 || socket->sndWnd <= (socket->sndNxt - socket->sndUna))
+         {
+            //Compute the duplicate ACK threshold used to trigger a
+            //retransmission
+            if(ownd <= (3 * socket->smss))
+               thresh = 1;
+            else if(ownd <= (4 * socket->smss))
+               thresh = 2;
+         }
+
+         //Check the number of duplicate ACKs that have been received
+         if(socket->dupAckCount >= thresh)
+         {
+            //The TCP sender first checks the value of recover to see if the
+            //cumulative acknowledgment field covers more than recover
+            if(TCP_CMP_SEQ(segment->ackNum, socket->recover + 1) > 0)
+            {
+               //Invoke Fast Retransmit (refer to RFC 6582)
+               tcpFastRetransmit(socket);
+            }
+            else
+            {
+               //If not, the TCP does not enter fast retransmit and does not
+               //reset ssthres...
+            }
+         }
+      }
+      else if(socket->congestState == TCP_CONGEST_STATE_RECOVERY)
+      {
+         //Duplicate ACK received?
+         if(duplicateFlag)
+         {
+            //For each additional duplicate ACK received (after the third),
+            //cwnd must be incremented by SMSS. This artificially inflates
+            //the congestion window in order to reflect the additional
+            //segment that has left the network
+            socket->cwnd += socket->smss;
+         }
+      }
+
+      //Limit the size of the congestion window
+      socket->cwnd = MIN(socket->cwnd, socket->txBufferSize);
+#endif
+   }
+
+   //Update TX events
+   tcpUpdateEvents(socket);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Test whether the incoming acknowledgment is a duplicate
+ * @param[in] socket Handle referencing the current socket
+ * @param[in] segment Pointer to the TCP segment to check
+ * @param[in] length Length of the segment data
+ * @return TRUE if the ACK is duplicate, else FALSE
+ **/
+
+bool_t tcpIsDuplicateAck(Socket *socket, TcpHeader *segment, size_t length)
+{
+   //An ACK is considered a duplicate when the following conditions are met
+   bool_t flag = FALSE;
+
+   //The receiver of the ACK has outstanding data
+   if(socket->retransmitQueue != NULL)
+   {
+      //The incoming acknowledgment carries no data
+      if(length == 0)
+      {
+         //the SYN and FIN bits are both off
+         if(!(segment->flags & (TCP_FLAG_SYN | TCP_FLAG_FIN)))
+         {
+            //The acknowledgment number is equal to the greatest
+            //acknowledgment received on the given connection
+            if(segment->ackNum == socket->sndUna)
+            {
+               //The advertised window in the incoming acknowledgment equals the
+               //advertised window in the last incoming acknowledgment
+               if(segment->window == socket->sndWnd)
+               {
+                  //Duplicate ACK
+                  flag = TRUE;
+               }
+            }
+         }
+      }
+   }
+
+   //Return TRUE if the acknowledgment is a duplicate
+   return flag;
+}
+
+
+/**
+ * @brief Fast retransmit procedure
+ * @param[in] socket Handle referencing the current socket
+ **/
+
+void tcpFastRetransmit(Socket *socket)
+{
+#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
+   uint_t flightSize;
+
+   //Amount of data that has been sent but not yet acknowledged
+   flightSize = socket->sndNxt - socket->sndUna;
+   //After receiving 3 duplicate ACKs, ssthresh must be adjusted
+   socket->ssthresh = MAX(flightSize / 2, 2 * socket->smss);
+
+   //The value of recover is incremented to the value of the highest
+   //sequence number transmitted by the TCP so far
+   socket->recover = socket->sndNxt - 1;
+
+   //Debug message
+   TRACE_INFO("TCP fast retransmit...\r\n");
+
+   //TCP performs a retransmission of what appears to be the missing segment,
+   //without waiting for the retransmission timer to expire
+   tcpRetransmitSegment(socket);
+
+   //cwnd must set to ssthresh plus 3*SMSS. This artificially inflates the
+   //congestion window by the number of segments (three) that have left
+   //the network and which the receiver has buffered
+   socket->cwnd = socket->ssthresh + TCP_FAST_RETRANSMIT_THRES * socket->smss;
+
+   //Enter the fast recovery procedure
+   socket->congestState = TCP_CONGEST_STATE_RECOVERY;
+#endif
+}
+
+
+/**
+ * @brief Fast recovery procedure
+ * @param[in] socket Handle referencing the current socket
+ * @param[in] segment Pointer to the incoming TCP segment
+ * @param[in] n Number of bytes acknowledged by the incoming ACK
+ **/
+
+void tcpFastRecovery(Socket *socket, TcpHeader *segment, uint_t n)
+{
+#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
+   //Check whether this ACK acknowledges all of the data up to and
+   //including recover
+   if(TCP_CMP_SEQ(segment->ackNum, socket->recover) > 0)
+   {
+      //This is a full acknowledgment
+      TRACE_INFO("TCP full acknowledgment\r\n");
+
+      //Set cwnd to ssthresh
+      socket->cwnd = socket->ssthresh;
+      //Exit the fast recovery procedure
+      socket->congestState = TCP_CONGEST_STATE_IDLE;
+   }
+   else
+   {
+      //If this ACK does not acknowledge all of the data up to and including
+      //recover, then this is a partial ACK
+      TRACE_INFO("TCP partial acknowledgment\r\n");
+
+      //Retransmit the first unacknowledged segment
+      tcpRetransmitSegment(socket);
+
+      //Deflate the congestion window by the amount of new data acknowledged
+      //by the cumulative acknowledgment field
+      if(socket->cwnd > n)
+         socket->cwnd -= n;
+
+      //If the partial ACK acknowledges at least one SMSS of new data, then
+      //add back SMSS bytes to the congestion window. This artificially
+      //inflates the congestion window in order to reflect the additional
+      //segment that has left the network
+      if(n >= socket->smss)
+         socket->cwnd += socket->smss;
+
+      //Do not exit the fast recovery procedure...
+      socket->congestState = TCP_CONGEST_STATE_RECOVERY;
+   }
+#endif
+}
+
+
+/**
+ * @brief Fast loss recovery procedure
+ * @param[in] socket Handle referencing the current socket
+ * @param[in] segment Pointer to the incoming TCP segment
+ **/
+
+void tcpFastLossRecovery(Socket *socket, TcpHeader *segment)
+{
+#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
+   //Check whether this ACK acknowledges all of the data up to and
+   //including recover
+   if(TCP_CMP_SEQ(segment->ackNum, socket->recover) > 0)
+   {
+      //This is a full acknowledgment
+      TRACE_INFO("TCP full acknowledgment\r\n");
+
+      //Exit the fast loss recovery procedure
+      socket->congestState = TCP_CONGEST_STATE_IDLE;
+   }
+   else
+   {
+      //If this ACK does not acknowledge all of the data up to and including
+      //recover, then this is a partial ACK
+      TRACE_INFO("TCP partial acknowledgment\r\n");
+
+      //Retransmit the first unacknowledged segment
+      tcpRetransmitSegment(socket);
+
+      //Do not exit the fast loss recovery procedure...
+      socket->congestState = TCP_CONGEST_STATE_LOSS_RECOVERY;
+   }
+#endif
+}
+
+
+/**
+ * @brief Process the segment text
+ * @param[in] socket Handle referencing the current socket
+ * @param[in] segment Pointer to the TCP header
+ * @param[in] buffer Multi-part buffer containing the incoming TCP segment
+ * @param[in] offset Offset to the first data byte
+ * @param[in] length Length of the segment data
+ **/
+
+void tcpProcessSegmentData(Socket *socket, TcpHeader *segment,
+   const NetBuffer *buffer, size_t offset, size_t length)
+{
+   uint32_t leftEdge;
+   uint32_t rightEdge;
+
+   //First sequence number occupied by the incoming segment
+   leftEdge = segment->seqNum;
+   //Sequence number immediately following the incoming segment
+   rightEdge = segment->seqNum + length;
+
+   //Check whether some data falls outside the receive window
+   if(TCP_CMP_SEQ(leftEdge, socket->rcvNxt) < 0)
+   {
+      //Position of the first byte to be read
+      offset += socket->rcvNxt - leftEdge;
+      //Ignore the data that falls outside the receive window
+      leftEdge = socket->rcvNxt;
+   }
+   if(TCP_CMP_SEQ(rightEdge, socket->rcvNxt + socket->rcvWnd) > 0)
+   {
+      //Ignore the data that falls outside the receive window
+      rightEdge = socket->rcvNxt + socket->rcvWnd;
+   }
+
+   //Copy the incoming data to the receive buffer
+   tcpWriteRxBuffer(socket, leftEdge, buffer, offset, rightEdge - leftEdge);
+
+   //Update the list of non-contiguous blocks of data that
+   //have been received and queued
+   tcpUpdateSackBlocks(socket, &leftEdge, &rightEdge);
+
+   //Check whether the segment was received out of order
+   if(TCP_CMP_SEQ(leftEdge, socket->rcvNxt) > 0)
+   {
+      //Out of order data segments should be acknowledged
+      //immediately, in order to accelerate loss recovery
+      tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt, socket->rcvNxt, 0, FALSE);
+   }
+   else
+   {
+      //Number of contiguous bytes that have been received
+      length = rightEdge - leftEdge;
+
+      //Next sequence number expected on incoming segments
+      socket->rcvNxt += length;
+      //Number of data available in the receive buffer
+      socket->rcvUser += length;
+      //Update the receive window
+      socket->rcvWnd -= length;
+
+      //Acknowledge the received data (delayed ACK not supported)
+      tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt, socket->rcvNxt, 0, FALSE);
+      //Notify user task that data is available
+      tcpUpdateEvents(socket);
+   }
+}
+
+
+/**
+ * @brief Delete TCB structure
+ * @param[in] socket Handle referencing the socket
+ **/
+
+void tcpDeleteControlBlock(Socket *socket)
+{
+   //Delete retransmission queue
+   tcpFlushRetransmitQueue(socket);
+
+   //Delete SYN queue
+   tcpFlushSynQueue(socket);
+
+   //Release transmit buffer
+   netBufferSetLength((NetBuffer *) &socket->txBuffer, 0);
+
+   //Release receive buffer
+   netBufferSetLength((NetBuffer *) &socket->rxBuffer, 0);
+}
+
+
+/**
+ * @brief Remove acknowledged segments from retransmission queue
+ * @param[in] socket Handle referencing the socket
+ **/
+
+void tcpUpdateRetransmitQueue(Socket *socket)
+{
+   size_t length;
+   TcpQueueItem *prevQueueItem;
+   TcpQueueItem *queueItem;
+   TcpHeader *header;
+
+   //Point to the first item of the retransmission queue
+   prevQueueItem = NULL;
+   queueItem = socket->retransmitQueue;
+
+   //Loop through retransmission queue
+   while(queueItem != NULL)
+   {
+      //Point to the TCP header
+      header = (TcpHeader *) queueItem->header;
+
+      //SYN segment?
+      if(header->flags & TCP_FLAG_SYN)
+         length = 1;
+      //FIN segment?
+      else if(header->flags & TCP_FLAG_FIN)
+         length = queueItem->length + 1;
+      //Segment containing data?
+      else
+         length = queueItem->length;
+
+      //If an acknowledgment is received for a segment before its timer
+      //expires, the segment is removed from the retransmission queue
+      if(TCP_CMP_SEQ(socket->sndUna, ntohl(header->seqNum) + length) >= 0)
+      {
+         //First item of the queue?
+         if(prevQueueItem == NULL)
+         {
+            //Remove the current item from the queue
+            socket->retransmitQueue = queueItem->next;
+            //The item can now be safely deleted
+            memPoolFree(queueItem);
+            //Point to the next item
+            queueItem = socket->retransmitQueue;
+         }
+         else
+         {
+            //Remove the current item from the queue
+            prevQueueItem->next = queueItem->next;
+            //The item can now be safely deleted
+            memPoolFree(queueItem);
+            //Point to the next item
+            queueItem = prevQueueItem->next;
+         }
+
+         //When an ACK is received that acknowledges new data, restart the
+         //retransmission timer so that it will expire after RTO seconds
+         tcpTimerStart(&socket->retransmitTimer, socket->rto);
+         //Reset retransmission counter
+         socket->retransmitCount = 0;
+      }
+      //No acknowledgment received for the current segment...
+      else
+      {
+         //Point to the next item
+         prevQueueItem = queueItem;
+         queueItem = queueItem->next;
+      }
+   }
+
+   //When all outstanding data has been acknowledged,
+   //turn off the retransmission timer
+   if(socket->retransmitQueue == NULL)
+      tcpTimerStop(&socket->retransmitTimer);
+}
+
+
+/**
+ * @brief Flush retransmission queue
+ * @param[in] socket Handle referencing the socket
+ **/
+
+void tcpFlushRetransmitQueue(Socket *socket)
+{
+   //Point to the first item in the retransmission queue
+   TcpQueueItem *queueItem = socket->retransmitQueue;
+
+   //Loop through retransmission queue
+   while(queueItem != NULL)
+   {
+      //Keep track of the next item in the queue
+      TcpQueueItem *nextQueueItem = queueItem->next;
+      //Free previously allocated memory
+      memPoolFree(queueItem);
+      //Point to the next item
+      queueItem = nextQueueItem;
+   }
+
+   //The retransmission queue is now flushed
+   socket->retransmitQueue = NULL;
+
+   //Turn off the retransmission timer
+   tcpTimerStop(&socket->retransmitTimer);
+}
+
+
+/**
+ * @brief Flush SYN queue
+ * @param[in] socket Handle referencing the socket
+ **/
+
+void tcpFlushSynQueue(Socket *socket)
+{
+   //Point to the first item in the SYN queue
+   TcpSynQueueItem *queueItem = socket->synQueue;
+
+   //Loop through SYN queue
+   while(queueItem != NULL)
+   {
+      //Keep track of the next item in the queue
+      TcpSynQueueItem *nextQueueItem = queueItem->next;
+      //Free previously allocated memory
+      memPoolFree(queueItem);
+      //Point to the next item
+      queueItem = nextQueueItem;
+   }
+
+   //SYN queue was successfully flushed
+   socket->synQueue = NULL;
+}
+
+
+/**
+ * @brief Update the list of non-contiguous blocks that have been received
+ * @param[in] socket Handle referencing the socket
+ * @param[in,out] leftEdge First sequence number occupied by the incoming data
+ * @param[in,out] rightEdge Sequence number immediately following the incoming data
+ **/
+
+void tcpUpdateSackBlocks(Socket *socket, uint32_t *leftEdge, uint32_t *rightEdge)
+{
+   uint_t i = 0;
+
+   //Loop through the blocks
+   while(i < socket->sackBlockCount)
+   {
+      //Find each block that overlaps the specified one
+      if(TCP_CMP_SEQ(*rightEdge, socket->sackBlock[i].leftEdge) >= 0 &&
+         TCP_CMP_SEQ(*leftEdge, socket->sackBlock[i].rightEdge) <= 0)
+      {
+         //Merge blocks to form a contiguous one
+         *leftEdge = MIN(*leftEdge, socket->sackBlock[i].leftEdge);
+         *rightEdge = MAX(*rightEdge, socket->sackBlock[i].rightEdge);
+
+         //Delete current block
+         memmove(socket->sackBlock + i, socket->sackBlock + i + 1,
+            (TCP_MAX_SACK_BLOCKS - i - 1) * sizeof(TcpSackBlock));
+
+         //Decrement the number of non-contiguous blocks
+         socket->sackBlockCount--;
+      }
+      else
+      {
+         //Point to the next block
+         i++;
+      }
+   }
+
+   //Check whether the incoming segment was received out of order
+   if(TCP_CMP_SEQ(*leftEdge, socket->rcvNxt) > 0)
+   {
+      //Make room for the new non-contiguous block
+      memmove(socket->sackBlock + 1, socket->sackBlock,
+         (TCP_MAX_SACK_BLOCKS - 1) * sizeof(TcpSackBlock));
+
+      //Insert the element in the list
+      socket->sackBlock[0].leftEdge = *leftEdge;
+      socket->sackBlock[0].rightEdge = *rightEdge;
+
+      //Increment the number of non-contiguous blocks
+      if(socket->sackBlockCount < TCP_MAX_SACK_BLOCKS)
+         socket->sackBlockCount++;
+   }
+}
+
+
+/**
+ * @brief Update send window
+ * @param[in] socket Handle referencing the socket
+ * @param[in] segment Pointer to the incoming TCP segment
+ **/
+
+void tcpUpdateSendWindow(Socket *socket, TcpHeader *segment)
+{
+   //Case where neither the sequence nor the acknowledgment number is increased
+   if(segment->seqNum == socket->sndWl1 && segment->ackNum == socket->sndWl2)
+   {
+      //TCP may ignore a window update with a smaller window than
+      //previously offered if neither the sequence number nor the
+      //acknowledgment number is increased (see RFC 1122 4.2.2.16)
+      if(segment->window > socket->sndWnd)
+      {
+         //Update the send window and record the sequence number and
+         //the acknowledgment number used to update SND.WND
+         socket->sndWnd = segment->window;
+         socket->sndWl1 = segment->seqNum;
+         socket->sndWl2 = segment->ackNum;
+
+         //Maximum send window it has seen so far on the connection
+         socket->maxSndWnd = MAX(socket->maxSndWnd, segment->window);
+      }
+   }
+   //Case where the sequence or the acknowledgment number is increased
+   else if(TCP_CMP_SEQ(segment->seqNum, socket->sndWl1) >= 0 &&
+      TCP_CMP_SEQ(segment->ackNum, socket->sndWl2) >= 0)
+   {
+      //The remote host advertises a zero window?
+      if(!segment->window && socket->sndWnd)
+      {
+         //Start the persist timer
+         socket->wndProbeCount = 0;
+         socket->wndProbeInterval = TCP_DEFAULT_PROBE_INTERVAL;
+         tcpTimerStart(&socket->persistTimer, socket->wndProbeInterval);
+      }
+
+      //Update the send window and record the sequence number and
+      //the acknowledgment number used to update SND.WND
+      socket->sndWnd = segment->window;
+      socket->sndWl1 = segment->seqNum;
+      socket->sndWl2 = segment->ackNum;
+
+      //Maximum send window it has seen so far on the connection
+      socket->maxSndWnd = MAX(socket->maxSndWnd, segment->window);
+   }
+}
+
+
+/**
+ * @brief Update receive window so as to avoid Silly Window Syndrome
+ * @param[in] socket Handle referencing the socket
+ **/
+
+void tcpUpdateReceiveWindow(Socket *socket)
+{
+   uint16_t reduction;
+
+   //Space available but not yet advertised
+   reduction = socket->rxBufferSize - socket->rcvUser - socket->rcvWnd;
+
+   //To avoid SWS, the receiver should not advertise small windows
+   if((socket->rcvWnd + reduction) >= MIN(socket->rmss, socket->rxBufferSize / 2))
+   {
+      //Check whether a window update should be sent
+      if(socket->rcvWnd < MIN(socket->rmss, socket->rxBufferSize / 2))
+      {
+         //Debug message
+         TRACE_INFO("%s: TCP sending window update...\r\n",
+            formatSystemTime(osGetSystemTime(), NULL));
+
+         //Update the receive window
+         socket->rcvWnd += reduction;
+         //Send an ACK segment to advertise the new window size
+         tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt, socket->rcvNxt, 0, FALSE);
+      }
+      else
+      {
+         //The receive window can be updated
+         socket->rcvWnd += reduction;
+      }
+   }
+}
+
+
+/**
+ * @brief Compute retransmission timeout
+ * @param[in] socket Handle referencing the socket
+ * @return TRUE if the RTT measurement is complete, else FALSE
+ **/
+
+bool_t tcpComputeRto(Socket *socket)
+{
+   bool_t flag;
+   systime_t r;
+   systime_t delta;
+
+   //Clear flag
+   flag = FALSE;
+
+   //TCP implementation takes one RTT measurement at a time
+   if(socket->rttBusy)
+   {
+      //Ensure the incoming ACK number covers the expected sequence number
+      if(TCP_CMP_SEQ(socket->sndUna, socket->rttSeqNum) > 0)
+      {
+         //Calculate round-time trip
+         r = osGetSystemTime() - socket->rttStartTime;
+
+         //First RTT measurement?
+         if(!socket->srtt && !socket->rttvar)
+         {
+            //Initialize RTO calculation algorithm
+            socket->srtt = r;
+            socket->rttvar = r / 2;
+         }
+         else
+         {
+            //Calculate the difference between the measured value and the
+            //current RTT estimator
+            delta = (r > socket->srtt) ? (r - socket->srtt) : (socket->srtt - r);
+
+            //Implement Van Jacobson's algorithm (as specified in RFC 6298 2.3)
+            socket->rttvar = (3 * socket->rttvar + delta) / 4;
+            socket->srtt = (7 * socket->srtt + r) / 8;
+         }
+
+         //Calculate the next retransmission timeout
+         socket->rto = socket->srtt + 4 * socket->rttvar;
+
+         //Whenever RTO is computed, if it is less than 1 second, then
+         //the RTO should be rounded up to 1 second
+         socket->rto = MAX(socket->rto, TCP_MIN_RTO);
+         //A maximum value may be placed on RTO provided it is at least 60 seconds
+         socket->rto = MIN(socket->rto, TCP_MAX_RTO);
+
+         //Debug message
+         TRACE_DEBUG("R=%" PRIu32 ", SRTT=%" PRIu32 ", RTTVAR=%" PRIu32 ", RTO=%" PRIu32 "\r\n",
+            r, socket->srtt, socket->rttvar, socket->rto);
+
+         //RTT measurement is complete
+         socket->rttBusy = FALSE;
+         //Set flag
+         flag = TRUE;
+      }
+   }
+
+   //Return TRUE if the RTT measurement is complete
+   return flag;
+}
+
+
+/**
+ * @brief TCP segment retransmission
+ * @param[in] socket Handle referencing the socket
+ * @return Error code
+ **/
+
+error_t tcpRetransmitSegment(Socket *socket)
+{
+   error_t error;
+   size_t offset;
+   size_t length;
+   NetBuffer *buffer;
+   TcpQueueItem *queueItem;
+   TcpHeader *header;
+
+   //Initialize error code
+   error = NO_ERROR;
+   //Total number of bytes that have been retransmitted
+   length = 0;
+
+   //Point to the retransmission queue
+   queueItem = socket->retransmitQueue;
+
+   //Any segment in the retransmission queue?
+   while(queueItem != NULL)
+   {
+      //Total number of bytes that have been retransmitted
+      length += queueItem->length;
+
+      //The amount of data that can be sent cannot exceed the MSS
+      if(length > socket->smss)
+      {
+         //We are done
+         error = NO_ERROR;
+         //Exit immediately
+         break;
+      }
+
+      //Point to the TCP header
+      header = (TcpHeader *) queueItem->header;
+
+      //Allocate a memory buffer to hold the TCP segment
+      buffer = ipAllocBuffer(0, &offset);
+      //Failed to allocate memory?
+      if(buffer == NULL)
+      {
+         //Report an error
+         error = ERROR_OUT_OF_MEMORY;
+         //Exit immediately
+         break;
+      }
+
+      //Start of exception handling block
+      do
+      {
+         //Copy TCP header
+         error = netBufferAppend(buffer, header, header->dataOffset * 4);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Copy data from send buffer
+         error = tcpReadTxBuffer(socket, ntohl(header->seqNum), buffer, queueItem->length);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Total number of segments retransmitted
+         MIB2_INC_COUNTER32(mib2Base.tcpGroup.tcpRetransSegs, 1);
+
+         //Dump TCP header contents for debugging purpose
+         tcpDumpHeader(header, queueItem->length, socket->iss, socket->irs);
+
+         //Retransmit the lost segment without waiting for the
+         //retransmission timer to expire
+         error = ipSendDatagram(socket->interface,
+            &queueItem->pseudoHeader, buffer, offset, 0);
+
+         //End of exception handling block
+      } while(0);
+
+      //Free previously allocated memory
+      netBufferFree(buffer);
+
+      //Any error to report?
+      if(error)
+      {
+         //Exit immediately
+         break;
+      }
+
+      //Point to the next segment in the queue
+      queueItem = queueItem->next;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Nagle algorithm implementation
+ * @param[in] socket Handle referencing the socket
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t tcpNagleAlgo(Socket *socket, uint_t flags)
+{
+   error_t error;
+   uint_t n;
+   uint_t u;
+
+   //The amount of data that can be sent at any given time is
+   //limited by the receiver window and the congestion window
+   n = MIN(socket->sndWnd, socket->txBufferSize);
+
+#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
+   //Check the congestion window
+   n = MIN(n, socket->cwnd);
+#endif
+
+   //Retrieve the size of the usable window
+   u = n - (socket->sndNxt - socket->sndUna);
+
+   //The remote host should not shrink its window. However, we
+   //must be robust against window shrinking, which may cause
+   //the usable window to become negative
+   if((int_t) u < 0)
+      return NO_ERROR;
+
+   //The Nagle algorithm discourages sending tiny segments when
+   //the data to be sent increases in small increments
+   while(socket->sndUser > 0)
+   {
+      //Calculate the number of bytes to send at a time
+      n = MIN(u, socket->sndUser);
+      n = MIN(n, socket->smss);
+
+      //Disable Nagle algorithm?
+      if(flags & SOCKET_FLAG_NO_DELAY)
+      {
+         //All packets will be send no matter what size they have
+         if(n > 0)
+         {
+            //Send TCP segment
+            error = tcpSendSegment(socket, TCP_FLAG_PSH | TCP_FLAG_ACK,
+               socket->sndNxt, socket->rcvNxt, n, TRUE);
+            //Failed to send TCP segment?
+            if(error)
+               return error;
+         }
+         else
+         {
+            //We are done...
+            break;
+         }
+      }
+      else if(flags & SOCKET_FLAG_DELAY)
+      {
+         //Transmit data if a maximum-sized segment can be sent
+         if(MIN(socket->sndUser, u) >= socket->smss)
+         {
+            //Send TCP segment
+            error = tcpSendSegment(socket, TCP_FLAG_PSH | TCP_FLAG_ACK,
+               socket->sndNxt, socket->rcvNxt, n, TRUE);
+            //Failed to send TCP segment?
+            if(error)
+               return error;
+         }
+         else
+         {
+            //Prevent the sender from sending tiny segments...
+            break;
+         }
+      }
+      else
+      {
+         //Transmit data if a maximum-sized segment can be sent
+         if(MIN(socket->sndUser, u) >= socket->smss)
+         {
+            //Send TCP segment
+            error = tcpSendSegment(socket, TCP_FLAG_PSH | TCP_FLAG_ACK,
+               socket->sndNxt, socket->rcvNxt, n, TRUE);
+            //Failed to send TCP segment?
+            if(error)
+               return error;
+         }
+         //Or if all queued data can be sent now
+         else if(socket->sndNxt == socket->sndUna && socket->sndUser <= u)
+         {
+            //Send TCP segment
+            error = tcpSendSegment(socket, TCP_FLAG_PSH | TCP_FLAG_ACK,
+               socket->sndNxt, socket->rcvNxt, n, TRUE);
+            //Failed to send TCP segment?
+            if(error)
+               return error;
+         }
+         //Or if at least a fraction of the maximum window can be sent
+         else if(MIN(socket->sndUser, u) >= (socket->maxSndWnd / 2))
+         {
+            //Send TCP segment
+            error = tcpSendSegment(socket, TCP_FLAG_PSH | TCP_FLAG_ACK,
+               socket->sndNxt, socket->rcvNxt, n, TRUE);
+            //Failed to send TCP segment?
+            if(error)
+               return error;
+         }
+         else
+         {
+            //Prevent the sender from sending tiny segments...
+            break;
+         }
+      }
+
+      //Advance SND.NXT pointer
+      socket->sndNxt += n;
+      //Update the number of data buffered but not yet sent
+      socket->sndUser -= n;
+      //Update the size of the usable window
+      u -= n;
+   }
+
+   //Check whether the transmitter can accept more data
+   tcpUpdateEvents(socket);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Update TCP FSM current state
+ * @param[in] socket Handle referencing the socket
+ * @param[in] newState New TCP state to switch to
+ **/
+
+void tcpChangeState(Socket *socket, TcpState newState)
+{
+   //Enter CLOSED state?
+   if(newState == TCP_STATE_CLOSED)
+   {
+      //Check previous state
+      if(socket->state == TCP_STATE_LAST_ACK ||
+         socket->state == TCP_STATE_TIME_WAIT)
+      {
+         //The connection has been closed properly
+         socket->closedFlag = TRUE;
+      }
+      else
+      {
+         //The connection has been reset by the peer
+         socket->resetFlag = TRUE;
+      }
+   }
+
+   //Enter the desired state
+   socket->state = newState;
+   //Update TCP related events
+   tcpUpdateEvents(socket);
+}
+
+
+/**
+ * @brief Update TCP related events
+ * @param[in] socket Handle referencing the socket
+ **/
+
+void tcpUpdateEvents(Socket *socket)
+{
+   //Clear event flags
+   socket->eventFlags = 0;
+
+   //Check current TCP state
+   switch(socket->state)
+   {
+   //ESTABLISHED or FIN-WAIT-1 state?
+   case TCP_STATE_ESTABLISHED:
+   case TCP_STATE_FIN_WAIT_1:
+      socket->eventFlags |= SOCKET_EVENT_CONNECTED;
+      break;
+   //FIN-WAIT-2 state?
+   case TCP_STATE_FIN_WAIT_2:
+      socket->eventFlags |= SOCKET_EVENT_CONNECTED;
+      socket->eventFlags |= SOCKET_EVENT_TX_SHUTDOWN;
+      break;
+   //CLOSE-WAIT, LAST-ACK or CLOSING state?
+   case TCP_STATE_CLOSE_WAIT:
+   case TCP_STATE_LAST_ACK:
+   case TCP_STATE_CLOSING:
+      socket->eventFlags |= SOCKET_EVENT_CONNECTED;
+      socket->eventFlags |= SOCKET_EVENT_RX_SHUTDOWN;
+      break;
+   //TIME-WAIT or CLOSED state?
+   case TCP_STATE_TIME_WAIT:
+   case TCP_STATE_CLOSED:
+      socket->eventFlags |= SOCKET_EVENT_CLOSED;
+      socket->eventFlags |= SOCKET_EVENT_TX_SHUTDOWN;
+      socket->eventFlags |= SOCKET_EVENT_RX_SHUTDOWN;
+      break;
+   //Any other state
+   default:
+      break;
+   }
+
+   //Handle TX specific events
+   if(socket->state == TCP_STATE_SYN_SENT ||
+      socket->state == TCP_STATE_SYN_RECEIVED)
+   {
+      //Disallow write operations until the connection is established
+      socket->eventFlags |= SOCKET_EVENT_TX_DONE;
+      socket->eventFlags |= SOCKET_EVENT_TX_ACKED;
+   }
+   else if(socket->state == TCP_STATE_ESTABLISHED ||
+      socket->state == TCP_STATE_CLOSE_WAIT)
+   {
+      //Check whether the send buffer is full or not
+      if((socket->sndUser + socket->sndNxt - socket->sndUna) < socket->txBufferSize)
+         socket->eventFlags |= SOCKET_EVENT_TX_READY;
+
+      //Check whether all the data in the send buffer has been transmitted
+      if(!socket->sndUser)
+      {
+         //All the pending data has been sent out
+         socket->eventFlags |= SOCKET_EVENT_TX_DONE;
+
+         //Check whether an acknowledgment has been received
+         if(TCP_CMP_SEQ(socket->sndUna, socket->sndNxt) >= 0)
+            socket->eventFlags |= SOCKET_EVENT_TX_ACKED;
+      }
+   }
+   else if(socket->state != TCP_STATE_LISTEN)
+   {
+      //Unblock user task if the connection is being closed
+      socket->eventFlags |= SOCKET_EVENT_TX_READY;
+      socket->eventFlags |= SOCKET_EVENT_TX_DONE;
+      socket->eventFlags |= SOCKET_EVENT_TX_ACKED;
+   }
+
+   //Handle RX specific events
+   if(socket->state == TCP_STATE_ESTABLISHED ||
+      socket->state == TCP_STATE_FIN_WAIT_1 ||
+      socket->state == TCP_STATE_FIN_WAIT_2)
+   {
+      //Data is available for reading?
+      if(socket->rcvUser > 0)
+         socket->eventFlags |= SOCKET_EVENT_RX_READY;
+   }
+   else if(socket->state == TCP_STATE_LISTEN)
+   {
+      //If the socket is currently in the listen state, it will be marked
+      //as readable if an incoming connection request has been received
+      if(socket->synQueue)
+         socket->eventFlags |= SOCKET_EVENT_RX_READY;
+   }
+   else if(socket->state != TCP_STATE_SYN_SENT &&
+      socket->state != TCP_STATE_SYN_RECEIVED)
+   {
+      //Readability can also indicate that a request to close
+      //the socket has been received from the peer
+      socket->eventFlags |= SOCKET_EVENT_RX_READY;
+   }
+
+   //Check whether the socket is bound to a particular network interface
+   if(socket->interface != NULL)
+   {
+      //Handle link up and link down events
+      if(socket->interface->linkState)
+         socket->eventFlags |= SOCKET_EVENT_LINK_UP;
+      else
+         socket->eventFlags |= SOCKET_EVENT_LINK_DOWN;
+   }
+
+   //Mask unused events
+   socket->eventFlags &= socket->eventMask;
+
+   //Any event to signal?
+   if(socket->eventFlags)
+   {
+      //Unblock I/O operations currently in waiting state
+      osSetEvent(&socket->event);
+
+      //Set user event to signaled state if necessary
+      if(socket->userEvent != NULL)
+         osSetEvent(socket->userEvent);
+   }
+}
+
+
+/**
+ * @brief Wait for a particular TCP event
+ * @param[in] socket Handle referencing the socket
+ * @param[in] eventMask Logic OR of all the TCP events that will complete the wait
+ * @param[in] timeout Maximum time to wait
+ * @return Logic OR of all the TCP events that satisfied the wait
+ **/
+
+uint_t tcpWaitForEvents(Socket *socket, uint_t eventMask, systime_t timeout)
+{
+   //Sanity check
+   if(socket == NULL)
+      return 0;
+
+   //Only one of the events listed here may complete the wait
+   socket->eventMask = eventMask;
+   //Update TCP related events
+   tcpUpdateEvents(socket);
+
+   //No event is signaled?
+   if(!socket->eventFlags)
+   {
+      //Reset the event object
+      osResetEvent(&socket->event);
+
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+      //Wait until an event is triggered
+      osWaitForEvent(&socket->event, timeout);
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+   }
+
+   //Return the list of TCP events that satisfied the wait
+   return socket->eventFlags;
+}
+
+
+/**
+ * @brief Copy incoming data to the send buffer
+ * @param[in] socket Handle referencing the socket
+ * @param[in] seqNum First sequence number occupied by the incoming data
+ * @param[in] data Data to write
+ * @param[in] length Number of data to write
+ **/
+
+void tcpWriteTxBuffer(Socket *socket, uint32_t seqNum,
+   const uint8_t *data, size_t length)
+{
+   //Offset of the first byte to write in the circular buffer
+   size_t offset = (seqNum - socket->iss - 1) % socket->txBufferSize;
+
+   //Check whether the specified data crosses buffer boundaries
+   if((offset + length) <= socket->txBufferSize)
+   {
+      //Copy the payload
+      netBufferWrite((NetBuffer *) &socket->txBuffer,
+         offset, data, length);
+   }
+   else
+   {
+      //Copy the first part of the payload
+      netBufferWrite((NetBuffer *) &socket->txBuffer,
+         offset, data, socket->txBufferSize - offset);
+      //Wrap around to the beginning of the circular buffer
+      netBufferWrite((NetBuffer *) &socket->txBuffer,
+         0, data + socket->txBufferSize - offset, length - socket->txBufferSize + offset);
+   }
+}
+
+
+/**
+ * @brief Copy data from the send buffer
+ * @param[in] socket Handle referencing the socket
+ * @param[in] seqNum Sequence number of the first data to read
+ * @param[out] buffer Pointer to the output buffer
+ * @param[in] length Number of data to read
+ * @return Error code
+ **/
+
+error_t tcpReadTxBuffer(Socket *socket, uint32_t seqNum,
+   NetBuffer *buffer, size_t length)
+{
+   error_t error;
+
+   //Offset of the first byte to read in the circular buffer
+   size_t offset = (seqNum - socket->iss - 1) % socket->txBufferSize;
+
+   //Check whether the specified data crosses buffer boundaries
+   if((offset + length) <= socket->txBufferSize)
+   {
+      //Copy the payload
+      error = netBufferConcat(buffer, (NetBuffer *) &socket->txBuffer,
+         offset, length);
+   }
+   else
+   {
+      //Copy the first part of the payload
+      error = netBufferConcat(buffer, (NetBuffer *) &socket->txBuffer,
+         offset, socket->txBufferSize - offset);
+
+      //Check status code
+      if(!error)
+      {
+         //Wrap around to the beginning of the circular buffer
+         error = netBufferConcat(buffer, (NetBuffer *) &socket->txBuffer,
+            0, length - socket->txBufferSize + offset);
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Copy incoming data to the receive buffer
+ * @param[in] socket Handle referencing the socket
+ * @param[in] seqNum First sequence number occupied by the incoming data
+ * @param[in] data Multi-part buffer containing the incoming data
+ * @param[in] dataOffset Offset to the first data byte
+ * @param[in] length Number of data to write
+ **/
+
+void tcpWriteRxBuffer(Socket *socket, uint32_t seqNum,
+   const NetBuffer *data, size_t dataOffset, size_t length)
+{
+   //Offset of the first byte to write in the circular buffer
+   size_t offset = (seqNum - socket->irs - 1) % socket->rxBufferSize;
+
+   //Check whether the specified data crosses buffer boundaries
+   if((offset + length) <= socket->rxBufferSize)
+   {
+      //Copy the payload
+      netBufferCopy((NetBuffer *) &socket->rxBuffer,
+         offset, data, dataOffset, length);
+   }
+   else
+   {
+      //Copy the first part of the payload
+      netBufferCopy((NetBuffer *) &socket->rxBuffer,
+         offset, data, dataOffset, socket->rxBufferSize - offset);
+      //Wrap around to the beginning of the circular buffer
+      netBufferCopy((NetBuffer *) &socket->rxBuffer, 0, data,
+         dataOffset + socket->rxBufferSize - offset, length - socket->rxBufferSize + offset);
+   }
+}
+
+
+/**
+ * @brief Copy data from the receive buffer
+ * @param[in] socket Handle referencing the socket
+ * @param[in] seqNum Sequence number of the first data to read
+ * @param[out] data Pointer to the output buffer
+ * @param[in] length Number of data to read
+ **/
+
+void tcpReadRxBuffer(Socket *socket, uint32_t seqNum, uint8_t *data, size_t length)
+{
+   //Offset of the first byte to read in the circular buffer
+   size_t offset = (seqNum - socket->irs - 1) % socket->rxBufferSize;
+
+   //Check whether the specified data crosses buffer boundaries
+   if((offset + length) <= socket->rxBufferSize)
+   {
+      //Copy the payload
+      netBufferRead(data, (NetBuffer *) &socket->rxBuffer,
+         offset, length);
+   }
+   else
+   {
+      //Copy the first part of the payload
+      netBufferRead(data, (NetBuffer *) &socket->rxBuffer,
+         offset, socket->rxBufferSize - offset);
+      //Wrap around to the beginning of the circular buffer
+      netBufferRead(data + socket->rxBufferSize - offset, (NetBuffer *) &socket->rxBuffer,
+         0, length - socket->rxBufferSize + offset);
+   }
+}
+
+
+/**
+ * @brief Dump TCP header for debugging purpose
+ * @param[in] segment Pointer to the TCP header
+ * @param[in] length Length of the segment data
+ * @param[in] iss Initial send sequence number (needed to compute relative SEQ number)
+ * @param[in] irs Initial receive sequence number (needed to compute relative ACK number)
+ **/
+
+void tcpDumpHeader(const TcpHeader *segment, size_t length, uint32_t iss, uint32_t irs)
+{
+   //Dump TCP header contents
+   TRACE_DEBUG("%" PRIu16 " > %" PRIu16 ": %c%c%c%c%c%c seq=%" PRIu32 "(%" PRIu32 ")"
+      "ack=%" PRIu32 "(%" PRIu32 ") win=%" PRIu16 " len=%" PRIuSIZE "\r\n",
+      ntohs(segment->srcPort), ntohs(segment->destPort),
+      (segment->flags & TCP_FLAG_FIN) ? 'F' : '-',
+      (segment->flags & TCP_FLAG_SYN) ? 'S' : '-',
+      (segment->flags & TCP_FLAG_RST) ? 'R' : '-',
+      (segment->flags & TCP_FLAG_PSH) ? 'P' : '-',
+      (segment->flags & TCP_FLAG_ACK) ? 'A' : '-',
+      (segment->flags & TCP_FLAG_URG) ? 'U' : '-',
+      ntohl(segment->seqNum), ntohl(segment->seqNum) - iss,
+      ntohl(segment->ackNum), ntohl(segment->ackNum) - irs,
+      ntohs(segment->window), length);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/tcp_misc.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,92 @@
+/**
+ * @file tcp_misc.h
+ * @brief Helper functions for TCP
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TCP_MISC_H
+#define _TCP_MISC_H
+
+//Dependencies
+#include "core/tcp.h"
+
+//TCP related functions
+error_t tcpSendSegment(Socket *socket, uint8_t flags, uint32_t seqNum,
+   uint32_t ackNum, size_t length, bool_t addToQueue);
+
+error_t tcpSendResetSegment(NetInterface *interface,
+   IpPseudoHeader *pseudoHeader, TcpHeader *segment, size_t length);
+
+error_t tcpAddOption(TcpHeader *segment, uint8_t kind, const void *value, uint8_t length);
+TcpOption *tcpGetOption(TcpHeader *segment, uint8_t kind);
+
+error_t tcpCheckSequenceNumber(Socket *socket, TcpHeader *segment, size_t length);
+error_t tcpCheckSyn(Socket *socket, TcpHeader *segment, size_t length);
+error_t tcpCheckAck(Socket *socket, TcpHeader *segment, size_t length);
+
+bool_t tcpIsDuplicateAck(Socket *socket, TcpHeader *segment, size_t length);
+
+void tcpFastRetransmit(Socket *socket);
+void tcpFastRecovery(Socket *socket, TcpHeader *segment, uint_t n);
+void tcpFastLossRecovery(Socket *socket, TcpHeader *segment);
+
+void tcpProcessSegmentData(Socket *socket, TcpHeader *segment,
+   const NetBuffer *buffer, size_t offset, size_t length);
+
+void tcpDeleteControlBlock(Socket *socket);
+
+void tcpUpdateRetransmitQueue(Socket *socket);
+void tcpFlushRetransmitQueue(Socket *socket);
+
+void tcpFlushSynQueue(Socket *socket);
+
+void tcpUpdateSackBlocks(Socket *socket, uint32_t *leftEdge, uint32_t *rightEdge);
+void tcpUpdateSendWindow(Socket *socket, TcpHeader *segment);
+void tcpUpdateReceiveWindow(Socket *socket);
+
+bool_t tcpComputeRto(Socket *socket);
+error_t tcpRetransmitSegment(Socket *socket);
+error_t tcpNagleAlgo(Socket *socket, uint_t flags);
+
+void tcpChangeState(Socket *socket, TcpState newState);
+
+void tcpUpdateEvents(Socket *socket);
+uint_t tcpWaitForEvents(Socket *socket, uint_t eventMask, systime_t timeout);
+
+void tcpWriteTxBuffer(Socket *socket, uint32_t seqNum,
+   const uint8_t *data, size_t length);
+
+error_t tcpReadTxBuffer(Socket *socket, uint32_t seqNum,
+   NetBuffer *buffer, size_t length);
+
+void tcpWriteRxBuffer(Socket *socket, uint32_t seqNum,
+   const NetBuffer *data, size_t dataOffset, size_t length);
+
+void tcpReadRxBuffer(Socket *socket, uint32_t seqNum, uint8_t *data, size_t length);
+
+void tcpDumpHeader(const TcpHeader *segment, size_t length, uint32_t iss, uint32_t irs);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/tcp_timer.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,346 @@
+/**
+ * @file tcp_timer.c
+ * @brief TCP timer management
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TCP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "core/socket.h"
+#include "core/tcp.h"
+#include "core/tcp_misc.h"
+#include "core/tcp_timer.h"
+#include "ipv4/ipv4.h"
+#include "ipv6/ipv6.h"
+#include "date_time.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (TCP_SUPPORT == ENABLED)
+
+
+/**
+ * @brief TCP timer handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * handle retransmissions and TCP related timers (persist timer,
+ * FIN-WAIT-2 timer and TIME-WAIT timer)
+ *
+ **/
+
+void tcpTick(void)
+{
+   error_t error;
+   uint_t i;
+   uint_t n;
+   uint_t u;
+
+   //Loop through opened sockets
+   for(i = 0; i < SOCKET_MAX_COUNT; i++)
+   {
+      //Shortcut to the current socket
+      Socket *socket = socketTable + i;
+      //Check socket type
+      if(socket->type != SOCKET_TYPE_STREAM)
+         continue;
+      //Check the current state of the TCP state machine
+      if(socket->state == TCP_STATE_CLOSED)
+         continue;
+
+      //Is there any packet in the retransmission queue?
+      if(socket->retransmitQueue != NULL)
+      {
+         //Retransmission timeout?
+         if(tcpTimerElapsed(&socket->retransmitTimer))
+         {
+#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
+            //When a TCP sender detects segment loss using the retransmission
+            //timer and the given segment has not yet been resent by way of
+            //the retransmission timer, the value of ssthresh must be updated
+            if(!socket->retransmitCount)
+            {
+               //Amount of data that has been sent but not yet acknowledged
+               uint_t flightSize = socket->sndNxt - socket->sndUna;
+               //Adjust ssthresh value
+               socket->ssthresh = MAX(flightSize / 2, 2 * socket->smss);
+            }
+
+            //Furthermore, upon a timeout cwnd must be set to no more than
+            //the loss window, LW, which equals 1 full-sized segment
+            socket->cwnd = MIN(TCP_LOSS_WINDOW * socket->smss, socket->txBufferSize);
+
+            //After a retransmit timeout, record the highest sequence number
+            //transmitted in the variable recover
+            socket->recover = socket->sndNxt - 1;
+
+            //Enter the fast loss recovery procedure
+            socket->congestState = TCP_CONGEST_STATE_LOSS_RECOVERY;
+#endif
+            //Make sure the maximum number of retransmissions has not been reached
+            if(socket->retransmitCount < TCP_MAX_RETRIES)
+            {
+               //Debug message
+               TRACE_INFO("%s: TCP segment retransmission #%u (%u data bytes)...\r\n",
+                  formatSystemTime(osGetSystemTime(), NULL), socket->retransmitCount + 1,
+                  socket->retransmitQueue->length);
+
+               //Retransmit the earliest segment that has not been
+               //acknowledged by the TCP receiver
+               tcpRetransmitSegment(socket);
+
+               //Use exponential back-off algorithm to calculate the new RTO
+               socket->rto = MIN(socket->rto * 2, TCP_MAX_RTO);
+               //Restart retransmission timer
+               tcpTimerStart(&socket->retransmitTimer, socket->rto);
+               //Increment retransmission counter
+               socket->retransmitCount++;
+            }
+            else
+            {
+               //The maximum number of retransmissions has been exceeded
+               tcpChangeState(socket, TCP_STATE_CLOSED);
+               //Turn off the retransmission timer
+               tcpTimerStop(&socket->retransmitTimer);
+            }
+
+            //TCP must use Karn's algorithm for taking RTT samples. That is, RTT
+            //samples must not be made using segments that were retransmitted
+            socket->rttBusy = FALSE;
+         }
+      }
+
+      //Check the current state of the TCP state machine
+      if(socket->state == TCP_STATE_CLOSED)
+         continue;
+
+      //The persist timer is used when the remote host advertises
+      //a window size of zero
+      if(!socket->sndWnd && socket->wndProbeInterval)
+      {
+         //Time to send a new probe?
+         if(tcpTimerElapsed(&socket->persistTimer))
+         {
+            //Make sure the maximum number of retransmissions has not been reached
+            if(socket->wndProbeCount < TCP_MAX_RETRIES)
+            {
+               //Debug message
+               TRACE_INFO("%s: TCP zero window probe #%u...\r\n",
+                  formatSystemTime(osGetSystemTime(), NULL), socket->wndProbeCount + 1);
+
+               //Zero window probes usually have the sequence number one less than expected
+               tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt - 1, socket->rcvNxt, 0, FALSE);
+               //The interval between successive probes should be increased exponentially
+               socket->wndProbeInterval = MIN(socket->wndProbeInterval * 2, TCP_MAX_PROBE_INTERVAL);
+               //Restart the persist timer
+               tcpTimerStart(&socket->persistTimer, socket->wndProbeInterval);
+               //Increment window probe counter
+               socket->wndProbeCount++;
+            }
+            else
+            {
+               //Enter CLOSED state
+               tcpChangeState(socket, TCP_STATE_CLOSED);
+            }
+         }
+      }
+
+      //To avoid a deadlock, it is necessary to have a timeout to force
+      //transmission of data, overriding the SWS avoidance algorithm. In
+      //practice, this timeout should seldom occur (see RFC 1122 4.2.3.4)
+      if(socket->state == TCP_STATE_ESTABLISHED || socket->state == TCP_STATE_CLOSE_WAIT)
+      {
+         //The override timeout occurred?
+         if(socket->sndUser && tcpTimerElapsed(&socket->overrideTimer))
+         {
+            //The amount of data that can be sent at any given time is
+            //limited by the receiver window and the congestion window
+            n = MIN(socket->sndWnd, socket->txBufferSize);
+
+#if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
+            //Check the congestion window
+            n = MIN(n, socket->cwnd);
+#endif
+            //Retrieve the size of the usable window
+            u = n - (socket->sndNxt - socket->sndUna);
+
+            //Send as much data as possible
+            while(socket->sndUser > 0)
+            {
+               //The usable window size may become zero or negative,
+               //preventing packet transmission
+               if((int_t) u <= 0)
+                  break;
+
+               //Calculate the number of bytes to send at a time
+               n = MIN(u, socket->sndUser);
+               n = MIN(n, socket->smss);
+
+               //Send TCP segment
+               error = tcpSendSegment(socket, TCP_FLAG_PSH | TCP_FLAG_ACK,
+                  socket->sndNxt, socket->rcvNxt, n, TRUE);
+               //Failed to send TCP segment?
+               if(error)
+                  break;
+
+               //Advance SND.NXT pointer
+               socket->sndNxt += n;
+               //Adjust the number of bytes buffered but not yet sent
+               socket->sndUser -= n;
+               //Update the size of the usable window
+               u -= n;
+            }
+
+            //Check whether the transmitter can accept more data
+            tcpUpdateEvents(socket);
+
+            //Restart override timer if necessary
+            if(socket->sndUser > 0)
+               tcpTimerStart(&socket->overrideTimer, TCP_OVERRIDE_TIMEOUT);
+         }
+      }
+
+      //The FIN-WAIT-2 timer prevents the connection
+      //from staying in the FIN-WAIT-2 state forever
+      if(socket->state == TCP_STATE_FIN_WAIT_2)
+      {
+         //Maximum FIN-WAIT-2 time has elapsed?
+         if(tcpTimerElapsed(&socket->finWait2Timer))
+         {
+            //Debug message
+            TRACE_WARNING("TCP FIN-WAIT-2 timer elapsed...\r\n");
+            //Enter CLOSED state
+            tcpChangeState(socket, TCP_STATE_CLOSED);
+         }
+      }
+
+      //TIME-WAIT timer
+      if(socket->state == TCP_STATE_TIME_WAIT)
+      {
+         //2MSL time has elapsed?
+         if(tcpTimerElapsed(&socket->timeWaitTimer))
+         {
+            //Debug message
+            TRACE_WARNING("TCP 2MSL timer elapsed (socket %u)...\r\n", i);
+            //Enter CLOSED state
+            tcpChangeState(socket, TCP_STATE_CLOSED);
+
+            //Dispose the socket if the user does not have the ownership anymore
+            if(!socket->ownedFlag)
+            {
+               //Delete the TCB
+               tcpDeleteControlBlock(socket);
+               //Mark the socket as closed
+               socket->type = SOCKET_TYPE_UNUSED;
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Start TCP timer
+ * @param[in] timer Pointer to the timer structure
+ * @param[in] delay Time interval
+ **/
+
+void tcpTimerStart(TcpTimer *timer, systime_t delay)
+{
+   //Start timer
+   timer->startTime = osGetSystemTime();
+   timer->interval = delay;
+
+   //The timer is now running...
+   timer->running = TRUE;
+}
+
+
+/**
+ * @brief Stop TCP timer
+ * @param[in] timer Pointer to the timer structure
+ **/
+
+void tcpTimerStop(TcpTimer *timer)
+{
+   //Stop timer
+   timer->running = FALSE;
+}
+
+
+/**
+ * @brief Check whether a TCP timer is running
+ * @param[in] timer Pointer to the timer structure
+ * @return Timer state
+ **/
+
+bool_t tcpTimerRunning(TcpTimer *timer)
+{
+   //Check whether the timer is running
+   return timer->running;
+}
+
+
+/**
+ * @brief Check whether a TCP timer has elapsed
+ * @param[in] timer Pointer to the timer structure
+ * @return Timer state
+ **/
+
+bool_t tcpTimerElapsed(TcpTimer *timer)
+{
+   systime_t time;
+
+   //Check whether the timer is running
+   if(!timer->running)
+      return FALSE;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check whether the specified time interval has elapsed
+   if(timeCompare(time, timer->startTime + timer->interval) >= 0)
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Get current time interval
+ * @param[in] timer Pointer to the timer structure
+ * @return Current time interval
+ **/
+
+systime_t tcpTimerGetInterval(TcpTimer *timer)
+{
+   //Return current time interval
+   return timer->interval;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/tcp_timer.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,44 @@
+/**
+ * @file tcp_timer.h
+ * @brief TCP timer management
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TCP_TIMER_H
+#define _TCP_TIMER_H
+
+//TCP timer related functions
+void tcpTick(void);
+
+void tcpTimerStart(TcpTimer *timer, systime_t delay);
+void tcpTimerStop(TcpTimer *timer);
+
+bool_t tcpTimerRunning(TcpTimer *timer);
+bool_t tcpTimerElapsed(TcpTimer *timer);
+
+systime_t tcpTimerGetInterval(TcpTimer *timer);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/udp.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,903 @@
+/**
+ * @file udp.c
+ * @brief UDP (User Datagram Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL UDP_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "core/net.h"
+#include "core/ip.h"
+#include "core/udp.h"
+#include "core/socket.h"
+#include "ipv4/ipv4.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_misc.h"
+#include "mibs/mib2_module.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (UDP_SUPPORT == ENABLED)
+
+//Ephemeral ports are used for dynamic port assignment
+static uint16_t udpDynamicPort;
+//Mutex to prevent simultaneous access to the callback table
+OsMutex udpCallbackMutex;
+//Table that holds the registered user callbacks
+UdpRxCallbackDesc udpCallbackTable[UDP_CALLBACK_TABLE_SIZE];
+
+
+/**
+ * @brief UDP related initialization
+ * @return Error code
+ **/
+
+error_t udpInit(void)
+{
+   //Reset ephemeral port number
+   udpDynamicPort = 0;
+
+   //Create a mutex to prevent simultaneous access to the callback table
+   if(!osCreateMutex(&udpCallbackMutex))
+   {
+      //Failed to create mutex
+      return ERROR_OUT_OF_RESOURCES;
+   }
+
+   //Initialize callback table
+   memset(udpCallbackTable, 0, sizeof(udpCallbackTable));
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Get an ephemeral port number
+ * @return Ephemeral port
+ **/
+
+uint16_t udpGetDynamicPort(void)
+{
+   uint_t port;
+
+   //Retrieve current port number
+   port = udpDynamicPort;
+
+   //Invalid port number?
+   if(port < SOCKET_EPHEMERAL_PORT_MIN || port > SOCKET_EPHEMERAL_PORT_MAX)
+   {
+      //Generate a random port number
+      port = SOCKET_EPHEMERAL_PORT_MIN + netGetRand() %
+         (SOCKET_EPHEMERAL_PORT_MAX - SOCKET_EPHEMERAL_PORT_MIN + 1);
+   }
+
+   //Next dynamic port to use
+   if(port < SOCKET_EPHEMERAL_PORT_MAX)
+   {
+      //Increment port number
+      udpDynamicPort = port + 1;
+   }
+   else
+   {
+      //Wrap around if necessary
+      udpDynamicPort = SOCKET_EPHEMERAL_PORT_MIN;
+   }
+
+   //Return an ephemeral port number
+   return port;
+}
+
+
+/**
+ * @brief Incoming UDP datagram processing
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader UDP pseudo header
+ * @param[in] buffer Multi-part buffer containing the incoming UDP datagram
+ * @param[in] offset Offset to the first byte of the UDP header
+ * @return Error code
+ **/
+
+error_t udpProcessDatagram(NetInterface *interface,
+   IpPseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset)
+{
+   error_t error;
+   uint_t i;
+   size_t length;
+   UdpHeader *header;
+   Socket *socket;
+   SocketQueueItem *queueItem;
+   NetBuffer *p;
+
+   //Retrieve the length of the UDP datagram
+   length = netBufferGetLength(buffer) - offset;
+
+   //Ensure the UDP header is valid
+   if(length < sizeof(UdpHeader))
+   {
+      //Number of received UDP datagrams that could not be delivered for
+      //reasons other than the lack of an application at the destination port
+      MIB2_INC_COUNTER32(mib2Base.udpGroup.udpInErrors, 1);
+
+      //Report an error
+      return ERROR_INVALID_HEADER;
+   }
+
+   //Point to the UDP header
+   header = netBufferAt(buffer, offset);
+   //Sanity check
+   if(header == NULL)
+      return ERROR_FAILURE;
+
+   //Debug message
+   TRACE_INFO("UDP datagram received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump UDP header contents for debugging purpose
+   udpDumpHeader(header);
+
+   //When UDP runs over IPv6, the checksum is mandatory
+   if(header->checksum != 0x0000 || pseudoHeader->length == sizeof(Ipv6PseudoHeader))
+   {
+      //Verify UDP checksum
+      if(ipCalcUpperLayerChecksumEx(pseudoHeader->data,
+         pseudoHeader->length, buffer, offset, length) != 0x0000)
+      {
+         //Debug message
+         TRACE_WARNING("Wrong UDP header checksum!\r\n");
+
+         //Number of received UDP datagrams that could not be delivered for
+         //reasons other than the lack of an application at the destination port
+         MIB2_INC_COUNTER32(mib2Base.udpGroup.udpInErrors, 1);
+
+         //Report an error
+         return ERROR_WRONG_CHECKSUM;
+      }
+   }
+
+   //Loop through opened sockets
+   for(i = 0; i < SOCKET_MAX_COUNT; i++)
+   {
+      //Point to the current socket
+      socket = socketTable + i;
+
+      //UDP socket found?
+      if(socket->type != SOCKET_TYPE_DGRAM)
+         continue;
+      //Check whether the socket is bound to a particular interface
+      if(socket->interface && socket->interface != interface)
+         continue;
+      //Check destination port number
+      if(socket->localPort != ntohs(header->destPort))
+         continue;
+      //Source port number filtering
+      if(socket->remotePort && socket->remotePort != ntohs(header->srcPort))
+         continue;
+
+#if (IPV4_SUPPORT == ENABLED)
+      //An IPv4 packet was received?
+      if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
+      {
+         //Destination IP address filtering
+         if(socket->localIpAddr.length)
+         {
+            //An IPv4 address is expected
+            if(socket->localIpAddr.length != sizeof(Ipv4Addr))
+               continue;
+            //Filter out non-matching addresses
+            if(socket->localIpAddr.ipv4Addr != pseudoHeader->ipv4Data.destAddr)
+               continue;
+         }
+
+         //Source IP address filtering
+         if(socket->remoteIpAddr.length)
+         {
+            //An IPv4 address is expected
+            if(socket->remoteIpAddr.length != sizeof(Ipv4Addr))
+               continue;
+            //Filter out non-matching addresses
+            if(socket->remoteIpAddr.ipv4Addr != pseudoHeader->ipv4Data.srcAddr)
+               continue;
+         }
+      }
+      else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+      //An IPv6 packet was received?
+      if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
+      {
+         //Destination IP address filtering
+         if(socket->localIpAddr.length)
+         {
+            //An IPv6 address is expected
+            if(socket->localIpAddr.length != sizeof(Ipv6Addr))
+               continue;
+            //Filter out non-matching addresses
+            if(!ipv6CompAddr(&socket->localIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.destAddr))
+               continue;
+         }
+
+         //Source IP address filtering
+         if(socket->remoteIpAddr.length)
+         {
+            //An IPv6 address is expected
+            if(socket->remoteIpAddr.length != sizeof(Ipv6Addr))
+               continue;
+            //Filter out non-matching addresses
+            if(!ipv6CompAddr(&socket->remoteIpAddr.ipv6Addr, &pseudoHeader->ipv6Data.srcAddr))
+               continue;
+         }
+      }
+      else
+#endif
+      //An invalid packet was received?
+      {
+         //This should never occur...
+         continue;
+      }
+
+      //The current socket meets all the criteria
+      break;
+   }
+
+   //Point to the payload
+   offset += sizeof(UdpHeader);
+   length -= sizeof(UdpHeader);
+
+   //No matching socket found?
+   if(i >= SOCKET_MAX_COUNT)
+   {
+      //Invoke user callback, if any
+      error = udpInvokeRxCallback(interface, pseudoHeader, header, buffer, offset);
+      //Return status code
+      return error;
+   }
+
+   //Empty receive queue?
+   if(!socket->receiveQueue)
+   {
+      //Allocate a memory buffer to hold the data and the associated descriptor
+      p = netBufferAlloc(sizeof(SocketQueueItem) + length);
+
+      //Successful memory allocation?
+      if(p != NULL)
+      {
+         //Point to the newly created item
+         queueItem = netBufferAt(p, 0);
+         queueItem->buffer = p;
+         //Add the newly created item to the queue
+         socket->receiveQueue = queueItem;
+      }
+      else
+      {
+         //Memory allocation failed
+         queueItem = NULL;
+      }
+   }
+   else
+   {
+      //Point to the very first item
+      queueItem = socket->receiveQueue;
+      //Reach the last item in the receive queue
+      for(i = 1; queueItem->next; i++)
+         queueItem = queueItem->next;
+
+      //Make sure the receive queue is not full
+      if(i >= UDP_RX_QUEUE_SIZE)
+         return ERROR_RECEIVE_QUEUE_FULL;
+
+      //Allocate a memory buffer to hold the data and the associated descriptor
+      p = netBufferAlloc(sizeof(SocketQueueItem) + length);
+
+      //Successful memory allocation?
+      if(p != NULL)
+      {
+         //Add the newly created item to the queue
+         queueItem->next = netBufferAt(p, 0);
+         //Point to the newly created item
+         queueItem = queueItem->next;
+         queueItem->buffer = p;
+      }
+      else
+      {
+         //Memory allocation failed
+         queueItem = NULL;
+      }
+   }
+
+   //Failed to allocate memory?
+   if(queueItem == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Initialize next field
+   queueItem->next = NULL;
+   //Record the source port number
+   queueItem->srcPort = ntohs(header->srcPort);
+
+#if (IPV4_SUPPORT == ENABLED)
+   //IPv4 remote address?
+   if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
+   {
+      //Save the source IPv4 address
+      queueItem->srcIpAddr.length = sizeof(Ipv4Addr);
+      queueItem->srcIpAddr.ipv4Addr = pseudoHeader->ipv4Data.srcAddr;
+      //Save the destination IPv4 address
+      queueItem->destIpAddr.length = sizeof(Ipv4Addr);
+      queueItem->destIpAddr.ipv4Addr = pseudoHeader->ipv4Data.destAddr;
+   }
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 remote address?
+   if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
+   {
+      //Save the source IPv6 address
+      queueItem->srcIpAddr.length = sizeof(Ipv6Addr);
+      queueItem->srcIpAddr.ipv6Addr = pseudoHeader->ipv6Data.srcAddr;
+      //Save the destination IPv6 address
+      queueItem->destIpAddr.length = sizeof(Ipv6Addr);
+      queueItem->destIpAddr.ipv6Addr = pseudoHeader->ipv6Data.destAddr;
+   }
+#endif
+
+   //Offset to the payload
+   queueItem->offset = sizeof(SocketQueueItem);
+   //Copy the payload
+   netBufferCopy(queueItem->buffer, queueItem->offset, buffer, offset, length);
+
+   //Notify user that data is available
+   udpUpdateEvents(socket);
+
+   //Total number of UDP datagrams delivered to UDP users
+   MIB2_INC_COUNTER32(mib2Base.udpGroup.udpInDatagrams, 1);
+   MIB2_INC_COUNTER64(mib2Base.udpGroup.udpHCInDatagrams, 1);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Send a UDP datagram
+ * @param[in] socket Handle referencing the socket
+ * @param[in] destIpAddr IP address of the target host
+ * @param[in] destPort Target port number
+ * @param[in] data Pointer to data payload
+ * @param[in] length Length of the payload data
+ * @param[out] written Actual number of bytes written (optional parameter)
+ * @return Error code
+ **/
+
+error_t udpSendDatagram(Socket *socket, const IpAddr *destIpAddr,
+   uint16_t destPort, const void *data, size_t length, size_t *written)
+{
+   error_t error;
+   size_t offset;
+   NetBuffer *buffer;
+
+   //Allocate a memory buffer to hold the UDP datagram
+   buffer = udpAllocBuffer(0, &offset);
+   //Failed to allocate buffer?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Copy data payload
+   error = netBufferAppend(buffer, data, length);
+
+   //Successful processing?
+   if(!error)
+   {
+      //Send UDP datagram
+      error = udpSendDatagramEx(socket->interface, socket->localPort,
+         destIpAddr, destPort, buffer, offset, socket->ttl);
+   }
+
+   //Successful processing?
+   if(!error)
+   {
+      //Total number of data bytes successfully transmitted
+      if(written != NULL)
+         *written = length;
+   }
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send a UDP datagram (raw interface)
+ * @param[in] interface Underlying network interface
+ * @param[in] srcPort Source port
+ * @param[in] destIpAddr IP address of the target host
+ * @param[in] destPort Target port number
+ * @param[in] buffer Multi-part buffer containing the payload
+ * @param[in] offset Offset to the first payload byte
+ * @param[in] ttl TTL value. Default Time-To-Live is used when this parameter is zero
+ * @return Error code
+ **/
+
+error_t udpSendDatagramEx(NetInterface *interface, uint16_t srcPort, const IpAddr *destIpAddr,
+   uint16_t destPort, NetBuffer *buffer, size_t offset, uint8_t ttl)
+{
+   error_t error;
+   size_t length;
+   UdpHeader *header;
+   IpPseudoHeader pseudoHeader;
+
+   //Make room for the UDP header
+   offset -= sizeof(UdpHeader);
+   //Retrieve the length of the datagram
+   length = netBufferGetLength(buffer) - offset;
+
+   //Point to the UDP header
+   header = netBufferAt(buffer, offset);
+   //Sanity check
+   if(header == NULL)
+      return ERROR_FAILURE;
+
+   //Format UDP header
+   header->srcPort = htons(srcPort);
+   header->destPort = htons(destPort);
+   header->length = htons(length);
+   header->checksum = 0;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //Destination address is an IPv4 address?
+   if(destIpAddr->length == sizeof(Ipv4Addr))
+   {
+      Ipv4Addr srcIpAddr;
+
+      //Select the source IPv4 address and the relevant network interface
+      //to use when sending data to the specified destination host
+      error = ipv4SelectSourceAddr(&interface, destIpAddr->ipv4Addr, &srcIpAddr);
+
+      //Check status code
+      if(error)
+      {
+         //Handle the special case where the destination address is the
+         //broadcast address
+         if(destIpAddr->ipv4Addr == IPV4_BROADCAST_ADDR)
+         {
+            //Use the unspecified address as source address
+            srcIpAddr = IPV4_UNSPECIFIED_ADDR;
+         }
+         else
+         {
+            //Source address selection failed
+            return error;
+         }
+      }
+
+      //Format IPv4 pseudo header
+      pseudoHeader.length = sizeof(Ipv4PseudoHeader);
+      pseudoHeader.ipv4Data.srcAddr = srcIpAddr;
+      pseudoHeader.ipv4Data.destAddr = destIpAddr->ipv4Addr;
+      pseudoHeader.ipv4Data.reserved = 0;
+      pseudoHeader.ipv4Data.protocol = IPV4_PROTOCOL_UDP;
+      pseudoHeader.ipv4Data.length = htons(length);
+
+      //Calculate UDP header checksum
+      header->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader.ipv4Data,
+         sizeof(Ipv4PseudoHeader), buffer, offset, length);
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //Destination address is an IPv6 address?
+   if(destIpAddr->length == sizeof(Ipv6Addr))
+   {
+      //Select the source IPv6 address and the relevant network interface
+      //to use when sending data to the specified destination host
+      error = ipv6SelectSourceAddr(&interface,
+         &destIpAddr->ipv6Addr, &pseudoHeader.ipv6Data.srcAddr);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Format IPv6 pseudo header
+      pseudoHeader.length = sizeof(Ipv6PseudoHeader);
+      pseudoHeader.ipv6Data.destAddr = destIpAddr->ipv6Addr;
+      pseudoHeader.ipv6Data.length = htonl(length);
+      pseudoHeader.ipv6Data.reserved = 0;
+      pseudoHeader.ipv6Data.nextHeader = IPV6_UDP_HEADER;
+
+      //Calculate UDP header checksum
+      header->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader.ipv6Data,
+         sizeof(Ipv6PseudoHeader), buffer, offset, length);
+   }
+   else
+#endif
+   //Invalid destination address?
+   {
+      //An internal error has occurred
+      return ERROR_FAILURE;
+   }
+
+   //If the computed checksum is zero, it is transmitted as all ones. An all
+   //zero transmitted checksum value means that the transmitter generated no
+   //checksum
+   if(header->checksum == 0x0000)
+      header->checksum = 0xFFFF;
+
+   //Total number of UDP datagrams sent from this entity
+   MIB2_INC_COUNTER32(mib2Base.udpGroup.udpOutDatagrams, 1);
+   MIB2_INC_COUNTER64(mib2Base.udpGroup.udpHCOutDatagrams, 1);
+
+   //Debug message
+   TRACE_INFO("Sending UDP datagram (%" PRIuSIZE " bytes)\r\n", length);
+   //Dump UDP header contents for debugging purpose
+   udpDumpHeader(header);
+
+   //Send UDP datagram
+   error = ipSendDatagram(interface, &pseudoHeader, buffer, offset, ttl);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Receive data from a UDP socket
+ * @param[in] socket Handle referencing the socket
+ * @param[out] srcIpAddr Source IP address (optional)
+ * @param[out] srcPort Source port number (optional)
+ * @param[out] destIpAddr Destination IP address (optional)
+ * @param[out] data Buffer where to store the incoming data
+ * @param[in] size Maximum number of bytes that can be received
+ * @param[out] received Number of bytes that have been received
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t udpReceiveDatagram(Socket *socket, IpAddr *srcIpAddr, uint16_t *srcPort,
+   IpAddr *destIpAddr, void *data, size_t size, size_t *received, uint_t flags)
+{
+   SocketQueueItem *queueItem;
+
+   //The SOCKET_FLAG_DONT_WAIT enables non-blocking operation
+   if(!(flags & SOCKET_FLAG_DONT_WAIT))
+   {
+      //The receive queue is empty?
+      if(!socket->receiveQueue)
+      {
+         //Set the events the application is interested in
+         socket->eventMask = SOCKET_EVENT_RX_READY;
+         //Reset the event object
+         osResetEvent(&socket->event);
+
+         //Release exclusive access
+         osReleaseMutex(&netMutex);
+         //Wait until an event is triggered
+         osWaitForEvent(&socket->event, socket->timeout);
+         //Get exclusive access
+         osAcquireMutex(&netMutex);
+      }
+   }
+
+   //Check whether the read operation timed out
+   if(!socket->receiveQueue)
+   {
+      //No data can be read
+      *received = 0;
+      //Report a timeout error
+      return ERROR_TIMEOUT;
+   }
+
+   //Point to the first item in the receive queue
+   queueItem = socket->receiveQueue;
+   //Copy data to user buffer
+   *received = netBufferRead(data, queueItem->buffer, queueItem->offset, size);
+
+   //Save the source IP address
+   if(srcIpAddr)
+      *srcIpAddr = queueItem->srcIpAddr;
+   //Save the source port number
+   if(srcPort)
+      *srcPort = queueItem->srcPort;
+   //Save the destination IP address
+   if(destIpAddr)
+      *destIpAddr = queueItem->destIpAddr;
+
+   //If the SOCKET_FLAG_PEEK flag is set, the data is copied
+   //into the buffer but is not removed from the input queue
+   if(!(flags & SOCKET_FLAG_PEEK))
+   {
+      //Remove the item from the receive queue
+      socket->receiveQueue = queueItem->next;
+      //Deallocate memory buffer
+      netBufferFree(queueItem->buffer);
+   }
+
+   //Update the state of events
+   udpUpdateEvents(socket);
+
+   //Successful read operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Allocate a buffer to hold a UDP packet
+ * @param[in] length Desired payload length
+ * @param[out] offset Offset to the first byte of the payload
+ * @return The function returns a pointer to the newly allocated
+ *   buffer. If the system is out of resources, NULL is returned
+ **/
+
+NetBuffer *udpAllocBuffer(size_t length, size_t *offset)
+{
+   NetBuffer *buffer;
+
+   //Allocate a buffer to hold the UDP header and the payload
+   buffer = ipAllocBuffer(length + sizeof(UdpHeader), offset);
+   //Failed to allocate buffer?
+   if(buffer == NULL)
+      return NULL;
+
+   //Offset to the first byte of the payload
+   *offset += sizeof(UdpHeader);
+
+   //Return a pointer to the freshly allocated buffer
+   return buffer;
+}
+
+
+/**
+ * @brief Update UDP related events
+ * @param[in] socket Handle referencing the socket
+ **/
+
+void udpUpdateEvents(Socket *socket)
+{
+   //Clear event flags
+   socket->eventFlags = 0;
+
+   //The socket is marked as readable if a datagram is pending in the queue
+   if(socket->receiveQueue)
+      socket->eventFlags |= SOCKET_EVENT_RX_READY;
+
+   //Check whether the socket is bound to a particular network interface
+   if(socket->interface != NULL)
+   {
+      //Handle link up and link down events
+      if(socket->interface->linkState)
+         socket->eventFlags |= SOCKET_EVENT_LINK_UP;
+      else
+         socket->eventFlags |= SOCKET_EVENT_LINK_DOWN;
+   }
+
+   //Mask unused events
+   socket->eventFlags &= socket->eventMask;
+
+   //Any event to signal?
+   if(socket->eventFlags)
+   {
+      //Unblock I/O operations currently in waiting state
+      osSetEvent(&socket->event);
+
+      //Set user event to signaled state if necessary
+      if(socket->userEvent != NULL)
+         osSetEvent(socket->userEvent);
+   }
+}
+
+
+/**
+ * @brief Register user callback
+ * @param[in] interface Underlying network interface
+ * @param[in] port UDP port number
+ * @param[in] callback Callback function to be called when a datagram is received
+ * @param[in] params Callback function parameter (optional)
+ * @return Error code
+ **/
+
+error_t udpAttachRxCallback(NetInterface *interface,
+   uint16_t port, UdpRxCallback callback, void *params)
+{
+   uint_t i;
+   UdpRxCallbackDesc *entry;
+
+   //Acquire exclusive access to the callback table
+   osAcquireMutex(&udpCallbackMutex);
+
+   //Loop through the table
+   for(i = 0; i < UDP_CALLBACK_TABLE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &udpCallbackTable[i];
+
+      //Check whether the entry is currently in used
+      if(entry->callback == NULL)
+      {
+         //Create a new entry
+         entry->interface = interface;
+         entry->port = port;
+         entry->callback = callback;
+         entry->params = params;
+         //We are done
+         break;
+      }
+   }
+
+   //Release exclusive access to the callback table
+   osReleaseMutex(&udpCallbackMutex);
+
+   //Failed to attach the specified user callback?
+   if(i >= UDP_CALLBACK_TABLE_SIZE)
+      return ERROR_OUT_OF_RESOURCES;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Unregister user callback
+ * @param[in] interface Underlying network interface
+ * @param[in] port UDP port number
+ * @return Error code
+ **/
+
+error_t udpDetachRxCallback(NetInterface *interface, uint16_t port)
+{
+   error_t error;
+   uint_t i;
+   UdpRxCallbackDesc *entry;
+
+   //Initialize status code
+   error = ERROR_FAILURE;
+
+   //Acquire exclusive access to the callback table
+   osAcquireMutex(&udpCallbackMutex);
+
+   //Loop through the table
+   for(i = 0; i < UDP_CALLBACK_TABLE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &udpCallbackTable[i];
+
+      //Check whether the entry is currently in used
+      if(entry->callback != NULL)
+      {
+         //Does the specified port number match the current entry?
+         if(entry->port == port && entry->interface == interface)
+         {
+            //Unregister user callback
+            entry->callback = NULL;
+            //A matching entry has been found
+            error = NO_ERROR;
+         }
+      }
+   }
+
+   //Release exclusive access to the callback table
+   osReleaseMutex(&udpCallbackMutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Invoke user callback
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader UDP pseudo header
+ * @param[in] header UDP header
+ * @param[in] buffer Multi-part buffer containing the payload
+ * @param[in] offset Offset to the first byte of the payload
+ * @return Error code
+ **/
+
+error_t udpInvokeRxCallback(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
+   const UdpHeader *header, const NetBuffer *buffer, size_t offset)
+{
+   error_t error;
+   uint_t i;
+   void *params;
+   UdpRxCallbackDesc *entry;
+
+   //Initialize status code
+   error = ERROR_PORT_UNREACHABLE;
+
+   //Acquire exclusive access to the callback table
+   osAcquireMutex(&udpCallbackMutex);
+
+   //Loop through the table
+   for(i = 0; i < UDP_CALLBACK_TABLE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &udpCallbackTable[i];
+
+      //Check whether the entry is currently in used
+      if(entry->callback != NULL)
+      {
+         //Bound to a particular interface?
+         if(entry->interface == NULL || entry->interface == interface)
+         {
+            //Does the specified port number match the current entry?
+            if(entry->port == ntohs(header->destPort))
+            {
+               //Retrieve callback parameters
+               params = entry->params;
+
+               //Release mutex to prevent any deadlock
+               if(params == NULL)
+                  osReleaseMutex(&udpCallbackMutex);
+
+               //Invoke user callback function
+               entry->callback(interface, pseudoHeader,
+                  header, buffer, offset, params);
+
+               //Acquire mutex
+               if(params == NULL)
+                  osAcquireMutex(&udpCallbackMutex);
+
+               //A matching entry has been found
+               error = NO_ERROR;
+            }
+         }
+      }
+   }
+
+   //Release exclusive access to the callback table
+   osReleaseMutex(&udpCallbackMutex);
+
+   //Check status code
+   if(error)
+   {
+      //Total number of received UDP datagrams for which there was
+      //no application at the destination port
+      MIB2_INC_COUNTER32(mib2Base.udpGroup.udpNoPorts, 1);
+   }
+   else
+   {
+      //Total number of UDP datagrams delivered to UDP users
+      MIB2_INC_COUNTER32(mib2Base.udpGroup.udpInDatagrams, 1);
+      MIB2_INC_COUNTER64(mib2Base.udpGroup.udpHCInDatagrams, 1);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Dump UDP header for debugging purpose
+ * @param[in] datagram Pointer to the UDP header
+ **/
+
+void udpDumpHeader(const UdpHeader *datagram)
+{
+   //Dump UDP header contents
+   TRACE_DEBUG("  Source Port = %" PRIu16 "\r\n", ntohs(datagram->srcPort));
+   TRACE_DEBUG("  Destination Port = %" PRIu16 "\r\n", ntohs(datagram->destPort));
+   TRACE_DEBUG("  Length = %" PRIu16 "\r\n", ntohs(datagram->length));
+   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(datagram->checksum));
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/core/udp.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,141 @@
+/**
+ * @file udp.h
+ * @brief UDP (User Datagram Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _UDP_H
+#define _UDP_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/tcp.h"
+
+//UDP support
+#ifndef UDP_SUPPORT
+   #define UDP_SUPPORT ENABLED
+#elif (UDP_SUPPORT != ENABLED && UDP_SUPPORT != DISABLED)
+   #error UDP_SUPPORT parameter is not valid
+#endif
+
+//Maximum number of callback functions that can be registered
+//to process incoming UDP datagrams
+#ifndef UDP_CALLBACK_TABLE_SIZE
+   #define UDP_CALLBACK_TABLE_SIZE 10
+#elif (UDP_CALLBACK_TABLE_SIZE < 1)
+   #error UDP_CALLBACK_TABLE_SIZE parameter is not valid
+#endif
+
+//Receive queue depth for connectionless sockets
+#ifndef UDP_RX_QUEUE_SIZE
+   #define UDP_RX_QUEUE_SIZE 4
+#elif (UDP_RX_QUEUE_SIZE < 1)
+   #error UDP_RX_QUEUE_SIZE parameter is not valid
+#endif
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief UDP header
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t srcPort;  //0-1
+   uint16_t destPort; //2-3
+   uint16_t length;   //4-5
+   uint16_t checksum; //6-7
+   uint8_t data[];    //8
+} __end_packed UdpHeader;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+/**
+ * @brief Data received callback
+ **/
+
+typedef void (*UdpRxCallback)(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
+   const UdpHeader *header, const NetBuffer *buffer, size_t offset, void *params);
+
+
+/**
+ * @brief Entry describing a user callback
+ **/
+
+typedef struct
+{
+   NetInterface *interface;
+   uint16_t port;
+   UdpRxCallback callback;
+   void *params;
+} UdpRxCallbackDesc;
+
+
+//Global variables
+extern OsMutex udpCallbackMutex;
+extern UdpRxCallbackDesc udpCallbackTable[UDP_CALLBACK_TABLE_SIZE];
+
+//UDP related functions
+error_t udpInit(void);
+uint16_t udpGetDynamicPort(void);
+
+error_t udpProcessDatagram(NetInterface *interface,
+   IpPseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset);
+
+error_t udpSendDatagram(Socket *socket, const IpAddr *destIpAddr,
+   uint16_t destPort, const void *data, size_t length, size_t *written);
+
+error_t udpSendDatagramEx(NetInterface *interface, uint16_t srcPort, const IpAddr *destIpAddr,
+   uint16_t destPort, NetBuffer *buffer, size_t offset, uint8_t ttl);
+
+error_t udpReceiveDatagram(Socket *socket, IpAddr *srcIpAddr, uint16_t *srcPort,
+   IpAddr *destIpAddr, void *data, size_t size, size_t *received, uint_t flags);
+
+NetBuffer *udpAllocBuffer(size_t length, size_t *offset);
+
+void udpUpdateEvents(Socket *socket);
+
+error_t udpAttachRxCallback(NetInterface *interface,
+   uint16_t port, UdpRxCallback callback, void *params);
+
+error_t udpDetachRxCallback(NetInterface *interface, uint16_t port);
+
+error_t udpInvokeRxCallback(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
+   const UdpHeader *header, const NetBuffer *buffer, size_t offset);
+
+void udpDumpHeader(const UdpHeader *datagram);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dhcp/dhcp_client.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1850 @@
+/**
+ * @file dhcp_client.c
+ * @brief DHCP client (Dynamic Host Configuration Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The Dynamic Host Configuration Protocol is used to provide configuration
+ * parameters to hosts. Refer to the following RFCs for complete details:
+ * - RFC 2131: Dynamic Host Configuration Protocol
+ * - RFC 2132: DHCP Options and BOOTP Vendor Extensions
+ * - RFC 4039: Rapid Commit Option for the DHCP version 4
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL DHCP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "dhcp/dhcp_client.h"
+#include "dhcp/dhcp_common.h"
+#include "dhcp/dhcp_debug.h"
+#include "mdns/mdns_responder.h"
+#include "date_time.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV4_SUPPORT == ENABLED && DHCP_CLIENT_SUPPORT == ENABLED)
+
+//Tick counter to handle periodic operations
+systime_t dhcpClientTickCounter;
+
+//Requested DHCP options
+const uint8_t dhcpOptionList[] =
+{
+   DHCP_OPT_SUBNET_MASK,
+   DHCP_OPT_ROUTER,
+   DHCP_OPT_DNS_SERVER,
+   DHCP_OPT_INTERFACE_MTU,
+   DHCP_OPT_IP_ADDRESS_LEASE_TIME,
+   DHCP_OPT_RENEWAL_TIME_VALUE,
+   DHCP_OPT_REBINDING_TIME_VALUE
+};
+
+
+/**
+ * @brief Initialize settings with default values
+ * @param[out] settings Structure that contains DHCP client settings
+ **/
+
+void dhcpClientGetDefaultSettings(DhcpClientSettings *settings)
+{
+   //Use default interface
+   settings->interface = netGetDefaultInterface();
+
+   //Use default host name
+   strcpy(settings->hostname, "");
+
+   //Support for quick configuration using rapid commit
+   settings->rapidCommit = FALSE;
+   //Use the DNS servers provided by the DHCP server
+   settings->manualDnsConfig = FALSE;
+   //DHCP configuration timeout
+   settings->timeout = 0;
+   //DHCP configuration timeout event
+   settings->timeoutEvent = NULL;
+   //Link state change event
+   settings->linkChangeEvent = NULL;
+   //FSM state change event
+   settings->stateChangeEvent = NULL;
+}
+
+
+/**
+ * @brief DHCP client initialization
+ * @param[in] context Pointer to the DHCP client context
+ * @param[in] settings DHCP client specific settings
+ * @return Error code
+ **/
+
+error_t dhcpClientInit(DhcpClientContext *context, const DhcpClientSettings *settings)
+{
+   error_t error;
+   size_t n;
+   NetInterface *interface;
+
+   //Debug message
+   TRACE_INFO("Initializing DHCP client...\r\n");
+
+   //Ensure the parameters are valid
+   if(context == NULL || settings == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //A valid pointer to the interface being configured is required
+   if(settings->interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Point to the underlying network interface
+   interface = settings->interface;
+
+   //Clear the DHCP client context
+   memset(context, 0, sizeof(DhcpClientContext));
+   //Save user settings
+   context->settings = *settings;
+
+   //No DHCP host name defined?
+   if(settings->hostname[0] == '\0')
+   {
+      //Use default host name
+      n = strlen(interface->hostname);
+      //Limit the length of the string
+      n = MIN(n, DHCP_CLIENT_MAX_HOSTNAME_LEN);
+
+      //Copy host name
+      strncpy(context->settings.hostname, interface->hostname, n);
+      //Properly terminate the string with a NULL character
+      context->settings.hostname[n] = '\0';
+   }
+
+   //Callback function to be called when a DHCP message is received
+   error = udpAttachRxCallback(interface, DHCP_CLIENT_PORT,
+      dhcpClientProcessMessage, context);
+   //Failed to register callback function?
+   if(error)
+      return error;
+
+   //DHCP client is currently suspended
+   context->running = FALSE;
+   //Initialize state machine
+   context->state = DHCP_STATE_INIT;
+
+   //Attach the DHCP client context to the network interface
+   interface->dhcpClientContext = context;
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Start DHCP client
+ * @param[in] context Pointer to the DHCP client context
+ * @return Error code
+ **/
+
+error_t dhcpClientStart(DhcpClientContext *context)
+{
+   //Check parameter
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Starting DHCP client...\r\n");
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Start DHCP client
+   context->running = TRUE;
+   //Initialize state machine
+   context->state = DHCP_STATE_INIT;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Stop DHCP client
+ * @param[in] context Pointer to the DHCP client context
+ * @return Error code
+ **/
+
+error_t dhcpClientStop(DhcpClientContext *context)
+{
+   //Check parameter
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Stopping DHCP client...\r\n");
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Stop DHCP client
+   context->running = FALSE;
+   //Reinitialize state machine
+   context->state = DHCP_STATE_INIT;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Retrieve current state
+ * @param[in] context Pointer to the DHCP client context
+ * @return Current DHCP client state
+ **/
+
+DhcpState dhcpClientGetState(DhcpClientContext *context)
+{
+   DhcpState state;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Get current state
+   state = context->state;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return current state
+   return state;
+}
+
+
+/**
+ * @brief DHCP client timer handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * manage DHCP client operation
+ *
+ * @param[in] context Pointer to the DHCP client context
+ **/
+
+
+void dhcpClientTick(DhcpClientContext *context)
+{
+   //Make sure the DHCP client has been properly instantiated
+   if(context == NULL)
+      return;
+
+   //DHCP client finite state machine
+   switch(context->state)
+   {
+   //Process INIT state
+   case DHCP_STATE_INIT:
+      //This is the initialization state, where a client begins the process of
+      //acquiring a lease. It also returns here when a lease ends, or when a
+      //lease negotiation fails
+      dhcpClientStateInit(context);
+      break;
+   //Process SELECTING state
+   case DHCP_STATE_SELECTING:
+      //The client is waiting to receive DHCPOFFER messages from one or more
+      //DHCP servers, so it can choose one
+      dhcpClientStateSelecting(context);
+      break;
+   //Process REQUESTING state
+   case DHCP_STATE_REQUESTING:
+      //The client is waiting to hear back from the server to which
+      //it sent its request
+      dhcpClientStateRequesting(context);
+      break;
+   //Process INIT REBOOT state
+   case DHCP_STATE_INIT_REBOOT:
+      //When a client that already has a valid lease starts up after a
+      //power-down or reboot, it starts here instead of the INIT state
+      dhcpClientStateInitReboot(context);
+      break;
+   //Process REBOOTING state
+   case DHCP_STATE_REBOOTING:
+      //A client that has rebooted with an assigned address is waiting for
+      //a confirming reply from a server
+      dhcpClientStateRebooting(context);
+      break;
+   //Process PROBING state
+   case DHCP_STATE_PROBING:
+      //The client probes the newly received address
+      dhcpClientStateProbing(context);
+      break;
+   //Process BOUND state
+   case DHCP_STATE_BOUND:
+      //Client has a valid lease and is in its normal operating state
+      dhcpClientStateBound(context);
+      break;
+   //Process RENEWING state
+   case DHCP_STATE_RENEWING:
+      //Client is trying to renew its lease. It regularly sends DHCPREQUEST messages with
+      //the server that gave it its current lease specified, and waits for a reply
+      dhcpClientStateRenewing(context);
+      break;
+   //Process REBINDING state
+   case DHCP_STATE_REBINDING:
+      //The client has failed to renew its lease with the server that originally granted it,
+      //and now seeks a lease extension with any server that can hear it. It periodically sends
+      //DHCPREQUEST messages with no server specified until it gets a reply or the lease ends
+      dhcpClientStateRebinding(context);
+      break;
+   //Invalid state...
+   default:
+      //Switch to the INIT state
+      context->state = DHCP_STATE_INIT;
+      break;
+   }
+}
+
+
+/**
+ * @brief Callback function for link change event
+ * @param[in] context Pointer to the DHCP client context
+ **/
+
+void dhcpClientLinkChangeEvent(DhcpClientContext *context)
+{
+   NetInterface *interface;
+
+   //Make sure the DHCP client has been properly instantiated
+   if(context == NULL)
+      return;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Check whether the DHCP client is running
+   if(context->running)
+   {
+      //The host address is no longer valid
+      interface->ipv4Context.addr = IPV4_UNSPECIFIED_ADDR;
+      interface->ipv4Context.addrState = IPV4_ADDR_STATE_INVALID;
+
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+      //Restart mDNS probing process
+      mdnsResponderStartProbing(interface->mdnsResponderContext);
+#endif
+
+      //Clear subnet mask
+      interface->ipv4Context.subnetMask = IPV4_UNSPECIFIED_ADDR;
+   }
+
+   //Check whether the client already has a valid lease
+   if(context->state >= DHCP_STATE_INIT_REBOOT)
+   {
+      //Switch to the INIT-REBOOT state
+      context->state = DHCP_STATE_INIT_REBOOT;
+   }
+   else
+   {
+      //Switch to the INIT state
+      context->state = DHCP_STATE_INIT;
+   }
+
+   //Any registered callback?
+   if(context->settings.linkChangeEvent != NULL)
+   {
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+      //Invoke user callback function
+      context->settings.linkChangeEvent(context, interface, interface->linkState);
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+   }
+}
+
+
+/**
+ * @brief INIT state
+ *
+ * This is the initialization state, where a client begins the process of
+ * acquiring a lease. It also returns here when a lease ends, or when a
+ * lease negotiation fails
+ *
+ * @param[in] context Pointer to the DHCP client context
+ **/
+
+void dhcpClientStateInit(DhcpClientContext *context)
+{
+   systime_t delay;
+   NetInterface *interface;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Check whether the DHCP client is running
+   if(context->running)
+   {
+      //Wait for the link to be up before starting DHCP configuration
+      if(interface->linkState)
+      {
+         //The client should wait for a random time to
+         //desynchronize the use of DHCP at startup
+         delay = netGetRandRange(0, DHCP_CLIENT_INIT_DELAY);
+
+         //Record the time at which the client started
+         //the address acquisition process
+         context->configStartTime = osGetSystemTime();
+         //Clear flag
+         context->timeoutEventDone = FALSE;
+
+         //Switch to the SELECTING state
+         dhcpClientChangeState(context, DHCP_STATE_SELECTING, delay);
+      }
+   }
+}
+
+
+/**
+ * @brief SELECTING state
+ *
+ * The client is waiting to receive DHCPOFFER messages from
+ * one or more DHCP servers, so it can choose one
+ *
+ * @param[in] context Pointer to the DHCP client context
+ **/
+
+void dhcpClientStateSelecting(DhcpClientContext *context)
+{
+   systime_t time;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check current time
+   if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+   {
+      //Check retransmission counter
+      if(context->retransmitCount == 0)
+      {
+         //A transaction identifier is used by the client to
+         //match incoming DHCP messages with pending requests
+         context->transactionId = netGetRand();
+
+         //Send a DHCPDISCOVER message
+         dhcpClientSendDiscover(context);
+
+         //Initial timeout value
+         context->retransmitTimeout = DHCP_CLIENT_DISCOVER_INIT_RT;
+      }
+      else
+      {
+         //Send a DHCPDISCOVER message
+         dhcpClientSendDiscover(context);
+
+         //The timeout value is doubled for each subsequent retransmission
+         context->retransmitTimeout *= 2;
+
+         //Limit the timeout value to a maximum of 64 seconds
+         if(context->retransmitTimeout > DHCP_CLIENT_DISCOVER_MAX_RT)
+            context->retransmitTimeout = DHCP_CLIENT_DISCOVER_MAX_RT;
+      }
+
+      //Save the time at which the message was sent
+      context->timestamp = time;
+
+      //The timeout value should be randomized by the value of a uniform
+      //number chosen from the range -1 to +1
+      context->timeout = context->retransmitTimeout +
+         netGetRandRange(-DHCP_CLIENT_RAND_FACTOR, DHCP_CLIENT_RAND_FACTOR);
+
+      //Increment retransmission counter
+      context->retransmitCount++;
+   }
+
+   //Manage DHCP configuration timeout
+   dhcpClientCheckTimeout(context);
+}
+
+
+/**
+ * @brief REQUESTING state
+ *
+ * The client is waiting to hear back from the server
+ * to which it sent its request
+ *
+ * @param[in] context Pointer to the DHCP client context
+ **/
+
+void dhcpClientStateRequesting(DhcpClientContext *context)
+{
+   systime_t time;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check current time
+   if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+   {
+      //Check retransmission counter
+      if(context->retransmitCount == 0)
+      {
+         //A transaction identifier is used by the client to
+         //match incoming DHCP messages with pending requests
+         context->transactionId = netGetRand();
+
+         //Send a DHCPREQUEST message
+         dhcpClientSendRequest(context);
+
+         //Initial timeout value
+         context->retransmitTimeout = DHCP_CLIENT_REQUEST_INIT_RT;
+
+         //Save the time at which the message was sent
+         context->timestamp = time;
+
+         //The timeout value should be randomized by the value of a uniform
+         //number chosen from the range -1 to +1
+         context->timeout = context->retransmitTimeout +
+            netGetRandRange(-DHCP_CLIENT_RAND_FACTOR, DHCP_CLIENT_RAND_FACTOR);
+
+         //Increment retransmission counter
+         context->retransmitCount++;
+      }
+      else if(context->retransmitCount < DHCP_CLIENT_REQUEST_MAX_RC)
+      {
+         //Send a DHCPREQUEST message
+         dhcpClientSendRequest(context);
+
+         //The timeout value is doubled for each subsequent retransmission
+         context->retransmitTimeout *= 2;
+
+         //Limit the timeout value to a maximum of 64 seconds
+         if(context->retransmitTimeout > DHCP_CLIENT_REQUEST_MAX_RT)
+            context->retransmitTimeout = DHCP_CLIENT_REQUEST_MAX_RT;
+
+         //Save the time at which the message was sent
+         context->timestamp = time;
+
+         //The timeout value should be randomized by the value of a uniform
+         //number chosen from the range -1 to +1
+         context->timeout = context->retransmitTimeout +
+            netGetRandRange(-DHCP_CLIENT_RAND_FACTOR, DHCP_CLIENT_RAND_FACTOR);
+
+         //Increment retransmission counter
+         context->retransmitCount++;
+      }
+      else
+      {
+         //If the client does not receive a response within a reasonable
+         //period of time, then it restarts the initialization procedure
+         dhcpClientChangeState(context, DHCP_STATE_INIT, 0);
+      }
+   }
+
+   //Manage DHCP configuration timeout
+   dhcpClientCheckTimeout(context);
+}
+
+
+/**
+ * @brief INIT-REBOOT state
+ *
+ * When a client that already has a valid lease starts up after a
+ * power-down or reboot, it starts here instead of the INIT state
+ *
+ * @param[in] context Pointer to the DHCP client context
+ **/
+
+void dhcpClientStateInitReboot(DhcpClientContext *context)
+{
+   systime_t delay;
+   NetInterface *interface;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Check whether the DHCP client is running
+   if(context->running)
+   {
+      //Wait for the link to be up before starting DHCP configuration
+      if(interface->linkState)
+      {
+         //The client should wait for a random time to
+         //desynchronize the use of DHCP at startup
+         delay = netGetRandRange(0, DHCP_CLIENT_INIT_DELAY);
+
+         //Record the time at which the client started
+         //the address acquisition process
+         context->configStartTime = osGetSystemTime();
+         //Clear flag
+         context->timeoutEventDone = FALSE;
+
+         //Switch to the REBOOTING state
+         dhcpClientChangeState(context, DHCP_STATE_REBOOTING, delay);
+      }
+   }
+}
+
+
+/**
+ * @brief REBOOTING state
+ *
+ * A client that has rebooted with an assigned address is
+ * waiting for a confirming reply from a server
+ *
+ * @param[in] context Pointer to the DHCP client context
+ **/
+
+void dhcpClientStateRebooting(DhcpClientContext *context)
+{
+   systime_t time;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check current time
+   if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+   {
+      //Check retransmission counter
+      if(context->retransmitCount == 0)
+      {
+         //A transaction identifier is used by the client to
+         //match incoming DHCP messages with pending requests
+         context->transactionId = netGetRand();
+
+         //Send a DHCPREQUEST message
+         dhcpClientSendRequest(context);
+
+         //Initial timeout value
+         context->retransmitTimeout = DHCP_CLIENT_REQUEST_INIT_RT;
+
+         //Save the time at which the message was sent
+         context->timestamp = time;
+
+         //The timeout value should be randomized by the value of a uniform
+         //number chosen from the range -1 to +1
+         context->timeout = context->retransmitTimeout +
+            netGetRandRange(-DHCP_CLIENT_RAND_FACTOR, DHCP_CLIENT_RAND_FACTOR);
+
+         //Increment retransmission counter
+         context->retransmitCount++;
+      }
+      else if(context->retransmitCount < DHCP_CLIENT_REQUEST_MAX_RC)
+      {
+         //Send a DHCPREQUEST message
+         dhcpClientSendRequest(context);
+
+         //The timeout value is doubled for each subsequent retransmission
+         context->retransmitTimeout *= 2;
+
+         //Limit the timeout value to a maximum of 64 seconds
+         if(context->retransmitTimeout > DHCP_CLIENT_REQUEST_MAX_RT)
+            context->retransmitTimeout = DHCP_CLIENT_REQUEST_MAX_RT;
+
+         //Save the time at which the message was sent
+         context->timestamp = time;
+
+         //The timeout value should be randomized by the value of a uniform
+         //number chosen from the range -1 to +1
+         context->timeout = context->retransmitTimeout +
+            netGetRandRange(-DHCP_CLIENT_RAND_FACTOR, DHCP_CLIENT_RAND_FACTOR);
+
+         //Increment retransmission counter
+         context->retransmitCount++;
+      }
+      else
+      {
+         //If the client does not receive a response within a reasonable
+         //period of time, then it restarts the initialization procedure
+         dhcpClientChangeState(context, DHCP_STATE_INIT, 0);
+      }
+   }
+
+   //Manage DHCP configuration timeout
+   dhcpClientCheckTimeout(context);
+}
+
+
+/**
+ * @brief PROBING state
+ *
+ * The client probes the newly received address
+ *
+ * @param[in] context Pointer to the DHCP client context
+ **/
+
+void dhcpClientStateProbing(DhcpClientContext *context)
+{
+   systime_t time;
+   NetInterface *interface;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check current time
+   if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+   {
+      //The address is already in use?
+      if(interface->ipv4Context.addrConflict)
+      {
+         //If the client detects that the address is already in use, the
+         //client must send a DHCPDECLINE message to the server and
+         //restarts the configuration process
+         dhcpClientSendDecline(context);
+
+         //The client should wait a minimum of ten seconds before
+         //restarting the configuration process to avoid excessive
+         //network traffic in case of looping
+         dhcpClientChangeState(context, DHCP_STATE_INIT, 0);
+      }
+      //Probing is on-going?
+      else if(context->retransmitCount < DHCP_CLIENT_PROBE_NUM)
+      {
+         //Conflict detection is done using ARP probes
+         arpSendProbe(interface, interface->ipv4Context.addr);
+
+         //Save the time at which the packet was sent
+         context->timestamp = time;
+         //Delay until repeated probe
+         context->timeout = DHCP_CLIENT_PROBE_DELAY;
+         //Increment retransmission counter
+         context->retransmitCount++;
+      }
+      //Probing is complete?
+      else
+      {
+         //The use of the IPv4 address is now unrestricted
+         interface->ipv4Context.addrState = IPV4_ADDR_STATE_VALID;
+
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+         //Restart mDNS probing process
+         mdnsResponderStartProbing(interface->mdnsResponderContext);
+#endif
+         //Dump current DHCP configuration for debugging purpose
+         dhcpClientDumpConfig(context);
+
+         //The client transitions to the BOUND state
+         dhcpClientChangeState(context, DHCP_STATE_BOUND, 0);
+      }
+   }
+}
+
+
+/**
+ * @brief BOUND state
+ *
+ * Client has a valid lease and is in its normal operating state
+ *
+ * @param[in] context Pointer to the DHCP client context
+ **/
+
+void dhcpClientStateBound(DhcpClientContext *context)
+{
+   systime_t t1;
+   systime_t time;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //A client will never attempt to extend the lifetime
+   //of the address when T1 set to 0xFFFFFFFF
+   if(context->t1 != DHCP_INFINITE_TIME)
+   {
+      //Convert T1 to milliseconds
+      if(context->t1 < (MAX_DELAY / 1000))
+         t1 = context->t1 * 1000;
+      else
+         t1 = MAX_DELAY;
+
+      //Check the time elapsed since the lease was obtained
+      if(timeCompare(time, context->leaseStartTime + t1) >= 0)
+      {
+         //Record the time at which the client started the address renewal process
+         context->configStartTime = time;
+
+         //Enter the RENEWING state
+         dhcpClientChangeState(context, DHCP_STATE_RENEWING, 0);
+      }
+   }
+}
+
+
+/**
+ * @brief RENEWING state
+ *
+ * Client is trying to renew its lease. It regularly sends
+ * DHCPREQUEST messages with the server that gave it its current
+ * lease specified, and waits for a reply
+ *
+ * @param[in] context Pointer to the DHCP client context
+ **/
+
+void dhcpClientStateRenewing(DhcpClientContext *context)
+{
+   systime_t t2;
+   systime_t time;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check current time
+   if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+   {
+      //Convert T2 to milliseconds
+      if(context->t2 < (MAX_DELAY / 1000))
+         t2 = context->t2 * 1000;
+      else
+         t2 = MAX_DELAY;
+
+      //Check whether T2 timer has expired
+      if(timeCompare(time, context->leaseStartTime + t2) < 0)
+      {
+         //First DHCPREQUEST message?
+         if(context->retransmitCount == 0)
+         {
+            //A transaction identifier is used by the client to
+            //match incoming DHCP messages with pending requests
+            context->transactionId = netGetRand();
+         }
+
+         //Send a DHCPREQUEST message
+         dhcpClientSendRequest(context);
+
+         //Save the time at which the message was sent
+         context->timestamp = time;
+
+         //Compute the remaining time until T2 expires
+         context->timeout = context->leaseStartTime + t2 - time;
+
+         //The client should wait one-half of the remaining time until T2, down to
+         //a minimum of 60 seconds, before retransmitting the DHCPREQUEST message
+         if(context->timeout > (2 * DHCP_CLIENT_REQUEST_MIN_DELAY))
+            context->timeout /= 2;
+
+         //Increment retransmission counter
+         context->retransmitCount++;
+      }
+      else
+      {
+         //If no DHCPACK arrives before time T2, the client moves to REBINDING
+         dhcpClientChangeState(context, DHCP_STATE_REBINDING, 0);
+      }
+   }
+}
+
+
+/**
+ * @brief REBINDING state
+ *
+ * The client has failed to renew its lease with the server that originally
+ * granted it, and now seeks a lease extension with any server that can
+ * hear it. It periodically sends DHCPREQUEST messages with no server specified
+ * until it gets a reply or the lease ends
+ *
+ * @param[in] context Pointer to the DHCP client context
+ **/
+
+void dhcpClientStateRebinding(DhcpClientContext *context)
+{
+   systime_t time;
+   systime_t leaseTime;
+   NetInterface *interface;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check current time
+   if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+   {
+      //Convert the lease time to milliseconds
+      if(context->leaseTime < (MAX_DELAY / 1000))
+         leaseTime = context->leaseTime * 1000;
+      else
+         leaseTime = MAX_DELAY;
+
+      //Check whether the lease has expired
+      if(timeCompare(time, context->leaseStartTime + leaseTime) < 0)
+      {
+         //First DHCPREQUEST message?
+         if(context->retransmitCount == 0)
+         {
+            //A transaction identifier is used by the client to
+            //match incoming DHCP messages with pending requests
+            context->transactionId = netGetRand();
+         }
+
+         //Send a DHCPREQUEST message
+         dhcpClientSendRequest(context);
+
+         //Save the time at which the message was sent
+         context->timestamp = time;
+
+         //Compute the remaining time until the lease expires
+         context->timeout = context->leaseStartTime + leaseTime - time;
+
+         //The client should wait one-half of the remaining lease time, down to a
+         //minimum of 60 seconds, before retransmitting the DHCPREQUEST message
+         if(context->timeout > (2 * DHCP_CLIENT_REQUEST_MIN_DELAY))
+            context->timeout /= 2;
+
+         //Increment retransmission counter
+         context->retransmitCount++;
+      }
+      else
+      {
+         //The host address is no longer valid...
+         interface->ipv4Context.addr = IPV4_UNSPECIFIED_ADDR;
+         interface->ipv4Context.addrState = IPV4_ADDR_STATE_INVALID;
+
+         //Clear subnet mask
+         interface->ipv4Context.subnetMask = IPV4_UNSPECIFIED_ADDR;
+
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+         //Restart mDNS probing process
+         mdnsResponderStartProbing(interface->mdnsResponderContext);
+#endif
+
+         //If the lease expires before the client receives
+         //a DHCPACK, the client moves to INIT state
+         dhcpClientChangeState(context, DHCP_STATE_INIT, 0);
+      }
+   }
+}
+
+
+/**
+ * @brief Send DHCPDISCOVER message
+ * @param[in] context Pointer to the DHCP client context
+ * @return Error code
+ **/
+
+error_t dhcpClientSendDiscover(DhcpClientContext *context)
+{
+   error_t error;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   NetInterface *interface;
+   DhcpMessage *message;
+   IpAddr destIpAddr;
+
+   //DHCP message type
+   const uint8_t messageType = DHCP_MESSAGE_TYPE_DISCOVER;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Allocate a memory buffer to hold the DHCP message
+   buffer = udpAllocBuffer(DHCP_MIN_MSG_SIZE, &offset);
+   //Failed to allocate buffer?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the DHCP message
+   message = netBufferAt(buffer, offset);
+   //Clear memory buffer contents
+   memset(message, 0, DHCP_MIN_MSG_SIZE);
+
+   //Format DHCPDISCOVER message
+   message->op = DHCP_OPCODE_BOOTREQUEST;
+   message->htype = DHCP_HARDWARE_TYPE_ETH;
+   message->hlen = sizeof(MacAddr);
+   message->xid = htonl(context->transactionId);
+   message->secs = dhcpClientComputeElapsedTime(context);
+   message->flags = HTONS(DHCP_FLAG_BROADCAST);
+   message->ciaddr = IPV4_UNSPECIFIED_ADDR;
+   message->chaddr = interface->macAddr;
+
+   //Write magic cookie before setting any option
+   message->magicCookie = HTONL(DHCP_MAGIC_COOKIE);
+   //Properly terminate options field
+   message->options[0] = DHCP_OPT_END;
+
+   //DHCP Message Type option
+   dhcpAddOption(message, DHCP_OPT_DHCP_MESSAGE_TYPE,
+      &messageType, sizeof(messageType));
+
+   //Retrieve the length of the host name
+   length = strlen(context->settings.hostname);
+
+   //Any host name defined?
+   if(length > 0)
+   {
+      //The Host Name option specifies the name of the client
+      dhcpAddOption(message, DHCP_OPT_HOST_NAME,
+         context->settings.hostname, length);
+   }
+
+   //Check whether rapid commit is enabled
+   if(context->settings.rapidCommit)
+   {
+      //Include the Rapid Commit option if the client is prepared
+      //to perform the DHCPDISCOVER-DHCPACK message exchange
+      dhcpAddOption(message, DHCP_OPT_RAPID_COMMIT, NULL, 0);
+   }
+
+   //Set destination IP address
+   destIpAddr.length = sizeof(Ipv4Addr);
+   destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
+
+   //Debug message
+   TRACE_DEBUG("\r\n%s: Sending DHCP message (%" PRIuSIZE " bytes)...\r\n",
+      formatSystemTime(osGetSystemTime(), NULL), DHCP_MIN_MSG_SIZE);
+
+   //Dump the contents of the message for debugging purpose
+   dhcpDumpMessage(message, DHCP_MIN_MSG_SIZE);
+
+   //Broadcast DHCPDISCOVER message
+   error = udpSendDatagramEx(interface, DHCP_CLIENT_PORT, &destIpAddr,
+      DHCP_SERVER_PORT, buffer, offset, IPV4_DEFAULT_TTL);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send DHCPREQUEST message
+ * @param[in] context Pointer to the DHCP client context
+ * @return Error code
+ **/
+
+error_t dhcpClientSendRequest(DhcpClientContext *context)
+{
+   error_t error;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   NetInterface *interface;
+   DhcpMessage *message;
+   IpAddr destIpAddr;
+
+   //DHCP message type
+   const uint8_t messageType = DHCP_MESSAGE_TYPE_REQUEST;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Allocate a memory buffer to hold the DHCP message
+   buffer = udpAllocBuffer(DHCP_MIN_MSG_SIZE, &offset);
+   //Failed to allocate buffer?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the DHCP message
+   message = netBufferAt(buffer, offset);
+   //Clear memory buffer contents
+   memset(message, 0, DHCP_MIN_MSG_SIZE);
+
+   //Format DHCPREQUEST message
+   message->op = DHCP_OPCODE_BOOTREQUEST;
+   message->htype = DHCP_HARDWARE_TYPE_ETH;
+   message->hlen = sizeof(MacAddr);
+   message->xid = htonl(context->transactionId);
+   message->secs = dhcpClientComputeElapsedTime(context);
+
+   //The client IP address must be included if the client
+   //is fully configured and can respond to ARP requests
+   if(context->state == DHCP_STATE_RENEWING ||
+      context->state == DHCP_STATE_REBINDING)
+   {
+      message->flags = 0;
+      message->ciaddr = interface->ipv4Context.addr;
+   }
+   else
+   {
+      message->flags = HTONS(DHCP_FLAG_BROADCAST);
+      message->ciaddr = IPV4_UNSPECIFIED_ADDR;
+   }
+
+   //Client hardware address
+   message->chaddr = interface->macAddr;
+   //Write magic cookie before setting any option
+   message->magicCookie = HTONL(DHCP_MAGIC_COOKIE);
+   //Properly terminate options field
+   message->options[0] = DHCP_OPT_END;
+
+   //DHCP Message Type option
+   dhcpAddOption(message, DHCP_OPT_DHCP_MESSAGE_TYPE,
+      &messageType, sizeof(messageType));
+
+   //Retrieve the length of the host name
+   length = strlen(context->settings.hostname);
+
+   //Any host name defined?
+   if(length > 0)
+   {
+      //The Host Name option specifies the name of the client
+      dhcpAddOption(message, DHCP_OPT_HOST_NAME,
+         context->settings.hostname, length);
+   }
+
+   //Server Identifier option
+   if(context->state == DHCP_STATE_REQUESTING)
+   {
+      dhcpAddOption(message, DHCP_OPT_SERVER_IDENTIFIER,
+         &context->serverIpAddr, sizeof(Ipv4Addr));
+   }
+
+   //Requested IP Address option
+   if(context->state == DHCP_STATE_REQUESTING ||
+      context->state == DHCP_STATE_REBOOTING)
+   {
+      dhcpAddOption(message, DHCP_OPT_REQUESTED_IP_ADDRESS,
+         &context->requestedIpAddr, sizeof(Ipv4Addr));
+   }
+
+   //Parameter Request List option
+   dhcpAddOption(message, DHCP_OPT_PARAM_REQUEST_LIST,
+      dhcpOptionList, sizeof(dhcpOptionList));
+
+   //IP address is being renewed?
+   if(context->state == DHCP_STATE_RENEWING)
+   {
+      //The client transmits the message directly to the
+      //server that initially granted the lease
+      destIpAddr.length = sizeof(Ipv4Addr);
+      destIpAddr.ipv4Addr = context->serverIpAddr;
+   }
+   else
+   {
+      //Broadcast the message
+      destIpAddr.length = sizeof(Ipv4Addr);
+      destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
+   }
+
+   //Debug message
+   TRACE_DEBUG("\r\n%s: Sending DHCP message (%" PRIuSIZE " bytes)...\r\n",
+      formatSystemTime(osGetSystemTime(), NULL), DHCP_MIN_MSG_SIZE);
+
+   //Dump the contents of the message for debugging purpose
+   dhcpDumpMessage(message, DHCP_MIN_MSG_SIZE);
+
+   //Send DHCPREQUEST message
+   error = udpSendDatagramEx(interface, DHCP_CLIENT_PORT, &destIpAddr,
+      DHCP_SERVER_PORT, buffer, offset, IPV4_DEFAULT_TTL);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send DHCPDECLINE message
+ * @param[in] context Pointer to the DHCP client context
+ * @return Error code
+ **/
+
+error_t dhcpClientSendDecline(DhcpClientContext *context)
+{
+   error_t error;
+   size_t offset;
+   NetBuffer *buffer;
+   NetInterface *interface;
+   DhcpMessage *message;
+   IpAddr destIpAddr;
+
+   //DHCP message type
+   const uint8_t messageType = DHCP_MESSAGE_TYPE_DECLINE;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Allocate a memory buffer to hold the DHCP message
+   buffer = udpAllocBuffer(DHCP_MIN_MSG_SIZE, &offset);
+   //Failed to allocate buffer?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the DHCP message
+   message = netBufferAt(buffer, offset);
+   //Clear memory buffer contents
+   memset(message, 0, DHCP_MIN_MSG_SIZE);
+
+   //Format DHCPDECLINE message
+   message->op = DHCP_OPCODE_BOOTREQUEST;
+   message->htype = DHCP_HARDWARE_TYPE_ETH;
+   message->hlen = sizeof(MacAddr);
+   message->xid = htonl(context->transactionId);
+   message->secs = 0;
+   message->flags = 0;
+   message->ciaddr = IPV4_UNSPECIFIED_ADDR;
+   message->chaddr = interface->macAddr;
+
+   //Write magic cookie before setting any option
+   message->magicCookie = HTONL(DHCP_MAGIC_COOKIE);
+   //Properly terminate options field
+   message->options[0] = DHCP_OPT_END;
+
+   //DHCP Message Type option
+   dhcpAddOption(message, DHCP_OPT_DHCP_MESSAGE_TYPE,
+      &messageType, sizeof(messageType));
+   //Server Identifier option
+   dhcpAddOption(message, DHCP_OPT_SERVER_IDENTIFIER,
+      &context->serverIpAddr, sizeof(Ipv4Addr));
+   //Requested IP Address option
+   dhcpAddOption(message, DHCP_OPT_REQUESTED_IP_ADDRESS,
+      &context->requestedIpAddr, sizeof(Ipv4Addr));
+
+   //Set destination IP address
+   destIpAddr.length = sizeof(Ipv4Addr);
+   destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
+
+   //Debug message
+   TRACE_DEBUG("\r\n%s: Sending DHCP message (%" PRIuSIZE " bytes)...\r\n",
+      formatSystemTime(osGetSystemTime(), NULL), DHCP_MIN_MSG_SIZE);
+
+   //Dump the contents of the message for debugging purpose
+   dhcpDumpMessage(message, DHCP_MIN_MSG_SIZE);
+
+   //Broadcast DHCPDECLINE message
+   error = udpSendDatagramEx(interface, DHCP_CLIENT_PORT, &destIpAddr,
+      DHCP_SERVER_PORT, buffer, offset, IPV4_DEFAULT_TTL);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process incoming DHCP message
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader UDP pseudo header
+ * @param[in] udpHeader UDP header
+ * @param[in] buffer Multi-part buffer containing the incoming DHCP message
+ * @param[in] offset Offset to the first byte of the DHCP message
+ * @param[in] params Pointer to the DHCP client context
+ **/
+
+void dhcpClientProcessMessage(NetInterface *interface,
+   const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader,
+   const NetBuffer *buffer, size_t offset, void *params)
+{
+   size_t length;
+   DhcpClientContext *context;
+   DhcpMessage *message;
+   DhcpOption *option;
+
+   //Point to the DHCP client context
+   context = (DhcpClientContext *) params;
+
+   //Retrieve the length of the DHCP message
+   length = netBufferGetLength(buffer) - offset;
+
+   //Make sure the DHCP message is valid
+   if(length < sizeof(DhcpMessage))
+      return;
+   if(length > DHCP_MAX_MSG_SIZE)
+      return;
+
+   //Point to the beginning of the DHCP message
+   message = netBufferAt(buffer, offset);
+   //Sanity check
+   if(message == NULL)
+      return;
+
+   //Debug message
+   TRACE_DEBUG("\r\n%s: DHCP message received (%" PRIuSIZE " bytes)...\r\n",
+      formatSystemTime(osGetSystemTime(), NULL), length);
+
+   //Dump the contents of the message for debugging purpose
+   dhcpDumpMessage(message, length);
+
+   //The DHCP server shall respond with a BOOTREPLY opcode
+   if(message->op != DHCP_OPCODE_BOOTREPLY)
+      return;
+   //Enforce hardware type
+   if(message->htype != DHCP_HARDWARE_TYPE_ETH)
+      return;
+   //Check the length of the hardware address
+   if(message->hlen != sizeof(MacAddr))
+      return;
+   //Check magic cookie
+   if(message->magicCookie != HTONL(DHCP_MAGIC_COOKIE))
+      return;
+
+   //The DHCP Message Type option must be included in every DHCP message
+   option = dhcpGetOption(message, length, DHCP_OPT_DHCP_MESSAGE_TYPE);
+
+   //Failed to retrieve the Message Type option?
+   if(option == NULL || option->length != 1)
+      return;
+
+   //Check message type
+   switch(option->value[0])
+   {
+   case DHCP_MESSAGE_TYPE_OFFER:
+      //Parse DHCPOFFER message
+      dhcpClientParseOffer(context, message, length);
+      break;
+   case DHCP_MESSAGE_TYPE_ACK:
+      //Parse DHCPACK message
+      dhcpClientParseAck(context, message, length);
+      break;
+   case DHCP_MESSAGE_TYPE_NAK:
+      //Parse DHCPNAK message
+      dhcpClientParseNak(context, message, length);
+      break;
+   default:
+      //Silently drop incoming message
+      break;
+   }
+}
+
+
+/**
+ * @brief Parse DHCPOFFER message
+ * @param[in] context Pointer to the DHCP client context
+ * @param[in] message Pointer to the incoming DHCP message
+ * @param[in] length Length of the incoming message to parse
+ **/
+
+void dhcpClientParseOffer(DhcpClientContext *context,
+   const DhcpMessage *message, size_t length)
+{
+   DhcpOption *serverIdOption;
+   NetInterface *interface;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Discard any received packet that does not match the transaction ID
+   if(ntohl(message->xid) != context->transactionId)
+      return;
+   //Make sure the IP address offered to the client is valid
+   if(message->yiaddr == IPV4_UNSPECIFIED_ADDR)
+      return;
+   //Check MAC address
+   if(!macCompAddr(&message->chaddr, &interface->macAddr))
+      return;
+
+   //Make sure that the DHCPOFFER message is received in response to
+   //a DHCPDISCOVER message
+   if(context->state != DHCP_STATE_SELECTING)
+      return;
+
+   //A DHCP server always returns its own address in the Server Identifier option
+   serverIdOption = dhcpGetOption(message, length, DHCP_OPT_SERVER_IDENTIFIER);
+
+   //Failed to retrieve the Server Identifier option?
+   if(serverIdOption == NULL || serverIdOption->length != 4)
+      return;
+
+   //Record the IP address of the DHCP server
+   ipv4CopyAddr(&context->serverIpAddr, serverIdOption->value);
+   //Record the IP address offered to the client
+   context->requestedIpAddr = message->yiaddr;
+
+   //Switch to the REQUESTING state
+   dhcpClientChangeState(context, DHCP_STATE_REQUESTING, 0);
+}
+
+
+/**
+ * @brief Parse DHCPACK message
+ * @param[in] context Pointer to the DHCP client context
+ * @param[in] message Pointer to the incoming DHCP message
+ * @param[in] length Length of the incoming message to parse
+ * @return Error code
+ **/
+
+void dhcpClientParseAck(DhcpClientContext *context,
+   const DhcpMessage *message, size_t length)
+{
+   uint_t i;
+   uint_t n;
+   DhcpOption *option;
+   DhcpOption *serverIdOption;
+   NetInterface *interface;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Discard any received packet that does not match the transaction ID
+   if(ntohl(message->xid) != context->transactionId)
+      return;
+   //Make sure the IP address assigned to the client is valid
+   if(message->yiaddr == IPV4_UNSPECIFIED_ADDR)
+      return;
+   //Check MAC address
+   if(!macCompAddr(&message->chaddr, &interface->macAddr))
+      return;
+
+   //A DHCP server always returns its own address in the Server Identifier option
+   serverIdOption = dhcpGetOption(message, length, DHCP_OPT_SERVER_IDENTIFIER);
+
+   //Failed to retrieve the Server Identifier option?
+   if(serverIdOption == NULL || serverIdOption->length != 4)
+      return;
+
+   //Check current state
+   if(context->state == DHCP_STATE_SELECTING)
+   {
+      //A DHCPACK message is not acceptable when rapid commit is disallowed
+      if(!context->settings.rapidCommit)
+         return;
+
+      //Search for the Rapid Commit option
+      option = dhcpGetOption(message, length, DHCP_OPT_RAPID_COMMIT);
+
+      //A server must include this option in a DHCPACK message sent
+      //in a response to a DHCPDISCOVER message when completing the
+      //DHCPDISCOVER-DHCPACK message exchange
+      if(option == NULL || option->length != 0)
+         return;
+   }
+   else if(context->state == DHCP_STATE_REQUESTING ||
+      context->state == DHCP_STATE_RENEWING)
+   {
+      //Check the server identifier
+      if(!ipv4CompAddr(serverIdOption->value, &context->serverIpAddr))
+         return;
+   }
+   else if(context->state == DHCP_STATE_REBOOTING ||
+      context->state == DHCP_STATE_REBINDING)
+   {
+      //Do not check the server identifier
+   }
+   else
+   {
+      //Silently discard the DHCPACK message
+      return;
+   }
+
+   //Retrieve IP Address Lease Time option
+   option = dhcpGetOption(message, length, DHCP_OPT_IP_ADDRESS_LEASE_TIME);
+
+   //Failed to retrieve specified option?
+   if(option == NULL  || option->length != 4)
+      return;
+
+   //Record the lease time
+   context->leaseTime = LOAD32BE(option->value);
+
+   //Retrieve Renewal Time Value option
+   option = dhcpGetOption(message, length, DHCP_OPT_RENEWAL_TIME_VALUE);
+
+   //Specified option found?
+   if(option != NULL && option->length == 4)
+   {
+      //This option specifies the time interval from address assignment
+      //until the client transitions to the RENEWING state
+      context->t1 = LOAD32BE(option->value);
+   }
+   else if(context->leaseTime != DHCP_INFINITE_TIME)
+   {
+      //By default, T1 is set to 50% of the lease time
+      context->t1 = context->leaseTime / 2;
+   }
+   else
+   {
+      //Infinite lease
+      context->t1 = DHCP_INFINITE_TIME;
+   }
+
+   //Retrieve Rebinding Time value option
+   option = dhcpGetOption(message, length, DHCP_OPT_REBINDING_TIME_VALUE);
+
+   //Specified option found?
+   if(option != NULL && option->length == 4)
+   {
+      //This option specifies the time interval from address assignment
+      //until the client transitions to the REBINDING state
+      context->t2 = LOAD32BE(option->value);
+   }
+   else if(context->leaseTime != DHCP_INFINITE_TIME)
+   {
+      //By default, T2 is set to 87.5% of the lease time
+      context->t2 = context->leaseTime * 7 / 8;
+   }
+   else
+   {
+      //Infinite lease
+      context->t2 = DHCP_INFINITE_TIME;
+   }
+
+   //Retrieve Subnet Mask option
+   option = dhcpGetOption(message, length, DHCP_OPT_SUBNET_MASK);
+
+   //The specified option has been found?
+   if(option != NULL && option->length == sizeof(Ipv4Addr))
+   {
+      //Record subnet mask
+      ipv4CopyAddr(&interface->ipv4Context.subnetMask, option->value);
+   }
+
+   //Retrieve Router option
+   option = dhcpGetOption(message, length, DHCP_OPT_ROUTER);
+
+   //The specified option has been found?
+   if(option != NULL && !(option->length % sizeof(Ipv4Addr)))
+   {
+      //Save default gateway
+      if(option->length >= sizeof(Ipv4Addr))
+         ipv4CopyAddr(&interface->ipv4Context.defaultGateway, option->value);
+   }
+
+   //Use the DNS servers provided by the DHCP server?
+   if(!context->settings.manualDnsConfig)
+   {
+      //Retrieve DNS Server option
+      option = dhcpGetOption(message, length, DHCP_OPT_DNS_SERVER);
+
+       //The specified option has been found?
+      if(option != NULL && !(option->length % sizeof(Ipv4Addr)))
+      {
+         //Get the number of addresses provided in the response
+         n = option->length / sizeof(Ipv4Addr);
+
+         //Loop through the list of addresses
+         for(i = 0; i < n && i < IPV4_DNS_SERVER_LIST_SIZE; i++)
+         {
+            //Record DNS server address
+            ipv4CopyAddr(&interface->ipv4Context.dnsServerList[i],
+               option->value + i * sizeof(Ipv4Addr));
+         }
+      }
+   }
+
+   //Retrieve MTU option
+   option = dhcpGetOption(message, length, DHCP_OPT_INTERFACE_MTU);
+
+   //The specified option has been found?
+   if(option != NULL && option->length == 2)
+   {
+      //This option specifies the MTU to use on this interface
+      n = LOAD16BE(option->value);
+
+      //Make sure that the option's value is acceptable
+      if(n >= IPV4_MINIMUM_MTU && n <= interface->nicDriver->mtu)
+      {
+         //Set the MTU to be used on the interface
+         interface->ipv4Context.linkMtu = n;
+      }
+   }
+
+   //Record the IP address of the DHCP server
+   ipv4CopyAddr(&context->serverIpAddr, serverIdOption->value);
+   //Record the IP address assigned to the client
+   context->requestedIpAddr = message->yiaddr;
+
+   //Save the time a which the lease was obtained
+   context->leaseStartTime = osGetSystemTime();
+
+   //Check current state
+   if(context->state == DHCP_STATE_REQUESTING ||
+      context->state == DHCP_STATE_REBOOTING)
+   {
+      //Use the IP address as a tentative address
+      interface->ipv4Context.addr = message->yiaddr;
+      interface->ipv4Context.addrState = IPV4_ADDR_STATE_TENTATIVE;
+
+      //Clear conflict flag
+      interface->ipv4Context.addrConflict = FALSE;
+
+      //The client should probe the newly received address
+      dhcpClientChangeState(context, DHCP_STATE_PROBING, 0);
+   }
+   else
+   {
+      //Assign the IP address to the client
+      interface->ipv4Context.addr = message->yiaddr;
+      interface->ipv4Context.addrState = IPV4_ADDR_STATE_VALID;
+
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+      //Restart mDNS probing process
+      mdnsResponderStartProbing(interface->mdnsResponderContext);
+#endif
+      //The client transitions to the BOUND state
+      dhcpClientChangeState(context, DHCP_STATE_BOUND, 0);
+   }
+}
+
+
+/**
+ * @brief Parse DHCPNAK message
+ * @param[in] context Pointer to the DHCP client context
+ * @param[in] message Pointer to the incoming DHCP message
+ * @param[in] length Length of the incoming message to parse
+ * @return Error code
+ **/
+
+void dhcpClientParseNak(DhcpClientContext *context,
+   const DhcpMessage *message, size_t length)
+{
+   DhcpOption *serverIdOption;
+   NetInterface *interface;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Discard any received packet that does not match the transaction ID
+   if(ntohl(message->xid) != context->transactionId)
+      return;
+   //Check MAC address
+   if(!macCompAddr(&message->chaddr, &interface->macAddr))
+      return;
+
+   //A DHCP server always returns its own address in the Server Identifier option
+   serverIdOption = dhcpGetOption(message, length, DHCP_OPT_SERVER_IDENTIFIER);
+
+   //Failed to retrieve the Server Identifier option?
+   if(serverIdOption == NULL || serverIdOption->length != 4)
+      return;
+
+   //Check current state
+   if(context->state == DHCP_STATE_REQUESTING ||
+      context->state == DHCP_STATE_RENEWING)
+   {
+      //Check the server identifier
+      if(!ipv4CompAddr(serverIdOption->value, &context->serverIpAddr))
+         return;
+   }
+   else if(context->state == DHCP_STATE_REBOOTING ||
+      context->state == DHCP_STATE_REBINDING)
+   {
+      //Do not check the server identifier
+   }
+   else
+   {
+      //Silently discard the DHCPNAK message
+      return;
+   }
+
+   //The host address is no longer appropriate for the link
+   interface->ipv4Context.addr = IPV4_UNSPECIFIED_ADDR;
+   interface->ipv4Context.addrState = IPV4_ADDR_STATE_INVALID;
+
+   //Clear subnet mask
+   interface->ipv4Context.subnetMask = IPV4_UNSPECIFIED_ADDR;
+
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+   //Restart mDNS probing process
+   mdnsResponderStartProbing(interface->mdnsResponderContext);
+#endif
+
+   //Restart DHCP configuration
+   dhcpClientChangeState(context, DHCP_STATE_INIT, 0);
+}
+
+
+/**
+ * @brief Manage DHCP configuration timeout
+ * @param[in] context Pointer to the DHCP client context
+ **/
+
+void dhcpClientCheckTimeout(DhcpClientContext *context)
+{
+   systime_t time;
+   NetInterface *interface;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Any registered callback?
+   if(context->settings.timeoutEvent != NULL)
+   {
+      //DHCP configuration timeout?
+      if(timeCompare(time, context->configStartTime + context->settings.timeout) >= 0)
+      {
+         //Ensure the callback function is only called once
+         if(!context->timeoutEventDone)
+         {
+            //Release exclusive access
+            osReleaseMutex(&netMutex);
+            //Invoke user callback function
+            context->settings.timeoutEvent(context, interface);
+            //Get exclusive access
+            osAcquireMutex(&netMutex);
+
+            //Set flag
+            context->timeoutEventDone = TRUE;
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Compute the appropriate secs field
+ *
+ * Compute the number of seconds elapsed since the client began
+ * address acquisition or renewal process
+ *
+ * @param[in] context Pointer to the DHCP client context
+ * @return The elapsed time expressed in seconds
+ **/
+
+uint16_t dhcpClientComputeElapsedTime(DhcpClientContext *context)
+{
+   systime_t time;
+
+   //Compute the time elapsed since the DHCP configuration process started
+   time = (osGetSystemTime() - context->configStartTime) / 1000;
+
+   //The value 0xFFFF is used to represent any elapsed time values
+   //greater than the largest time value that can be represented
+   time = MIN(time, 0xFFFF);
+
+   //Convert the 16-bit value to network byte order
+   return htons(time);
+}
+
+
+/**
+ * @brief Update DHCP FSM state
+ * @param[in] context Pointer to the DHCP client context
+ * @param[in] newState New DHCP state to switch to
+ * @param[in] delay Initial delay
+ **/
+
+void dhcpClientChangeState(DhcpClientContext *context,
+   DhcpState newState, systime_t delay)
+{
+   systime_t time;
+
+   //Get current time
+   time = osGetSystemTime();
+
+#if (DHCP_TRACE_LEVEL >= TRACE_LEVEL_INFO)
+   //Sanity check
+   if(newState <= DHCP_STATE_REBINDING)
+   {
+      //DHCP FSM states
+      static const char_t *stateLabel[] =
+      {
+         "INIT",
+         "SELECTING",
+         "REQUESTING",
+         "INIT-REBOOT",
+         "REBOOTING",
+         "PROBING",
+         "BOUND",
+         "RENEWING",
+         "REBINDING"
+      };
+
+      //Debug message
+      TRACE_INFO("%s: DHCP client %s state\r\n",
+         formatSystemTime(time, NULL), stateLabel[newState]);
+   }
+#endif
+
+   //Set time stamp
+   context->timestamp = time;
+   //Set initial delay
+   context->timeout = delay;
+   //Reset retransmission counter
+   context->retransmitCount = 0;
+   //Switch to the new state
+   context->state = newState;
+
+   //Any registered callback?
+   if(context->settings.stateChangeEvent != NULL)
+   {
+      NetInterface *interface;
+
+      //Point to the underlying network interface
+      interface = context->settings.interface;
+
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+      //Invoke user callback function
+      context->settings.stateChangeEvent(context, interface, newState);
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+   }
+}
+
+
+/**
+ * @brief Dump DHCP configuration for debugging purpose
+ * @param[in] context Pointer to the DHCP client context
+ **/
+
+void dhcpClientDumpConfig(DhcpClientContext *context)
+{
+#if (DHCP_TRACE_LEVEL >= TRACE_LEVEL_INFO)
+   uint_t i;
+   NetInterface *interface;
+   Ipv4Context *ipv4Context;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+   //Point to the IPv4 context
+   ipv4Context = &interface->ipv4Context;
+
+   //Debug message
+   TRACE_INFO("\r\n");
+   TRACE_INFO("DHCP configuration:\r\n");
+
+   //Lease start time
+   TRACE_INFO("  Lease Start Time = %s\r\n",
+      formatSystemTime(context->leaseStartTime, NULL));
+
+   //Lease time
+   TRACE_INFO("  Lease Time = %" PRIu32 "s\r\n", context->leaseTime);
+   //Renewal time
+   TRACE_INFO("  T1 = %" PRIu32 "s\r\n", context->t1);
+   //Rebinding time
+   TRACE_INFO("  T2 = %" PRIu32 "s\r\n", context->t2);
+
+   //Host address
+   TRACE_INFO("  IPv4 Address = %s\r\n",
+      ipv4AddrToString(ipv4Context->addr, NULL));
+
+   //Subnet mask
+   TRACE_INFO("  Subnet Mask = %s\r\n",
+      ipv4AddrToString(ipv4Context->subnetMask, NULL));
+
+   //Default gateway
+   TRACE_INFO("  Default Gateway = %s\r\n",
+      ipv4AddrToString(ipv4Context->defaultGateway, NULL));
+
+   //DNS servers
+   for(i = 0; i < IPV4_DNS_SERVER_LIST_SIZE; i++)
+   {
+      TRACE_INFO("  DNS Server %u = %s\r\n", i + 1,
+         ipv4AddrToString(ipv4Context->dnsServerList[i], NULL));
+   }
+
+   //Maximum transmit unit
+   TRACE_INFO("  MTU = %" PRIuSIZE "\r\n", interface->ipv4Context.linkMtu);
+   TRACE_INFO("\r\n");
+#endif
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dhcp/dhcp_client.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,268 @@
+/**
+ * @file dhcp_client.h
+ * @brief DHCP client (Dynamic Host Configuration Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DHCP_CLIENT_H
+#define _DHCP_CLIENT_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/socket.h"
+#include "core/udp.h"
+#include "dhcp/dhcp_common.h"
+
+//DHCP client support
+#ifndef DHCP_CLIENT_SUPPORT
+   #define DHCP_CLIENT_SUPPORT ENABLED
+#elif (DHCP_CLIENT_SUPPORT != ENABLED && DHCP_CLIENT_SUPPORT != DISABLED)
+   #error DHCP_CLIENT_SUPPORT parameter is not valid
+#endif
+
+//DHCP client tick interval
+#ifndef DHCP_CLIENT_TICK_INTERVAL
+   #define DHCP_CLIENT_TICK_INTERVAL 200
+#elif (DHCP_CLIENT_TICK_INTERVAL < 10)
+   #error DHCP_CLIENT_TICK_INTERVAL parameter is not valid
+#endif
+
+//Maximum length of host name
+#ifndef DHCP_CLIENT_MAX_HOSTNAME_LEN
+   #define DHCP_CLIENT_MAX_HOSTNAME_LEN 15
+#elif (DHCP_CLIENT_MAX_HOSTNAME_LEN < 1)
+   #error DHCP_CLIENT_MAX_HOSTNAME_LEN parameter is not valid
+#endif
+
+//Random delay before sending the first message
+#ifndef DHCP_CLIENT_INIT_DELAY
+   #define DHCP_CLIENT_INIT_DELAY 2000
+#elif (DHCP_CLIENT_INIT_DELAY < 0)
+   #error DHCP_CLIENT_INIT_DELAY parameter is not valid
+#endif
+
+//Initial retransmission timeout (DHCPDISCOVER)
+#ifndef DHCP_CLIENT_DISCOVER_INIT_RT
+   #define DHCP_CLIENT_DISCOVER_INIT_RT 4000
+#elif (DHCP_CLIENT_DISCOVER_INIT_RT < 1000)
+   #error DHCP_CLIENT_DISCOVER_INIT_RT parameter is not valid
+#endif
+
+//Maximum retransmission timeout (DHCPDISCOVER)
+#ifndef DHCP_CLIENT_DISCOVER_MAX_RT
+   #define DHCP_CLIENT_DISCOVER_MAX_RT 16000
+#elif (DHCP_CLIENT_DISCOVER_MAX_RT < 1000)
+   #error DHCP_CLIENT_DISCOVER_MAX_RT parameter is not valid
+#endif
+
+//Maximum retransmission count (DHCPREQUEST)
+#ifndef DHCP_CLIENT_REQUEST_MAX_RC
+   #define DHCP_CLIENT_REQUEST_MAX_RC 4
+#elif (DHCP_CLIENT_REQUEST_MAX_RC < 1)
+   #error DHCP_CLIENT_REQUEST_MAX_RC parameter is not valid
+#endif
+
+//Initial retransmission timeout (DHCPREQUEST)
+#ifndef DHCP_CLIENT_REQUEST_INIT_RT
+   #define DHCP_CLIENT_REQUEST_INIT_RT 4000
+#elif (DHCP_CLIENT_REQUEST_INIT_RT < 1000)
+   #error DHCP_CLIENT_REQUEST_INIT_RT parameter is not valid
+#endif
+
+//Maximum retransmission timeout (DHCPREQUEST)
+#ifndef DHCP_CLIENT_REQUEST_MAX_RT
+   #define DHCP_CLIENT_REQUEST_MAX_RT 64000
+#elif (DHCP_CLIENT_REQUEST_MAX_RT < 1000)
+   #error DHCP_CLIENT_REQUEST_MAX_RT parameter is not valid
+#endif
+
+//Minimum delay between DHCPREQUEST messages in RENEWING and REBINDING states
+#ifndef DHCP_CLIENT_REQUEST_MIN_DELAY
+   #define DHCP_CLIENT_REQUEST_MIN_DELAY 60000
+#elif (DHCP_CLIENT_REQUEST_MIN_DELAY < 1000)
+   #error DHCP_CLIENT_REQUEST_MIN_DELAY parameter is not valid
+#endif
+
+//Number of probe packets
+#ifndef DHCP_CLIENT_PROBE_NUM
+   #define DHCP_CLIENT_PROBE_NUM 1
+#elif (DHCP_CLIENT_PROBE_NUM < 0)
+   #error DHCP_CLIENT_PROBE_NUM parameter is not valid
+#endif
+
+//Delay until repeated probe
+#ifndef DHCP_CLIENT_PROBE_DELAY
+   #define DHCP_CLIENT_PROBE_DELAY 1000
+#elif (DHCP_CLIENT_PROBE_DELAY < 100)
+   #error DHCP_CLIENT_PROBE_DELAY parameter is not valid
+#endif
+
+//Random factor used to determine the delay between retransmissions
+#ifndef DHCP_CLIENT_RAND_FACTOR
+   #define DHCP_CLIENT_RAND_FACTOR 1000
+#elif (DHCP_CLIENT_RAND_FACTOR < 100)
+   #error DHCP_CLIENT_RAND_FACTOR parameter is not valid
+#endif
+
+//Forward declaration of DhcpClientContext structure
+struct _DhcpClientContext;
+#define DhcpClientContext struct _DhcpClientContext
+
+
+/**
+ * @brief DHCP FSM states
+ **/
+
+typedef enum
+{
+   DHCP_STATE_INIT        = 0,
+   DHCP_STATE_SELECTING   = 1,
+   DHCP_STATE_REQUESTING  = 2,
+   DHCP_STATE_INIT_REBOOT = 3,
+   DHCP_STATE_REBOOTING   = 4,
+   DHCP_STATE_PROBING     = 5,
+   DHCP_STATE_BOUND       = 6,
+   DHCP_STATE_RENEWING    = 7,
+   DHCP_STATE_REBINDING   = 8
+} DhcpState;
+
+
+/**
+ * @brief DHCP configuration timeout callback
+ **/
+
+typedef void (*DhcpTimeoutCallback)(DhcpClientContext *context,
+   NetInterface *interface);
+
+
+/**
+ * @brief Link state change callback
+ **/
+
+typedef void (*DhcpLinkChangeCallback)(DhcpClientContext *context,
+   NetInterface *interface, bool_t linkState);
+
+
+/**
+ * @brief FSM state change callback
+ **/
+
+typedef void (*DhcpStateChangeCallback)(DhcpClientContext *context,
+   NetInterface *interface, DhcpState state);
+
+
+/**
+ * @brief DHCP client settings
+ **/
+
+typedef struct
+{
+   NetInterface *interface;                           ///<Network interface to configure
+   char_t hostname[DHCP_CLIENT_MAX_HOSTNAME_LEN + 1]; //Host name
+   bool_t rapidCommit;                                ///<Quick configuration using rapid commit
+   bool_t manualDnsConfig;                            ///<Force manual DNS configuration
+   systime_t timeout;                                 ///<DHCP configuration timeout
+   DhcpTimeoutCallback timeoutEvent;                  ///<DHCP configuration timeout event
+   DhcpLinkChangeCallback linkChangeEvent;            ///<Link state change event
+   DhcpStateChangeCallback stateChangeEvent;          ///<FSM state change event
+} DhcpClientSettings;
+
+
+/**
+ * @brief DHCP client context
+ **/
+
+struct _DhcpClientContext
+{
+   DhcpClientSettings settings; ///<DHCP client settings
+   bool_t running;              ///<This flag tells whether the DHCP client is running or not
+   DhcpState state;             ///<Current state of the FSM
+   bool_t timeoutEventDone;     ///<Timeout callback function has been called
+   systime_t timestamp;         ///<Timestamp to manage retransmissions
+   systime_t timeout;           ///<Timeout value
+   systime_t retransmitTimeout; ///<Retransmission timeout
+   uint_t retransmitCount;      ///<Retransmission counter
+   Ipv4Addr serverIpAddr;       ///<DHCP server IPv4 address
+   Ipv4Addr requestedIpAddr;    ///<Requested IPv4 address
+   uint32_t transactionId;      ///<Value to match requests with replies
+   systime_t configStartTime;   ///<Address acquisition or renewal process start time
+   systime_t leaseStartTime;    ///<Lease start time
+   uint32_t leaseTime;          ///<Lease time
+   uint32_t t1;                 ///<Time at which the client enters the RENEWING state
+   uint32_t t2;                 ///<Time at which the client enters the REBINDING state
+};
+
+
+//Tick counter to handle periodic operations
+extern systime_t dhcpClientTickCounter;
+
+//DHCP client related functions
+void dhcpClientGetDefaultSettings(DhcpClientSettings *settings);
+error_t dhcpClientInit(DhcpClientContext *context, const DhcpClientSettings *settings);
+error_t dhcpClientStart(DhcpClientContext *context);
+error_t dhcpClientStop(DhcpClientContext *context);
+DhcpState dhcpClientGetState(DhcpClientContext *context);
+
+void dhcpClientTick(DhcpClientContext *context);
+void dhcpClientLinkChangeEvent(DhcpClientContext *context);
+
+void dhcpClientStateInit(DhcpClientContext *context);
+void dhcpClientStateSelecting(DhcpClientContext *context);
+void dhcpClientStateRequesting(DhcpClientContext *context);
+void dhcpClientStateInitReboot(DhcpClientContext *context);
+void dhcpClientStateRebooting(DhcpClientContext *context);
+void dhcpClientStateProbing(DhcpClientContext *context);
+void dhcpClientStateBound(DhcpClientContext *context);
+void dhcpClientStateRenewing(DhcpClientContext *context);
+void dhcpClientStateRebinding(DhcpClientContext *context);
+
+error_t dhcpClientSendDiscover(DhcpClientContext *context);
+error_t dhcpClientSendRequest(DhcpClientContext *context);
+error_t dhcpClientSendDecline(DhcpClientContext *context);
+
+void dhcpClientProcessMessage(NetInterface *interface,
+   const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader,
+   const NetBuffer *buffer, size_t offset, void *params);
+
+void dhcpClientParseOffer(DhcpClientContext *context,
+   const DhcpMessage *message, size_t length);
+
+void dhcpClientParseAck(DhcpClientContext *context,
+   const DhcpMessage *message, size_t length);
+
+void dhcpClientParseNak(DhcpClientContext *context,
+   const DhcpMessage *message, size_t length);
+
+void dhcpClientCheckTimeout(DhcpClientContext *context);
+
+uint16_t dhcpClientComputeElapsedTime(DhcpClientContext *context);
+
+void dhcpClientChangeState(DhcpClientContext *context,
+   DhcpState newState, systime_t delay);
+
+void dhcpClientDumpConfig(DhcpClientContext *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dhcp/dhcp_common.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,155 @@
+/**
+ * @file dhcp_common.c
+ * @brief Functions common to DHCP client, server and BOOTP relay agent
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The Dynamic Host Configuration Protocol is used to provide configuration
+ * parameters to hosts. Refer to the following RFCs for complete details:
+ * - RFC 2131: Dynamic Host Configuration Protocol
+ * - RFC 2132: DHCP Options and BOOTP Vendor Extensions
+ * - RFC 4039: Rapid Commit Option for the DHCP version 4
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL DHCP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "dhcp/dhcp_common.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV4_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Append an option to a DHCP message
+ * @param[in] message Pointer to the DHCP message
+ * @param[in] optionCode Option code
+ * @param[in] optionValue Option value
+ * @param[in] optionLength Length of the option value
+ **/
+
+void dhcpAddOption(DhcpMessage *message, uint8_t optionCode,
+   const void *optionValue, size_t optionLength)
+{
+   size_t n;
+   DhcpOption *option;
+
+   //Point to the very first option
+   n = 0;
+
+   //Parse DHCP options
+   while(1)
+   {
+      //Point to the current option
+      option = (DhcpOption *) (message->options + n);
+
+      //End option detected?
+      if(option->code == DHCP_OPT_END)
+         break;
+
+      //Jump to next the next option
+      n += sizeof(DhcpOption) + option->length;
+   }
+
+   //Sanity check
+   if(optionLength <= UINT8_MAX)
+   {
+      //Point to the buffer where the option is to be written
+      option = (DhcpOption *) (message->options + n);
+
+      //Option code
+      option->code = optionCode;
+      //Option length
+      option->length = (uint8_t) optionLength;
+      //Option value
+      memcpy(option->value, optionValue, optionLength);
+
+      //Jump to next the next option
+      n += sizeof(DhcpOption) + option->length;
+
+      //Point to the buffer where the option is to be written
+      option = (DhcpOption *) (message->options + n);
+
+      //Always terminate the options field with 255
+      option->code = DHCP_OPT_END;
+   }
+}
+
+
+/**
+ * @brief Find the specified option in a DHCP message
+ * @param[in] message Pointer to the DHCP message
+ * @param[in] length Length of the message
+ * @param[in] optionCode Code of the option to find
+ * @return If the specified option is found, a pointer to the corresponding
+ *   option is returned. Otherwise NULL pointer is returned
+ **/
+
+DhcpOption *dhcpGetOption(const DhcpMessage *message,
+   size_t length, uint8_t optionCode)
+{
+   uint_t i;
+   DhcpOption *option;
+
+   //Make sure the DHCP header is valid
+   if(length < sizeof(DhcpMessage))
+      return NULL;
+   //Get the length of the options field
+   length -= sizeof(DhcpMessage);
+
+   //Parse DHCP options
+   for(i = 0; i < length; i++)
+   {
+      //Point to the current option
+      option = (DhcpOption *) (message->options + i);
+
+      //Pad option detected?
+      if(option->code == DHCP_OPT_PAD)
+         continue;
+      //End option detected?
+      if(option->code == DHCP_OPT_END)
+         break;
+      //Check option length
+      if((i + 1) >= length || (i + 1 + option->length) >= length)
+         break;
+
+      //Current option code matches the specified one?
+      if(option->code == optionCode)
+         return option;
+
+      //Jump to the next option
+      i += option->length + 1;
+   }
+
+   //Specified option code not found
+   return NULL;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dhcp/dhcp_common.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,237 @@
+/**
+ * @file dhcp_common.h
+ * @brief Functions common to DHCP client, server and BOOTP relay agent
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DHCP_COMMON_H
+#define _DHCP_COMMON_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/ethernet.h"
+#include "ipv4/ipv4.h"
+
+//UDP ports used by DHCP servers and clients
+#define DHCP_SERVER_PORT 67
+#define DHCP_CLIENT_PORT 68
+
+//Minimum size of DHCP messages
+#define DHCP_MIN_MSG_SIZE 300
+//Maximum size of DHCP messages
+#define DHCP_MAX_MSG_SIZE 548
+
+//Hardware type
+#define DHCP_HARDWARE_TYPE_ETH 1
+//DHCP magic cookie
+#define DHCP_MAGIC_COOKIE 0x63825363
+//Infinite lifetime representation
+#define DHCP_INFINITE_TIME 0xFFFFFFFF
+
+
+/**
+ * @brief DHCP opcodes
+ **/
+
+typedef enum
+{
+   DHCP_OPCODE_BOOTREQUEST = 1,
+   DHCP_OPCODE_BOOTREPLY   = 2
+} DhcpOpcode;
+
+
+/**
+ * @brief DHCP flags
+ **/
+
+typedef enum
+{
+   DHCP_FLAG_UNICAST   = 0x0000,
+   DHCP_FLAG_BROADCAST = 0x8000
+} DhcpFlags;
+
+
+/**
+ * @brief DHCP message types
+ **/
+
+typedef enum
+{
+   DHCP_MESSAGE_TYPE_DISCOVER = 1,
+   DHCP_MESSAGE_TYPE_OFFER    = 2,
+   DHCP_MESSAGE_TYPE_REQUEST  = 3,
+   DHCP_MESSAGE_TYPE_DECLINE  = 4,
+   DHCP_MESSAGE_TYPE_ACK      = 5,
+   DHCP_MESSAGE_TYPE_NAK      = 6,
+   DHCP_MESSAGE_TYPE_RELEASE  = 7,
+   DHCP_MESSAGE_TYPE_INFORM   = 8
+} DhcpMessageType;
+
+
+/**
+ * @brief DHCP option codes
+ **/
+
+typedef enum
+{
+   DHCP_OPT_PAD                          = 0,
+   DHCP_OPT_SUBNET_MASK                  = 1,
+   DHCP_OPT_TIME_OFFSET                  = 2,
+   DHCP_OPT_ROUTER                       = 3,
+   DHCP_OPT_TIME_SERVER                  = 4,
+   DHCP_OPT_NAME_SERVER                  = 5,
+   DHCP_OPT_DNS_SERVER                   = 6,
+   DHCP_OPT_LOG_SERVER                   = 7,
+   DHCP_OPT_COOKIE_SERVER                = 8,
+   DHCP_OPT_LPR_SERVER                   = 9,
+   DHCP_OPT_IMPRESS_SERVER               = 10,
+   DHCP_OPT_RESOURCE_LOCATION_SERVER     = 11,
+   DHCP_OPT_HOST_NAME                    = 12,
+   DHCP_OPT_BOOT_FILE_SIZE               = 13,
+   DHCP_OPT_MERIT_DUMP_FILE              = 14,
+   DHCP_OPT_DOMAIN_NAME                  = 15,
+   DHCP_OPT_SWAP_SERVER                  = 16,
+   DHCP_OPT_ROOT_PATH                    = 17,
+   DHCP_OPT_EXTENSIONS_PATH              = 18,
+   DHCP_OPT_IP_FORWARDING                = 19,
+   DHCP_OPT_NON_LOCAL_SOURCE_ROUTING     = 20,
+   DHCP_OPT_POLICY_FILTER                = 21,
+   DHCP_OPT_MAX_DATAGRAM_REASSEMBLY_SIZE = 22,
+   DHCP_OPT_DEFAULT_IP_TTL               = 23,
+   DHCP_OPT_PATH_MTU_AGING_TIMEOUT       = 24,
+   DHCP_OPT_PATH_MTU_PLATEAU_TABLE       = 25,
+   DHCP_OPT_INTERFACE_MTU                = 26,
+   DHCP_OPT_ALL_SUBNETS_ARE_LOCAL        = 27,
+   DHCP_OPT_BROADCAST_ADDRESS            = 28,
+   DHCP_OPT_PERFORM_MASK_DISCOVERY       = 29,
+   DHCP_OPT_MASK_SUPPLIER                = 30,
+   DHCP_OPT_PERFORM_ROUTER_DISCOVERY     = 31,
+   DHCP_OPT_ROUTER_SOLICITATION_ADDRESS  = 32,
+   DHCP_OPT_STATIC_ROUTE                 = 33,
+   DHCP_OPT_TRAILER_ENCAPSULATION        = 34,
+   DHCP_OPT_ARP_CACHE_TIMEOUT            = 35,
+   DHCP_OPT_ETHERNET_ENCAPSULATION       = 36,
+   DHCP_OPT_TCP_DEFAULT_TTL              = 37,
+   DHCP_OPT_TCP_KEEPALIVE_INTERVAL       = 38,
+   DHCP_OPT_TCP_KEEPALIVE_GARBAGE        = 39,
+   DHCP_OPT_NIS_DOMAIN                   = 40,
+   DHCP_OPT_NIS_SERVER                   = 41,
+   DHCP_OPT_NTP_SERVER                   = 42,
+   DHCP_OPT_VENDOR_SPECIFIC_INFO         = 43,
+   DHCP_OPT_NETBIOS_NBNS_SERVER          = 44,
+   DHCP_OPT_NETBIOS_NBDD_SERVER          = 45,
+   DHCP_OPT_NETBIOS_NODE_TYPE            = 46,
+   DHCP_OPT_NETBIOS_SCOPE                = 47,
+   DHCP_OPT_X11_FONT_SERVER              = 48,
+   DHCP_OPT_X11_DISPLAY_MANAGER          = 49,
+   DHCP_OPT_REQUESTED_IP_ADDRESS         = 50,
+   DHCP_OPT_IP_ADDRESS_LEASE_TIME        = 51,
+   DHCP_OPT_OPTION_OVERLOAD              = 52,
+   DHCP_OPT_DHCP_MESSAGE_TYPE            = 53,
+   DHCP_OPT_SERVER_IDENTIFIER            = 54,
+   DHCP_OPT_PARAM_REQUEST_LIST           = 55,
+   DHCP_OPT_MESSAGE                      = 56,
+   DHCP_OPT_MAX_DHCP_MESSAGE_SIZE        = 57,
+   DHCP_OPT_RENEWAL_TIME_VALUE           = 58,
+   DHCP_OPT_REBINDING_TIME_VALUE         = 59,
+   DHCP_OPT_VENDOR_CLASS_IDENTIFIER      = 60,
+   DHCP_OPT_CLIENT_IDENTIFIER            = 61,
+   DHCP_OPT_NISP_DOMAIN                  = 64,
+   DHCP_OPT_NISP_SERVER                  = 65,
+   DHCP_OPT_TFTP_SERVER_NAME             = 66,
+   DHCP_OPT_BOOTFILE_NAME                = 67,
+   DHCP_OPT_MOBILE_IP_HOME_AGENT         = 68,
+   DHCP_OPT_SMTP_SERVER                  = 69,
+   DHCP_OPT_POP3_SERVER                  = 70,
+   DHCP_OPT_NNTP_SERVER                  = 71,
+   DHCP_OPT_DEFAULT_WWW_SERVER           = 72,
+   DHCP_OPT_DEFAULT_FINGER_SERVER        = 73,
+   DHCP_OPT_DEFAULT_IRC_SERVER           = 74,
+   DHCP_OPT_STREETTALK_SERVER            = 75,
+   DHCP_OPT_STDA_SERVER                  = 76,
+   DHCP_OPT_RAPID_COMMIT                 = 80,
+   DHCP_OPT_END                          = 255
+} DhcpOptionCode;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief DHCP message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t op;           //0
+   uint8_t htype;        //1
+   uint8_t hlen;         //2
+   uint8_t hops;         //3
+   uint32_t xid;         //4-7
+   uint16_t secs;        //8-9
+   uint16_t flags;       //10-11
+   Ipv4Addr ciaddr;      //12-15
+   Ipv4Addr yiaddr;      //16-19
+   Ipv4Addr siaddr;      //20-23
+   Ipv4Addr giaddr;      //24-27
+   MacAddr chaddr;       //28-33
+   uint8_t unused[10];   //34-43
+   uint8_t sname[64];    //44-107
+   uint8_t file[128];    //108-235
+   uint32_t magicCookie; //236-239
+   uint8_t options[];    //240
+} __end_packed DhcpMessage;
+
+
+/**
+ * @brief DHCP option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t code;    //0
+   uint8_t length;  //1
+   uint8_t value[]; //2
+} __end_packed DhcpOption;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+//DHCP related functions
+void dhcpAddOption(DhcpMessage *message, uint8_t optionCode,
+   const void *optionValue, size_t optionLength);
+
+DhcpOption *dhcpGetOption(const DhcpMessage *message,
+   size_t length, uint8_t optionCode);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dhcp/dhcp_debug.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,579 @@
+/**
+ * @file dhcp_debug.c
+ * @brief Data logging functions for debugging purpose (DHCP)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL DHCP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "dhcp/dhcp_debug.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV4_SUPPORT == ENABLED && DHCP_TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+
+//DHCP message opcodes
+static const char_t *opcodeLabel[] =
+{
+   "",            //0
+   "BOOTREQUEST", //1
+   "BOOTREPLY"    //2
+};
+
+//DHCP message types
+static const char_t *messageLabel[] =
+{
+   "",             //0
+   "DHCPDISCOVER", //1
+   "DHCPOFFER",    //2
+   "DHCPREQUEST",  //3
+   "DHCPDECLINE",  //4
+   "DHCPACK",      //5
+   "DHCPNAK",      //6
+   "DHCPRELEASE",  //7
+   "DHCPINFORM"    //8
+};
+
+//DHCP options
+static const char_t *optionLabel[] =
+{
+   "Pad",                          //0
+   "Subnet Mask",                  //1
+   "Time Offset",                  //2
+   "Router",                       //3
+   "Time Server",                  //4
+   "Name Server",                  //5
+   "DNS Server",                   //6
+   "Log Server",                   //7
+   "Cookie Server",                //8
+   "LPR Server",                   //9
+   "Impress Server",               //10
+   "Resource Location Server",     //11
+   "Host Name",                    //12
+   "Boot File Size",               //13
+   "Merit Dump File",              //14
+   "Domain Name",                  //15
+   "Swap Server",                  //16
+   "Root Path",                    //17
+   "Extensions Path",              //18
+   "IP Forwarding",                //19
+   "Non-Local Source Routing",     //20
+   "Policy Filter",                //21
+   "Max Datagram Reassembly Size", //22
+   "Default IP TTL",               //23
+   "Path MTU Aging Timeout",       //24
+   "Path MTU Plateau Table",       //25
+   "Interface MTU",                //26
+   "All Subnets Are Local",        //27
+   "Broadcast Address",            //28
+   "Perform Mask Discovery",       //29
+   "Mask Supplier",                //30
+   "Perform Router Discovery",     //31
+   "Router Solicitation Address",  //32
+   "Static Route",                 //33
+   "Trailer Encapsulation",        //34
+   "ARP Cache Timeout",            //35
+   "Ethernet Encapsulation",       //36
+   "TCP Default TTL",              //37
+   "TCP Keepalive Interval",       //38
+   "TCP Keepalive Garbage",        //39
+   "NIS Domain",                   //40
+   "NIS Server",                   //41
+   "NTP Server",                   //42
+   "Vendor Specific Information",  //43
+   "NetBIOS NBNS Server",          //44
+   "NetBIOS NBDD Server",          //45
+   "NetBIOS Node Type",            //46
+   "NetBIOS Scope",                //47
+   "X11 Font Server",              //48
+   "X11 Display Manager",          //49
+   "Requested IP Address",         //50
+   "IP Address Lease Time",        //51
+   "Option Overload",              //52
+   "DHCP Message Type",            //53
+   "Server Identifier",            //54
+   "Parameter Request List",       //55
+   "Message",                      //56
+   "Max DHCP Message Size",        //57
+   "Renewal (T1) Time Value",      //58
+   "Rebinding (T2) Time Value",    //59
+   "Vendor Class Identifier",      //60
+   "Client Identifier",            //61
+   "",                             //62
+   "",                             //63
+   "NISP Domain",                  //64
+   "NISP Server",                  //65
+   "TFTP Server Name",             //66
+   "Bootfile Name",                //67
+   "Mobile IP Home Agent",         //68
+   "SMTP Server",                  //69
+   "POP3 Server",                  //70
+   "NNTP Server",                  //71
+   "Default WWW Server",           //72
+   "Default Finger Server",        //73
+   "Default IRC Server",           //74
+   "StreetTalk Server",            //75
+   "STDA Server",                  //76
+   "",                             //77
+   "",                             //78
+   "",                             //79
+   "Rapid Commit"                  //80
+};
+
+
+/**
+ * @brief Dump DHCP message for debugging purpose
+ * @param[in] message Pointer to the DHCP message to dump
+ * @param[in] length Length of the message
+ * @return Error code
+ **/
+
+error_t dhcpDumpMessage(const DhcpMessage *message, size_t length)
+{
+   error_t error;
+   uint_t i;
+   const char_t *label;
+   DhcpOption *option;
+
+   //Ensure the length of the DHCP message is acceptable
+   if(length < sizeof(DhcpMessage))
+   {
+      //Report a warning
+      TRACE_WARNING("DHCP message length is invalid!\r\n");
+      //Dump message contents for debugging purpose
+      TRACE_DEBUG_ARRAY("  ", message, length);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Retrieve the name associated with the opcode
+   label = (message->op < arraysize(opcodeLabel)) ? opcodeLabel[message->op] : "";
+
+   //Dump DHCP message contents
+   TRACE_DEBUG("  Op Code (op) = %" PRIu8 " (%s)\r\n", message->op, label);
+   TRACE_DEBUG("  Hardware Type (htype) = %" PRIu8 "\r\n", message->htype);
+   TRACE_DEBUG("  Hardware Address Length (hlen) = %" PRIu8 "\r\n", message->hlen);
+   TRACE_DEBUG("  Hops (hops) = %" PRIu8 "\r\n", message->hops);
+   TRACE_DEBUG("  Transaction ID (xid) = 0x%08" PRIX32 "\r\n", ntohl(message->xid));
+   TRACE_DEBUG("  Seconds (secs) = %" PRIu16 "s\r\n", ntohs(message->secs));
+   TRACE_DEBUG("  Flags (flags) = 0x%04" PRIX16 "\r\n", ntohs(message->flags));
+   TRACE_DEBUG("  Client IP Address (ciaddr) = %s\r\n", ipv4AddrToString(message->ciaddr, NULL));
+   TRACE_DEBUG("  Your IP Address (yiaddr) = %s\r\n", ipv4AddrToString(message->yiaddr, NULL));
+   TRACE_DEBUG("  Server IP Address (siaddr) = %s\r\n", ipv4AddrToString(message->siaddr, NULL));
+   TRACE_DEBUG("  Relay IP Address (giaddr) = %s\r\n", ipv4AddrToString(message->giaddr, NULL));
+   TRACE_DEBUG("  Client Hardware Address (chaddr) = %s\r\n", macAddrToString(&message->chaddr, NULL));
+   TRACE_DEBUG("  Magic Cookie = 0x%08" PRIX32 "\r\n", ntohl(message->magicCookie));
+
+   //Get the length of the options field
+   length -= sizeof(DhcpMessage);
+
+   //Parse DHCP options
+   for(i = 0; i < length; i++)
+   {
+      //Point to the current option
+      option = (DhcpOption *) (message->options + i);
+
+      //Pad option detected?
+      if(option->code == DHCP_OPT_PAD)
+         continue;
+      //End option detected?
+      if(option->code == DHCP_OPT_END)
+         break;
+      //Check option length
+      if((i + 1) >= length || (i + 1 + option->length) >= length)
+      {
+         //Report a warning
+         TRACE_WARNING("DHCP option length is invalid!\r\n");
+         //Dump message contents for debugging purpose
+         TRACE_DEBUG_ARRAY("  ", message, length);
+         //Report an error
+         return ERROR_INVALID_LENGTH;
+      }
+
+      //Display the name of the current option
+      if(option->code < arraysize(optionLabel))
+         TRACE_DEBUG("  %s option (%" PRIu8 " bytes)\r\n", optionLabel[option->code], option->length);
+      else
+         TRACE_DEBUG("  Option %" PRIu8 " (%" PRIu8 " bytes)\r\n", option->code, option->length);
+
+      //Check option code
+      switch(option->code)
+      {
+      //Message type?
+      case DHCP_OPT_DHCP_MESSAGE_TYPE:
+         error = dhcpDumpMessageType(option);
+         break;
+      //Parameter Request List option
+      case DHCP_OPT_PARAM_REQUEST_LIST:
+         error = dhcpDumpParamRequestList(option);
+         break;
+      //Boolean value?
+      case DHCP_OPT_IP_FORWARDING:
+      case DHCP_OPT_NON_LOCAL_SOURCE_ROUTING:
+      case DHCP_OPT_ALL_SUBNETS_ARE_LOCAL:
+      case DHCP_OPT_PERFORM_MASK_DISCOVERY:
+      case DHCP_OPT_MASK_SUPPLIER:
+      case DHCP_OPT_PERFORM_ROUTER_DISCOVERY:
+      case DHCP_OPT_TRAILER_ENCAPSULATION:
+      case DHCP_OPT_ETHERNET_ENCAPSULATION:
+      case DHCP_OPT_TCP_KEEPALIVE_GARBAGE:
+         error = dhcpDumpBoolean(option);
+         break;
+      //8-bit unsigned integer?
+      case DHCP_OPT_DEFAULT_IP_TTL:
+      case DHCP_OPT_TCP_DEFAULT_TTL:
+      case DHCP_OPT_NETBIOS_NODE_TYPE:
+      case DHCP_OPT_OPTION_OVERLOAD:
+         error = dhcpDumpInt8(option);
+         break;
+      //16-bit unsigned integer?
+      case DHCP_OPT_BOOT_FILE_SIZE:
+      case DHCP_OPT_MAX_DATAGRAM_REASSEMBLY_SIZE:
+      case DHCP_OPT_INTERFACE_MTU:
+      case DHCP_OPT_MAX_DHCP_MESSAGE_SIZE:
+         error = dhcpDumpInt16(option);
+         break;
+      //32-bit unsigned integer?
+      case DHCP_OPT_PATH_MTU_AGING_TIMEOUT:
+      case DHCP_OPT_ARP_CACHE_TIMEOUT:
+      case DHCP_OPT_TCP_KEEPALIVE_INTERVAL:
+      case DHCP_OPT_IP_ADDRESS_LEASE_TIME:
+      case DHCP_OPT_RENEWAL_TIME_VALUE:
+      case DHCP_OPT_REBINDING_TIME_VALUE:
+         error = dhcpDumpInt32(option);
+         break;
+      //Character strings?
+      case DHCP_OPT_HOST_NAME:
+      case DHCP_OPT_MERIT_DUMP_FILE:
+      case DHCP_OPT_DOMAIN_NAME:
+      case DHCP_OPT_ROOT_PATH:
+      case DHCP_OPT_EXTENSIONS_PATH:
+      case DHCP_OPT_NIS_DOMAIN:
+      case DHCP_OPT_MESSAGE:
+      case DHCP_OPT_NISP_DOMAIN:
+      case DHCP_OPT_TFTP_SERVER_NAME:
+      case DHCP_OPT_BOOTFILE_NAME:
+         error = dhcpDumpString(option);
+         break;
+      //IPv4 address?
+      case DHCP_OPT_SUBNET_MASK:
+      case DHCP_OPT_SWAP_SERVER:
+      case DHCP_OPT_BROADCAST_ADDRESS:
+      case DHCP_OPT_ROUTER_SOLICITATION_ADDRESS:
+      case DHCP_OPT_REQUESTED_IP_ADDRESS:
+      case DHCP_OPT_SERVER_IDENTIFIER:
+         error = dhcpDumpIpv4Addr(option);
+         break;
+      //List of IPv4 addresses?
+      case DHCP_OPT_ROUTER:
+      case DHCP_OPT_TIME_SERVER:
+      case DHCP_OPT_NAME_SERVER:
+      case DHCP_OPT_DNS_SERVER:
+      case DHCP_OPT_LOG_SERVER:
+      case DHCP_OPT_COOKIE_SERVER:
+      case DHCP_OPT_LPR_SERVER:
+      case DHCP_OPT_IMPRESS_SERVER:
+      case DHCP_OPT_RESOURCE_LOCATION_SERVER:
+      case DHCP_OPT_NIS_SERVER:
+      case DHCP_OPT_NTP_SERVER:
+      case DHCP_OPT_NETBIOS_NBNS_SERVER:
+      case DHCP_OPT_NETBIOS_NBDD_SERVER:
+      case DHCP_OPT_X11_FONT_SERVER:
+      case DHCP_OPT_X11_DISPLAY_MANAGER:
+      case DHCP_OPT_NISP_SERVER:
+      case DHCP_OPT_MOBILE_IP_HOME_AGENT:
+      case DHCP_OPT_SMTP_SERVER:
+      case DHCP_OPT_POP3_SERVER:
+      case DHCP_OPT_NNTP_SERVER:
+      case DHCP_OPT_DEFAULT_WWW_SERVER:
+      case DHCP_OPT_DEFAULT_FINGER_SERVER:
+      case DHCP_OPT_DEFAULT_IRC_SERVER:
+      case DHCP_OPT_STREETTALK_SERVER:
+      case DHCP_OPT_STDA_SERVER:
+         error = dhcpDumpIpv4AddrList(option);
+         break;
+      //Raw data?
+      default:
+         error = dhcpDumpRawData(option);
+         break;
+      }
+
+      //Failed to parse current option?
+      if(error)
+      {
+         //Report a warning
+         TRACE_WARNING("Failed to parse DHCP options!\r\n");
+         //Dump message contents for debugging purpose
+         TRACE_DEBUG_ARRAY("  ", message, length);
+      }
+
+      //Jump to the next option
+      i += option->length + 1;
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump Message Type option
+ * @param[in] option Pointer to the option to dump
+ * @return Error code
+ **/
+
+error_t dhcpDumpMessageType(const DhcpOption *option)
+{
+   uint8_t type;
+   const char_t *label;
+
+   //Check option length
+   if(option->length != 1)
+      return ERROR_INVALID_OPTION;
+
+   //Get the message type
+   type = option->value[0];
+   //Retrieve the name of the current DHCP message
+   label = (type < arraysize(messageLabel)) ? messageLabel[type] : "Unknown";
+   //Display message type
+   TRACE_DEBUG("    %" PRIu8 " (%s)\r\n", type, label);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump Parameter Request List option
+ * @param[in] option Pointer to the option to dump
+ * @return Error code
+ **/
+
+error_t dhcpDumpParamRequestList(const DhcpOption *option)
+{
+   size_t i;
+   uint8_t code;
+   const char_t *label;
+
+   //Parse the list of requested options
+   for(i = 0; i < option->length; i++)
+   {
+      //Get current option code
+      code = option->value[i];
+      //Find the name associated with this option code
+      label = (code < arraysize(optionLabel)) ? optionLabel[code] : "Unknown";
+      //Display option code and option name
+      TRACE_DEBUG("    %" PRIu8 " (%s option)\r\n", code, label);
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump an option containing a boolean
+ * @param[in] option Pointer to the option to dump
+ * @return Error code
+ **/
+
+error_t dhcpDumpBoolean(const DhcpOption *option)
+{
+   //Check option length
+   if(option->length != 1)
+      return ERROR_INVALID_OPTION;
+
+   //Dump option contents
+   TRACE_DEBUG("    %" PRIu8 " (%s)\r\n", option->value[0], option->value[0] ? "True" : "False");
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump an option containing a 8-bit integer
+ * @param[in] option Pointer to the option to dump
+ * @return Error code
+ **/
+
+error_t dhcpDumpInt8(const DhcpOption *option)
+{
+   //Check option length
+   if(option->length != 1)
+      return ERROR_INVALID_OPTION;
+
+   //Dump option contents
+   TRACE_DEBUG("    %" PRIu8 "\r\n", option->value[0]);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump an option containing a 16-bit integer
+ * @param[in] option Pointer to the option to dump
+ * @return Error code
+ **/
+
+error_t dhcpDumpInt16(const DhcpOption *option)
+{
+   uint16_t value;
+
+   //Check option length
+   if(option->length != 2)
+      return ERROR_INVALID_OPTION;
+
+   //Retrieve 16-bit value
+   value = LOAD16BE(option->value);
+   //Dump option contents
+   TRACE_DEBUG("    %" PRIu16 "\r\n", value);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump an option containing a 32-bit integer
+ * @param[in] option Pointer to the option to dump
+ * @return Error code
+ **/
+
+error_t dhcpDumpInt32(const DhcpOption *option)
+{
+   uint32_t value;
+
+   //Check option length
+   if(option->length != 4)
+      return ERROR_INVALID_OPTION;
+
+   //Retrieve 32-bit value
+   value = LOAD32BE(option->value);
+   //Dump option contents
+   TRACE_DEBUG("    %" PRIu32 "\r\n", value);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump an option containing a string
+ * @param[in] option Pointer to the option to dump
+ * @return Error code
+ **/
+
+error_t dhcpDumpString(const DhcpOption *option)
+{
+   size_t i;
+
+   //Append prefix
+   TRACE_DEBUG("    ");
+   //Dump option contents
+   for(i = 0; i < option->length; i++)
+      TRACE_DEBUG("%c", option->value[i]);
+   //Add a line feed
+   TRACE_DEBUG("\r\n");
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump an option containing an IPv4 address
+ * @param[in] option Pointer to the option to dump
+ * @return Error code
+ **/
+
+error_t dhcpDumpIpv4Addr(const DhcpOption *option)
+{
+   Ipv4Addr ipAddr;
+
+   //Check option length
+   if(option->length != sizeof(Ipv4Addr))
+      return ERROR_INVALID_OPTION;
+
+   //Retrieve IPv4 address
+   ipv4CopyAddr(&ipAddr, option->value);
+   //Dump option contents
+   TRACE_DEBUG("    %s\r\n", ipv4AddrToString(ipAddr, NULL));
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump an option containing a list of IPv4 addresses
+ * @param[in] option Pointer to the option to dump
+ * @return Error code
+ **/
+
+error_t dhcpDumpIpv4AddrList(const DhcpOption *option)
+{
+   size_t i;
+   Ipv4Addr ipAddr;
+
+   //Check option length
+   if((option->length % sizeof(Ipv4Addr)) != 0)
+      return ERROR_INVALID_OPTION;
+
+   //Parse the list of IPv4 addresses
+   for(i = 0; i < option->length; i += sizeof(Ipv4Addr))
+   {
+      //Retrieve the current IPv4 address
+      ipv4CopyAddr(&ipAddr, option->value + i);
+      //Display current address
+      TRACE_DEBUG("    %s\r\n", ipv4AddrToString(ipAddr, NULL));
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump an option containing raw data
+ * @param[in] option Pointer to the option to dump
+ * @return Error code
+ **/
+
+error_t dhcpDumpRawData(const DhcpOption *option)
+{
+   //Dump option contents
+   TRACE_DEBUG_ARRAY("    ", option->value, option->length);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dhcp/dhcp_debug.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,58 @@
+/**
+ * @file dhcp_debug.h
+ * @brief Data logging functions for debugging purpose (DHCP)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DHCP_DEBUG_H
+#define _DHCP_DEBUG_H
+
+//Dependencies
+#include "core/net.h"
+#include "dhcp/dhcp_common.h"
+#include "debug.h"
+
+//Check current trace level
+#if (DHCP_TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+
+//Related functions
+error_t dhcpDumpMessage(const DhcpMessage *message, size_t length);
+error_t dhcpDumpMessageType(const DhcpOption *option);
+error_t dhcpDumpParamRequestList(const DhcpOption *option);
+error_t dhcpDumpBoolean(const DhcpOption *option);
+error_t dhcpDumpInt8(const DhcpOption *option);
+error_t dhcpDumpInt16(const DhcpOption *option);
+error_t dhcpDumpInt32(const DhcpOption *option);
+error_t dhcpDumpString(const DhcpOption *option);
+error_t dhcpDumpIpv4Addr(const DhcpOption *option);
+error_t dhcpDumpIpv4AddrList(const DhcpOption *option);
+error_t dhcpDumpRawData(const DhcpOption *option);
+
+#else
+   #define dhcpDumpMessage(message, length)
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dhcp/dhcp_server.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1034 @@
+/**
+ * @file dhcp_server.c
+ * @brief DHCP server (Dynamic Host Configuration Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL DHCP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "dhcp/dhcp_server.h"
+#include "dhcp/dhcp_common.h"
+#include "dhcp/dhcp_debug.h"
+#include "date_time.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV4_SUPPORT == ENABLED && DHCP_SERVER_SUPPORT == ENABLED)
+
+//Tick counter to handle periodic operations
+systime_t dhcpServerTickCounter;
+
+
+/**
+ * @brief Initialize settings with default values
+ * @param[out] settings Structure that contains DHCP server settings
+ **/
+
+void dhcpServerGetDefaultSettings(DhcpServerSettings *settings)
+{
+   uint_t i;
+
+   //Use default interface
+   settings->interface = netGetDefaultInterface();
+
+   //Support for quick configuration using rapid commit
+   settings->rapidCommit = FALSE;
+   //Lease time, in seconds, assigned to the DHCP clients
+   settings->leaseTime = DHCP_SERVER_DEFAULT_LEASE_TIME;
+
+   //Lowest and highest IP addresses in the pool that are available
+   //for dynamic address assignment
+   settings->ipAddrRangeMin = IPV4_UNSPECIFIED_ADDR;
+   settings->ipAddrRangeMax = IPV4_UNSPECIFIED_ADDR;
+
+   //Subnet mask
+   settings->subnetMask = IPV4_UNSPECIFIED_ADDR;
+   //Default gateway
+   settings->defaultGateway = IPV4_UNSPECIFIED_ADDR;
+
+   //DNS servers
+   for(i = 0; i < DHCP_SERVER_MAX_DNS_SERVERS; i++)
+      settings->dnsServer[i] = IPV4_UNSPECIFIED_ADDR;
+}
+
+
+/**
+ * @brief DHCP server initialization
+ * @param[in] context Pointer to the DHCP server context
+ * @param[in] settings DHCP server specific settings
+ * @return Error code
+ **/
+
+error_t dhcpServerInit(DhcpServerContext *context, const DhcpServerSettings *settings)
+{
+   error_t error;
+   NetInterface *interface;
+
+   //Debug message
+   TRACE_INFO("Initializing DHCP server...\r\n");
+
+   //Ensure the parameters are valid
+   if(context == NULL || settings == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Valid network interface?
+   if(settings->interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Point to the underlying network interface
+   interface = settings->interface;
+
+   //Clear the DHCP server context
+   memset(context, 0, sizeof(DhcpServerContext));
+   //Save user settings
+   context->settings = *settings;
+
+   //Next IP address that will be assigned by the DHCP server
+   context->nextIpAddr = settings->ipAddrRangeMin;
+   //DHCP server is currently suspended
+   context->running = FALSE;
+
+   //Callback function to be called when a DHCP message is received
+   error = udpAttachRxCallback(interface, DHCP_SERVER_PORT,
+      dhcpServerProcessMessage, context);
+
+   //Check status code
+   if(!error)
+   {
+      //Attach the DHCP server context to the network interface
+      interface->dhcpServerContext = context;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Start DHCP server
+ * @param[in] context Pointer to the DHCP server context
+ * @return Error code
+ **/
+
+error_t dhcpServerStart(DhcpServerContext *context)
+{
+   //Check parameter
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Starting DHCP server...\r\n");
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Start DHCP server
+   context->running = TRUE;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Stop DHCP server
+ * @param[in] context Pointer to the DHCP server context
+ * @return Error code
+ **/
+
+error_t dhcpServerStop(DhcpServerContext *context)
+{
+   //Check parameter
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Stopping DHCP server...\r\n");
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Stop DHCP server
+   context->running = FALSE;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief DHCP server timer handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * manage DHCP server operation
+ *
+ * @param[in] context Pointer to the DHCP server context
+ **/
+
+void dhcpServerTick(DhcpServerContext *context)
+{
+   uint_t i;
+   systime_t time;
+   systime_t leaseTime;
+   DhcpServerBinding *binding;
+
+   //Make sure the DHCP server has been properly instantiated
+   if(context == NULL)
+      return;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Convert the lease time to milliseconds
+   if(context->settings.leaseTime < (MAX_DELAY / 1000))
+      leaseTime = context->settings.leaseTime * 1000;
+   else
+      leaseTime = MAX_DELAY;
+
+   //Loop through the list of bindings
+   for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
+   {
+      //Point to the current binding
+      binding = &context->clientBinding[i];
+
+      //Valid binding?
+      if(!macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR))
+      {
+         //Check whether the network address has been committed
+         if(binding->validLease)
+         {
+            //Check whether the lease has expired
+            if(timeCompare(time, binding->timestamp + leaseTime) >= 0)
+            {
+               //The address lease is not more valid
+               binding->validLease = FALSE;
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Process incoming DHCP message
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader UDP pseudo header
+ * @param[in] udpHeader UDP header
+ * @param[in] buffer Multi-part buffer containing the incoming DHCP message
+ * @param[in] offset Offset to the first byte of the DHCP message
+ * @param[in] params Pointer to the DHCP server context
+ **/
+
+void dhcpServerProcessMessage(NetInterface *interface,
+   const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader,
+   const NetBuffer *buffer, size_t offset, void *params)
+{
+   size_t length;
+   DhcpServerContext *context;
+   DhcpMessage *message;
+   DhcpOption *option;
+
+   //Point to the DHCP server context
+   context = (DhcpServerContext *) params;
+
+   //Retrieve the length of the DHCP message
+   length = netBufferGetLength(buffer) - offset;
+
+   //Make sure the DHCP message is valid
+   if(length < sizeof(DhcpMessage))
+      return;
+   if(length > DHCP_MAX_MSG_SIZE)
+      return;
+
+   //Point to the beginning of the DHCP message
+   message = netBufferAt(buffer, offset);
+   //Sanity check
+   if(message == NULL)
+      return;
+
+   //Debug message
+   TRACE_DEBUG("\r\n%s: DHCP message received (%" PRIuSIZE " bytes)...\r\n",
+      formatSystemTime(osGetSystemTime(), NULL), length);
+
+   //Dump the contents of the message for debugging purpose
+   dhcpDumpMessage(message, length);
+
+   //Check opcode
+   if(message->op != DHCP_OPCODE_BOOTREQUEST)
+      return;
+   //Enforce hardware type
+   if(message->htype != DHCP_HARDWARE_TYPE_ETH)
+      return;
+   //Check the length of the hardware address
+   if(message->hlen != sizeof(MacAddr))
+      return;
+   //Check magic cookie
+   if(message->magicCookie != HTONL(DHCP_MAGIC_COOKIE))
+      return;
+
+   //Retrieve DHCP Message Type option
+   option = dhcpGetOption(message, length, DHCP_OPT_DHCP_MESSAGE_TYPE);
+
+   //Failed to retrieve specified option?
+   if(option == NULL || option->length != 1)
+      return;
+
+   //Check message type
+   switch(option->value[0])
+   {
+   case DHCP_MESSAGE_TYPE_DISCOVER:
+      //Parse DHCPDISCOVER message
+      dhcpServerParseDiscover(context, message, length);
+      break;
+   case DHCP_MESSAGE_TYPE_REQUEST:
+      //Parse DHCPREQUEST message
+      dhcpServerParseRequest(context, message, length);
+      break;
+   case DHCP_MESSAGE_TYPE_DECLINE:
+      //Parse DHCPDECLINE message
+      dhcpServerParseDecline(context, message, length);
+      break;
+   case DHCP_MESSAGE_TYPE_RELEASE:
+      //Parse DHCPRELEASE message
+      dhcpServerParseRelease(context, message, length);
+      break;
+   case DHCP_MESSAGE_TYPE_INFORM:
+      //Parse DHCPINFORM message
+      dhcpServerParseInform(context, message, length);
+      break;
+   default:
+      //Silently drop incoming message
+      break;
+   }
+}
+
+
+/**
+ * @brief Parse DHCPDISCOVER message
+ * @param[in] context Pointer to the DHCP server context
+ * @param[in] message Pointer to the incoming DHCP message
+ * @param[in] length Length of the incoming message to parse
+ **/
+
+void dhcpServerParseDiscover(DhcpServerContext *context,
+   const DhcpMessage *message, size_t length)
+{
+   error_t error;
+   NetInterface *interface;
+   Ipv4Addr requestedIpAddr;
+   DhcpOption *option;
+   DhcpServerBinding *binding;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Retrieve Server Identifier option
+   option = dhcpGetOption(message, length, DHCP_OPT_SERVER_IDENTIFIER);
+
+   //Option found?
+   if(option != NULL && option->length == 4)
+   {
+      //Unexpected server identifier?
+      if(!ipv4CompAddr(option->value, &interface->ipv4Context.addr))
+         return;
+   }
+
+   //Retrieve Requested IP Address option
+   option = dhcpGetOption(message, length, DHCP_OPT_REQUESTED_IP_ADDRESS);
+
+   //The client may include the 'requested IP address' option to suggest
+   //that a particular IP address be assigned
+   if(option != NULL && option->length == 4)
+      ipv4CopyAddr(&requestedIpAddr, option->value);
+   else
+      requestedIpAddr = IPV4_UNSPECIFIED_ADDR;
+
+   //Search the list for a matching binding
+   binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr);
+
+   //Matching binding found?
+   if(binding != NULL)
+   {
+      //Different IP address than cached?
+      if(requestedIpAddr != binding->ipAddr)
+      {
+         //Ensure the IP address is in the server's pool of available addresses
+         if(ntohl(requestedIpAddr) >= ntohl(context->settings.ipAddrRangeMin) &&
+            ntohl(requestedIpAddr) <= ntohl(context->settings.ipAddrRangeMax))
+         {
+            //Make sure the IP address is not already allocated
+            if(!dhcpServerFindBindingByIpAddr(context, requestedIpAddr))
+            {
+               //Record IP address
+               binding->ipAddr = requestedIpAddr;
+               //Get current time
+               binding->timestamp = osGetSystemTime();
+            }
+         }
+      }
+
+      //Sucessful processing
+      error = NO_ERROR;
+   }
+   else
+   {
+      //Create a new binding
+      binding = dhcpServerCreateBinding(context);
+
+      //Binding successfully created
+      if(binding != NULL)
+      {
+         //Ensure the IP address is in the server's pool of available addresses
+         if(ntohl(requestedIpAddr) >= ntohl(context->settings.ipAddrRangeMin) &&
+            ntohl(requestedIpAddr) <= ntohl(context->settings.ipAddrRangeMax))
+         {
+            //Make sure the IP address is not already allocated
+            if(!dhcpServerFindBindingByIpAddr(context, requestedIpAddr))
+            {
+               //Record IP address
+               binding->ipAddr = requestedIpAddr;
+               //Sucessful processing
+               error = NO_ERROR;
+            }
+            else
+            {
+               //Retrieve the next available IP address from the pool of addresses
+               error = dhcpServerGetNextIpAddr(context, &binding->ipAddr);
+            }
+         }
+         else
+         {
+            //Retrieve the next available IP address from the pool of addresses
+            error = dhcpServerGetNextIpAddr(context, &binding->ipAddr);
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Record MAC address
+            binding->macAddr = message->chaddr;
+            //Get current time
+            binding->timestamp = osGetSystemTime();
+         }
+      }
+      else
+      {
+         //Failed to create a new binding
+         error = ERROR_FAILURE;
+      }
+   }
+
+   //Check status code
+   if(!error)
+   {
+      //The server responds with a DHCPOFFER message that includes an
+      //available network address in the 'yiaddr' field (and other
+      //configuration parameters in DHCP options)
+      dhcpServerSendReply(context, DHCP_MESSAGE_TYPE_OFFER,
+         binding->ipAddr, message, length);
+   }
+}
+
+
+/**
+ * @brief Parse DHCPREQUEST message
+ * @param[in] context Pointer to the DHCP server context
+ * @param[in] message Pointer to the incoming DHCP message
+ * @param[in] length Length of the incoming message to parse
+ **/
+
+void dhcpServerParseRequest(DhcpServerContext *context,
+   const DhcpMessage *message, size_t length)
+{
+   NetInterface *interface;
+   Ipv4Addr clientIpAddr;
+   DhcpOption *option;
+   DhcpServerBinding *binding;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Retrieve Server Identifier option
+   option = dhcpGetOption(message, length, DHCP_OPT_SERVER_IDENTIFIER);
+
+   //Option found?
+   if(option != NULL && option->length == 4)
+   {
+      //Unexpected server identifier?
+      if(!ipv4CompAddr(option->value, &interface->ipv4Context.addr))
+         return;
+   }
+
+   //Check the 'ciaddr' field
+   if(message->ciaddr != IPV4_UNSPECIFIED_ADDR)
+   {
+      //Save client's network address
+      clientIpAddr = message->ciaddr;
+   }
+   else
+   {
+      //Retrieve Requested IP Address option
+      option = dhcpGetOption(message, length, DHCP_OPT_REQUESTED_IP_ADDRESS);
+
+      //Option found?
+      if(option != NULL && option->length == 4)
+         ipv4CopyAddr(&clientIpAddr, option->value);
+      else
+         clientIpAddr = IPV4_UNSPECIFIED_ADDR;
+   }
+
+   //Valid client IP address?
+   if(clientIpAddr != IPV4_UNSPECIFIED_ADDR)
+   {
+      //Search the list for a matching binding
+      binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr);
+
+      //Matching binding found?
+      if(binding != NULL)
+      {
+         //Make sure the client's IP address is valid
+         if(clientIpAddr == binding->ipAddr)
+         {
+            //Commit network address
+            binding->validLease = TRUE;
+            //Save lease start time
+            binding->timestamp = osGetSystemTime();
+
+            //The server responds with a DHCPACK message containing the
+            //configuration parameters for the requesting client
+            dhcpServerSendReply(context, DHCP_MESSAGE_TYPE_ACK,
+               binding->ipAddr, message, length);
+
+            //Exit immediately
+            return;
+         }
+      }
+      else
+      {
+         //Ensure the IP address is in the server's pool of available addresses
+         if(ntohl(clientIpAddr) >= ntohl(context->settings.ipAddrRangeMin) &&
+            ntohl(clientIpAddr) <= ntohl(context->settings.ipAddrRangeMax))
+         {
+            //Make sure the IP address is not already allocated
+            if(!dhcpServerFindBindingByIpAddr(context, clientIpAddr))
+            {
+               //Create a new binding
+               binding = dhcpServerCreateBinding(context);
+
+               //Binding successfully created
+               if(binding != NULL)
+               {
+                  //Record MAC address
+                  binding->macAddr = message->chaddr;
+                  //Record IP address
+                  binding->ipAddr = clientIpAddr;
+                  //Commit network address
+                  binding->validLease = TRUE;
+                  //Get current time
+                  binding->timestamp = osGetSystemTime();
+
+                  //The server responds with a DHCPACK message containing the
+                  //configuration parameters for the requesting client
+                  dhcpServerSendReply(context, DHCP_MESSAGE_TYPE_ACK,
+                     binding->ipAddr, message, length);
+
+                  //Exit immediately
+                  return;
+               }
+            }
+         }
+      }
+   }
+
+   //If the server is unable to satisfy the DHCPREQUEST message, the
+   //server should respond with a DHCPNAK message
+   dhcpServerSendReply(context, DHCP_MESSAGE_TYPE_NAK,
+      IPV4_UNSPECIFIED_ADDR, message, length);
+}
+
+
+/**
+ * @brief Parse DHCPDECLINE message
+ * @param[in] context Pointer to the DHCP server context
+ * @param[in] message Pointer to the incoming DHCP message
+ * @param[in] length Length of the incoming message to parse
+ **/
+
+void dhcpServerParseDecline(DhcpServerContext *context,
+   const DhcpMessage *message, size_t length)
+{
+   DhcpOption *option;
+   DhcpServerBinding *binding;
+   Ipv4Addr requestedIpAddr;
+
+   //Retrieve Requested IP Address option
+   option = dhcpGetOption(message, length, DHCP_OPT_REQUESTED_IP_ADDRESS);
+
+   //Option found?
+   if(option != NULL && option->length == 4)
+   {
+      //Copy the requested IP address
+      ipv4CopyAddr(&requestedIpAddr, option->value);
+
+      //Search the list for a matching binding
+      binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr);
+
+      //Matching binding found?
+      if(binding != NULL)
+      {
+         //Check the IP address against the requested IP address
+         if(binding->ipAddr == requestedIpAddr)
+         {
+            //Remote the binding from the list
+            memset(binding, 0, sizeof(DhcpServerBinding));
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Parse DHCPRELEASE message
+ * @param[in] context Pointer to the DHCP server context
+ * @param[in] message Pointer to the incoming DHCP message
+ * @param[in] length Length of the incoming message to parse
+ **/
+
+void dhcpServerParseRelease(DhcpServerContext *context,
+   const DhcpMessage *message, size_t length)
+{
+   DhcpServerBinding *binding;
+
+   //Search the list for a matching binding
+   binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr);
+
+   //Matching binding found?
+   if(binding != NULL)
+   {
+      //Check the IP address against the client IP address
+      if(binding->ipAddr == message->ciaddr)
+      {
+         //Release the network address and cancel remaining lease
+         binding->validLease = FALSE;
+      }
+   }
+}
+
+
+/**
+ * @brief Parse DHCPINFORM message
+ * @param[in] context Pointer to the DHCP server context
+ * @param[in] message Pointer to the incoming DHCP message
+ * @param[in] length Length of the incoming message to parse
+ **/
+
+void dhcpServerParseInform(DhcpServerContext *context,
+   const DhcpMessage *message, size_t length)
+{
+   //Make sure the client IP address is valid
+   if(message->ciaddr != IPV4_UNSPECIFIED_ADDR)
+   {
+      //Servers receiving a DHCPINFORM message construct a DHCPACK message
+      //with any local configuration parameters appropriate for the client
+      dhcpServerSendReply(context, DHCP_MESSAGE_TYPE_ACK,
+         IPV4_UNSPECIFIED_ADDR, message, length);
+   }
+}
+
+
+/**
+ * @brief Send DHCP reply message
+ * @param[in] context Pointer to the DHCP server context
+ * @param[in] type DHCP message type (DHCPOFFER, DHCPACK or DHCPNAK)
+ * @param[in] yourIpAddr The IP address to be placed in the 'yiaddr' field
+ * @param[in] request Pointer to DHCP message received from the client
+ * @param[in] length Length of the DHCP message received from the client
+ * @return Error code
+ **/
+
+error_t dhcpServerSendReply(DhcpServerContext *context, uint8_t type,
+   Ipv4Addr yourIpAddr, const DhcpMessage *request, size_t length)
+{
+   error_t error;
+   uint_t n;
+   uint32_t value;
+   size_t offset;
+   NetBuffer *buffer;
+   NetInterface *interface;
+   DhcpMessage *reply;
+   IpAddr destIpAddr;
+   uint16_t destPort;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Allocate a memory buffer to hold the DHCP message
+   buffer = udpAllocBuffer(DHCP_MIN_MSG_SIZE, &offset);
+   //Failed to allocate buffer?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the DHCP message
+   reply = netBufferAt(buffer, offset);
+   //Clear memory buffer contents
+   memset(reply, 0, DHCP_MIN_MSG_SIZE);
+
+   //Format DHCP reply message
+   reply->op = DHCP_OPCODE_BOOTREPLY;
+   reply->htype = DHCP_HARDWARE_TYPE_ETH;
+   reply->hlen = sizeof(MacAddr);
+   reply->xid = request->xid;
+   reply->secs = 0;
+   reply->flags = request->flags;
+   reply->ciaddr = IPV4_UNSPECIFIED_ADDR;
+   reply->yiaddr = yourIpAddr;
+   reply->siaddr = IPV4_UNSPECIFIED_ADDR;
+   reply->giaddr = request->giaddr;
+   reply->chaddr = request->chaddr;
+
+   //Write magic cookie before setting any option
+   reply->magicCookie = HTONL(DHCP_MAGIC_COOKIE);
+   //Properly terminate options field
+   reply->options[0] = DHCP_OPT_END;
+
+   //Add DHCP Message Type option
+   dhcpAddOption(reply, DHCP_OPT_DHCP_MESSAGE_TYPE,
+      &type, sizeof(type));
+
+   //Add Server Identifier option
+   dhcpAddOption(reply, DHCP_OPT_SERVER_IDENTIFIER,
+      &interface->ipv4Context.addr, sizeof(Ipv4Addr));
+
+   //DHCPOFFER or DHCPACK message?
+   if(type == DHCP_MESSAGE_TYPE_OFFER || type == DHCP_MESSAGE_TYPE_ACK)
+   {
+      //Convert the lease time to network byte order
+      value = htonl(context->settings.leaseTime);
+
+      //When responding to a DHCPINFORM message, the server must not
+      //send a lease expiration time to the client
+      if(yourIpAddr != IPV4_UNSPECIFIED_ADDR)
+      {
+         //Add IP Address Lease Time option
+         dhcpAddOption(reply, DHCP_OPT_IP_ADDRESS_LEASE_TIME,
+            &value, sizeof(value));
+      }
+
+      //Add Subnet Mask option
+      if(context->settings.subnetMask != IPV4_UNSPECIFIED_ADDR)
+      {
+         dhcpAddOption(reply, DHCP_OPT_SUBNET_MASK,
+            &context->settings.subnetMask, sizeof(Ipv4Addr));
+      }
+
+      //Add Router option
+      if(context->settings.defaultGateway != IPV4_UNSPECIFIED_ADDR)
+      {
+         dhcpAddOption(reply, DHCP_OPT_ROUTER,
+            &context->settings.defaultGateway, sizeof(Ipv4Addr));
+      }
+
+      //Retrieve the number of DNS servers
+      for(n = 0; n < DHCP_SERVER_MAX_DNS_SERVERS; n++)
+      {
+         //Check whether the current DNS server is valid
+         if(context->settings.dnsServer[n] == IPV4_UNSPECIFIED_ADDR)
+            break;
+      }
+
+      //Add DNS Server option
+      if(n > 0)
+      {
+         dhcpAddOption(reply, DHCP_OPT_DNS_SERVER,
+            context->settings.dnsServer, n * sizeof(Ipv4Addr));
+      }
+   }
+
+   //Check whether the 'giaddr' field is non-zero
+   if(request->giaddr != IPV4_UNSPECIFIED_ADDR)
+   {
+      //If the 'giaddr' field in a DHCP message from a client is non-zero,
+      //the server sends any return messages to the 'DHCP server' port
+      destPort = DHCP_SERVER_PORT;
+
+      //The DHCP message is sent to the BOOTP relay agent whose address
+      //appears in 'giaddr'
+      destIpAddr.length = sizeof(Ipv4Addr);
+      destIpAddr.ipv4Addr = request->giaddr;
+   }
+   else
+   {
+      //If the 'giaddr' field in a DHCP message from a client is zero,
+      //the server sends any return messages to the 'DHCP client'
+      destPort = DHCP_CLIENT_PORT;
+
+      //DHCPOFFER or DHCPACK message?
+      if(type == DHCP_MESSAGE_TYPE_OFFER || type == DHCP_MESSAGE_TYPE_ACK)
+      {
+         //Check whether the 'giaddr' field is non-zero
+         if(request->ciaddr != IPV4_UNSPECIFIED_ADDR)
+         {
+            //If the 'giaddr' field is zero and the 'ciaddr' field is nonzero,
+            //then the server unicasts DHCPOFFER and DHCPACK messages to the
+            //address in 'ciaddr'
+            destIpAddr.length = sizeof(Ipv4Addr);
+            destIpAddr.ipv4Addr = request->ciaddr;
+         }
+         else
+         {
+            //Check whether the broadcast bit is set
+            if(ntohs(request->flags) & DHCP_FLAG_BROADCAST)
+            {
+               //If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is
+               //set, then the server broadcasts DHCPOFFER and DHCPACK messages
+               destIpAddr.length = sizeof(Ipv4Addr);
+               destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
+            }
+            else
+            {
+               //If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is
+               //not set, then the server unicasts DHCPOFFER and DHCPACK messages
+               //to the client's hardware address and 'yiaddr' address
+               destIpAddr.length = sizeof(Ipv4Addr);
+               destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
+            }
+         }
+      }
+      //DHCPNAK message?
+      else
+      {
+         //In all cases, when 'giaddr' is zero, the server broadcasts any
+         //DHCPNAK messages
+         destIpAddr.length = sizeof(Ipv4Addr);
+         destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
+      }
+   }
+
+   //Debug message
+   TRACE_DEBUG("\r\n%s: Sending DHCP message (%" PRIuSIZE " bytes)...\r\n",
+      formatSystemTime(osGetSystemTime(), NULL), DHCP_MIN_MSG_SIZE);
+
+   //Dump the contents of the message for debugging purpose
+   dhcpDumpMessage(reply, DHCP_MIN_MSG_SIZE);
+
+   //Broadcast DHCPDECLINE message
+   error = udpSendDatagramEx(interface, DHCP_SERVER_PORT, &destIpAddr,
+      destPort, buffer, offset, IPV4_DEFAULT_TTL);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Create a new binding
+ * @param[in] context Pointer to the DHCP server context
+ * @return Pointer to the newly created binding
+ **/
+
+DhcpServerBinding *dhcpServerCreateBinding(DhcpServerContext *context)
+{
+   uint_t i;
+   DhcpServerBinding *binding;
+   DhcpServerBinding *oldestBinding;
+
+   //Keep track of the oldest binding
+   oldestBinding = NULL;
+
+   //Loop through the list of bindings
+   for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
+   {
+      //Point to the current binding
+      binding = &context->clientBinding[i];
+
+      //Check whether the binding is available
+      if(macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR))
+      {
+         //Erase contents
+         memset(binding, 0, sizeof(DhcpServerBinding));
+         //Return a pointer to the newly created binding
+         return binding;
+      }
+      else
+      {
+         //Bindings that have been committed cannot be removed
+         if(!binding->validLease)
+         {
+            //Keep track of the oldest binding in the list
+            if(oldestBinding != NULL)
+            {
+               if(timeCompare(binding->timestamp, oldestBinding->timestamp) < 0)
+                  oldestBinding = binding;
+            }
+            else
+            {
+               oldestBinding = binding;
+            }
+         }
+      }
+   }
+
+   //Any binding available in the list?
+   if(oldestBinding != NULL)
+   {
+      //Erase contents
+      memset(oldestBinding, 0, sizeof(DhcpServerBinding));
+   }
+
+   //Return a pointer to the oldest binding
+   return oldestBinding;
+}
+
+
+/**
+ * @brief Search the list of bindings for a given MAC address
+ * @param[in] context Pointer to the DHCP server context
+ * @param[in] macAddr MAC address
+ * @return Pointer to the corresponding DHCP binding
+ **/
+
+DhcpServerBinding *dhcpServerFindBindingByMacAddr(DhcpServerContext *context,
+   const MacAddr* macAddr)
+{
+   uint_t i;
+   DhcpServerBinding *binding;
+
+   //Loop through the list of bindings
+   for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
+   {
+      //Point to the current binding
+      binding = &context->clientBinding[i];
+
+      //Valid binding?
+      if(!macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR))
+      {
+         //Check whether the current binding matches the specified MAC address
+         if(macCompAddr(&binding->macAddr, macAddr))
+         {
+            //Return the pointer to the corresponding binding
+            return binding;
+         }
+      }
+   }
+
+   //No matching binding...
+   return NULL;
+}
+
+
+/**
+ * @brief Search the list of bindings for a given IP address
+ * @param[in] context Pointer to the DHCP server context
+ * @param[in] ipAddr IP address
+ * @return Pointer to the corresponding DHCP binding
+ **/
+
+DhcpServerBinding *dhcpServerFindBindingByIpAddr(DhcpServerContext *context,
+   Ipv4Addr ipAddr)
+{
+   uint_t i;
+   DhcpServerBinding *binding;
+
+   //Loop through the list of bindings
+   for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
+   {
+      //Point to the current binding
+      binding = &context->clientBinding[i];
+
+      //Valid binding?
+      if(!macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR))
+      {
+         //Check whether the current binding matches the specified IP address
+         if(binding->ipAddr == ipAddr)
+         {
+            //Return the pointer to the corresponding binding
+            return binding;
+         }
+      }
+   }
+
+   //No matching binding...
+   return NULL;
+}
+
+
+/**
+ * @brief Retrieve the next IP address to be used
+ * @param[in] context Pointer to the DHCP server context
+ * @param[out] ipAddr Next IP address to be used
+ * @return Error code
+ **/
+
+error_t dhcpServerGetNextIpAddr(DhcpServerContext *context, Ipv4Addr *ipAddr)
+{
+   uint_t i;
+   DhcpServerBinding *binding;
+
+   //Search the pool for any available IP address
+   for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
+   {
+      //Check whether the current IP address is already allocated
+      binding = dhcpServerFindBindingByIpAddr(context, context->nextIpAddr);
+
+      //If the IP address is available, then it can be assigned to a new client
+      if(binding == NULL)
+         *ipAddr = context->nextIpAddr;
+
+      //Compute the next IP address that will be assigned by the DHCP server
+      if(ntohl(context->nextIpAddr) >= ntohl(context->settings.ipAddrRangeMax))
+      {
+         //Wrap around to the beginning of the pool
+         context->nextIpAddr = context->settings.ipAddrRangeMin;
+      }
+      else
+      {
+         //Increment IP address
+         context->nextIpAddr = htonl(ntohl(context->nextIpAddr) + 1);
+      }
+
+      //If the IP address is available, we are done
+      if(binding == NULL)
+         return NO_ERROR;
+   }
+
+   //No available addresses in the pool...
+   return ERROR_NO_ADDRESS;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dhcp/dhcp_server.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,162 @@
+/**
+ * @file dhcp_server.h
+ * @brief DHCP server (Dynamic Host Configuration Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DHCP_SERVER_H
+#define _DHCP_SERVER_H
+
+//Dependencies
+#include "core/net.h"
+
+//DHCP server support
+#ifndef DHCP_SERVER_SUPPORT
+   #define DHCP_SERVER_SUPPORT DISABLED
+#elif (DHCP_SERVER_SUPPORT != ENABLED && DHCP_SERVER_SUPPORT != DISABLED)
+   #error DHCP_SERVER_SUPPORT parameter is not valid
+#endif
+
+//DHCP server tick interval
+#ifndef DHCP_SERVER_TICK_INTERVAL
+   #define DHCP_SERVER_TICK_INTERVAL 1000
+#elif (DHCP_SERVER_TICK_INTERVAL < 10)
+   #error DHCP_SERVER_TICK_INTERVAL parameter is not valid
+#endif
+
+//Maximum number of clients
+#ifndef DHCP_SERVER_MAX_CLIENTS
+   #define DHCP_SERVER_MAX_CLIENTS 16
+#elif (DHCP_SERVER_MAX_CLIENTS < 1)
+   #error DHCP_SERVER_MAX_CLIENTS parameter is not valid
+#endif
+
+//Default lease time, in seconds
+#ifndef DHCP_SERVER_DEFAULT_LEASE_TIME
+   #define DHCP_SERVER_DEFAULT_LEASE_TIME 86400
+#elif (DHCP_SERVER_DEFAULT_LEASE_TIME < 1)
+   #error DHCP_SERVER_DEFAULT_LEASE_TIME parameter is not valid
+#endif
+
+//Maximum number of DNS servers
+#ifndef DHCP_SERVER_MAX_DNS_SERVERS
+   #define DHCP_SERVER_MAX_DNS_SERVERS 2
+#elif (DHCP_SERVER_MAX_DNS_SERVERS < 1)
+   #error DHCP_SERVER_MAX_DNS_SERVERS parameter is not valid
+#endif
+
+
+/**
+ * @brief DHCP binding
+ *
+ * A binding is a collection of configuration parameters associated
+ * with a DHCP client
+ *
+ **/
+
+typedef struct
+{
+   MacAddr macAddr;     ///<Client's MAC address
+   Ipv4Addr ipAddr;     ///<Client's IPv4 address
+   bool_t validLease;   ///<Valid lease
+   systime_t timestamp; ///<Timestamp
+} DhcpServerBinding;
+
+
+/**
+ * @brief DHCP server settings
+ **/
+
+typedef struct
+{
+   NetInterface *interface;                         ///<Underlying network interface
+   bool_t rapidCommit;                              ///<Quick configuration using rapid commit
+   uint32_t leaseTime;                              ///<Lease time, in seconds, assigned to the DHCP clients
+   Ipv4Addr ipAddrRangeMin;                         ///<Lowest IP address in the pool that is available for dynamic address assignment
+   Ipv4Addr ipAddrRangeMax;                         ///<Highest IP address in the pool that is available for dynamic address assignment
+   Ipv4Addr subnetMask;                             ///<Subnet mask
+   Ipv4Addr defaultGateway;                         ///<Default gateway
+   Ipv4Addr dnsServer[DHCP_SERVER_MAX_DNS_SERVERS]; ///<DNS servers
+} DhcpServerSettings;
+
+
+/**
+ * @brief DHCP server context
+ **/
+
+typedef struct
+{
+   DhcpServerSettings settings;                              ///<DHCP server settings
+   bool_t running;                                           ///<This flag tells whether the DHCP server is running or not
+   Ipv4Addr nextIpAddr;                                      ///<Next IP address to be assigned
+   DhcpServerBinding clientBinding[DHCP_SERVER_MAX_CLIENTS]; ///<List of bindings
+} DhcpServerContext;
+
+
+//Tick counter to handle periodic operations
+extern systime_t dhcpServerTickCounter;
+
+//DHCP server related functions
+void dhcpServerGetDefaultSettings(DhcpServerSettings *settings);
+error_t dhcpServerInit(DhcpServerContext *context, const DhcpServerSettings *settings);
+error_t dhcpServerStart(DhcpServerContext *context);
+error_t dhcpServerStop(DhcpServerContext *context);
+
+void dhcpServerTick(DhcpServerContext *context);
+
+void dhcpServerProcessMessage(NetInterface *interface,
+   const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader,
+   const NetBuffer *buffer, size_t offset, void *params);
+
+void dhcpServerParseDiscover(DhcpServerContext *context,
+   const DhcpMessage *message, size_t length);
+
+void dhcpServerParseRequest(DhcpServerContext *context,
+   const DhcpMessage *message, size_t length);
+
+void dhcpServerParseDecline(DhcpServerContext *context,
+   const DhcpMessage *message, size_t length);
+
+void dhcpServerParseRelease(DhcpServerContext *context,
+   const DhcpMessage *message, size_t length);
+
+void dhcpServerParseInform(DhcpServerContext *context,
+   const DhcpMessage *message, size_t length);
+
+error_t dhcpServerSendReply(DhcpServerContext *context, uint8_t type,
+   Ipv4Addr yourIpAddr, const DhcpMessage *request, size_t length);
+
+DhcpServerBinding *dhcpServerCreateBinding(DhcpServerContext *context);
+
+DhcpServerBinding *dhcpServerFindBindingByMacAddr(DhcpServerContext *context,
+   const MacAddr* macAddr);
+
+DhcpServerBinding *dhcpServerFindBindingByIpAddr(DhcpServerContext *context,
+   Ipv4Addr ipAddr);
+
+error_t dhcpServerGetNextIpAddr(DhcpServerContext *context, Ipv4Addr *ipAddr);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dhcpv6/dhcpv6_client.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,2609 @@
+/**
+ * @file dhcpv6_client.c
+ * @brief DHCPv6 client (Dynamic Host Configuration Protocol for IPv6)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The Dynamic Host Configuration Protocol for IPv6 enables DHCP servers to
+ * pass configuration parameters such as IPv6 network addresses to IPv6
+ * nodes. This protocol is a stateful counterpart to IPv6 Stateless Address
+ * Autoconfiguration (RFC 2462), and can be used separately or concurrently
+ * with the latter to obtain configuration parameters. Refer to RFC 3315
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL DHCPV6_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include "core/net.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_misc.h"
+#include "ipv6/ndp.h"
+#include "dhcpv6/dhcpv6_client.h"
+#include "dhcpv6/dhcpv6_common.h"
+#include "dhcpv6/dhcpv6_debug.h"
+#include "dns/dns_common.h"
+#include "date_time.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV6_SUPPORT == ENABLED && DHCPV6_CLIENT_SUPPORT == ENABLED)
+
+//Tick counter to handle periodic operations
+systime_t dhcpv6ClientTickCounter;
+
+//Requested DHCPv6 options
+static const uint16_t dhcpv6OptionList[] =
+{
+   HTONS(DHCPV6_OPTION_DNS_SERVERS),
+   HTONS(DHCPV6_OPTION_DOMAIN_LIST),
+   HTONS(DHCPV6_OPTION_FQDN)
+};
+
+
+/**
+ * @brief Initialize settings with default values
+ * @param[out] settings Structure that contains DHCPv6 client settings
+ **/
+
+void dhcpv6ClientGetDefaultSettings(Dhcpv6ClientSettings *settings)
+{
+   //Use default interface
+   settings->interface = netGetDefaultInterface();
+
+   //Support for quick configuration using rapid commit
+   settings->rapidCommit = FALSE;
+   //Use the DNS servers provided by the DHCPv6 server
+   settings->manualDnsConfig = FALSE;
+   //DHCPv6 configuration timeout
+   settings->timeout = 0;
+   //DHCPv6 configuration timeout event
+   settings->timeoutEvent = NULL;
+   //Link state change event
+   settings->linkChangeEvent = NULL;
+   //FSM state change event
+   settings->stateChangeEvent = NULL;
+}
+
+
+/**
+ * @brief DHCPv6 client initialization
+ * @param[in] context Pointer to the DHCPv6 client context
+ * @param[in] settings DHCPv6 client specific settings
+ * @return Error code
+ **/
+
+error_t dhcpv6ClientInit(Dhcpv6ClientContext *context, const Dhcpv6ClientSettings *settings)
+{
+   error_t error;
+   NetInterface *interface;
+
+   //Debug message
+   TRACE_INFO("Initializing DHCPv6 client...\r\n");
+
+   //Ensure the parameters are valid
+   if(context == NULL || settings == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //A valid pointer to the interface being configured is required
+   if(settings->interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Point to the underlying network interface
+   interface = settings->interface;
+
+   //Clear the DHCPv6 client context
+   memset(context, 0, sizeof(Dhcpv6ClientContext));
+   //Save user settings
+   context->settings = *settings;
+
+   //Generate client's DUID
+   error = dhcpv6ClientGenerateDuid(context);
+   //any error to report?
+   if(error)
+      return error;
+
+   //Generate client's fully qualified domain name
+   error = dhcpv6ClientGenerateFqdn(context);
+   //any error to report?
+   if(error)
+      return error;
+
+   //Callback function to be called when a DHCPv6 message is received
+   error = udpAttachRxCallback(interface, DHCPV6_CLIENT_PORT,
+      dhcpv6ClientProcessMessage, context);
+   //Failed to register callback function?
+   if(error)
+      return error;
+
+   //DHCPv6 client is currently suspended
+   context->running = FALSE;
+   //Initialize state machine
+   context->state = DHCPV6_STATE_INIT;
+
+   //Attach the DHCPv6 client context to the network interface
+   interface->dhcpv6ClientContext = context;
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Start DHCPv6 client
+ * @param[in] context Pointer to the DHCPv6 client context
+ * @return Error code
+ **/
+
+error_t dhcpv6ClientStart(Dhcpv6ClientContext *context)
+{
+   NetInterface *interface;
+
+   //Check parameter
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Starting DHCPv6 client...\r\n");
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Flush the list of IPv6 addresses from the client's IA
+   dhcpv6ClientFlushAddrList(context);
+
+   //Automatic DNS server configuration?
+   if(!context->settings.manualDnsConfig)
+   {
+      //Clear the list of DNS servers
+      ipv6FlushDnsServerList(interface);
+   }
+
+   //Check if the link is up?
+   if(interface->linkState)
+   {
+      //A link-local address is formed by combining the well-known
+      //link-local prefix fe80::/10 with the interface identifier
+      dhcpv6ClientGenerateLinkLocalAddr(context);
+   }
+
+   //Start DHCPv6 client
+   context->running = TRUE;
+   //Initialize state machine
+   context->state = DHCPV6_STATE_INIT;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Stop DHCPv6 client
+ * @param[in] context Pointer to the DHCPv6 client context
+ * @return Error code
+ **/
+
+error_t dhcpv6ClientStop(Dhcpv6ClientContext *context)
+{
+   //Check parameter
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Stopping DHCPv6 client...\r\n");
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Stop DHCPv6 client
+   context->running = FALSE;
+   //Reinitialize state machine
+   context->state = DHCPV6_STATE_INIT;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Release DHCPv6 lease
+ * @param[in] context Pointer to the DHCPv6 client context
+ * @return Error code
+ **/
+
+error_t dhcpv6ClientRelease(Dhcpv6ClientContext *context)
+{
+   uint_t i;
+   NetInterface *interface;
+   Dhcpv6ClientAddrEntry *entry;
+
+   //Check parameter
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Releasing DHCPv6 lease...\r\n");
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Check whether the DHCPv6 client is running
+   if(context->running)
+   {
+      //BOUND state?
+      if(context->state == DHCPV6_STATE_BOUND)
+      {
+         //Loop through the IPv6 addresses recorded by the DHCPv6 client
+         for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
+         {
+            //Point to the current entry
+            entry = &context->ia.addrList[i];
+
+            //Valid IPv6 address?
+            if(entry->validLifetime > 0)
+            {
+               //The client must stop using the addresses being released as soon
+               //as the client begins the Release message exchange process
+               ipv6RemoveAddr(interface, &entry->addr);
+            }
+         }
+
+         //Switch to the RELEASE state
+         dhcpv6ClientChangeState(context, DHCPV6_STATE_RELEASE, 0);
+      }
+      else
+      {
+         //Stop DHCPv6 client
+         context->running = FALSE;
+         //Reinitialize state machine
+         context->state = DHCPV6_STATE_INIT;
+      }
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Retrieve current state
+ * @param[in] context Pointer to the DHCPv6 client context
+ * @return Current DHCPv6 client state
+ **/
+
+Dhcpv6State dhcpv6ClientGetState(Dhcpv6ClientContext *context)
+{
+   Dhcpv6State state;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Get current state
+   state = context->state;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return current state
+   return state;
+}
+
+
+/**
+ * @brief DHCPv6 client timer handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * manage DHCPv6 client operation
+ *
+ * @param[in] context Pointer to the DHCPv6 client context
+ **/
+
+
+void dhcpv6ClientTick(Dhcpv6ClientContext *context)
+{
+   //Make sure the DHCPv6 client has been properly instantiated
+   if(context == NULL)
+      return;
+
+   //DHCPv6 client finite state machine
+   switch(context->state)
+   {
+   //Process INIT state
+   case DHCPV6_STATE_INIT:
+      //This is the initialization state, where a client begins the process of
+      //acquiring a lease. It also returns here when a lease ends, or when a
+      //lease negotiation fails
+      dhcpv6ClientStateInit(context);
+      break;
+   //Process SOLICIT state
+   case DHCPV6_STATE_SOLICIT:
+      //The client sends a Solicit message to locate servers
+      dhcpv6ClientStateSolicit(context);
+      break;
+   //Process REQUEST state
+   case DHCPV6_STATE_REQUEST:
+      //The client sends a Request message to request configuration
+      //parameters, including IP addresses, from a specific server
+      dhcpv6ClientStateRequest(context);
+      break;
+   //Process INIT-CONFIRM state
+   case DHCPV6_STATE_INIT_CONFIRM:
+      //When a client that already has a valid lease starts up after a
+      //power-down or reboot, it starts here instead of the INIT state
+      dhcpv6ClientStateInitConfirm(context);
+      break;
+   //Process CONFIRM state
+   case DHCPV6_STATE_CONFIRM:
+      //The client sends a Confirm message to any available server
+      //to determine whether the addresses it was assigned are still
+      //appropriate to the link to which the client is connected
+      dhcpv6ClientStateConfirm(context);
+      break;
+   //Process DAD state
+   case DHCPV6_STATE_DAD:
+      //The client should perform duplicate address detection on each
+      //of the addresses in any IAs it receives in the Reply message
+      //before using that address for traffic
+      dhcpv6ClientStateDad(context);
+      break;
+   //Process BOUND state
+   case DHCPV6_STATE_BOUND:
+      //The client has a valid lease and is in its normal operating state
+      dhcpv6ClientStateBound(context);
+      break;
+   //Process RENEW state
+   case DHCPV6_STATE_RENEW:
+      //The client sends a Renew message to the server that originally
+      //provided the client's addresses and configuration parameters to
+      //extend the lifetimes on the addresses assigned to the client
+      //and to update other configuration parameters
+      dhcpv6ClientStateRenew(context);
+      break;
+   //Process REBIND state
+   case DHCPV6_STATE_REBIND:
+      //The client sends a Rebind message to any available server to extend
+      //the lifetimes on the addresses assigned to the client and to update
+      //other configuration parameters. This message is sent after a client
+      //receives no response to a Renew message
+      dhcpv6ClientStateRebind(context);
+      break;
+   //Process RELEASE state
+   case DHCPV6_STATE_RELEASE:
+      //To release one or more addresses, a client sends a Release message
+      //to the server
+      dhcpv6ClientStateRelease(context);
+      break;
+   //Process DECLINE state
+   case DHCPV6_STATE_DECLINE:
+      //If a client detects that one or more addresses assigned to it by a
+      //server are already in use by another node, the client sends a Decline
+      //message to the server to inform it that the address is suspect
+      dhcpv6ClientStateDecline(context);
+      break;
+   //Invalid state...
+   default:
+      //Switch to the default state
+      context->state = DHCPV6_STATE_INIT;
+      break;
+   }
+}
+
+
+/**
+ * @brief Callback function for link change event
+ * @param[in] context Pointer to the DHCPv6 client context
+ **/
+
+void dhcpv6ClientLinkChangeEvent(Dhcpv6ClientContext *context)
+{
+   NetInterface *interface;
+
+   //Make sure the DHCPv6 client has been properly instantiated
+   if(context == NULL)
+      return;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Check whether the DHCPv6 client is running
+   if(context->running)
+   {
+      //Automatic DNS server configuration?
+      if(!context->settings.manualDnsConfig)
+      {
+         //Clear the list of DNS servers
+         ipv6FlushDnsServerList(interface);
+      }
+
+      //Link-up event?
+      if(interface->linkState)
+      {
+         //A link-local address is formed by combining the well-known
+         //link-local prefix fe80::/10 with the interface identifier
+         dhcpv6ClientGenerateLinkLocalAddr(context);
+      }
+   }
+
+   //Check the state of the DHCPv6 client
+   switch(context->state)
+   {
+   case DHCPV6_STATE_INIT_CONFIRM:
+   case DHCPV6_STATE_CONFIRM :
+   case DHCPV6_STATE_DAD:
+   case DHCPV6_STATE_BOUND:
+   case DHCPV6_STATE_RENEW:
+   case DHCPV6_STATE_REBIND:
+      //The client already has a valid lease
+      context->state = DHCPV6_STATE_INIT_CONFIRM;
+      break;
+   case DHCPV6_STATE_RELEASE:
+      //Stop DHCPv6 client
+      context->running = FALSE;
+      //Reinitialize state machine
+      context->state = DHCPV6_STATE_INIT;
+      break;
+   default:
+      //Switch to the INIT state
+      context->state = DHCPV6_STATE_INIT;
+      break;
+   }
+
+   //Any registered callback?
+   if(context->settings.linkChangeEvent != NULL)
+   {
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+      //Invoke user callback function
+      context->settings.linkChangeEvent(context, interface, interface->linkState);
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+   }
+}
+
+
+/**
+ * @brief INIT state
+ *
+ * This is the initialization state, where a client begins the process of
+ * acquiring a lease. It also returns here when a lease ends, or when a
+ * lease negotiation fails
+ *
+ * @param[in] context Pointer to the DHCPv6 client context
+ **/
+
+void dhcpv6ClientStateInit(Dhcpv6ClientContext *context)
+{
+   systime_t delay;
+   NetInterface *interface;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Check whether the DHCPv6 client is running
+   if(context->running)
+   {
+      //Wait for the link to be up before starting DHCPv6 configuration
+      if(interface->linkState)
+      {
+         //Make sure that a valid link-local address has been assigned to the interface
+         if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED)
+         {
+            //Flush the list of IPv6 addresses from the client's IA
+            dhcpv6ClientFlushAddrList(context);
+
+            //The first Solicit message from the client on the interface must be
+            //delayed by a random amount of time between 0 and SOL_MAX_DELAY
+            delay = dhcpv6RandRange(0, DHCPV6_CLIENT_SOL_MAX_DELAY);
+
+            //Record the time at which the client started
+            //the address acquisition process
+            context->configStartTime = osGetSystemTime();
+            //Clear flag
+            context->timeoutEventDone = FALSE;
+
+            //Switch to the SOLICIT state
+            dhcpv6ClientChangeState(context, DHCPV6_STATE_SOLICIT, delay);
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief SOLICIT state
+ *
+ * A client uses the Solicit message to discover DHCPv6 servers
+ *
+ * @param[in] context Pointer to the DHCPv6 client context
+ **/
+
+void dhcpv6ClientStateSolicit(Dhcpv6ClientContext *context)
+{
+   systime_t time;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check current time
+   if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+   {
+      //Check retransmission counter
+      if(context->retransmitCount == 0)
+      {
+         //Reset server preference value
+         context->serverPreference = -1;
+         //Generate a 24-bit transaction ID
+         context->transactionId = netGetRand() & 0x00FFFFFF;
+
+         //Send a Solicit message
+         dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_SOLICIT);
+
+         //Save the time at which the message was sent
+         context->exchangeStartTime = time;
+         context->timestamp = time;
+
+         //If the client is waiting for an Advertise message, the first RT
+         //must be selected to be strictly greater than IRT
+         context->timeout = DHCPV6_CLIENT_SOL_TIMEOUT +
+            abs(dhcpv6Rand(DHCPV6_CLIENT_SOL_TIMEOUT));
+
+         //Increment retransmission counter
+         context->retransmitCount++;
+      }
+      else
+      {
+         //Check whether a valid Advertise message has been received
+         if(context->serverPreference >= 0)
+         {
+            //Continue configuration procedure
+            dhcpv6ClientChangeState(context, DHCPV6_STATE_REQUEST, 0);
+         }
+         else
+         {
+            //Send a Solicit message
+            dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_SOLICIT);
+
+            //Save the time at which the message was sent
+            context->timestamp = time;
+
+            //The RT is doubled for each subsequent retransmission
+            context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
+
+            //MRT specifies an upper bound on the value of RT
+            if(context->timeout > DHCPV6_CLIENT_SOL_MAX_RT)
+            {
+               //Compute retransmission timeout
+               context->timeout = DHCPV6_CLIENT_SOL_MAX_RT +
+                  dhcpv6Rand(DHCPV6_CLIENT_SOL_MAX_RT);
+            }
+
+            //Increment retransmission counter
+            context->retransmitCount++;
+         }
+      }
+   }
+
+   //Manage DHCPv6 configuration timeout
+   dhcpv6ClientCheckTimeout(context);
+}
+
+
+/**
+ * @brief REQUEST state
+ *
+ * The client uses a Request message to populate IAs with addresses and obtain
+ * other configuration information. The client includes one or more more IA
+ * options in the Request message. The server then returns addresses and other
+ * information about the IAs to the client in IA options in a Reply message
+ *
+ * @param[in] context Pointer to the DHCPv6 client context
+ **/
+
+void dhcpv6ClientStateRequest(Dhcpv6ClientContext *context)
+{
+   systime_t time;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check current time
+   if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+   {
+      //Check retransmission counter
+      if(context->retransmitCount == 0)
+      {
+         //Generate a 24-bit transaction ID
+         context->transactionId = netGetRand() & 0x00FFFFFF;
+
+         //Send a Request message
+         dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_REQUEST);
+
+         //Save the time at which the message was sent
+         context->exchangeStartTime = time;
+         context->timestamp = time;
+
+         //Initial retransmission timeout
+         context->timeout = DHCPV6_CLIENT_REQ_TIMEOUT +
+            dhcpv6Rand(DHCPV6_CLIENT_REQ_TIMEOUT);
+
+         //Increment retransmission counter
+         context->retransmitCount++;
+      }
+      else if(context->retransmitCount < DHCPV6_CLIENT_REQ_MAX_RC)
+      {
+         //Send a Request message
+         dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_REQUEST);
+
+         //Save the time at which the message was sent
+         context->timestamp = time;
+
+         //The RT is doubled for each subsequent retransmission
+         context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
+
+         //MRT specifies an upper bound on the value of RT
+         if(context->timeout > DHCPV6_CLIENT_REQ_MAX_RT)
+         {
+            //Compute retransmission timeout
+            context->timeout = DHCPV6_CLIENT_REQ_MAX_RT +
+               dhcpv6Rand(DHCPV6_CLIENT_REQ_MAX_RT);
+         }
+
+         //Increment retransmission counter
+         context->retransmitCount++;
+      }
+      else
+      {
+         //If the client does not receive a response within a reasonable
+         //period of time, then it restarts the initialization procedure
+         dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
+      }
+   }
+
+   //Manage DHCPv6 configuration timeout
+   dhcpv6ClientCheckTimeout(context);
+}
+
+
+/**
+ * @brief INIT-CONFIRM state
+ *
+ * When a client that already has a valid lease starts up after a
+ * power-down or reboot, it starts here instead of the INIT state
+ *
+ * @param[in] context Pointer to the DHCPv6 client context
+ **/
+
+void dhcpv6ClientStateInitConfirm(Dhcpv6ClientContext *context)
+{
+   systime_t delay;
+   NetInterface *interface;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Check whether the DHCPv6 client is running
+   if(context->running)
+   {
+      //Wait for the link to be up before starting DHCPv6 configuration
+      if(interface->linkState)
+      {
+         //Make sure that a valid link-local address has been assigned to the interface
+         if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED)
+         {
+            //The first Confirm message from the client on the interface must be
+            //delayed by a random amount of time between 0 and CNF_MAX_DELAY
+            delay = dhcpv6RandRange(0, DHCPV6_CLIENT_CNF_MAX_DELAY);
+
+            //Record the time at which the client started
+            //the address acquisition process
+            context->configStartTime = osGetSystemTime();
+            //Clear flag
+            context->timeoutEventDone = FALSE;
+
+            //Switch to the CONFIRM state
+            dhcpv6ClientChangeState(context, DHCPV6_STATE_CONFIRM, delay);
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief CONFIRM state
+ *
+ * Whenever a client may have moved to a new link, the prefixes from
+ * the addresses assigned to the interfaces on that link may no longer
+ * be appropriate for the link to which the client is attached. In such
+ * the client must initiate a Confirm/Reply message exchange
+ *
+ * @param[in] context Pointer to the DHCPv6 client context
+ **/
+
+void dhcpv6ClientStateConfirm(Dhcpv6ClientContext *context)
+{
+   systime_t time;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check current time
+   if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+   {
+      //Check retransmission counter
+      if(context->retransmitCount == 0)
+      {
+         //Generate a 24-bit transaction ID
+         context->transactionId = netGetRand() & 0x00FFFFFF;
+
+         //Send a Confirm message
+         dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_CONFIRM);
+
+         //Save the time at which the client sent the first message
+         context->exchangeStartTime = time;
+         context->timestamp = time;
+
+         //Initial retransmission timeout
+         context->timeout = DHCPV6_CLIENT_CNF_TIMEOUT +
+            dhcpv6Rand(DHCPV6_CLIENT_CNF_TIMEOUT);
+
+         //Increment retransmission counter
+         context->retransmitCount++;
+      }
+      else
+      {
+         //Send a Confirm message
+         dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_CONFIRM);
+
+         //Save the time at which the message was sent
+         context->timestamp = time;
+
+         //The RT is doubled for each subsequent retransmission
+         context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
+
+         //MRT specifies an upper bound on the value of RT
+         if(context->timeout > DHCPV6_CLIENT_CNF_MAX_RT)
+         {
+            //Compute retransmission timeout
+            context->timeout = DHCPV6_CLIENT_CNF_MAX_RT +
+               dhcpv6Rand(DHCPV6_CLIENT_CNF_MAX_RT);
+         }
+
+         //Increment retransmission counter
+         context->retransmitCount++;
+      }
+   }
+   else
+   {
+      //Check retransmission counter
+      if(context->retransmitCount > 0)
+      {
+         //The message exchange fails once MRD seconds have elapsed since
+         //the client first transmitted the message
+         if(timeCompare(time, context->exchangeStartTime + DHCPV6_CLIENT_CNF_MAX_RD) >= 0)
+         {
+            //If the client receives no responses before the message transmission
+            //process terminates, the client should continue to use any IP
+            //addresses using the last known lifetimes for those addresses
+            dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
+         }
+      }
+   }
+
+   //Manage DHCPv6 configuration timeout
+   dhcpv6ClientCheckTimeout(context);
+}
+
+
+/**
+ * @brief DAD state
+ *
+ * The client perform duplicate address detection on each
+ * of the addresses in any IAs it receives in the Reply message
+ * before using that address for traffic
+ *
+ * @param[in] context Pointer to the DHCPv6 client context
+ **/
+
+void dhcpv6ClientStateDad(Dhcpv6ClientContext *context)
+{
+   uint_t i;
+   NetInterface *interface;
+   Ipv6AddrState state;
+   Dhcpv6ClientAddrEntry *entry;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Loop through the IPv6 addresses recorded by the DHCPv6 client
+   for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &context->ia.addrList[i];
+
+      //Check the IPv6 address is a tentative address?
+      if(entry->validLifetime > 0)
+      {
+         //Get the state of the current IPv6 address
+         state = ipv6GetAddrState(interface, &entry->addr);
+
+         //Duplicate Address Detection in progress?
+         if(state == IPV6_ADDR_STATE_TENTATIVE)
+         {
+            //Exit immediately
+            return;
+         }
+         //Duplicate Address Detection failed?
+         else if(state == IPV6_ADDR_STATE_INVALID)
+         {
+            //Switch to the DECLINE state
+            dhcpv6ClientChangeState(context, DHCPV6_STATE_DECLINE, 0);
+            //Exit immediately
+            return;
+         }
+      }
+   }
+
+   //Dump current DHCPv6 configuration for debugging purpose
+   dhcpv6ClientDumpConfig(context);
+   //Switch to the BOUND state
+   dhcpv6ClientChangeState(context, DHCPV6_STATE_BOUND, 0);
+}
+
+
+/**
+ * @brief BOUND state
+ *
+ * Client has a valid lease and is in its normal operating state
+ *
+ * @param[in] context Pointer to the DHCPv6 client context
+ **/
+
+void dhcpv6ClientStateBound(Dhcpv6ClientContext *context)
+{
+   systime_t t1;
+   systime_t time;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //A client will never attempt to extend the lifetime of any
+   //address in an IA with T1 set to 0xffffffff
+   if(context->ia.t1 != DHCPV6_INFINITE_TIME)
+   {
+      //Convert T1 to milliseconds
+      if(context->ia.t1 < (MAX_DELAY / 1000))
+         t1 = context->ia.t1 * 1000;
+      else
+         t1 = MAX_DELAY;
+
+      //Check the time elapsed since the lease was obtained
+      if(timeCompare(time, context->leaseStartTime + t1) >= 0)
+      {
+         //Record the time at which the client started the address renewal process
+         context->configStartTime = time;
+
+         //Enter the RENEW state
+         dhcpv6ClientChangeState(context, DHCPV6_STATE_RENEW, 0);
+      }
+   }
+}
+
+
+/**
+ * @brief RENEW state
+ *
+ * The client sends a Renew message to the server that originally
+ * provided the client's addresses and configuration parameters to
+ * extend the lifetimes on the addresses assigned to the client
+ * and to update other configuration parameters
+ *
+ * @param[in] context Pointer to the DHCPv6 client context
+ **/
+
+void dhcpv6ClientStateRenew(Dhcpv6ClientContext *context)
+{
+   systime_t t2;
+   systime_t time;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check current time
+   if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+   {
+      //Check retransmission counter
+      if(context->retransmitCount == 0)
+      {
+         //Generate a 24-bit transaction ID
+         context->transactionId = netGetRand() & 0x00FFFFFF;
+
+         //Send a Renew message
+         dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_RENEW);
+
+         //Save the time at which the message was sent
+         context->exchangeStartTime = time;
+         context->timestamp = time;
+
+         //Initial retransmission timeout
+         context->timeout = DHCPV6_CLIENT_REN_TIMEOUT +
+            dhcpv6Rand(DHCPV6_CLIENT_REN_TIMEOUT);
+      }
+      else
+      {
+         //Send a Renew message
+         dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_RENEW);
+
+         //Save the time at which the message was sent
+         context->timestamp = time;
+
+         //The RT is doubled for each subsequent retransmission
+         context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
+
+         //MRT specifies an upper bound on the value of RT
+         if(context->timeout > DHCPV6_CLIENT_REN_MAX_RT)
+         {
+            //Compute retransmission timeout
+            context->timeout = DHCPV6_CLIENT_REN_MAX_RT +
+               dhcpv6Rand(DHCPV6_CLIENT_REN_MAX_RT);
+         }
+      }
+
+      //Increment retransmission counter
+      context->retransmitCount++;
+   }
+   else
+   {
+      //A client will never attempt to use a Rebind message to locate a
+      //different server to extend the lifetime of any address in an IA
+      //with T2 set to 0xffffffff
+      if(context->ia.t2 != DHCPV6_INFINITE_TIME)
+      {
+         //Convert T2 to milliseconds
+         if(context->ia.t2 < (MAX_DELAY / 1000))
+            t2 = context->ia.t2 * 1000;
+         else
+            t2 = MAX_DELAY;
+
+         //Check whether T2 timer has expired
+         if(timeCompare(time, context->leaseStartTime + t2) >= 0)
+         {
+            //Switch to the REBIND state
+            dhcpv6ClientChangeState(context, DHCPV6_STATE_REBIND, 0);
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief REBIND state
+ *
+ * The client sends a Rebind message to any available server to extend
+ * the lifetimes on the addresses assigned to the client and to update
+ * other configuration parameters. This message is sent after a client
+ * receives no response to a Renew message
+ *
+ * @param[in] context Pointer to the DHCPv6 client context
+ **/
+
+void dhcpv6ClientStateRebind(Dhcpv6ClientContext *context)
+{
+   uint_t i;
+   systime_t time;
+   NetInterface *interface;
+   Dhcpv6ClientAddrEntry *entry;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check current time
+   if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+   {
+      //Check retransmission counter
+      if(context->retransmitCount == 0)
+      {
+         //Generate a 24-bit transaction ID
+         context->transactionId = netGetRand() & 0x00FFFFFF;
+
+         //Send a Rebind message
+         dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_REBIND);
+
+         //Save the time at which the message was sent
+         context->exchangeStartTime = time;
+         context->timestamp = time;
+
+         //Initial retransmission timeout
+         context->timeout = DHCPV6_CLIENT_REB_TIMEOUT +
+            dhcpv6Rand(DHCPV6_CLIENT_REB_TIMEOUT);
+      }
+      else
+      {
+         //Send a Rebind message
+         dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_REBIND);
+
+         //Save the time at which the message was sent
+         context->timestamp = time;
+
+         //The RT is doubled for each subsequent retransmission
+         context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
+
+         //MRT specifies an upper bound on the value of RT
+         if(context->timeout > DHCPV6_CLIENT_REB_MAX_RT)
+         {
+            //Compute retransmission timeout
+            context->timeout = DHCPV6_CLIENT_REB_MAX_RT +
+               dhcpv6Rand(DHCPV6_CLIENT_REB_MAX_RT);
+         }
+      }
+
+      //Increment retransmission counter
+      context->retransmitCount++;
+   }
+   else
+   {
+      //Loop through the IPv6 addresses recorded by the DHCPv6 client
+      for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
+      {
+         //Point to the current entry
+         entry = &context->ia.addrList[i];
+
+         //Valid IPv6 address?
+         if(entry->validLifetime > 0)
+         {
+            //Check whether the valid lifetime has expired
+            if(ipv6GetAddrState(interface, &entry->addr) == IPV6_ADDR_STATE_INVALID)
+            {
+               //Restart DHCPv6 configuration
+               dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief RELEASE state
+ *
+ * To release one or more addresses, a client sends a Release message
+ * to the server
+ *
+ * @param[in] context Pointer to the DHCPv6 client context
+ **/
+
+void dhcpv6ClientStateRelease(Dhcpv6ClientContext *context)
+{
+   systime_t time;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check current time
+   if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+   {
+      //Check retransmission counter
+      if(context->retransmitCount == 0)
+      {
+         //Generate a 24-bit transaction ID
+         context->transactionId = netGetRand() & 0x00FFFFFF;
+
+         //Send a Release message
+         dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_RELEASE);
+
+         //Save the time at which the message was sent
+         context->exchangeStartTime = time;
+         context->timestamp = time;
+
+         //Initial retransmission timeout
+         context->timeout = DHCPV6_CLIENT_REL_TIMEOUT +
+            dhcpv6Rand(DHCPV6_CLIENT_REL_TIMEOUT);
+
+         //Increment retransmission counter
+         context->retransmitCount++;
+      }
+      else if(context->retransmitCount < DHCPV6_CLIENT_REL_MAX_RC)
+      {
+         //Send a Release message
+         dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_RELEASE);
+
+         //Save the time at which the message was sent
+         context->timestamp = time;
+
+         //The RT is doubled for each subsequent retransmission
+         context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
+
+         //Increment retransmission counter
+         context->retransmitCount++;
+      }
+      else
+      {
+         //Implementations should retransmit one or more times, but may
+         //choose to terminate the retransmission procedure early
+         context->running = FALSE;
+
+         //Reinitialize state machine
+         dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
+      }
+   }
+}
+
+
+/**
+ * @brief DECLINE state
+ *
+ * If a client detects that one or more addresses assigned to it by a
+ * server are already in use by another node, the client sends a Decline
+ * message to the server to inform it that the address is suspect
+ *
+ * @param[in] context Pointer to the DHCPv6 client context
+ **/
+
+void dhcpv6ClientStateDecline(Dhcpv6ClientContext *context)
+{
+   systime_t time;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check current time
+   if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+   {
+      //Check retransmission counter
+      if(context->retransmitCount == 0)
+      {
+         //Generate a 24-bit transaction ID
+         context->transactionId = netGetRand() & 0x00FFFFFF;
+
+         //Send a Decline message
+         dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_DECLINE);
+
+         //Save the time at which the message was sent
+         context->exchangeStartTime = time;
+         context->timestamp = time;
+
+         //Initial retransmission timeout
+         context->timeout = DHCPV6_CLIENT_DEC_TIMEOUT +
+            dhcpv6Rand(DHCPV6_CLIENT_DEC_TIMEOUT);
+
+         //Increment retransmission counter
+         context->retransmitCount++;
+      }
+      else if(context->retransmitCount < DHCPV6_CLIENT_DEC_MAX_RC)
+      {
+         //Send a Decline message
+         dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_DECLINE);
+
+         //Save the time at which the message was sent
+         context->timestamp = time;
+
+         //The RT is doubled for each subsequent retransmission
+         context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout);
+
+         //Increment retransmission counter
+         context->retransmitCount++;
+      }
+      else
+      {
+         //If the client does not receive a response within a reasonable
+         //period of time, then it restarts the initialization procedure
+         dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
+      }
+   }
+}
+
+
+/**
+ * @brief Send Solicit message
+ * @param[in] context Pointer to the DHCPv6 client context
+ * @param[in] type DHCPv6 message type
+ * @return Error code
+ **/
+
+error_t dhcpv6ClientSendMessage(Dhcpv6ClientContext *context,
+   Dhcpv6MessageType type)
+{
+   error_t error;
+   uint_t i;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   NetInterface *interface;
+   Dhcpv6Message *message;
+   Dhcpv6Option *option;
+   Dhcpv6IaNaOption iaNaOption;
+   Dhcpv6IaAddrOption iaAddrOption;
+   Dhcpv6FqdnOption *fqdnOption;
+   Dhcpv6ElapsedTimeOption elapsedTimeOption;
+   Dhcpv6ClientAddrEntry *entry;
+   IpAddr destIpAddr;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Allocate a memory buffer to hold the DHCPv6 message
+   buffer = udpAllocBuffer(DHCPV6_MAX_MSG_SIZE, &offset);
+   //Failed to allocate buffer?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the DHCPv6 message
+   message = netBufferAt(buffer, offset);
+
+   //Set DHCPv6 message type
+   message->msgType = type;
+
+   //The transaction ID is chosen by the client
+   STORE24BE(context->transactionId, message->transactionId);
+
+   //Size of the DHCPv6 message
+   length = sizeof(Dhcpv6Message);
+
+   //The client must include a Client Identifier option
+   //to identify itself to the server
+   dhcpv6AddOption(message, &length, DHCPV6_OPTION_CLIENTID,
+      context->clientId, context->clientIdLength);
+
+   //Request, Renew, Release or Decline message?
+   if(type == DHCPV6_MSG_TYPE_REQUEST ||
+      type == DHCPV6_MSG_TYPE_RENEW ||
+      type == DHCPV6_MSG_TYPE_RELEASE ||
+      type == DHCPV6_MSG_TYPE_DECLINE)
+   {
+      //The client places the identifier of the destination
+      //server in a Server Identifier option
+      dhcpv6AddOption(message, &length, DHCPV6_OPTION_SERVERID,
+         context->serverId, context->serverIdLength);
+   }
+
+   //Solicit message?
+   if(type == DHCPV6_MSG_TYPE_SOLICIT)
+   {
+      //Check whether rapid commit is enabled
+      if(context->settings.rapidCommit)
+      {
+         //Include the Rapid Commit option if the client is prepared
+         //to perform the Solicit/Reply message exchange
+         dhcpv6AddOption(message, &length, DHCPV6_OPTION_RAPID_COMMIT, NULL, 0);
+      }
+   }
+
+   //Solicit, Request, Confirm, Renew or Rebind message?
+   if(type == DHCPV6_MSG_TYPE_SOLICIT ||
+      type == DHCPV6_MSG_TYPE_REQUEST ||
+      type == DHCPV6_MSG_TYPE_CONFIRM ||
+      type == DHCPV6_MSG_TYPE_RENEW ||
+      type == DHCPV6_MSG_TYPE_REBIND)
+   {
+      //Point to the client's fully qualified domain name
+      fqdnOption = (Dhcpv6FqdnOption *) context->clientFqdn;
+
+      //The FQDN option can be used by the client to convey its
+      //fully qualified domain name to the server
+      dhcpv6AddOption(message, &length, DHCPV6_OPTION_FQDN,
+         fqdnOption, sizeof(Dhcpv6FqdnOption) + context->clientFqdnLength);
+
+      //The client should include an Option Request option to indicate
+      //the options the client is interested in receiving
+      dhcpv6AddOption(message, &length, DHCPV6_OPTION_ORO,
+         &dhcpv6OptionList, sizeof(dhcpv6OptionList));
+   }
+
+   //Prepare an IA_NA option for a the current interface
+   iaNaOption.iaId = htonl(interface->id);
+
+   //Solicit, Request or Confirm message?
+   if(type == DHCPV6_MSG_TYPE_SOLICIT ||
+      type == DHCPV6_MSG_TYPE_REQUEST ||
+      type == DHCPV6_MSG_TYPE_CONFIRM)
+   {
+      //The client should set the T1 and T2 fields in any IA_NA options to 0
+      iaNaOption.t1 = 0;
+      iaNaOption.t2 = 0;
+   }
+   else
+   {
+      //T1 and T2 are provided as a hint
+      iaNaOption.t1 = htonl(context->ia.t1);
+      iaNaOption.t2 = htonl(context->ia.t2);
+   }
+
+   //The client includes IA options for any IAs to which
+   //it wants the server to assign addresses
+   option = dhcpv6AddOption(message, &length, DHCPV6_OPTION_IA_NA,
+      &iaNaOption, sizeof(Dhcpv6IaNaOption));
+
+   //Request, Confirm, Renew, Rebind, Release or Decline message?
+   if(type == DHCPV6_MSG_TYPE_REQUEST ||
+      type == DHCPV6_MSG_TYPE_CONFIRM ||
+      type == DHCPV6_MSG_TYPE_RENEW ||
+      type == DHCPV6_MSG_TYPE_REBIND ||
+      type == DHCPV6_MSG_TYPE_RELEASE ||
+      type == DHCPV6_MSG_TYPE_DECLINE)
+   {
+      //Loop through the IPv6 addresses recorded by the client
+      for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
+      {
+         //Point to the current entry
+         entry = &context->ia.addrList[i];
+
+         //Valid IPv6 address?
+         if(entry->validLifetime > 0)
+         {
+            //Prepare an IA Address option
+            iaAddrOption.address = entry->addr;
+
+            //Confirm message?
+            if(type == DHCPV6_MSG_TYPE_CONFIRM)
+            {
+               //The client should set the preferred and valid lifetimes to 0
+               iaAddrOption.preferredLifetime = 0;
+               iaAddrOption.validLifetime = 0;
+            }
+            else
+            {
+               //Preferred and valid lifetimes are provided as a hint
+               iaAddrOption.preferredLifetime = htonl(entry->preferredLifetime);
+               iaAddrOption.validLifetime = htonl(entry->validLifetime);
+            }
+
+            //Add the IA Address option
+            dhcpv6AddSubOption(option, &length, DHCPV6_OPTION_IAADDR,
+               &iaAddrOption, sizeof(iaAddrOption));
+         }
+      }
+   }
+
+   //Compute the time elapsed since the client sent the first message
+   elapsedTimeOption.value = dhcpv6ClientComputeElapsedTime(context);
+
+   //The client must include an Elapsed Time option in messages to indicate
+   //how long the client has been trying to complete a DHCP message exchange
+   dhcpv6AddOption(message, &length, DHCPV6_OPTION_ELAPSED_TIME,
+      &elapsedTimeOption, sizeof(Dhcpv6ElapsedTimeOption));
+
+   //Adjust the length of the multi-part buffer
+   netBufferSetLength(buffer, offset + length);
+
+   //Destination address
+   destIpAddr.length = sizeof(Ipv6Addr);
+   destIpAddr.ipv6Addr = DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS_ADDR;
+
+   //Debug message
+   TRACE_DEBUG("\r\n%s: Sending DHCPv6 message (%" PRIuSIZE " bytes)...\r\n",
+      formatSystemTime(osGetSystemTime(), NULL), length);
+
+   //Dump the contents of the message for debugging purpose
+   dhcpv6DumpMessage(message, length);
+
+   //Send DHCPv6 message
+   error = udpSendDatagramEx(interface, DHCPV6_CLIENT_PORT,
+      &destIpAddr, DHCPV6_SERVER_PORT, buffer, offset, 0);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process incoming DHCPv6 message
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader UDP pseudo header
+ * @param[in] udpHeader UDP header
+ * @param[in] buffer Multi-part buffer containing the incoming DHCPv6 message
+ * @param[in] offset Offset to the first byte of the DHCPv6 message
+ * @param[in] params Pointer to the DHCPv6 client context
+ **/
+
+void dhcpv6ClientProcessMessage(NetInterface *interface,
+   const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader,
+   const NetBuffer *buffer, size_t offset, void *params)
+{
+   size_t length;
+   Dhcpv6ClientContext *context;
+   Dhcpv6Message *message;
+
+   //Point to the DHCPv6 client context
+   context = (Dhcpv6ClientContext *) params;
+
+   //Retrieve the length of the DHCPv6 message
+   length = netBufferGetLength(buffer) - offset;
+
+   //Make sure the DHCPv6 message is valid
+   if(length < sizeof(Dhcpv6Message))
+      return;
+
+   //Point to the beginning of the DHCPv6 message
+   message = netBufferAt(buffer, offset);
+   //Sanity check
+   if(message == NULL)
+      return;
+
+   //Debug message
+   TRACE_DEBUG("\r\n%s: DHCPv6 message received (%" PRIuSIZE " bytes)...\r\n",
+      formatSystemTime(osGetSystemTime(), NULL), length);
+
+   //Dump the contents of the message for debugging purpose
+   dhcpv6DumpMessage(message, length);
+
+   //Check message type
+   switch(message->msgType)
+   {
+   case DHCPV6_MSG_TYPE_ADVERTISE:
+      //Parse Advertise message
+      dhcpv6ClientParseAdvertise(context, message, length);
+      break;
+   case DHCPV6_MSG_TYPE_REPLY:
+      //Parse Reply message
+      dhcpv6ClientParseReply(context, message, length);
+      break;
+   default:
+      //Silently drop incoming message
+      break;
+   }
+}
+
+
+/**
+ * @brief Parse Advertise message
+ * @param[in] context Pointer to the DHCPv6 client context
+ * @param[in] message Pointer to the incoming message to parse
+ * @param[in] length Length of the incoming message
+ **/
+
+void dhcpv6ClientParseAdvertise(Dhcpv6ClientContext *context,
+   const Dhcpv6Message *message, size_t length)
+{
+   uint_t i;
+   int_t serverPreference;
+   NetInterface *interface;
+   Dhcpv6StatusCode status;
+   Dhcpv6Option *option;
+   Dhcpv6Option *serverIdOption;
+   Dhcpv6IaNaOption *iaNaOption;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Make sure that the Advertise message is received in response to
+   //a Solicit message
+   if(context->state != DHCPV6_STATE_SOLICIT)
+      return;
+
+   //Discard any received packet that does not match the transaction ID
+   if(LOAD24BE(message->transactionId) != context->transactionId)
+      return;
+
+   //Get the length of the Options field
+   length -= sizeof(Dhcpv6Message);
+
+   //Search for the Client Identifier option
+   option = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_CLIENTID);
+
+   //Discard any received packet that does not include a Client Identifier option
+   if(option == NULL)
+      return;
+   //Check the length of the option
+   if(ntohs(option->length) != context->clientIdLength)
+      return;
+   //Check whether the Client Identifier matches our identifier
+   if(memcmp(option->value, context->clientId, context->clientIdLength))
+      return;
+
+   //Search for the Server Identifier option
+   serverIdOption = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_SERVERID);
+
+   //Discard any received packet that does not include a Server Identifier option
+   if(serverIdOption == NULL)
+      return;
+   //Check the length of the server DUID
+   if(ntohs(serverIdOption->length) == 0)
+      return;
+   if(ntohs(serverIdOption->length) > DHCPV6_MAX_DUID_SIZE)
+      return;
+
+   //Get the status code returned by the server
+   status = dhcpv6GetStatusCode(message->options, length);
+
+   //If the message contains a Status Code option indicating a failure,
+   //then the Advertise message is discarded by the client
+   if(status != DHCPV6_STATUS_SUCCESS)
+      return;
+
+   //Search for the Preference option
+   option = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_PREFERENCE);
+
+   //Check whether the option has been found
+   if(option != NULL && ntohs(option->length) == sizeof(Dhcpv6PreferenceOption))
+   {
+      //Server server preference value
+      serverPreference = option->value[0];
+   }
+   else
+   {
+      //Any Advertise that does not include a Preference option
+      //is considered to have a preference value of 0
+      serverPreference = 0;
+   }
+
+   //Select the Advertise message that offers the highest server preference value
+   if(serverPreference > context->serverPreference)
+   {
+      //Save the length of the DUID
+      context->serverIdLength = ntohs(serverIdOption->length);
+      //Record the server DUID
+      memcpy(context->serverId, serverIdOption->value, context->serverIdLength);
+
+      //Flush the list of IPv6 addresses from the client's IA
+      dhcpv6ClientFlushAddrList(context);
+   }
+
+   //Point to the first option
+   i = 0;
+
+   //Loop through DHCPv6 options
+   while(i < length)
+   {
+      //Search for an IA_NA option
+      option = dhcpv6GetOption(message->options + i, length - i, DHCPV6_OPTION_IA_NA);
+
+      //Unable to find the specified option?
+      if(option == NULL)
+         break;
+
+      //Make sure the IA_NA option is valid
+      if(ntohs(option->length) >= sizeof(Dhcpv6IaNaOption))
+      {
+         //Get the parameters associated with the IA_NA
+         iaNaOption = (Dhcpv6IaNaOption *) option->value;
+
+         //Check the IA identifier
+         if(ntohl(iaNaOption->iaId) == interface->id)
+         {
+            //The client examines the status code in each IA individually
+            status = dhcpv6GetStatusCode(iaNaOption->options,
+               ntohs(option->length) - sizeof(Dhcpv6IaNaOption));
+
+            //The client must ignore any Advertise message that includes a Status
+            //Code option containing the value NoAddrsAvail
+            if(status == DHCPV6_STATUS_NO_ADDRS_AVAILABLE)
+               return;
+         }
+
+         //Check the server preference value
+         if(serverPreference > context->serverPreference)
+         {
+            //Parse the contents of the IA_NA option
+            dhcpv6ClientParseIaNaOption(context, option);
+         }
+      }
+
+      //Jump to the next option
+      i += sizeof(Dhcpv6Option) + ntohs(option->length);
+   }
+
+   //Record the highest server preference value
+   if(serverPreference > context->serverPreference)
+      context->serverPreference = serverPreference;
+
+   //If the client receives an Advertise message that includes a
+   //Preference option with a preference value of 255, the client
+   //immediately completes the message exchange
+   if(serverPreference == DHCPV6_MAX_SERVER_PREFERENCE)
+   {
+      //Continue configuration procedure
+      dhcpv6ClientChangeState(context, DHCPV6_STATE_REQUEST, 0);
+   }
+   //The message exchange is not terminated before the first RT has elapsed
+   else if(context->retransmitCount > 1)
+   {
+      //Continue configuration procedure
+      dhcpv6ClientChangeState(context, DHCPV6_STATE_REQUEST, 0);
+   }
+}
+
+
+/**
+ * @brief Parse Reply message
+ * @param[in] context Pointer to the DHCPv6 client context
+ * @param[in] message Pointer to the incoming message to parse
+ * @param[in] length Length of the incoming message
+ **/
+
+void dhcpv6ClientParseReply(Dhcpv6ClientContext *context,
+   const Dhcpv6Message *message, size_t length)
+{
+   error_t error;
+   uint_t i;
+   uint_t k;
+   uint_t n;
+   bool_t iaNaOptionFound;
+   systime_t minPreferredLifetime;
+   NetInterface *interface;
+   Dhcpv6StatusCode status;
+   Dhcpv6Option *option;
+   Dhcpv6Option *serverIdOption;
+   Dhcpv6ClientAddrEntry *entry;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Discard any received packet that does not match the transaction ID
+   if(LOAD24BE(message->transactionId) != context->transactionId)
+      return;
+
+   //Get the length of the Options field
+   length -= sizeof(Dhcpv6Message);
+
+   //Search for the Client Identifier option
+   option = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_CLIENTID);
+
+   //Discard any received packet that does not include a Client Identifier option
+   if(option == NULL)
+      return;
+   //Check the length of the option
+   if(ntohs(option->length) != context->clientIdLength)
+      return;
+   //Check whether the Client Identifier matches our identifier
+   if(memcmp(option->value, context->clientId, context->clientIdLength))
+      return;
+
+   //Search for the Server Identifier option
+   serverIdOption = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_SERVERID);
+
+   //Discard any received packet that does not include a Server Identifier option
+   if(serverIdOption == NULL)
+      return;
+   //Check the length of the server DUID
+   if(ntohs(serverIdOption->length) == 0)
+      return;
+   if(ntohs(serverIdOption->length) > DHCPV6_MAX_DUID_SIZE)
+      return;
+
+   //Get the status code returned by the server
+   status = dhcpv6GetStatusCode(message->options, length);
+
+   //Check current state
+   if(context->state == DHCPV6_STATE_SOLICIT)
+   {
+      //A Reply message is not acceptable when rapid commit is disallowed
+      if(!context->settings.rapidCommit)
+         return;
+
+      //Search for the Rapid Commit option
+      option = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_RAPID_COMMIT);
+
+      //The client discards any message that does not include a Rapid Commit option
+      if(option == NULL || ntohs(option->length) != 0)
+         return;
+   }
+   else if(context->state == DHCPV6_STATE_REQUEST)
+   {
+      //The client must discard the Reply message if the contents of the
+      //Server Identifier option do not match the server’s DUID
+      if(!dhcpv6ClientCheckServerId(context, serverIdOption))
+         return;
+   }
+   else if(context->state == DHCPV6_STATE_CONFIRM)
+   {
+      //When the client receives a NotOnLink status from the server in response
+      //to a Confirm message, the client performs DHCP server solicitation
+      if(status == DHCPV6_STATUS_NOT_ON_LINK)
+      {
+         //Restart the DHCP server discovery process
+         dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
+
+         //Exit immediately
+         return;
+      }
+   }
+   else if(context->state == DHCPV6_STATE_RENEW)
+   {
+      //The client must discard the Reply message if the contents of the
+      //Server Identifier option do not match the server’s DUID
+      if(!dhcpv6ClientCheckServerId(context, serverIdOption))
+         return;
+   }
+   else if(context->state == DHCPV6_STATE_REBIND)
+   {
+      //Do not check the server's DUID when the Reply message is
+      //received in response to a Rebind message
+   }
+   else if(context->state == DHCPV6_STATE_RELEASE)
+   {
+      //The client must discard the Reply message if the contents of the
+      //Server Identifier option do not match the server’s DUID
+      if(!dhcpv6ClientCheckServerId(context, serverIdOption))
+         return;
+
+      //When the client receives a valid Reply message in response to a
+      //Release message, the client considers the Release event completed,
+      //regardless of the Status Code option(s) returned by the server
+      context->running = FALSE;
+
+      //Reinitialize state machine
+      dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
+
+      //Exit immediately
+      return;
+   }
+   else if(context->state == DHCPV6_STATE_DECLINE)
+   {
+      //The client must discard the Reply message if the contents of the
+      //Server Identifier option do not match the server’s DUID
+      if(!dhcpv6ClientCheckServerId(context, serverIdOption))
+         return;
+
+      //When the client receives a valid Reply message in response to a
+      //Decline message, the client considers the Decline event completed,
+      //regardless of the Status Code option returned by the server
+      dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
+
+      //Exit immediately
+      return;
+   }
+   else
+   {
+      //Silently discard the Reply message
+      return;
+   }
+
+   //Check status code
+   if(status == DHCPV6_STATUS_USE_MULTICAST)
+   {
+      //When the client receives a Reply message with a Status Code option
+      //with the value UseMulticast, the client records the receipt of the
+      //message and sends subsequent messages to the server through the
+      //interface on which the message was received using multicast
+      return;
+   }
+   else if(status == DHCPV6_STATUS_UNSPEC_FAILURE)
+   {
+      //If the client receives a Reply message with a Status Code containing
+      //UnspecFail, the server is indicating that it was unable to process
+      //the message due to an unspecified failure condition
+      return;
+   }
+
+   //This flag will be set if a valid IA_NA option is found
+   iaNaOptionFound = FALSE;
+   //Point to the first option
+   i = 0;
+
+   //Loop through DHCPv6 options
+   while(i < length)
+   {
+      //Search for an IA_NA option
+      option = dhcpv6GetOption(message->options + i, length - i, DHCPV6_OPTION_IA_NA);
+
+      //Unable to find the specified option?
+      if(option == NULL)
+         break;
+
+      //Parse the contents of the IA_NA option
+      error = dhcpv6ClientParseIaNaOption(context, option);
+
+      //Check error code
+      if(error == NO_ERROR)
+      {
+         //A valid IA_NA option has been found
+         iaNaOptionFound = TRUE;
+      }
+      else if(error == ERROR_NOT_ON_LINK)
+      {
+         //When the client receives a NotOnLink status from the server
+         //in response to a Request, the client can either re-issue the
+         //Request without specifying any addresses or restart the DHCP
+         //server discovery process
+         dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
+
+         //Exit immediately
+         return;
+      }
+      else if(error == ERROR_NO_BINDING)
+      {
+         //When the client receives a Reply message in response to a Renew
+         //or Rebind message, the client sends a Request message if any of
+         //the IAs in the Reply message contains the NoBinding status code
+         dhcpv6ClientChangeState(context, DHCPV6_STATE_REQUEST, 0);
+
+         //Exit immediately
+         return;
+      }
+      else
+      {
+         //If an invalid option is received, the client discards
+         //the option and process the rest of the message...
+      }
+
+      //Jump to the next option
+      i += sizeof(Dhcpv6Option) + ntohs(option->length);
+   }
+
+   //No usable addresses in any of the IAs?
+   if(!iaNaOptionFound)
+   {
+      //Check whether the client receives a Reply message in response
+      //to a Renew or Rebind message
+      if(context->state == DHCPV6_STATE_RENEW ||
+         context->state == DHCPV6_STATE_REBIND)
+      {
+         //The client sends a Renew/Rebind if the IA is not in the Reply message
+      }
+      else
+      {
+         //If the client finds no usable addresses in any of the IAs, it may try
+         //another server (perhaps restarting the DHCP server discovery process)
+         dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
+      }
+
+      //Exit immediately
+      return;
+   }
+
+   //Total number of valid IPv6 in the IA
+   n = 0;
+   //Number of new IPv6 addresses in the IA
+   k = 0;
+   //Minimum preferred lifetime observed in the IA
+   minPreferredLifetime = 0;
+
+   //Loop through the IPv6 addresses recorded by the DHCPv6 client
+   for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &context->ia.addrList[i];
+
+      //Valid IPv6 address?
+      if(entry->validLifetime > 0)
+      {
+         //Total number of valid IPv6 in the IA
+         n++;
+
+         //Save the minimum preferred lifetime that has been observed so far
+         if(minPreferredLifetime < entry->preferredLifetime)
+            minPreferredLifetime = entry->preferredLifetime;
+
+         //Update lifetimes of the current IPv6 address
+         ipv6AddAddr(interface, &entry->addr, entry->validLifetime,
+            entry->preferredLifetime);
+
+         //New IPv6 address added?
+         if(ipv6GetAddrState(interface, &entry->addr) == IPV6_ADDR_STATE_TENTATIVE)
+            k++;
+      }
+   }
+
+   //Make sure that the IA contains at least one IPv6 address
+   if(n > 0)
+   {
+      //Save the length of the DUID
+      context->serverIdLength = ntohs(serverIdOption->length);
+      //Record the server DUID
+      memcpy(context->serverId, serverIdOption->value, context->serverIdLength);
+
+      //Save the time a which the lease was obtained
+      context->leaseStartTime = osGetSystemTime();
+
+      //Check the value of T1
+      if(context->ia.t1 == 0)
+      {
+         //If T1 is set to 0 by the server, the client may send
+         //a Renew message at the client's discretion
+         if(minPreferredLifetime == DHCPV6_INFINITE_TIME)
+            context->ia.t1 = DHCPV6_INFINITE_TIME;
+         else
+            context->ia.t1 = minPreferredLifetime / 2;
+      }
+
+      //Check the value of T2
+      if(context->ia.t2 == 0)
+      {
+         //If T2 is set to 0 by the server, the client may send
+         //a Rebind message at the client's discretion
+         if(context->ia.t1 == DHCPV6_INFINITE_TIME)
+            context->ia.t2 = DHCPV6_INFINITE_TIME;
+         else
+            context->ia.t2 = context->ia.t1 + context->ia.t1 / 2;
+      }
+
+      //Any addresses added in the IA?
+      if(k > 0)
+      {
+         //Perform Duplicate Address Detection for the new IPv6 addresses
+         dhcpv6ClientChangeState(context, DHCPV6_STATE_DAD, 0);
+      }
+      else
+      {
+         //Switch to the BOUND state
+         dhcpv6ClientChangeState(context, DHCPV6_STATE_BOUND, 0);
+      }
+   }
+   else
+   {
+      //If the client finds no usable addresses in any of the IAs, it may try
+      //another server (perhaps restarting the DHCP server discovery process)
+      dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0);
+   }
+}
+
+
+/**
+ * @brief Parse IA_NA option
+ * @param[in] context Pointer to the DHCPv6 client context
+ * @param[in] option Pointer to the IA_NA option to parse
+ * @return Error code
+ **/
+
+error_t dhcpv6ClientParseIaNaOption(Dhcpv6ClientContext *context,
+   const Dhcpv6Option *option)
+{
+   error_t error;
+   uint_t n;
+   size_t i;
+   size_t length;
+   NetInterface *interface;
+   Dhcpv6StatusCode status;
+   Dhcpv6IaNaOption *iaNaOption;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Number of addresses found in the IA_NA option
+   n = 0;
+
+   //Make sure the IA_NA option is valid
+   if(ntohs(option->length) < sizeof(Dhcpv6IaNaOption))
+      return ERROR_INVALID_LENGTH;
+
+   //Get the parameters associated with the IA_NA
+   iaNaOption = (Dhcpv6IaNaOption *) option->value;
+   //Compute the length of IA_NA Options field
+   length = ntohs(option->length) - sizeof(Dhcpv6IaNaOption);
+
+   //Check the IA identifier
+   if(ntohl(iaNaOption->iaId) != interface->id)
+      return ERROR_WRONG_IDENTIFIER;
+
+   //If a client receives an IA_NA with T1 greater than T2, and both T1
+   //and T2 are greater than 0, the client discards the IA_NA option and
+   //processes the remainder of the message as though the server had not
+   //included the invalid IA_NA option
+   if(ntohl(iaNaOption->t1) > ntohl(iaNaOption->t2) && ntohl(iaNaOption->t2) > 0)
+      return ERROR_INVALID_PARAMETER;
+
+   //The client examines the status code in each IA individually
+   status = dhcpv6GetStatusCode(iaNaOption->options, length);
+
+   //Check error code
+   if(status == DHCPV6_STATUS_NO_ADDRS_AVAILABLE)
+   {
+      //The client has received no usable address in the IA
+      return ERROR_NO_ADDRESS;
+   }
+   else if(status == DHCPV6_STATUS_NO_BINDING)
+   {
+      //Client record (binding) unavailable
+      return ERROR_NO_BINDING;
+   }
+   else if(status == DHCPV6_STATUS_NOT_ON_LINK)
+   {
+      //The prefix for the address is not appropriate for the
+      //link to which the client is attached
+      return ERROR_NOT_ON_LINK;
+   }
+   else if(status != DHCPV6_STATUS_SUCCESS)
+   {
+      //Failure, reason unspecified
+      return ERROR_FAILURE;
+   }
+
+   //Record T1 and T2 times
+   context->ia.t1 = ntohl(iaNaOption->t1);
+   context->ia.t2 = ntohl(iaNaOption->t2);
+
+   //Point to the first option
+   i = 0;
+
+   //Loop through IA_NA options
+   while(i < length)
+   {
+      //Search for an IA Address option
+      option = dhcpv6GetOption(iaNaOption->options + i, length - i, DHCPV6_OPTION_IAADDR);
+
+      //Unable to find the specified option?
+      if(option == NULL)
+         break;
+
+      //Parse the contents of the IA Address option
+      error = dhcpv6ClientParseIaAddrOption(context, option);
+
+      //Check status code
+      if(!error)
+      {
+         //Increment the number of addresses found in the IA_NA option
+         n++;
+      }
+
+      //Jump to the next option
+      i += sizeof(Dhcpv6Option) + ntohs(option->length);
+   }
+
+   //No usable addresses in the IA_NA option?
+   if(n == 0)
+   {
+      //Report an error
+      return ERROR_NO_ADDRESS;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse IA Address option
+ * @param[in] context Pointer to the DHCPv6 client context
+ * @param[in] option Pointer to the IA Address option to parse
+ * @return Error code
+ **/
+
+error_t dhcpv6ClientParseIaAddrOption(Dhcpv6ClientContext *context,
+   const Dhcpv6Option *option)
+{
+   size_t length;
+   uint32_t validLifetime;
+   uint32_t preferredLifetime;
+   Dhcpv6StatusCode status;
+   Dhcpv6IaAddrOption *iaAddrOption;
+
+   //Make sure the IA Address option is valid
+   if(ntohs(option->length) < sizeof(Dhcpv6IaAddrOption))
+      return ERROR_INVALID_LENGTH;
+
+   //Point to the contents of the IA Address option
+   iaAddrOption = (Dhcpv6IaAddrOption *) option->value;
+   //Compute the length of IA Address Options field
+   length = ntohs(option->length) - sizeof(Dhcpv6IaAddrOption);
+
+   //Convert lifetimes to host byte order
+   validLifetime = ntohl(iaAddrOption->validLifetime);
+   preferredLifetime = ntohl(iaAddrOption->preferredLifetime);
+
+   //A client discards any addresses for which the preferred lifetime
+   //is greater than the valid lifetime
+   if(preferredLifetime > validLifetime)
+      return ERROR_INVALID_PARAMETER;
+
+   //The client examines the status code in each IA Address
+   status = dhcpv6GetStatusCode(iaAddrOption->options, length);
+
+   //Any error to report?
+   if(status != DHCPV6_STATUS_SUCCESS)
+      return ERROR_FAILURE;
+
+   //Check the value of the Valid Lifetime
+   if(iaAddrOption->validLifetime > 0)
+   {
+      //Add any new addresses in the IA option to the IA as recorded
+      //by the client
+      dhcpv6ClientAddAddr(context, &iaAddrOption->address,
+         validLifetime, preferredLifetime);
+   }
+   else
+   {
+      //Discard any addresses from the IA, as recorded by the client,
+      //that have a valid lifetime of 0 in the IA Address option
+      dhcpv6ClientRemoveAddr(context, &iaAddrOption->address);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Add an IPv6 address to the IA
+ * @param[in] context Pointer to the DHCPv6 client context
+ * @param[in] addr IPv6 address to be added
+ * @param[in] validLifetime Valid lifetime, in seconds
+ * @param[in] preferredLifetime Preferred lifetime, in seconds
+ **/
+
+void dhcpv6ClientAddAddr(Dhcpv6ClientContext *context, const Ipv6Addr *addr,
+   uint32_t validLifetime, uint32_t preferredLifetime)
+{
+   uint_t i;
+   Dhcpv6ClientAddrEntry *entry;
+   Dhcpv6ClientAddrEntry *firstFreeEntry;
+
+   //Keep track of the first free entry
+   firstFreeEntry = NULL;
+
+   //Loop through the IPv6 addresses recorded by the DHCPv6 client
+   for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &context->ia.addrList[i];
+
+      //Valid IPv6 address?
+      if(entry->validLifetime > 0)
+      {
+         //Check whether the current entry matches the specified address
+         if(ipv6CompAddr(&entry->addr, addr))
+            break;
+      }
+      else
+      {
+         //Keep track of the first free entry
+         if(firstFreeEntry == NULL)
+            firstFreeEntry = entry;
+      }
+   }
+
+   //No matching entry found?
+   if(i >= IPV6_PREFIX_LIST_SIZE)
+      entry = firstFreeEntry;
+
+   //Update the entry if necessary
+   if(entry != NULL)
+   {
+      //Save IPv6 address
+      entry->addr = *addr;
+
+      //Save lifetimes
+      entry->validLifetime = validLifetime;
+      entry->preferredLifetime = preferredLifetime;
+   }
+}
+
+
+/**
+ * @brief Remove an IPv6 address from the IA
+ * @param[in] context Pointer to the DHCPv6 client context
+ * @param[in] addr IPv6 address to be removed
+ **/
+
+void dhcpv6ClientRemoveAddr(Dhcpv6ClientContext *context, const Ipv6Addr *addr)
+{
+   uint_t i;
+   NetInterface *interface;
+   Dhcpv6ClientAddrEntry *entry;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Loop through the IPv6 addresses recorded by the DHCPv6 client
+   for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &context->ia.addrList[i];
+
+      //Valid IPv6 address?
+      if(entry->validLifetime > 0)
+      {
+         //Check whether the current entry matches the specified address
+         if(ipv6CompAddr(&entry->addr, addr))
+         {
+            //The IPv6 address is no more valid and should be removed from
+            //the list of IPv6 addresses assigned to the interface
+            ipv6RemoveAddr(interface, addr);
+
+            //Remove the IPv6 address from the IA
+            entry->validLifetime = 0;
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Flush the list of IPv6 addresses from the IA
+ * @param[in] context Pointer to the DHCPv6 client context
+ **/
+
+void dhcpv6ClientFlushAddrList(Dhcpv6ClientContext *context)
+{
+   uint_t i;
+   NetInterface *interface;
+   Dhcpv6ClientAddrEntry *entry;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Loop through the IPv6 addresses recorded by the DHCPv6 client
+   for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &context->ia.addrList[i];
+
+      //Valid IPv6 address?
+      if(entry->validLifetime > 0)
+      {
+         //The IPv6 address is no more valid and should be removed from
+         //the list of IPv6 addresses assigned to the interface
+         ipv6RemoveAddr(interface, &entry->addr);
+
+         //Remove the IPv6 address from the IA
+         entry->validLifetime = 0;
+      }
+   }
+}
+
+
+/**
+ * @brief Generate client's DUID
+ * @param[in] context Pointer to the DHCPv6 client context
+ * @return Error code
+ **/
+
+error_t dhcpv6ClientGenerateDuid(Dhcpv6ClientContext *context)
+{
+   NetInterface *interface;
+   Dhcpv6DuidLl *duid;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Point to the buffer where to format the client's DUID
+   duid = (Dhcpv6DuidLl *) context->clientId;
+
+#if (ETH_SUPPORT == ENABLED)
+   //Generate a DUID-LL from the MAC address
+   duid->type = HTONS(DHCPV6_DUID_LL);
+   duid->hardwareType = HTONS(DHCPV6_HARDWARE_TYPE_ETH);
+   duid->linkLayerAddr = interface->macAddr;
+#else
+   //Generate a DUID-LL from the EUI-64 identifier
+   duid->type = HTONS(DHCPV6_DUID_LL);
+   duid->hardwareType = HTONS(DHCPV6_HARDWARE_TYPE_EUI64);
+   duid->linkLayerAddr = interface->eui64;
+#endif
+
+   //Length of the newly generated DUID
+   context->clientIdLength = sizeof(Dhcpv6DuidLl);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Generate client's fully qualified domain name
+ * @param[in] context Pointer to the DHCPv6 client context
+ * @return Error code
+ **/
+
+error_t dhcpv6ClientGenerateFqdn(Dhcpv6ClientContext *context)
+{
+   NetInterface *interface;
+   Dhcpv6FqdnOption *fqdnOption;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Point to the buffer where to format the client's FQDN
+   fqdnOption = (Dhcpv6FqdnOption *) context->clientFqdn;
+
+   //Set flags
+   fqdnOption->mbz = 0;
+   fqdnOption->n = FALSE;
+   fqdnOption->o = FALSE;
+   fqdnOption->s = FALSE;
+
+   //Encode client's FQDN
+   context->clientFqdnLength = dnsEncodeName(interface->hostname,
+      fqdnOption->domainName);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Generate a link-local address
+ * @param[in] context Pointer to the DHCPv6 client context
+ * @return Error code
+ **/
+
+error_t dhcpv6ClientGenerateLinkLocalAddr(Dhcpv6ClientContext *context)
+{
+   error_t error;
+   NetInterface *interface;
+   Ipv6Addr addr;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Check whether a link-local address has been manually assigned
+   if(interface->ipv6Context.addrList[0].state != IPV6_ADDR_STATE_INVALID &&
+      interface->ipv6Context.addrList[0].permanent)
+   {
+      //Keep using the current link-local address
+      error = NO_ERROR;
+   }
+   else
+   {
+      //A link-local address is formed by combining the well-known
+      //link-local prefix fe80::/10 with the interface identifier
+      ipv6GenerateLinkLocalAddr(&interface->eui64, &addr);
+
+#if (NDP_SUPPORT == ENABLED)
+      //Check whether Duplicate Address Detection should be performed
+      if(interface->ndpContext.dupAddrDetectTransmits > 0)
+      {
+         //Use the link-local address as a tentative address
+         error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_TENTATIVE,
+            NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, FALSE);
+      }
+      else
+#endif
+      {
+         //The use of the link-local address is now unrestricted
+         error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_PREFERRED,
+            NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, FALSE);
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Check the Server Identifier option
+ * @param[in] context Pointer to the DHCPv6 client context
+ * @param[in] serverIdOption Pointer to the Server Identifier option
+ * @return TRUE if the option matches the server’s DUID, else FALSE
+ **/
+
+bool_t dhcpv6ClientCheckServerId(Dhcpv6ClientContext *context,
+   Dhcpv6Option *serverIdOption)
+{
+   bool_t valid = FALSE;
+
+   //Check the length of the Server Identifier option
+   if(ntohs(serverIdOption->length) == context->serverIdLength)
+   {
+      //Check whether the Server Identifier option matches the server’s DUID
+      if(!memcmp(serverIdOption->value, context->serverId, context->serverIdLength))
+         valid = TRUE;
+   }
+
+   //Return TRUE if the option matches the server’s DUID
+   return valid;
+}
+
+
+/**
+ * @brief Manage DHCPv6 configuration timeout
+ * @param[in] context Pointer to the DHCPv6 client context
+ **/
+
+void dhcpv6ClientCheckTimeout(Dhcpv6ClientContext *context)
+{
+   systime_t time;
+   NetInterface *interface;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Any registered callback?
+   if(context->settings.timeoutEvent != NULL)
+   {
+      //DHCPv6 configuration timeout?
+      if(timeCompare(time, context->configStartTime + context->settings.timeout) >= 0)
+      {
+         //Ensure the callback function is only called once
+         if(!context->timeoutEventDone)
+         {
+            //Release exclusive access
+            osReleaseMutex(&netMutex);
+            //Invoke user callback function
+            context->settings.timeoutEvent(context, interface);
+            //Get exclusive access
+            osAcquireMutex(&netMutex);
+
+            //Set flag
+            context->timeoutEventDone = TRUE;
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Compute the time elapsed since the client sent the first message
+ * @param[in] context Pointer to the DHCPv6 client context
+ * @return The elapsed time expressed in hundredths of a second
+ **/
+
+uint16_t dhcpv6ClientComputeElapsedTime(Dhcpv6ClientContext *context)
+{
+   systime_t time;
+
+   //Check retransmission counter
+   if(context->retransmitCount == 0)
+   {
+      //The elapsed time must be 0 for the first message
+      time = 0;
+   }
+   else
+   {
+      //Compute the time elapsed since the client sent the
+      //first message (in hundredths of a second)
+      time = (osGetSystemTime() - context->exchangeStartTime) / 10;
+
+      //The value 0xFFFF is used to represent any elapsed time values
+      //greater than the largest time value that can be represented
+      time = MIN(time, 0xFFFF);
+   }
+
+   //Convert the 16-bit value to network byte order
+   return htons(time);
+}
+
+
+/**
+ * @brief Update DHCPv6 FSM state
+ * @param[in] context Pointer to the DHCPv6 client context
+ * @param[in] newState New DHCPv6 state to switch to
+ * @param[in] delay Initial delay
+ **/
+
+void dhcpv6ClientChangeState(Dhcpv6ClientContext *context,
+   Dhcpv6State newState, systime_t delay)
+{
+   systime_t time;
+
+   //Get current time
+   time = osGetSystemTime();
+
+#if (DHCPV6_TRACE_LEVEL >= TRACE_LEVEL_INFO)
+   //Sanity check
+   if(newState <= DHCPV6_STATE_DECLINE)
+   {
+      //DHCPv6 FSM states
+      static const char_t *stateLabel[] =
+      {
+         "INIT",
+         "SOLICIT",
+         "REQUEST",
+         "INIT-CONFIRM",
+         "CONFIRM",
+         "DAD",
+         "BOUND",
+         "RENEW",
+         "REBIND",
+         "RELEASE",
+         "DECLINE"
+      };
+
+      //Debug message
+      TRACE_INFO("%s: DHCPv6 client %s state\r\n",
+         formatSystemTime(time, NULL), stateLabel[newState]);
+   }
+#endif
+
+   //Set time stamp
+   context->timestamp = time;
+   //Set initial delay
+   context->timeout = delay;
+   //Reset retransmission counter
+   context->retransmitCount = 0;
+   //Switch to the new state
+   context->state = newState;
+
+   //Any registered callback?
+   if(context->settings.stateChangeEvent != NULL)
+   {
+      NetInterface *interface;
+
+      //Point to the underlying network interface
+      interface = context->settings.interface;
+
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+      //Invoke user callback function
+      context->settings.stateChangeEvent(context, interface, newState);
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+   }
+}
+
+
+/**
+ * @brief Dump DHCPv6 configuration for debugging purpose
+ * @param[in] context Pointer to the DHCPv6 client context
+ **/
+
+void dhcpv6ClientDumpConfig(Dhcpv6ClientContext *context)
+{
+#if (DHCPV6_TRACE_LEVEL >= TRACE_LEVEL_INFO)
+   uint_t i;
+   NetInterface *interface;
+   Ipv6Context *ipv6Context;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+   //Point to the IPv6 context
+   ipv6Context = &interface->ipv6Context;
+
+   //Debug message
+   TRACE_INFO("\r\n");
+   TRACE_INFO("DHCPv6 configuration:\r\n");
+
+   //Lease start time
+   TRACE_INFO("  Lease Start Time = %s\r\n",
+      formatSystemTime(context->leaseStartTime, NULL));
+
+   //T1 parameter
+   TRACE_INFO("  T1 = %" PRIu32 "s\r\n", context->ia.t1);
+   //T2 parameter
+   TRACE_INFO("  T2 = %" PRIu32 "s\r\n", context->ia.t2);
+
+   //Global addresses
+   for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++)
+   {
+      TRACE_INFO("  Global Address %u = %s\r\n", i,
+         ipv6AddrToString(&ipv6Context->addrList[i].addr, NULL));
+   }
+
+   //DNS servers
+   for(i = 0; i < IPV6_DNS_SERVER_LIST_SIZE; i++)
+   {
+      TRACE_INFO("  DNS Server %u = %s\r\n", i + 1,
+         ipv6AddrToString(&ipv6Context->dnsServerList[i], NULL));
+   }
+
+   //Debug message
+   TRACE_INFO("\r\n");
+#endif
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dhcpv6/dhcpv6_client.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,411 @@
+/**
+ * @file dhcpv6_client.h
+ * @brief DHCPv6 client (Dynamic Host Configuration Protocol for IPv6)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DHCPV6_CLIENT_H
+#define _DHCPV6_CLIENT_H
+
+//Dependencies
+#include "dhcpv6/dhcpv6_common.h"
+#include "core/socket.h"
+
+//DHCPv6 client support
+#ifndef DHCPV6_CLIENT_SUPPORT
+   #define DHCPV6_CLIENT_SUPPORT DISABLED
+#elif (DHCPV6_CLIENT_SUPPORT != ENABLED && DHCPV6_CLIENT_SUPPORT != DISABLED)
+   #error DHCPV6_CLIENT_SUPPORT parameter is not valid
+#endif
+
+//DHCPv6 client tick interval
+#ifndef DHCPV6_CLIENT_TICK_INTERVAL
+   #define DHCPV6_CLIENT_TICK_INTERVAL 200
+#elif (DHCPV6_CLIENT_TICK_INTERVAL < 10)
+   #error DHCPV6_CLIENT_TICK_INTERVAL parameter is not valid
+#endif
+
+//Maximum number of IPv6 addresses in the client's IA
+#ifndef DHCPV6_CLIENT_ADDR_LIST_SIZE
+   #define DHCPV6_CLIENT_ADDR_LIST_SIZE 2
+#elif (DHCPV6_CLIENT_ADDR_LIST_SIZE < 1)
+   #error DHCPV6_CLIENT_ADDR_LIST_SIZE parameter is not valid
+#endif
+
+//Maximum size of the client's FQDN
+#ifndef DHCPV6_CLIENT_MAX_FQDN_SIZE
+   #define DHCPV6_CLIENT_MAX_FQDN_SIZE 16
+#elif (DHCPV6_CLIENT_MAX_FQDN_SIZE < 1)
+   #error DHCPV6_CLIENT_MAX_FQDN_SIZE parameter is not valid
+#endif
+
+//Max delay of first Solicit
+#ifndef DHCPV6_CLIENT_SOL_MAX_DELAY
+   #define DHCPV6_CLIENT_SOL_MAX_DELAY 1000
+#elif (DHCPV6_CLIENT_SOL_MAX_DELAY < 100)
+   #error DHCPV6_CLIENT_SOL_MAX_DELAY parameter is not valid
+#endif
+
+//Initial Solicit timeout
+#ifndef DHCPV6_CLIENT_SOL_TIMEOUT
+   #define DHCPV6_CLIENT_SOL_TIMEOUT 1000
+#elif (DHCPV6_CLIENT_SOL_TIMEOUT < 100)
+   #error DHCPV6_CLIENT_SOL_TIMEOUT parameter is not valid
+#endif
+
+//Max Solicit timeout value
+#ifndef DHCPV6_CLIENT_SOL_MAX_RT
+   #define DHCPV6_CLIENT_SOL_MAX_RT 120000
+#elif (DHCPV6_CLIENT_SOL_MAX_RT < 100)
+   #error DHCPV6_CLIENT_SOL_MAX_RT parameter is not valid
+#endif
+
+//Initial Request timeout
+#ifndef DHCPV6_CLIENT_REQ_TIMEOUT
+   #define DHCPV6_CLIENT_REQ_TIMEOUT 1000
+#elif (DHCPV6_CLIENT_REQ_TIMEOUT < 100)
+   #error DHCPV6_CLIENT_REQ_TIMEOUT parameter is not valid
+#endif
+
+//Max Request timeout value
+#ifndef DHCPV6_CLIENT_REQ_MAX_RT
+   #define DHCPV6_CLIENT_REQ_MAX_RT 30000
+#elif (DHCPV6_CLIENT_REQ_MAX_RT < 100)
+   #error DHCPV6_CLIENT_REQ_MAX_RT parameter is not valid
+#endif
+
+//Max Request retry attempts
+#ifndef DHCPV6_CLIENT_REQ_MAX_RC
+   #define DHCPV6_CLIENT_REQ_MAX_RC 10
+#elif (DHCPV6_CLIENT_REQ_MAX_RC < 1)
+   #error DHCPV6_CLIENT_REQ_MAX_RC parameter is not valid
+#endif
+
+//Max delay of first Confirm
+#ifndef DHCPV6_CLIENT_CNF_MAX_DELAY
+   #define DHCPV6_CLIENT_CNF_MAX_DELAY 1000
+#elif (DHCPV6_CLIENT_CNF_MAX_DELAY < 100)
+   #error DHCPV6_CLIENT_CNF_MAX_DELAY parameter is not valid
+#endif
+
+//Initial Confirm timeout
+#ifndef DHCPV6_CLIENT_CNF_TIMEOUT
+   #define DHCPV6_CLIENT_CNF_TIMEOUT 1000
+#elif (DHCPV6_CLIENT_CNF_TIMEOUT < 100)
+   #error DHCPV6_CLIENT_CNF_TIMEOUT parameter is not valid
+#endif
+
+//Max Confirm timeout
+#ifndef DHCPV6_CLIENT_CNF_MAX_RT
+   #define DHCPV6_CLIENT_CNF_MAX_RT 4000
+#elif (DHCPV6_CLIENT_CNF_MAX_RT < 100)
+   #error DHCPV6_CLIENT_CNF_MAX_RT parameter is not valid
+#endif
+
+//Max Confirm duration
+#ifndef DHCPV6_CLIENT_CNF_MAX_RD
+   #define DHCPV6_CLIENT_CNF_MAX_RD 10000
+#elif (DHCPV6_CLIENT_CNF_MAX_RD < 100)
+   #error DHCPV6_CLIENT_CNF_MAX_RD parameter is not valid
+#endif
+
+//Initial Renew timeout
+#ifndef DHCPV6_CLIENT_REN_TIMEOUT
+   #define DHCPV6_CLIENT_REN_TIMEOUT 10000
+#elif (DHCPV6_CLIENT_REN_TIMEOUT < 100)
+   #error DHCPV6_CLIENT_REN_TIMEOUT parameter is not valid
+#endif
+
+//Max Renew timeout value
+#ifndef DHCPV6_CLIENT_REN_MAX_RT
+   #define DHCPV6_CLIENT_REN_MAX_RT 600000
+#elif (DHCPV6_CLIENT_REN_MAX_RT < 100)
+   #error DHCPV6_CLIENT_REN_MAX_RT parameter is not valid
+#endif
+
+//Initial Rebind timeout
+#ifndef DHCPV6_CLIENT_REB_TIMEOUT
+   #define DHCPV6_CLIENT_REB_TIMEOUT 10000
+#elif (DHCPV6_CLIENT_REB_TIMEOUT < 100)
+   #error DHCPV6_CLIENT_REB_TIMEOUT parameter is not valid
+#endif
+
+//Max Rebind timeout value
+#ifndef DHCPV6_CLIENT_REB_MAX_RT
+   #define DHCPV6_CLIENT_REB_MAX_RT 600000
+#elif (DHCPV6_CLIENT_REB_MAX_RT < 100)
+   #error DHCPV6_CLIENT_REB_MAX_RT parameter is not valid
+#endif
+
+//Max delay of first Information-request
+#ifndef DHCPV6_CLIENT_INF_MAX_DELAY
+   #define DHCPV6_CLIENT_INF_MAX_DELAY 1000
+#elif (DHCPV6_CLIENT_INF_MAX_DELAY < 100)
+   #error DHCPV6_CLIENT_INF_MAX_DELAY parameter is not valid
+#endif
+
+//Initial Information-request timeout
+#ifndef DHCPV6_CLIENT_INF_TIMEOUT
+   #define DHCPV6_CLIENT_INF_TIMEOUT 1000
+#elif (DHCPV6_CLIENT_INF_TIMEOUT < 100)
+   #error DHCPV6_CLIENT_INF_TIMEOUT parameter is not valid
+#endif
+
+//Max Information-request timeout value
+#ifndef DHCPV6_CLIENT_INF_MAX_RT
+   #define DHCPV6_CLIENT_INF_MAX_RT 120000
+#elif (DHCPV6_CLIENT_INF_MAX_RT < 1000)
+   #error DHCPV6_CLIENT_INF_MAX_RT parameter is not valid
+#endif
+
+//Initial Release timeout
+#ifndef DHCPV6_CLIENT_REL_TIMEOUT
+   #define DHCPV6_CLIENT_REL_TIMEOUT 1000
+#elif (DHCPV6_CLIENT_REL_TIMEOUT < 100)
+   #error DHCPV6_CLIENT_REL_TIMEOUT parameter is not valid
+#endif
+
+//Max Release attempts
+#ifndef DHCPV6_CLIENT_REL_MAX_RC
+   #define DHCPV6_CLIENT_REL_MAX_RC 5
+#elif (DHCPV6_CLIENT_REL_MAX_RC < 1)
+   #error DHCPV6_CLIENT_REL_MAX_RC parameter is not valid
+#endif
+
+//Initial Decline timeout
+#ifndef DHCPV6_CLIENT_DEC_TIMEOUT
+   #define DHCPV6_CLIENT_DEC_TIMEOUT 1000
+#elif (DHCPV6_CLIENT_DEC_TIMEOUT < 100)
+   #error DHCPV6_CLIENT_DEC_TIMEOUT parameter is not valid
+#endif
+
+//Max Decline attempts
+#ifndef DHCPV6_CLIENT_DEC_MAX_RC
+   #define DHCPV6_CLIENT_DEC_MAX_RC 5
+#elif (DHCPV6_CLIENT_DEC_MAX_RC < 1)
+   #error DHCPV6_CLIENT_DEC_MAX_RC parameter is not valid
+#endif
+
+//Initial Reconfigure timeout
+#ifndef DHCPV6_CLIENT_REC_TIMEOUT
+   #define DHCPV6_CLIENT_REC_TIMEOUT 2000
+#elif (DHCPV6_CLIENT_REC_TIMEOUT < 100)
+   #error DHCPV6_CLIENT_REC_TIMEOUT parameter is not valid
+#endif
+
+//Max Reconfigure attempts
+#ifndef DHCPV6_CLIENT_REC_MAX_RC
+   #define DHCPV6_CLIENT_REC_MAX_RC 8
+#elif (DHCPV6_CLIENT_REC_MAX_RC < 1)
+   #error DHCPV6_CLIENT_REC_MAX_RC parameter is not valid
+#endif
+
+//Forward declaration of Dhcpv6ClientContext structure
+struct _Dhcpv6ClientContext;
+#define Dhcpv6ClientContext struct _Dhcpv6ClientContext
+
+
+/**
+ * @brief DHCPv6 client FSM states
+ **/
+
+typedef enum
+{
+   DHCPV6_STATE_INIT         = 0,
+   DHCPV6_STATE_SOLICIT      = 1,
+   DHCPV6_STATE_REQUEST      = 2,
+   DHCPV6_STATE_INIT_CONFIRM = 3,
+   DHCPV6_STATE_CONFIRM      = 4,
+   DHCPV6_STATE_DAD          = 5,
+   DHCPV6_STATE_BOUND        = 6,
+   DHCPV6_STATE_RENEW        = 7,
+   DHCPV6_STATE_REBIND       = 8,
+   DHCPV6_STATE_RELEASE      = 9,
+   DHCPV6_STATE_DECLINE      = 10
+} Dhcpv6State;
+
+
+/**
+ * @brief DHCPv6 configuration timeout callback
+ **/
+
+typedef void (*Dhcpv6TimeoutCallback)(Dhcpv6ClientContext *context,
+   NetInterface *interface);
+
+
+/**
+ * @brief Link state change callback
+ **/
+
+typedef void (*Dhcpv6LinkChangeCallback)(Dhcpv6ClientContext *context,
+   NetInterface *interface, bool_t linkState);
+
+
+/**
+ * @brief FSM state change callback
+ **/
+
+typedef void (*Dhcpv6StateChangeCallback)(Dhcpv6ClientContext *context,
+   NetInterface *interface, Dhcpv6State state);
+
+
+/**
+ * @brief DHCPv6 client settings
+ **/
+
+typedef struct
+{
+   NetInterface *interface;                    ///<Network interface to configure
+   bool_t rapidCommit;                         ///<Quick configuration using rapid commit
+   bool_t manualDnsConfig;                     ///<Force manual DNS configuration
+   systime_t timeout;                          ///<DHCPv6 configuration timeout
+   Dhcpv6TimeoutCallback timeoutEvent;         ///<DHCPv6 configuration timeout event
+   Dhcpv6LinkChangeCallback linkChangeEvent;   ///<Link state change event
+   Dhcpv6StateChangeCallback stateChangeEvent; ///<FSM state change event
+} Dhcpv6ClientSettings;
+
+
+/**
+ * @brief IA address entry
+ **/
+
+typedef struct
+{
+   Ipv6Addr addr;              ///<IPv6 address
+   uint32_t validLifetime;     ///<Valid lifetime
+   uint32_t preferredLifetime; ///<Preferred lifetime
+} Dhcpv6ClientAddrEntry;
+
+
+/**
+ * @brief Client's IA (Identity Association)
+ **/
+
+typedef struct
+{
+   uint32_t t1;                                                  ///<T1 parameter
+   uint32_t t2;                                                  ///<T2 parameter
+   Dhcpv6ClientAddrEntry addrList[DHCPV6_CLIENT_ADDR_LIST_SIZE]; ///<Set of IPv6 addresses
+} Dhcpv6ClientIa;
+
+
+/**
+ * @brief DHCPv6 client context
+ **/
+
+struct _Dhcpv6ClientContext
+{
+   Dhcpv6ClientSettings settings;                   ///<DHCPv6 client settings
+   bool_t running;                                  ///<This flag tells whether the DHCP client is running or not
+   Dhcpv6State state;                               ///<Current state of the FSM
+   bool_t timeoutEventDone;                         ///<Timeout callback function has been called
+   systime_t timestamp;                             ///<Timestamp to manage retransmissions
+   systime_t timeout;                               ///<Timeout value
+   uint_t retransmitCount;                          ///<Retransmission counter
+   uint8_t clientId[DHCPV6_MAX_DUID_SIZE];          ///<Client DUID
+   size_t clientIdLength;                           ///<Length of the client DUID
+   uint8_t clientFqdn[DHCPV6_CLIENT_MAX_FQDN_SIZE]; ///<Client's fully qualified domain name
+   size_t clientFqdnLength;                         ///<Length of the client's FQDN
+   uint8_t serverId[DHCPV6_MAX_DUID_SIZE];          ///<Server DUID
+   size_t serverIdLength;                           ///<Length of the server DUID
+   int_t serverPreference;                          ///<Preference value for the server
+   uint32_t transactionId;                          ///<Value to match requests with replies
+   systime_t configStartTime;                       ///<Address acquisition or renewal process start time
+   systime_t exchangeStartTime;                     ///<Time at which the client sent the first message
+   systime_t leaseStartTime;                        ///<Lease start time
+   Dhcpv6ClientIa ia;                               ///<Identity association
+};
+
+
+//Tick counter to handle periodic operations
+extern systime_t dhcpv6ClientTickCounter;
+
+//DHCPv6 client related functions
+void dhcpv6ClientGetDefaultSettings(Dhcpv6ClientSettings *settings);
+error_t dhcpv6ClientInit(Dhcpv6ClientContext *context, const Dhcpv6ClientSettings *settings);
+error_t dhcpv6ClientStart(Dhcpv6ClientContext *context);
+error_t dhcpv6ClientStop(Dhcpv6ClientContext *context);
+error_t dhcpv6ClientRelease(Dhcpv6ClientContext *context);
+Dhcpv6State dhcpv6ClientGetState(Dhcpv6ClientContext *context);
+
+void dhcpv6ClientTick(Dhcpv6ClientContext *context);
+void dhcpv6ClientLinkChangeEvent(Dhcpv6ClientContext *context);
+
+void dhcpv6ClientStateInit(Dhcpv6ClientContext *context);
+void dhcpv6ClientStateSolicit(Dhcpv6ClientContext *context);
+void dhcpv6ClientStateRequest(Dhcpv6ClientContext *context);
+void dhcpv6ClientStateInitConfirm(Dhcpv6ClientContext *context);
+void dhcpv6ClientStateConfirm(Dhcpv6ClientContext *context);
+void dhcpv6ClientStateDad(Dhcpv6ClientContext *context);
+void dhcpv6ClientStateBound(Dhcpv6ClientContext *context);
+void dhcpv6ClientStateRenew(Dhcpv6ClientContext *context);
+void dhcpv6ClientStateRebind(Dhcpv6ClientContext *context);
+void dhcpv6ClientStateRelease(Dhcpv6ClientContext *context);
+void dhcpv6ClientStateDecline(Dhcpv6ClientContext *context);
+
+error_t dhcpv6ClientSendMessage(Dhcpv6ClientContext *context,
+   Dhcpv6MessageType type);
+
+void dhcpv6ClientProcessMessage(NetInterface *interface,
+   const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader,
+   const NetBuffer *buffer, size_t offset, void *params);
+
+void dhcpv6ClientParseAdvertise(Dhcpv6ClientContext *context,
+   const Dhcpv6Message *message, size_t length);
+
+void dhcpv6ClientParseReply(Dhcpv6ClientContext *context,
+   const Dhcpv6Message *message, size_t length);
+
+error_t dhcpv6ClientParseIaNaOption(Dhcpv6ClientContext *context,
+   const Dhcpv6Option *option);
+
+error_t dhcpv6ClientParseIaAddrOption(Dhcpv6ClientContext *context,
+   const Dhcpv6Option *option);
+
+void dhcpv6ClientAddAddr(Dhcpv6ClientContext *context, const Ipv6Addr *addr,
+   uint32_t validLifetime, uint32_t preferredLifetime);
+
+void dhcpv6ClientRemoveAddr(Dhcpv6ClientContext *context, const Ipv6Addr *addr);
+
+void dhcpv6ClientFlushAddrList(Dhcpv6ClientContext *context);
+
+error_t dhcpv6ClientGenerateDuid(Dhcpv6ClientContext *context);
+error_t dhcpv6ClientGenerateFqdn(Dhcpv6ClientContext *context);
+error_t dhcpv6ClientGenerateLinkLocalAddr(Dhcpv6ClientContext *context);
+
+bool_t dhcpv6ClientCheckServerId(Dhcpv6ClientContext *context,
+   Dhcpv6Option *serverIdOption);
+
+void dhcpv6ClientCheckTimeout(Dhcpv6ClientContext *context);
+
+uint16_t dhcpv6ClientComputeElapsedTime(Dhcpv6ClientContext *context);
+
+void dhcpv6ClientChangeState(Dhcpv6ClientContext *context,
+   Dhcpv6State newState, systime_t delay);
+
+void dhcpv6ClientDumpConfig(Dhcpv6ClientContext *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dhcpv6/dhcpv6_common.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,270 @@
+/**
+ * @file dhcpv6_common.c
+ * @brief Functions common to DHCPv6 client, server and relay agent
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The Dynamic Host Configuration Protocol for IPv6 enables DHCP servers to
+ * pass configuration parameters such as IPv6 network addresses to IPv6
+ * nodes. This protocol is a stateful counterpart to IPv6 Stateless Address
+ * Autoconfiguration (RFC 2462), and can be used separately or concurrently
+ * with the latter to obtain configuration parameters. Refer to RFC 3315
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL DHCPV6_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "dhcpv6/dhcpv6_common.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV6_SUPPORT == ENABLED)
+
+//All DHCPv6 relay agents and servers (FF02::1:2)
+const Ipv6Addr DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS_ADDR =
+   IPV6_ADDR(0xFF02, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002);
+
+//All DHCPv6 servers (FF05::1:3)
+const Ipv6Addr DHCPV6_ALL_SERVERS_ADDR =
+   IPV6_ADDR(0xFF05, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0003);
+
+
+/**
+ * @brief Retrieve status code
+ *
+ * This function returns a status indication related to the DHCPv6
+ * message or option in which it appears
+ *
+ * @param[in] options Pointer to the Options field
+ * @param[in] length Length of the Options field
+ * @return Status code
+ **/
+
+Dhcpv6StatusCode dhcpv6GetStatusCode(const uint8_t *options, size_t length)
+{
+   uint16_t statusCode;
+   Dhcpv6Option *option;
+   Dhcpv6StatusCodeOption *statusCodeOption;
+
+   //Search for the Status Code option
+   option = dhcpv6GetOption(options, length, DHCPV6_OPTION_STATUS_CODE);
+
+   //Check whether the option has been found
+   if(option != NULL && ntohs(option->length) >= sizeof(Dhcpv6StatusCodeOption))
+   {
+      //The option contains a status code and a status message
+      statusCodeOption = (Dhcpv6StatusCodeOption *) option->value;
+
+      //Convert the status code from network byte order
+      statusCode = ntohs(statusCodeOption->statusCode);
+   }
+   else
+   {
+      //If the Status Code option does not appear in a message in which the option
+      //could appear, the status of the message is assumed to be Success
+      statusCode = DHCPV6_STATUS_SUCCESS;
+   }
+
+   //Return status code
+   return (Dhcpv6StatusCode) statusCode;
+}
+
+
+/**
+ * @brief Add an option to a DHCPv6 message
+ * @param[in] message Pointer to the DHCPv6 message
+ * @param[in,out] messageLength Length of the overall DHCPv6 message
+ * @param[in] optionCode Option code
+ * @param[in] optionValue Option value
+ * @param[in] optionLength Length of the option value
+ * @return If the option was successfully added, a pointer to the freshly
+ *   created option is returned. Otherwise NULL pointer is returned
+ **/
+
+Dhcpv6Option *dhcpv6AddOption(void *message, size_t *messageLength,
+   uint16_t optionCode, const void *optionValue, size_t optionLength)
+{
+   Dhcpv6Option *option;
+
+   //Check the length of the DHCPv6 message
+   if(*messageLength < sizeof(Dhcpv6Message))
+      return NULL;
+   //Check the length of the option
+   if(optionLength > UINT16_MAX)
+      return NULL;
+
+   //Make sure there is enough room to add the option
+   if((*messageLength + sizeof(Dhcpv6Option) + optionLength) > DHCPV6_MAX_MSG_SIZE)
+      return NULL;
+
+   //Point to the end of the DHCPv6 message
+   option = (Dhcpv6Option *) ((uint8_t *) message + *messageLength);
+   //Write specified option at current location
+   option->code = htons(optionCode);
+   option->length = htons(optionLength);
+   //Copy option data
+   memcpy(option->value, optionValue, optionLength);
+
+   //Update the length of the DHCPv6 message
+   *messageLength += sizeof(Dhcpv6Option) + optionLength;
+   //Return a pointer to the freshly created option
+   return option;
+}
+
+
+/**
+ * @brief Add a suboption under an existing base option
+ * @param[in] baseOption Pointer to the base option
+ * @param[in,out] messageLength Length of the overall DHCPv6 message
+ * @param[in] optionCode Option code
+ * @param[in] optionValue Option value
+ * @param[in] optionLength Length of the option value
+ * @return If the option was successfully added, a pointer to the freshly
+ *   created option is returned. Otherwise NULL pointer is returned
+ **/
+
+Dhcpv6Option *dhcpv6AddSubOption(Dhcpv6Option *baseOption, size_t *messageLength,
+   uint16_t optionCode, const void *optionValue, size_t optionLength)
+{
+   uint_t n;
+   Dhcpv6Option *option;
+
+   //The pointer to the base option must be valid
+   if(baseOption == NULL)
+      return NULL;
+   //Check the length of the DHCPv6 message
+   if(*messageLength < sizeof(Dhcpv6Message))
+      return NULL;
+   //Check the length of the suboption
+   if(optionLength > UINT16_MAX)
+      return NULL;
+
+   //Make sure there is enough room to add the option
+   if((*messageLength + sizeof(Dhcpv6Option) + optionLength) > DHCPV6_MAX_MSG_SIZE)
+      return NULL;
+
+   //Get the actual length of the base option
+   n = ntohs(baseOption->length);
+
+   //Point to the location that follows the base option
+   option = (Dhcpv6Option *) (baseOption->value + n);
+
+   //Write specified option at current location
+   option->code = htons(optionCode);
+   option->length = htons(optionLength);
+   //Copy option data
+   memcpy(option->value, optionValue, optionLength);
+
+   //Update the length of the base option
+   n += sizeof(Dhcpv6Option) + optionLength;
+   //Convert the 16-bit value to network byte order
+   baseOption->length = htons(n);
+
+   //Update the length of the DHCPv6 message
+   *messageLength += sizeof(Dhcpv6Option) + optionLength;
+   //Return a pointer to the freshly created option
+   return option;
+}
+
+
+/**
+ * @brief Find the specified option in a DHCPv6 message
+ * @param[in] options Pointer to the Options field
+ * @param[in] optionsLength Length of the Options field
+ * @param[in] optionCode Code of the option to find
+ * @return If the specified option was found, a pointer to the corresponding
+ *   option is returned. Otherwise NULL pointer is returned
+ **/
+
+Dhcpv6Option *dhcpv6GetOption(const uint8_t *options,
+   size_t optionsLength, uint16_t optionCode)
+{
+   uint_t i;
+   Dhcpv6Option *option;
+
+   //Parse DHCPv6 options
+   for(i = 0; i < optionsLength; )
+   {
+      //Point to the current option
+      option = (Dhcpv6Option *) (options + i);
+
+      //Make sure the option is valid
+      if((i + sizeof(Dhcpv6Option)) > optionsLength)
+         break;
+      //Check the length of the option data
+      if((i + sizeof(Dhcpv6Option) + ntohs(option->length)) > optionsLength)
+         break;
+
+      //Option code matches the specified one?
+      if(ntohs(option->code) == optionCode)
+         return option;
+
+      //Jump to the next option
+      i += sizeof(Dhcpv6Option) + ntohs(option->length);
+   }
+
+   //The specified option code was not found
+   return NULL;
+}
+
+
+/**
+ * @brief Multiplication by a randomization factor
+ *
+ * Each of the computations of a new RT include a randomization factor
+ * RAND, which is a random number chosen with a uniform distribution
+ * between -0.1 and +0.1. The randomization factor is included to
+ * minimize synchronization of messages transmitted by DHCPv6 clients
+ *
+ * @param[in] value Input value
+ * @return Value resulting from the randomization process
+ **/
+
+int32_t dhcpv6Rand(int32_t value)
+{
+   //Use a randomization factor chosen with a uniform
+   //distribution between -0.1 and +0.1
+   return value * dhcpv6RandRange(-100, 100) / 1000;
+}
+
+
+/**
+ * @brief Get a random value in the specified range
+ * @param[in] min Lower bound
+ * @param[in] max Upper bound
+ * @return Random value in the specified range
+ **/
+
+int32_t dhcpv6RandRange(int32_t min, int32_t max)
+{
+   //Return a random value in the given range
+   return min + netGetRand() % (max - min + 1);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dhcpv6/dhcpv6_common.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,438 @@
+/**
+ * @file dhcpv6_common.h
+ * @brief Functions common to DHCPv6 client, server and relay agent
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DHCPV6_COMMON_H
+#define _DHCPV6_COMMON_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/ethernet.h"
+#include "ipv6/ipv6.h"
+
+//UDP ports used by DHCPv6 clients and servers
+#define DHCPV6_CLIENT_PORT 546
+#define DHCPV6_SERVER_PORT 547
+
+//Maximum DHCPv6 message size
+#define DHCPV6_MAX_MSG_SIZE 1232
+//Maximum DUID size (128 octets not including the type code)
+#define DHCPV6_MAX_DUID_SIZE 130
+
+//Maximum hop count in a relay-forward message
+#define DHCPV6_HOP_COUNT_LIMIT 32
+//Highest server preference value
+#define DHCPV6_MAX_SERVER_PREFERENCE 255
+//Infinite lifetime representation
+#define DHCPV6_INFINITE_TIME 0xFFFFFFFF
+
+
+/**
+ * @brief DUID types
+ **/
+
+typedef enum
+{
+   DHCPV6_DUID_LLT = 1,
+   DHCPV6_DUID_EN  = 2,
+   DHCPV6_DUID_LL  = 3
+} Dhcpv6DuidType;
+
+
+/**
+ * @brief Hardware types
+ **/
+
+typedef enum
+{
+   DHCPV6_HARDWARE_TYPE_ETH   = 1,
+   DHCPV6_HARDWARE_TYPE_EUI64 = 27
+} Dhcpv6HardwareType;
+
+
+/**
+ * @brief DHCPv6 message types
+ **/
+
+typedef enum
+{
+   DHCPV6_MSG_TYPE_SOLICIT      = 1,
+   DHCPV6_MSG_TYPE_ADVERTISE    = 2,
+   DHCPV6_MSG_TYPE_REQUEST      = 3,
+   DHCPV6_MSG_TYPE_CONFIRM      = 4,
+   DHCPV6_MSG_TYPE_RENEW        = 5,
+   DHCPV6_MSG_TYPE_REBIND       = 6,
+   DHCPV6_MSG_TYPE_REPLY        = 7,
+   DHCPV6_MSG_TYPE_RELEASE      = 8,
+   DHCPV6_MSG_TYPE_DECLINE      = 9,
+   DHCPV6_MSG_TYPE_RECONFIGURE  = 10,
+   DHCPV6_MSG_TYPE_INFO_REQUEST = 11,
+   DHCPV6_MSG_TYPE_RELAY_FORW   = 12,
+   DHCPV6_MSG_TYPE_RELAY_REPL   = 13
+} Dhcpv6MessageType;
+
+
+/**
+ * @brief DHCPv6 option codes
+ **/
+
+typedef enum
+{
+   DHCPV6_OPTION_CLIENTID      = 1,
+   DHCPV6_OPTION_SERVERID      = 2,
+   DHCPV6_OPTION_IA_NA         = 3,
+   DHCPV6_OPTION_IA_TA         = 4,
+   DHCPV6_OPTION_IAADDR        = 5,
+   DHCPV6_OPTION_ORO           = 6,
+   DHCPV6_OPTION_PREFERENCE    = 7,
+   DHCPV6_OPTION_ELAPSED_TIME  = 8,
+   DHCPV6_OPTION_RELAY_MSG     = 9,
+   DHCPV6_OPTION_AUTH          = 11,
+   DHCPV6_OPTION_UNICAST       = 12,
+   DHCPV6_OPTION_STATUS_CODE   = 13,
+   DHCPV6_OPTION_RAPID_COMMIT  = 14,
+   DHCPV6_OPTION_USER_CLASS    = 15,
+   DHCPV6_OPTION_VENDOR_CLASS  = 16,
+   DHCPV6_OPTION_VENDOR_OPTS   = 17,
+   DHCPV6_OPTION_INTERFACE_ID  = 18,
+   DHCPV6_OPTION_RECONF_MSG    = 19,
+   DHCPV6_OPTION_RECONF_ACCEPT = 20,
+   DHCPV6_OPTION_DNS_SERVERS   = 23,
+   DHCPV6_OPTION_DOMAIN_LIST   = 24,
+   DHCPV6_OPTION_IA_PD         = 25,
+   DHCPV6_OPTION_IAPREFIX      = 26,
+   DHCPV6_OPTION_FQDN          = 39
+} Dhcpv6OptionCode;
+
+
+/**
+ * @brief Status code
+ **/
+
+typedef enum
+{
+   DHCPV6_STATUS_SUCCESS            = 0,
+   DHCPV6_STATUS_UNSPEC_FAILURE     = 1,
+   DHCPV6_STATUS_NO_ADDRS_AVAILABLE = 2,
+   DHCPV6_STATUS_NO_BINDING         = 3,
+   DHCPV6_STATUS_NOT_ON_LINK        = 4,
+   DHCPV6_STATUS_USE_MULTICAST      = 5
+} Dhcpv6StatusCode;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief DUID-LLT structure
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t type;           //0-1
+   uint16_t hardwareType;   //2-3
+   uint32_t time;           //4-7
+   MacAddr linkLayerAddr;   //8-13
+} __end_packed Dhcpv6DuidLlt;
+
+
+/**
+ * @brief DUID-EN structure
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t type;             //0-1
+   uint32_t enterpriseNumber; //2-5
+   uint8_t identifier[];      //6
+} __end_packed Dhcpv6DuidEn;
+
+
+/**
+ * @brief DUID-LL structure
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t type;         //0-1
+   uint16_t hardwareType; //2-3
+#if (ETH_SUPPORT == ENABLED)
+   MacAddr linkLayerAddr; //4-9
+#else
+   Eui64 linkLayerAddr;   //4-11
+#endif
+} __end_packed Dhcpv6DuidLl;
+
+
+/**
+ * @brief DHCPv6 message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t msgType;          //0
+   uint8_t transactionId[3]; //1-3
+   uint8_t options[];        //4
+} __end_packed Dhcpv6Message;
+
+
+/**
+ * @brief DHCPv6 relay agent message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t msgType;      //0
+   uint8_t hopCount;     //1
+   Ipv6Addr linkAddress; //2-17
+   Ipv6Addr peerAddress; //18-33
+   uint8_t options[];    //34
+} __end_packed Dhcpv6RelayMessage;
+
+
+/**
+ * @brief DHCPv6 option
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t code;   //0-1
+   uint16_t length; //2-3
+   uint8_t value[]; //4
+} __end_packed Dhcpv6Option;
+
+
+/**
+ * @brief Identity Association for Non-temporary Addresses option
+ **/
+
+typedef __start_packed struct
+{
+   uint32_t iaId;     //0-3
+   uint32_t t1;       //4-7
+   uint32_t t2;       //8-11
+   uint8_t options[]; //12
+} __end_packed Dhcpv6IaNaOption;
+
+
+/**
+ * @brief Identity Association for Temporary Addresses option
+ **/
+
+typedef __start_packed struct
+{
+   uint32_t iaId;     //0-3
+   uint8_t options[]; //4
+} Dhcpv6IaTaOption;
+
+
+/**
+ * @brief IA Address option
+ **/
+
+typedef __start_packed struct
+{
+   Ipv6Addr address;           //0-15
+   uint32_t preferredLifetime; //16-19
+   uint32_t validLifetime;     //20-23
+   uint8_t options[];          //24
+} __end_packed Dhcpv6IaAddrOption;
+
+
+/**
+ * @brief Option Request option
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t requestedOption[1]; //0-1
+} __end_packed Dhcpv6OroOption;
+
+
+/**
+ * @brief Preference option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t value; //0
+} __end_packed Dhcpv6PreferenceOption;
+
+
+/**
+ * @brief Elapsed Time option
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t value; //0-1
+} __end_packed Dhcpv6ElapsedTimeOption;
+
+
+/**
+ * @brief Authentication option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t protocol;           //0
+   uint8_t algorithm;          //1
+   uint8_t rdm;                //2
+   uint8_t replayDetection[8]; //3-10
+   uint8_t authInfo[];         //11
+} __end_packed Dhcpv6AuthOption;
+
+
+/**
+ * @brief Server Unicast option
+ **/
+
+typedef __start_packed struct
+{
+   Ipv6Addr serverAddr; //0-15
+} __end_packed Dhcpv6ServerUnicastOption;
+
+
+/**
+ * @brief Status Code option
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t statusCode;    //0-1
+   char_t statusMessage[]; //2
+} __end_packed Dhcpv6StatusCodeOption;
+
+
+/**
+ * @brief Reconfigure Message option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t msgType; //0
+} __end_packed Dhcpv6ReconfMessageOption;
+
+
+/**
+ * @brief DNS Recursive Name Server option
+ **/
+
+typedef __start_packed struct
+{
+   Ipv6Addr address[1]; //0-15
+} __end_packed Dhcpv6DnsServersOption;
+
+
+/**
+ * @brief Domain Search List option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t searchList[1]; //0
+} __end_packed Dhcpv6DomainListOption;
+
+
+/**
+ * @brief Identity Association for Prefix Delegation Option
+ **/
+
+typedef __start_packed struct
+{
+   uint32_t iaId;     //0-3
+   uint32_t t1;       //4-7
+   uint32_t t2;       //8-11
+   uint8_t options[]; //12
+} __end_packed Dhcpv6IaPdOption;
+
+
+/**
+ * @brief IA_PD Prefix option
+ **/
+
+typedef __start_packed struct
+{
+   uint32_t preferredLifetime; //0-3
+   uint32_t validLifetime;     //4-7
+   uint8_t prefixLength;       //8
+   Ipv6Addr prefix;            //9-24
+   uint8_t options[];          //25
+} __end_packed Dhcpv6IaPrefixOption;
+
+
+/**
+ * @brief Fully Qualified Domain Name option
+ **/
+
+typedef __start_packed struct
+{
+#ifdef _CPU_BIG_ENDIAN
+   uint8_t mbz : 5;      //0
+   uint8_t n : 1;
+   uint8_t o : 1;
+   uint8_t s : 1;
+#else
+   uint8_t s : 1;        //0
+   uint8_t o : 1;
+   uint8_t n : 1;
+   uint8_t mbz : 5;
+#endif
+   uint8_t domainName[]; //1
+} __end_packed Dhcpv6FqdnOption;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+//DHCPv6 related constants
+extern const Ipv6Addr DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS_ADDR;
+extern const Ipv6Addr DHCPV6_ALL_SERVERS_ADDR;
+
+//DHCPv6 related functions
+Dhcpv6StatusCode dhcpv6GetStatusCode(const uint8_t *options, size_t length);
+
+Dhcpv6Option *dhcpv6AddOption(void *message, size_t *messageLength,
+   uint16_t optionCode, const void *optionValue, size_t optionLength);
+
+Dhcpv6Option *dhcpv6AddSubOption(Dhcpv6Option *baseOption, size_t *messageLength,
+   uint16_t optionCode, const void *optionValue, size_t optionLength);
+
+Dhcpv6Option *dhcpv6GetOption(const uint8_t *options,
+   size_t optionsLength, uint16_t optionCode);
+
+int32_t dhcpv6Rand(int32_t value);
+int32_t dhcpv6RandRange(int32_t min, int32_t max);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dhcpv6/dhcpv6_debug.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,942 @@
+/**
+ * @file dhcpv6_debug.c
+ * @brief Data logging functions for debugging purpose (DHCPv6)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL DHCPV6_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "dhcpv6/dhcpv6_debug.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV6_SUPPORT == ENABLED && DHCPV6_TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+
+//DHCPv6 message types
+static const char_t *messageLabel[] =
+{
+   "",
+   "SOLICIT",
+   "ADVERTISE",
+   "REQUEST",
+   "CONFIRM",
+   "RENEW",
+   "REBIND",
+   "REPLY",
+   "RELEASE",
+   "DECLINE",
+   "RECONFIGURE",
+   "INFO-REQUEST",
+   "RELAY-FORW",
+   "RELAY-REPL"
+};
+
+//DHCPv6 options
+static const char_t *optionLabel[] =
+{
+   "",
+   "Client Identifier",
+   "Server Identifier",
+   "IA_NA",
+   "IA_TA",
+   "IA Address",
+   "Option Request",
+   "Preference",
+   "Elapsed time",
+   "Relay Message",
+   "",
+   "Authentication",
+   "Server Unicast",
+   "Status Code",
+   "Rapid Commit",
+   "User Class",
+   "Vendor Class",
+   "Vendor Specific Information",
+   "Interface ID",
+   "Reconfigure Message",
+   "Reconfigure Accept",
+   "",
+   "",
+   "DNS Recursive Name Server",
+   "Domain Search List"
+};
+
+//DHCPv6 status codes
+static const char_t *statusLabel[] =
+{
+   "Success",
+   "Unspecified Failure",
+   "No Address Available",
+   "No Binding",
+   "Not On Link",
+   "Use Multicast",
+};
+
+//Prefix used to format the structure
+static const char_t *prefix[8] =
+{
+   "",
+   "  ",
+   "    ",
+   "      ",
+   "        ",
+   "          ",
+   "            ",
+   "              "
+};
+
+
+/**
+ * @brief Dump DHCPv6 message for debugging purpose
+ * @param[in] message Pointer to the DHCPv6 message to dump
+ * @param[in] length Length of the message
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpMessage(const void *message, size_t length)
+{
+   error_t error;
+   uint8_t type;
+   const char_t *label;
+
+   //Empty message?
+   if(!length)
+      return ERROR_INVALID_LENGTH;
+
+   //Retrieve the message type
+   type = *((uint8_t *) message);
+   //Get the corresponding label
+   label = (type < arraysize(messageLabel)) ? messageLabel[type] : "Unknown";
+
+   //Relay agent/server message?
+   if(type == DHCPV6_MSG_TYPE_RELAY_FORW || type == DHCPV6_MSG_TYPE_RELAY_REPL)
+   {
+      //Ensure the length of the DHCPv6 message is acceptable
+      if(length < sizeof(Dhcpv6RelayMessage))
+      {
+         //Report an error
+         error = ERROR_INVALID_LENGTH;
+      }
+      else
+      {
+         //Point to the DHCPv6 message
+         const Dhcpv6RelayMessage *relayMessage = message;
+
+         //Dump message header
+         TRACE_DEBUG("  Message Type = %" PRIu8 " (%s)\r\n", relayMessage->msgType, label);
+         TRACE_DEBUG("  Hop Count = %" PRIu8 "\r\n", relayMessage->hopCount);
+         TRACE_DEBUG("  Link Address = %s\r\n", ipv6AddrToString(&relayMessage->linkAddress, NULL));
+         TRACE_DEBUG("  Peer Address = %s\r\n", ipv6AddrToString(&relayMessage->peerAddress, NULL));
+
+         //Dump message options
+         error = dhcpv6DumpOptions(relayMessage->options, length - sizeof(Dhcpv6RelayMessage), 1);
+      }
+
+   }
+   //Client/server message?
+   else
+   {
+      //Ensure the length of the DHCPv6 message is acceptable
+      if(length < sizeof(Dhcpv6Message))
+      {
+         //Report an error
+         error = ERROR_INVALID_LENGTH;
+      }
+      else
+      {
+         //Point to the DHCPv6 message
+         const Dhcpv6Message *clientMessage = message;
+
+         //Dump message header
+         TRACE_DEBUG("  Message Type = %" PRIu8 " (%s)\r\n", clientMessage->msgType, label);
+         TRACE_DEBUG("  Transaction ID = 0x%06" PRIX32 "\r\n", LOAD24BE(clientMessage->transactionId));
+
+         //Dump message options
+         error = dhcpv6DumpOptions(clientMessage->options, length - sizeof(Dhcpv6Message), 1);
+      }
+   }
+
+   //Did we encounter an error?
+   if(error)
+   {
+      //Debug message
+      TRACE_WARNING("DHCPv6 message is not valid!\r\n");
+      //Dump message contents for debugging purpose
+      TRACE_DEBUG_ARRAY("  ", message, length);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Dump DHCPv6 options for debugging purpose
+ * @param[in] options Pointer to the DHCPv6 options to dump
+ * @param[in] length Length of the options
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpOptions(const uint8_t *options, size_t length, uint_t level)
+{
+   error_t error;
+   size_t i;
+   Dhcpv6Option *option;
+
+   //Check whether the maximum level of recursion is reached
+   if(level >= 6)
+   {
+      //If the maximum level of recursion is reached, then dump contents
+      TRACE_DEBUG("%sOptions (%" PRIuSIZE " bytes)\r\n", prefix[level], length);
+      TRACE_DEBUG_ARRAY(prefix[level + 1], options, length);
+      //Exit immediately
+      return NO_ERROR;
+   }
+
+   //Parse DHCPv6 options
+   for(i = 0; i < length; )
+   {
+      //Point to the current option
+      option = (Dhcpv6Option *) (options + i);
+
+      //Make sure the option is valid
+      if((i + sizeof(Dhcpv6Option)) > length)
+         return ERROR_INVALID_OPTION;
+      //Check the length of the option data
+      if((i + sizeof(Dhcpv6Option) + ntohs(option->length)) > length)
+         return ERROR_INVALID_OPTION;
+
+      //Check option code
+      switch(ntohs(option->code))
+      {
+      //Client Identifier option
+      case DHCPV6_OPTION_CLIENTID:
+         error = dhcpv6DumpClientIdOption(option, level);
+         break;
+      //Server Identifier option
+      case DHCPV6_OPTION_SERVERID:
+         error = dhcpv6DumpServerIdOption(option, level);
+         break;
+      //IA_NA option
+      case DHCPV6_OPTION_IA_NA:
+         error = dhcpv6DumpIaNaOption(option, level);
+         break;
+      //IA_TA option
+      case DHCPV6_OPTION_IA_TA:
+         error = dhcpv6DumpIaTaOption(option, level);
+         break;
+      //IA Address option
+      case DHCPV6_OPTION_IAADDR:
+         error = dhcpv6DumpIaAddrOption(option, level);
+         break;
+      //Option Request option
+      case DHCPV6_OPTION_ORO:
+         error = dhcpv6DumpOroOption(option, level);
+         break;
+       //Preference option
+      case DHCPV6_OPTION_PREFERENCE:
+         error = dhcpv6DumpPreferenceOption(option, level);
+         break;
+      //Elapsed Time option
+      case DHCPV6_OPTION_ELAPSED_TIME:
+         error = dhcpv6DumpElapsedTimeOption(option, level);
+         break;
+      //Relay Message option
+      case DHCPV6_OPTION_RELAY_MSG:
+         error = dhcpv6DumpRelayMessageOption(option, level);
+         break;
+      //Authentication option
+      case DHCPV6_OPTION_AUTH:
+         error = dhcpv6DumpAuthOption(option, level);
+         break;
+      //Server Unicast option
+      case DHCPV6_OPTION_UNICAST:
+         error = dhcpv6DumpServerUnicastOption(option, level);
+         break;
+      //Status Code option
+      case DHCPV6_OPTION_STATUS_CODE:
+         error = dhcpv6DumpStatusCodeOption(option, level);
+         break;
+      //Rapid Commit option
+      case DHCPV6_OPTION_RAPID_COMMIT:
+         error = dhcpv6DumpRapidCommitOption(option, level);
+         break;
+      //User Class option
+      case DHCPV6_OPTION_USER_CLASS:
+         error = dhcpv6DumpUserClassOption(option, level);
+         break;
+      //Vendor Class option
+      case DHCPV6_OPTION_VENDOR_CLASS:
+         error = dhcpv6DumpVendorClassOption(option, level);
+         break;
+      //Vendor Specific Information option
+      case DHCPV6_OPTION_VENDOR_OPTS:
+         error = dhcpv6DumpVendorSpecificInfoOption(option, level);
+         break;
+      //Interface ID option
+      case DHCPV6_OPTION_INTERFACE_ID:
+         error = dhcpv6DumpInterfaceIdOption(option, level);
+         break;
+      //Reconfigure Message option
+      case DHCPV6_OPTION_RECONF_MSG:
+         error = dhcpv6DumpReconfMessageOption(option, level);
+         break;
+      //Reconfigure Accept option
+      case DHCPV6_OPTION_RECONF_ACCEPT:
+         error = dhcpv6DumpReconfAcceptOption(option, level);
+         break;
+      //DNS Recursive Name Server option
+      case DHCPV6_OPTION_DNS_SERVERS:
+         error = dhcpv6DumpDnsServersOption(option, level);
+         break;
+      //Domain Search List option
+      case DHCPV6_OPTION_DOMAIN_LIST:
+         error = dhcpv6DumpDomainListOption(option, level);
+         break;
+      //Unknown option...
+      default:
+         error = dhcpv6DumpGenericOption(option, level);
+         break;
+      }
+
+      //Failed to parse current option?
+      if(error)
+         return error;
+
+      //Jump to the next option
+      i += sizeof(Dhcpv6Option) + ntohs(option->length);
+   }
+
+   //No error to report
+   return NO_ERROR;
+
+}
+
+
+/**
+ * @brief Dump generic DHCPv6 option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpGenericOption(const Dhcpv6Option *option, uint_t level)
+{
+   //Dump contents
+   TRACE_DEBUG("%sOption %" PRIu16 " (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->code), ntohs(option->length));
+   TRACE_DEBUG_ARRAY(prefix[level + 1], option->value, ntohs(option->length));
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump Client Identifier option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpClientIdOption(const Dhcpv6Option *option, uint_t level)
+{
+   //Dump contents
+   TRACE_DEBUG("%sClient Identifier option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+   TRACE_DEBUG_ARRAY(prefix[level + 1], option->value, ntohs(option->length));
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump Server Identifier option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpServerIdOption(const Dhcpv6Option *option, uint_t level)
+{
+   //Dump contents
+   TRACE_DEBUG("%sServer Identifier option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+   TRACE_DEBUG_ARRAY(prefix[level + 1], option->value, ntohs(option->length));
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump IA_NA option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpIaNaOption(const Dhcpv6Option *option, uint_t level)
+{
+   Dhcpv6IaNaOption *iaNaOption;
+
+   //Check the length of the option
+   if(ntohs(option->length) < sizeof(Dhcpv6IaNaOption))
+      return ERROR_INVALID_OPTION;
+
+   //Point to the option contents
+   iaNaOption = (Dhcpv6IaNaOption *) option->value;
+
+   //Dump contents
+   TRACE_DEBUG("%sIA_NA option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+   TRACE_DEBUG("%sIA ID = 0x%08" PRIX32 "\r\n", prefix[level + 1], ntohl(iaNaOption->iaId));
+   TRACE_DEBUG("%sT1 = %" PRIu32 "s\r\n", prefix[level + 1], ntohl(iaNaOption->t1));
+   TRACE_DEBUG("%sT2 = %" PRIu32 "s\r\n", prefix[level + 1], ntohl(iaNaOption->t2));
+
+   //Dump the options associated with this IA_NA
+   dhcpv6DumpOptions(iaNaOption->options, ntohs(option->length) - sizeof(Dhcpv6IaNaOption), level + 1);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump IA_TA option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpIaTaOption(const Dhcpv6Option *option, uint_t level)
+{
+   Dhcpv6IaTaOption *iaTaOption;
+
+   //Check the length of the option
+   if(ntohs(option->length) < sizeof(Dhcpv6IaTaOption))
+      return ERROR_INVALID_OPTION;
+
+   //Point to the option contents
+   iaTaOption = (Dhcpv6IaTaOption *) option->value;
+
+   //Dump contents
+   TRACE_DEBUG("%sIA_TA option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+   TRACE_DEBUG("%sIA ID = 0x%08" PRIX32 "\r\n", prefix[level + 1], ntohl(iaTaOption->iaId));
+
+   //Dump the options associated with this IA_TA
+   dhcpv6DumpOptions(iaTaOption->options, ntohs(option->length) - sizeof(Dhcpv6IaTaOption), level + 1);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump IA Address option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpIaAddrOption(const Dhcpv6Option *option, uint_t level)
+{
+   Dhcpv6IaAddrOption *iaAddrOption;
+
+   //Check the length of the option
+   if(ntohs(option->length) < sizeof(Dhcpv6IaAddrOption))
+      return ERROR_INVALID_OPTION;
+
+   //Point to the option contents
+   iaAddrOption = (Dhcpv6IaAddrOption *) option->value;
+
+   //Dump contents
+   TRACE_DEBUG("%sIA Address option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+   TRACE_DEBUG("%sIPv6 Address = %s\r\n", prefix[level + 1], ipv6AddrToString(&iaAddrOption->address, NULL));
+   TRACE_DEBUG("%sPreferred Lifetime = %" PRIu32 "s\r\n", prefix[level + 1], ntohl(iaAddrOption->preferredLifetime));
+   TRACE_DEBUG("%sValid Lifetime = %" PRIu32 "s\r\n", prefix[level + 1], ntohl(iaAddrOption->validLifetime));
+
+   //Dump the options associated with this IA address
+   dhcpv6DumpOptions(iaAddrOption->options, ntohs(option->length) - sizeof(Dhcpv6IaAddrOption), level + 1);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump Option Request option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpOroOption(const Dhcpv6Option *option, uint_t level)
+{
+   uint_t i;
+   uint_t n;
+   uint16_t code;
+   const char_t *label;
+   Dhcpv6OroOption *oroOption;
+
+   //Check the length of the option
+   if(ntohs(option->length) % 2)
+      return ERROR_INVALID_OPTION;
+
+   //Point to the option contents
+   oroOption = (Dhcpv6OroOption *) option->value;
+   //Get the number of requested options
+   n = ntohs(option->length) / 2;
+
+   //Dump contents
+   TRACE_DEBUG("%sOption Request option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+
+   //Parse the list of requested options
+   for(i = 0; i < n; i++)
+   {
+      //Get current option code
+      code = ntohs(oroOption->requestedOption[i]);
+      //Find the name associated with this option code
+      label = (code < arraysize(optionLabel)) ? optionLabel[code] : "Unknown";
+      //Display option code and option name
+      TRACE_DEBUG("%s%" PRIu16 " (%s option)\r\n", prefix[level + 1], code, label);
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump Preference option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpPreferenceOption(const Dhcpv6Option *option, uint_t level)
+{
+   Dhcpv6PreferenceOption *preferenceOption;
+
+   //Check the length of the option
+   if(ntohs(option->length) != sizeof(Dhcpv6PreferenceOption))
+      return ERROR_INVALID_OPTION;
+
+   //Point to the option contents
+   preferenceOption = (Dhcpv6PreferenceOption *) option->value;
+
+   //Dump contents
+   TRACE_DEBUG("%sPreference option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+   TRACE_DEBUG("%s%" PRIu8 "\r\n", prefix[level + 1], preferenceOption->value);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump Elapsed Time option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpElapsedTimeOption(const Dhcpv6Option *option, uint_t level)
+{
+   uint32_t value;
+   Dhcpv6ElapsedTimeOption *elapsedTimeOption;
+
+   //Check the length of the option
+   if(ntohs(option->length) != sizeof(Dhcpv6ElapsedTimeOption))
+      return ERROR_INVALID_OPTION;
+
+   //Point to the option contents
+   elapsedTimeOption = (Dhcpv6ElapsedTimeOption *) option->value;
+   //Convert the value to milliseconds
+   value = ntohs(elapsedTimeOption->value) * 10;
+
+   //Dump contents
+   TRACE_DEBUG("%sElapsed Time option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+   TRACE_DEBUG("%s%" PRIu32 "ms\r\n", prefix[level + 1], value);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump Relay Message option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpRelayMessageOption(const Dhcpv6Option *option, uint_t level)
+{
+   uint8_t type;
+   const char_t *label;
+
+   //Check the length of the option
+   if(!ntohs(option->length))
+      return ERROR_INVALID_OPTION;
+
+   //Retrieve the message type
+   type = option->value[0];
+   //Get the corresponding label
+   label = (type < arraysize(messageLabel)) ? messageLabel[type] : "Unknown";
+
+   //Relay agent/server message?
+   if(type == DHCPV6_MSG_TYPE_RELAY_FORW || type == DHCPV6_MSG_TYPE_RELAY_REPL)
+   {
+      //Get the inner message
+      const Dhcpv6RelayMessage *message = (Dhcpv6RelayMessage *) option->value;
+
+      //Ensure the length of the DHCPv6 message is acceptable
+      if(ntohs(option->length) < sizeof(Dhcpv6RelayMessage))
+         return ERROR_INVALID_OPTION;
+
+      //Dump message header
+      TRACE_DEBUG("%sRelay Message option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+      TRACE_DEBUG("%sMessage Type = %" PRIu8 " (%s)\r\n", prefix[level + 1], message->msgType, label);
+      TRACE_DEBUG("%sHop Count = %" PRIu8 "\r\n", prefix[level + 1], message->hopCount);
+      TRACE_DEBUG("%sLink Address = %s\r\n", prefix[level + 1], ipv6AddrToString(&message->linkAddress, NULL));
+      TRACE_DEBUG("%sPeer Address = %s\r\n", prefix[level + 1], ipv6AddrToString(&message->peerAddress, NULL));
+
+      //Dump message options
+      return dhcpv6DumpOptions(message->options, ntohs(option->length) - sizeof(Dhcpv6RelayMessage), level + 1);
+   }
+   //Client/server message?
+   else
+   {
+      //Get the inner message
+      const Dhcpv6Message *message = (Dhcpv6Message *) option->value;
+
+      //Ensure the length of the DHCPv6 message is acceptable
+      if(ntohs(option->length) < sizeof(Dhcpv6Message))
+         return ERROR_INVALID_OPTION;
+
+      //Dump message header
+      TRACE_DEBUG("%sRelay Message option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+      TRACE_DEBUG("%sMessage Type = %" PRIu8 " (%s)\r\n", prefix[level + 1], message->msgType, label);
+      TRACE_DEBUG("%sTransaction ID = 0x%06" PRIX32 "\r\n", prefix[level + 1], LOAD24BE(message->transactionId));
+
+      //Dump message options
+      return dhcpv6DumpOptions(message->options, ntohs(option->length) - sizeof(Dhcpv6Message), level + 1);
+   }
+}
+
+
+/**
+ * @brief Dump Authentication option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpAuthOption(const Dhcpv6Option *option, uint_t level)
+{
+   size_t n;
+   Dhcpv6AuthOption *authOption;
+
+   //Check the length of the option
+   if(ntohs(option->length) < sizeof(Dhcpv6AuthOption))
+      return ERROR_INVALID_OPTION;
+
+   //Point to the option contents
+   authOption = (Dhcpv6AuthOption *) option->value;
+   //Get the length of the authentication information
+   n = ntohs(option->length) - sizeof(Dhcpv6AuthOption);
+
+   //Dump contents
+   TRACE_DEBUG("%sAuthentication option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+   TRACE_DEBUG("%sProtocol = %" PRIu8 "\r\n", prefix[level + 1], authOption->protocol);
+   TRACE_DEBUG("%sAlgorithm = %" PRIu8 "\r\n", prefix[level + 1], authOption->algorithm);
+   TRACE_DEBUG("%sRDM = %" PRIu8 "\r\n", prefix[level + 1], authOption->rdm);
+   TRACE_DEBUG("%sReplay Detection\r\n", prefix[level + 1]);
+   TRACE_DEBUG_ARRAY(prefix[level + 2], authOption->replayDetection, 8);
+   TRACE_DEBUG("%sAuthentication Information (%" PRIuSIZE " bytes)\r\n", prefix[level + 1], n);
+   TRACE_DEBUG_ARRAY(prefix[level + 2], authOption->authInfo, n);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump Server Unicast option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpServerUnicastOption(const Dhcpv6Option *option, uint_t level)
+{
+   Dhcpv6ServerUnicastOption *serverUnicastOption;
+
+   //Check the length of the option
+   if(ntohs(option->length) != sizeof(Dhcpv6ServerUnicastOption))
+      return ERROR_INVALID_OPTION;
+
+   //Point to the option contents
+   serverUnicastOption = (Dhcpv6ServerUnicastOption *) option->value;
+
+   //Dump contents
+   TRACE_DEBUG("%sServer Unicast option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+   TRACE_DEBUG("%s%s\r\n", prefix[level + 1], ipv6AddrToString(&serverUnicastOption->serverAddr, NULL));
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump Status Code option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpStatusCodeOption(const Dhcpv6Option *option, uint_t level)
+{
+   uint16_t code;
+   const char_t *label;
+   Dhcpv6StatusCodeOption *statusCodeOption;
+
+   //Check the length of the option
+   if(ntohs(option->length) < sizeof(Dhcpv6StatusCodeOption))
+      return ERROR_INVALID_OPTION;
+
+   //Point to the option contents
+   statusCodeOption = (Dhcpv6StatusCodeOption *) option->value;
+   //Get the status code
+   code = ntohs(statusCodeOption->statusCode);
+   //Get the label associated with the status code
+   label = (code < arraysize(statusLabel)) ? statusLabel[code] : "Unknown";
+
+   //Dump contents
+   TRACE_DEBUG("%sStatus Code option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+   TRACE_DEBUG("%sCode = %" PRIu16 " (%s)\r\n", prefix[level + 1], code, label);
+   TRACE_DEBUG("%sMessage = %s\r\n", prefix[level + 1], statusCodeOption->statusMessage);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump Rapid Commit option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpRapidCommitOption(const Dhcpv6Option *option, uint_t level)
+{
+   //Check the length of the option
+   if(ntohs(option->length) != 0)
+      return ERROR_INVALID_OPTION;
+
+   //Dump contents
+   TRACE_DEBUG("%sRapid Commit option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump User Class option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpUserClassOption(const Dhcpv6Option *option, uint_t level)
+{
+   //Dump contents
+   TRACE_DEBUG("%sUser Class option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+   TRACE_DEBUG_ARRAY(prefix[level + 1], option->value, ntohs(option->length));
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump Vendor Class option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpVendorClassOption(const Dhcpv6Option *option, uint_t level)
+{
+   //Dump contents
+   TRACE_DEBUG("%sVendor Class option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+   TRACE_DEBUG_ARRAY(prefix[level + 1], option->value, ntohs(option->length));
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump Vendor Specific Information option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpVendorSpecificInfoOption(const Dhcpv6Option *option, uint_t level)
+{
+   //Dump contents
+   TRACE_DEBUG("%sVendor Specific Information option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+   TRACE_DEBUG_ARRAY(prefix[level + 1], option->value, ntohs(option->length));
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump Interface ID option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpInterfaceIdOption(const Dhcpv6Option *option, uint_t level)
+{
+   //Dump contents
+   TRACE_DEBUG("%sInterface ID option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+   TRACE_DEBUG_ARRAY(prefix[level + 1], option->value, ntohs(option->length));
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump Reconfigure Message option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpReconfMessageOption(const Dhcpv6Option *option, uint_t level)
+{
+   Dhcpv6ReconfMessageOption *reconfMessageOption;
+
+   //Check the length of the option
+   if(ntohs(option->length) != sizeof(Dhcpv6ReconfMessageOption))
+      return ERROR_INVALID_OPTION;
+
+   //Point to the option contents
+   reconfMessageOption = (Dhcpv6ReconfMessageOption *) option->value;
+
+   //Dump contents
+   TRACE_DEBUG("%sReconfigure Message option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+   TRACE_DEBUG("%sMessage Type = %" PRIu8 "\r\n", prefix[level + 1], reconfMessageOption->msgType);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump Reconfigure Accept option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpReconfAcceptOption(const Dhcpv6Option *option, uint_t level)
+{
+   //Check the length of the option
+   if(ntohs(option->length) != 0)
+      return ERROR_INVALID_OPTION;
+
+   //Dump contents
+   TRACE_DEBUG("%sReconfigure Accept option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump DNS Recursive Name Server option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpDnsServersOption(const Dhcpv6Option *option, uint_t level)
+{
+   uint_t i;
+   uint_t n;
+   Dhcpv6DnsServersOption *dnsServersOption;
+
+   //Check the length of the option
+   if(ntohs(option->length) % sizeof(Ipv6Addr))
+      return ERROR_INVALID_OPTION;
+
+   //Point to the option contents
+   dnsServersOption = (Dhcpv6DnsServersOption *) option->value;
+   //Calculate the number of IPv6 addresses in the list
+   n = ntohs(option->length) / sizeof(Ipv6Addr);
+
+   //Dump contents
+   TRACE_DEBUG("%sDNS Recursive Name Server option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+
+   //Diplay the DNS servers
+   for(i = 0; i < n; i++)
+      TRACE_DEBUG("%s%s\r\n", prefix[level + 1], ipv6AddrToString(dnsServersOption->address + i, NULL));
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump Domain Search List option
+ * @param[in] option Pointer to the option to dump
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t dhcpv6DumpDomainListOption(const Dhcpv6Option *option, uint_t level)
+{
+   //Dump contents
+   TRACE_DEBUG("%sDomain Search List option (%" PRIu16 " bytes)\r\n", prefix[level], ntohs(option->length));
+   TRACE_DEBUG_ARRAY(prefix[level + 1], option->value, ntohs(option->length));
+
+   //No error to report
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dhcpv6/dhcpv6_debug.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,71 @@
+/**
+ * @file dhcpv6_debug.h
+ * @brief Data logging functions for debugging purpose (DHCPv6)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DHCPV6_DEBUG_H
+#define _DHCPV6_DEBUG_H
+
+//Dependencies
+#include "core/net.h"
+#include "dhcpv6/dhcpv6_common.h"
+#include "debug.h"
+
+//Check current trace level
+#if (DHCPV6_TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+
+//Related functions
+error_t dhcpv6DumpMessage(const void *message, size_t length);
+error_t dhcpv6DumpOptions(const uint8_t *options, size_t length, uint_t level);
+error_t dhcpv6DumpGenericOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpClientIdOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpServerIdOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpIaNaOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpIaTaOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpIaAddrOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpOroOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpPreferenceOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpElapsedTimeOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpRelayMessageOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpAuthOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpServerUnicastOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpStatusCodeOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpRapidCommitOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpUserClassOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpVendorClassOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpVendorSpecificInfoOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpInterfaceIdOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpReconfMessageOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpReconfAcceptOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpDnsServersOption(const Dhcpv6Option *option, uint_t level);
+error_t dhcpv6DumpDomainListOption(const Dhcpv6Option *option, uint_t level);
+
+#else
+   #define dhcpv6DumpMessage(message, length)
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dhcpv6/dhcpv6_relay.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,656 @@
+/**
+ * @file dhcpv6_relay.c
+ * @brief DHCPv6 relay agent (Dynamic Host Configuration Protocol for IPv6)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * DHCPv6 Relay-Agents are deployed to forward DHCPv6 messages between clients
+ * and servers when they are not on the same IPv6 link and are often implemented
+ * alongside a routing function in a common node. Refer to RFC 3315
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL DHCPV6_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "dhcpv6_relay.h"
+#include "dhcpv6/dhcpv6_common.h"
+#include "dhcpv6/dhcpv6_debug.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV6_SUPPORT == ENABLED && DHCPV6_RELAY_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Start DHCPv6 relay agent
+ * @param[in] context Pointer to the DHCPv6 relay agent context
+ * @param[in] settings DHCPv6 relay agent specific settings
+ * @return Error code
+ **/
+
+error_t dhcpv6RelayStart(Dhcpv6RelayContext *context, const Dhcpv6RelaySettings *settings)
+{
+   error_t error;
+   uint_t i;
+   OsTask *task;
+
+   //Debug message
+   TRACE_INFO("Starting DHCPv6 relay agent...\r\n");
+
+   //Ensure the parameters are valid
+   if(!context || !settings)
+      return ERROR_INVALID_PARAMETER;
+   //The pointer to the network-facing interface shall be valid
+   if(!settings->serverInterface)
+      return ERROR_INVALID_INTERFACE;
+   //Check the number of client-facing interfaces
+   if(!settings->clientInterfaceCount)
+      return ERROR_INVALID_PARAMETER;
+   if(settings->clientInterfaceCount >= DHCPV6_RELAY_MAX_CLIENT_IF)
+      return ERROR_INVALID_PARAMETER;
+
+   //Loop through the client-facing interfaces
+   for(i = 0; i < settings->clientInterfaceCount; i++)
+   {
+      //A valid pointer is required for each interface
+      if(!settings->clientInterface[i])
+         return ERROR_INVALID_INTERFACE;
+   }
+
+   //Check the address to be used when forwarding messages to the server
+   if(ipv6CompAddr(&settings->serverAddress, &IPV6_UNSPECIFIED_ADDR))
+      return ERROR_INVALID_ADDRESS;
+
+   //Clear the DHCPv6 relay agent context
+   memset(context, 0, sizeof(Dhcpv6RelayContext));
+
+   //Save the network-facing interface
+   context->serverInterface = settings->serverInterface;
+   //Save the number of client-facing interfaces
+   context->clientInterfaceCount = settings->clientInterfaceCount;
+
+   //Save all the client-facing interfaces
+   for(i = 0; i < context->clientInterfaceCount; i++)
+      context->clientInterface[i] = settings->clientInterface[i];
+
+   //Save the address to be used when relaying client messages to the server
+   context->serverAddress = settings->serverAddress;
+
+   //Join the All_DHCP_Relay_Agents_and_Servers multicast group
+   //for each client-facing interface
+   error = dhcpv6RelayJoinMulticastGroup(context);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Start of exception handling block
+   do
+   {
+      //Open a UDP socket to handle the network-facing interface
+      context->serverSocket = socketOpen(SOCKET_TYPE_DGRAM, SOCKET_IP_PROTO_UDP);
+      //Failed to open socket?
+      if(!context->serverSocket)
+      {
+         //Report an error
+         error = ERROR_OPEN_FAILED;
+         //Stop processing
+         break;
+      }
+
+      //Explicitly associate the socket with the relevant interface
+      error = socketBindToInterface(context->serverSocket, context->serverInterface);
+      //Unable to bind the socket to the desired interface?
+      if(error)
+         break;
+
+      //Relay agents listen for DHCPv6 messages on UDP port 547
+      error = socketBind(context->serverSocket, &IP_ADDR_ANY, DHCPV6_SERVER_PORT);
+      //Unable to bind the socket to the desired port?
+      if(error)
+         break;
+
+      //Only accept datagrams with source port number 547
+      error = socketConnect(context->serverSocket, &IP_ADDR_ANY, DHCPV6_SERVER_PORT);
+      //Any error to report?
+      if(error)
+         break;
+
+      //If the relay agent relays messages to the All_DHCP_Servers address
+      //or other multicast addresses, it sets the Hop Limit field to 32
+
+      //Loop through the client-facing interfaces
+      for(i = 0; i < context->clientInterfaceCount; i++)
+      {
+         //Open a UDP socket to handle the current interface
+         context->clientSocket[i] = socketOpen(SOCKET_TYPE_DGRAM, SOCKET_IP_PROTO_UDP);
+         //Failed to open socket?
+         if(!context->clientSocket[i])
+         {
+            //Report an error
+            error = ERROR_OPEN_FAILED;
+            //Stop processing
+            break;
+         }
+
+         //Explicitly associate the socket with the relevant interface
+         error = socketBindToInterface(context->clientSocket[i], context->clientInterface[i]);
+         //Unable to bind the socket to the desired interface?
+         if(error)
+            break;
+
+         //Relay agents listen for DHCPv6 messages on UDP port 547
+         error = socketBind(context->clientSocket[i], &IP_ADDR_ANY, DHCPV6_SERVER_PORT);
+         //Unable to bind the socket to the desired port?
+         if(error)
+            break;
+
+         //Only accept datagrams with source port number 546
+         error = socketConnect(context->clientSocket[i], &IP_ADDR_ANY, DHCPV6_CLIENT_PORT);
+         //Any error to report?
+         if(error)
+            break;
+      }
+
+      //Propagate exception if necessary...
+      if(error)
+         break;
+
+      //Initialize event object
+      if(!osCreateEvent(&context->event))
+      {
+         //Failed to create event
+         error = ERROR_OUT_OF_RESOURCES;
+         //Stop processing
+         break;
+      }
+
+      //Initialize ACK event object
+      if(!osCreateEvent(&context->ackEvent))
+      {
+         //Failed to create event
+         error = ERROR_OUT_OF_RESOURCES;
+         //Stop processing
+         break;
+      }
+
+      //The DHCPv6 relay agent is now running
+      context->running = TRUE;
+
+      //Start the DHCPv6 relay agent service
+      task = osCreateTask("DHCPv6 Relay", dhcpv6RelayTask,
+         context, DHCPV6_RELAY_STACK_SIZE, DHCPV6_RELAY_PRIORITY);
+
+      //Unable to create the task?
+      if(task == OS_INVALID_HANDLE)
+         error = ERROR_OUT_OF_RESOURCES;
+
+      //End of exception handling block
+   } while(0);
+
+   //Did we encounter an error?
+   if(error)
+   {
+      //Close the socket associated with the network-facing interface
+      socketClose(context->serverSocket);
+
+      //Close the socket associated with each client-facing interface
+      for(i = 0; i < context->clientInterfaceCount; i++)
+         socketClose(context->clientSocket[i]);
+
+      //Leave the All_DHCP_Relay_Agents_and_Servers multicast group
+      //for each client-facing interface
+      dhcpv6RelayLeaveMulticastGroup(context);
+
+      //Delete event objects
+      osDeleteEvent(&context->event);
+      osDeleteEvent(&context->ackEvent);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Stop DHCPv6 relay agent
+ * @param[in] context Pointer to the DHCPv6 relay agent context
+ * @return Error code
+ **/
+
+error_t dhcpv6RelayStop(Dhcpv6RelayContext *context)
+{
+   uint_t i;
+
+   //Debug message
+   TRACE_INFO("Stopping DHCPv6 relay agent...\r\n");
+
+   //Ensure the specified pointer is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Check DHCPv6 relay agent state
+   if(!context->running)
+      return ERROR_WRONG_STATE;
+
+   //Reset ACK event before sending the kill signal
+   osResetEvent(&context->ackEvent);
+   //Stop the DHCPv6 relay agent task
+   context->stopRequest = TRUE;
+   //Send a signal to the task in order to abort any blocking operation
+   osSetEvent(&context->event);
+
+   //Wait for the process to terminate...
+   osWaitForEvent(&context->ackEvent, INFINITE_DELAY);
+
+   //Leave the All_DHCP_Relay_Agents_and_Servers multicast group
+   //for each client-facing interface
+   dhcpv6RelayLeaveMulticastGroup(context);
+
+   //Close the socket that carries traffic towards the DHCPv6 server
+   socketClose(context->serverSocket);
+
+   //Properly dispose the sockets that carry traffic towards the DHCPv6 clients
+   for(i = 0; i < context->clientInterfaceCount; i++)
+      socketClose(context->clientSocket[i]);
+
+   //Delete event objects
+   osDeleteEvent(&context->event);
+   osDeleteEvent(&context->ackEvent);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Join All_DHCP_Relay_Agents_and_Servers multicast group
+ * @param[in] context Pointer to the DHCPv6 relay agent context
+ **/
+
+error_t dhcpv6RelayJoinMulticastGroup(Dhcpv6RelayContext *context)
+{
+   uint_t i;
+   uint_t j;
+
+   //Initialize status code
+   error_t error = NO_ERROR;
+
+   //Loop through the client-facing interfaces
+   for(i = 0; i < context->clientInterfaceCount; i++)
+   {
+      //Join the All_DHCP_Relay_Agents_and_Servers multicast
+      //group for each interface
+      error = ipv6JoinMulticastGroup(context->clientInterface[i],
+         &DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS_ADDR);
+      //Unable to join the specified multicast group?
+      if(error)
+         break;
+   }
+
+   //Did we encounter an error?
+   if(error)
+   {
+      //Clean up side effects before returning...
+      for(j = 0; j < i; j++)
+      {
+         //Leave the multicast group for each interface
+         ipv6LeaveMulticastGroup(context->clientInterface[j],
+            &DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS_ADDR);
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Leave All_DHCP_Relay_Agents_and_Servers multicast group
+ * @param[in] context Pointer to the DHCPv6 relay agent context
+ **/
+
+error_t dhcpv6RelayLeaveMulticastGroup(Dhcpv6RelayContext *context)
+{
+   uint_t i;
+
+   //Loop through the client-facing interfaces
+   for(i = 0; i < context->clientInterfaceCount; i++)
+   {
+      //Leave the All_DHCP_Relay_Agents_and_Servers multicast
+      //group for each interface
+      ipv6LeaveMulticastGroup(context->clientInterface[i],
+         &DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS_ADDR);
+   }
+
+   //Successsful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief DHCPv6 relay agent task
+ * @param[in] param Pointer to the DHCPv6 relay agent context
+ **/
+
+void dhcpv6RelayTask(void *param)
+{
+   error_t error;
+   uint_t i;
+
+   //Point to the DHCPv6 relay agent context
+   Dhcpv6RelayContext *context = (Dhcpv6RelayContext *) param;
+
+   //Specify the events the application is interested in for
+   //each client-facing sockets
+   for(i = 0; i < context->clientInterfaceCount; i++)
+   {
+      context->eventDesc[i].socket = context->clientSocket[i];
+      context->eventDesc[i].eventMask = SOCKET_EVENT_RX_READY;
+   }
+
+   //Specify the events the application is interested in for
+   //the network-facing socket
+   context->eventDesc[i].socket = context->serverSocket;
+   context->eventDesc[i].eventMask = SOCKET_EVENT_RX_READY;
+
+   //Main loop
+   while(1)
+   {
+      //Wait for incoming packets on network-facing or client-facing interfaces
+      error = socketPoll(context->eventDesc, context->clientInterfaceCount + 1,
+         &context->event, INFINITE_DELAY);
+
+      //Stop DHCPv6 relay agent?
+      if(context->stopRequest)
+      {
+         //The DHCPv6 relay agent is about to stop
+         context->stopRequest = FALSE;
+         context->running = FALSE;
+         //Acknowledge the reception of the user request
+         osSetEvent(&context->ackEvent);
+         //Kill ourselves
+         osDeleteTask(NULL);
+      }
+
+      //Verify status code
+      if(!error)
+      {
+         //Check the state of each client-facing socket
+         for(i = 0; i < context->clientInterfaceCount; i++)
+         {
+            //Relay client messages if applicable
+            if(context->eventDesc[i].eventFlags & SOCKET_EVENT_RX_READY)
+               dhcpv6ForwardClientMessage(context, i);
+         }
+
+         //Check the state of the network-facing socket
+         if(context->eventDesc[i].eventFlags & SOCKET_EVENT_RX_READY)
+         {
+            //Forward Relay-Reply messages from the network
+            dhcpv6ForwardRelayReplyMessage(context);
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Forward client message
+ * @param[in] context Pointer to the DHCPv6 relay agent context
+ * @param[in] index Index identifying the interface on which the message was received
+ * @return Error code
+ **/
+
+error_t dhcpv6ForwardClientMessage(Dhcpv6RelayContext *context, uint_t index)
+{
+   error_t error;
+   uint32_t interfaceId;
+   size_t inputMessageLen;
+   size_t outputMessageLen;
+   Dhcpv6RelayMessage *inputMessage;
+   Dhcpv6RelayMessage *outputMessage;
+   Dhcpv6Option *option;
+   IpAddr ipAddr;
+
+   //Point to the buffer where to store the incoming DHCPv6 message
+   inputMessage = (Dhcpv6RelayMessage *) (context->buffer + DHCPV6_RELAY_FORW_OVERHEAD);
+   //Message that will be forwarded by the DHCPv6 relay agent
+   outputMessage = (Dhcpv6RelayMessage *) context->buffer;
+
+   //Read incoming message
+   error = socketReceiveFrom(context->clientSocket[index], &ipAddr, NULL, inputMessage,
+      DHCPV6_MAX_MSG_SIZE - DHCPV6_RELAY_FORW_OVERHEAD, &inputMessageLen, 0);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Debug message
+   TRACE_INFO("\r\nDHCPv6 message received on client-facing interface %s (%" PRIuSIZE " bytes)...\r\n",
+      context->clientInterface[index]->name, inputMessageLen);
+
+   //Dump the contents of the message for debugging purpose
+   dhcpv6DumpMessage(inputMessage, inputMessageLen);
+
+   //The source address must be a valid IPv6 address
+   if(ipAddr.length != sizeof(Ipv6Addr))
+      return ERROR_INVALID_ADDRESS;
+   //Check the length of the DHCPv6 message
+   if(inputMessageLen < sizeof(Dhcpv6Message))
+      return ERROR_INVALID_MESSAGE;
+
+   //When the relay agent receives a valid message to be relayed,
+   //it constructs a new Relay-Forward message
+   outputMessage->msgType = DHCPV6_MSG_TYPE_RELAY_FORW;
+
+   //Inspect the message type
+   switch(inputMessage->msgType)
+   {
+   //Message received from a client?
+   case DHCPV6_MSG_TYPE_SOLICIT:
+   case DHCPV6_MSG_TYPE_REQUEST:
+   case DHCPV6_MSG_TYPE_CONFIRM:
+   case DHCPV6_MSG_TYPE_RENEW:
+   case DHCPV6_MSG_TYPE_REBIND:
+   case DHCPV6_MSG_TYPE_RELEASE:
+   case DHCPV6_MSG_TYPE_DECLINE:
+   case DHCPV6_MSG_TYPE_INFO_REQUEST:
+      //If the relay agent received the message to be relayed from a client
+      //the hop-count in the Relay-Forward message is set to 0
+      outputMessage->hopCount = 0;
+      //Continue processing
+      break;
+
+   //Message received from another relay agent?
+   case DHCPV6_MSG_TYPE_RELAY_FORW:
+      //If the message received by the relay agent is a Relay-Forward message
+      //and the hop-count in the message is greater than or equal to 32, the
+      //relay agent discards the received message
+      if(inputMessage->hopCount >= DHCPV6_HOP_COUNT_LIMIT)
+         return ERROR_INVALID_MESSAGE;
+      //Set the hop-count field to the value of the hop-count field in
+      //the received message incremented by 1
+      outputMessage->hopCount = inputMessage->hopCount + 1;
+      //Continue processing
+      break;
+
+   //Message received from a server?
+   default:
+      //Discard ADVERTISE, REPLY, RECONFIGURE and RELAY-REPL messages
+      return ERROR_INVALID_MESSAGE;
+   }
+
+   //Set the link-address field to the unspecified address
+   outputMessage->linkAddress = IPV6_UNSPECIFIED_ADDR;
+   //Copy the source address from the header of the IP datagram in
+   //which the message was received to the peer-address field
+   outputMessage->peerAddress = ipAddr.ipv6Addr;
+   //Size of the Relay-Forward message
+   outputMessageLen = sizeof(Dhcpv6RelayMessage);
+
+   //Get the interface identifier
+   interfaceId = context->clientInterface[index]->id;
+   //Convert the 32-bit integer to network byte order
+   interfaceId = htonl(interfaceId);
+
+   //If the relay agent cannot use the address in the link-address field
+   //to identify the interface through which the response to the client
+   //will be relayed, the relay agent must include an Interface ID option
+   dhcpv6AddOption(outputMessage, &outputMessageLen,
+      DHCPV6_OPTION_INTERFACE_ID, &interfaceId, sizeof(interfaceId));
+
+   //Copy the received DHCPv6 message into a Relay Message option
+   option = dhcpv6AddOption(outputMessage, &outputMessageLen,
+      DHCPV6_OPTION_RELAY_MSG, NULL, 0);
+
+   //Set the appropriate length of the option
+   option->length = htons(inputMessageLen);
+   //Adjust the length of the Relay-Forward message
+   outputMessageLen += inputMessageLen;
+
+   //Debug message
+   TRACE_INFO("Forwarding DHCPv6 message on network-facing interface %s (%" PRIuSIZE " bytes)...\r\n",
+      context->serverInterface->name, outputMessageLen);
+
+   //Dump the contents of the message for debugging purpose
+   dhcpv6DumpMessage(outputMessage, outputMessageLen);
+
+   //Destination address to be used when relaying the client message
+   ipAddr.length = sizeof(Ipv6Addr);
+   ipAddr.ipv6Addr = context->serverAddress;
+
+   //Relay the client message to the server
+   return socketSendTo(context->serverSocket, &ipAddr,
+      DHCPV6_SERVER_PORT, outputMessage, outputMessageLen, NULL, 0);
+}
+
+
+/**
+ * @brief Forward Relay-Reply message
+ * @param[in] context Pointer to the DHCPv6 relay agent context
+ * @return Error code
+ **/
+
+error_t dhcpv6ForwardRelayReplyMessage(Dhcpv6RelayContext *context)
+{
+   error_t error;
+   uint_t i;
+   uint32_t interfaceId;
+   size_t inputMessageLen;
+   size_t outputMessageLen;
+   Dhcpv6RelayMessage *inputMessage;
+   Dhcpv6Message *outputMessage;
+   Dhcpv6Option *option;
+   IpAddr ipAddr;
+   uint16_t port;
+
+   //Point to the buffer where to store the incoming DHCPv6 message
+   inputMessage = (Dhcpv6RelayMessage *) context->buffer;
+
+   //Read incoming message
+   error = socketReceiveFrom(context->serverSocket, &ipAddr, &port,
+      inputMessage, DHCPV6_MAX_MSG_SIZE, &inputMessageLen, 0);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Debug message
+   TRACE_INFO("\r\nDHCPv6 message received on network-facing interface %s (%" PRIuSIZE " bytes)...\r\n",
+      context->serverInterface->name, inputMessageLen);
+
+   //Dump the contents of the message for debugging purpose
+   dhcpv6DumpMessage(inputMessage, inputMessageLen);
+
+   //Check the length of the DHCPv6 message
+   if(inputMessageLen < sizeof(Dhcpv6RelayMessage))
+      return ERROR_INVALID_MESSAGE;
+
+   //Inspect the message type and only forward Relay-Reply messages.
+   //Other DHCPv6 message types must be silently discarded
+   if(inputMessage->msgType != DHCPV6_MSG_TYPE_RELAY_REPL)
+      return ERROR_INVALID_MESSAGE;
+
+   //Get the length of the Options field
+   inputMessageLen -= sizeof(Dhcpv6Message);
+
+   //Check whether an Interface ID option is included in the Relay-Reply
+   option = dhcpv6GetOption(inputMessage->options, inputMessageLen, DHCPV6_OPTION_INTERFACE_ID);
+   //Failed to retrieve specified option?
+   if(option == NULL || ntohs(option->length) != sizeof(interfaceId))
+      return ERROR_INVALID_MESSAGE;
+
+   //Read the Interface ID option contents
+   memcpy(&interfaceId, option->value, sizeof(interfaceId));
+   //Convert the 32-bit integer from network byte order
+   interfaceId = ntohl(interfaceId);
+
+   //The Relay-Reply message must include a Relay Message option
+   option = dhcpv6GetOption(inputMessage->options, inputMessageLen, DHCPV6_OPTION_RELAY_MSG);
+   //Failed to retrieve specified option?
+   if(option == NULL || ntohs(option->length) < sizeof(Dhcpv6Message))
+      return ERROR_INVALID_MESSAGE;
+
+   //Extract the message from the Relay Message option
+   outputMessage = (Dhcpv6Message *) option->value;
+   //Save the length of the message
+   outputMessageLen = ntohs(option->length);
+
+   //Loop through client-facing interfaces
+   for(i = 0; i < context->clientInterfaceCount; i++)
+   {
+      //Check whether the current interface matches the Interface ID option
+      if(context->clientInterface[i]->id == interfaceId)
+      {
+         //Debug message
+         TRACE_INFO("Forwarding DHCPv6 message on client-facing interface %s (%" PRIuSIZE " bytes)...\r\n",
+            context->clientInterface[i]->name, outputMessageLen);
+
+         //Dump the contents of the message for debugging purpose
+         dhcpv6DumpMessage(outputMessage, outputMessageLen);
+
+         //Copy the peer-address into the destination IP address
+         ipAddr.length = sizeof(Ipv6Addr);
+         ipAddr.ipv6Addr = inputMessage->peerAddress;
+
+         //Select the relevant port number to use
+          if(outputMessage->msgType == DHCPV6_MSG_TYPE_RELAY_REPL)
+            port = DHCPV6_SERVER_PORT;
+         else
+            port = DHCPV6_CLIENT_PORT;
+
+         //Relay the DHCPv6 message to the client on the link
+         //identified by the Interface ID option
+         return socketSendTo(context->clientSocket[i], &ipAddr,
+            port, outputMessage, outputMessageLen, NULL, 0);
+      }
+   }
+
+   //Unknown interface identifier...
+   return ERROR_INVALID_OPTION;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dhcpv6/dhcpv6_relay.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,113 @@
+/**
+ * @file dhcpv6_relay.h
+ * @brief DHCPv6 relay agent (Dynamic Host Configuration Protocol for IPv6)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DHCPV6_RELAY_H
+#define _DHCPV6_RELAY_H
+
+//Dependencies
+#include "dhcpv6/dhcpv6_common.h"
+#include "core/socket.h"
+
+//DHCPv6 relay agent support
+#ifndef DHCPV6_RELAY_SUPPORT
+   #define DHCPV6_RELAY_SUPPORT ENABLED
+#elif (DHCPV6_RELAY_SUPPORT != ENABLED && DHCPV6_RELAY_SUPPORT != DISABLED)
+   #error DHCPV6_RELAY_SUPPORT parameter is not valid
+#endif
+
+//Stack size required to run the DHCPv6 relay agent
+#ifndef DHCPV6_RELAY_STACK_SIZE
+   #define DHCPV6_RELAY_STACK_SIZE 500
+#elif (DHCPV6_RELAY_STACK_SIZE < 1)
+   #error DHCPV6_RELAY_STACK_SIZE parameter is not valid
+#endif
+
+//Priority at which the DHCPv6 relay agent should run
+#ifndef DHCPV6_RELAY_PRIORITY
+   #define DHCPV6_RELAY_PRIORITY OS_TASK_PRIORITY_NORMAL
+#endif
+
+//Maximum number of client-facing interfaces
+#ifndef DHCPV6_RELAY_MAX_CLIENT_IF
+   #define DHCPV6_RELAY_MAX_CLIENT_IF 4
+#elif (DHCPV6_RELAY_MAX_CLIENT_IF < 1)
+   #error DHCPV6_RELAY_MAX_CLIENT_IF parameter is not valid
+#endif
+
+//The amount of overhead added by relay forwarding
+#define DHCPV6_RELAY_FORW_OVERHEAD (sizeof(Dhcpv6RelayMessage) + 2 * sizeof(Dhcpv6Option) + sizeof(uint32_t))
+
+
+/**
+ * @brief DHCPv6 relay agent settings
+ **/
+
+typedef struct
+{
+   NetInterface *serverInterface;                             ///<Network-facing interface
+   NetInterface *clientInterface[DHCPV6_RELAY_MAX_CLIENT_IF]; ///<Client-facing interfaces
+   uint_t clientInterfaceCount;                               ///<Number of client-facing interfaces
+   Ipv6Addr serverAddress;                                    ///<Address to be used when relaying messages to the server
+} Dhcpv6RelaySettings;
+
+
+/**
+ * @brief DHCPv6 relay agent context
+ **/
+
+typedef struct
+{
+   NetInterface *serverInterface;                             ///<Network-facing interface
+   NetInterface *clientInterface[DHCPV6_RELAY_MAX_CLIENT_IF]; ///<Client-facing interfaces
+   uint_t clientInterfaceCount;                               ///<Number of client-facing interfaces
+   Ipv6Addr serverAddress;                                    ///<Address to be used when relaying messages to the server
+   Socket *serverSocket;                                      ///<Socket that handles the network-facing interface
+   Socket *clientSocket[DHCPV6_RELAY_MAX_CLIENT_IF];          ///<Sockets that handle client-facing interfaces
+   SocketEventDesc eventDesc[DHCPV6_RELAY_MAX_CLIENT_IF];     ///<The events the application is interested in
+   bool_t running;                                            ///<DHCPv6 relay agent is currently running or not?
+   bool_t stopRequest;                                        ///<Stop request
+   OsEvent ackEvent;                                          ///<Event object use to acknowledge user requests
+   OsEvent event;                                             ///<Event object used to poll the sockets
+   uint8_t buffer[DHCPV6_MAX_MSG_SIZE];                       ///<Scratch buffer to store DHCPv6 messages
+} Dhcpv6RelayContext;
+
+
+//DHCPv6 relay agent specific functions
+error_t dhcpv6RelayStart(Dhcpv6RelayContext *context, const Dhcpv6RelaySettings *settings);
+error_t dhcpv6RelayStop(Dhcpv6RelayContext *context);
+
+error_t dhcpv6RelayJoinMulticastGroup(Dhcpv6RelayContext *context);
+error_t dhcpv6RelayLeaveMulticastGroup(Dhcpv6RelayContext *context);
+
+void dhcpv6RelayTask(void *param);
+
+error_t dhcpv6ForwardClientMessage(Dhcpv6RelayContext *context, uint_t index);
+error_t dhcpv6ForwardRelayReplyMessage(Dhcpv6RelayContext *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dns/dns_cache.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,339 @@
+/**
+ * @file dns_cache.c
+ * @brief DNS cache management
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL DNS_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include "core/net.h"
+#include "dns/dns_cache.h"
+#include "dns/dns_client.h"
+#include "mdns/mdns_client.h"
+#include "netbios/nbns_client.h"
+#include "core/udp.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (DNS_CLIENT_SUPPORT == ENABLED || MDNS_CLIENT_SUPPORT == ENABLED || \
+   NBNS_CLIENT_SUPPORT == ENABLED)
+
+//Tick counter to handle periodic operations
+systime_t dnsTickCounter;
+//DNS cache
+DnsCacheEntry dnsCache[DNS_CACHE_SIZE];
+
+
+/**
+ * @brief DNS cache initialization
+ * @return Error code
+ **/
+
+error_t dnsInit(void)
+{
+   //Initialize DNS cache
+   memset(dnsCache, 0, sizeof(dnsCache));
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Flush DNS cache
+ * @param[in] interface Underlying network interface
+ **/
+
+void dnsFlushCache(NetInterface *interface)
+{
+   uint_t i;
+   DnsCacheEntry *entry;
+
+   //Go through DNS cache
+   for(i = 0; i < DNS_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &dnsCache[i];
+
+      //Check whether the entry is currently in used
+      if(entry->state != DNS_STATE_NONE)
+      {
+         //Delete DNS entries only for the given network interface
+         if(entry->interface == interface)
+            dnsDeleteEntry(entry);
+      }
+   }
+}
+
+
+/**
+ * @brief Create a new entry in the DNS cache
+ * @return Pointer to the newly created entry
+ **/
+
+DnsCacheEntry *dnsCreateEntry(void)
+{
+   uint_t i;
+   DnsCacheEntry *entry;
+   DnsCacheEntry *oldestEntry;
+
+   //Keep track of the oldest entry
+   oldestEntry = &dnsCache[0];
+
+   //Loop through DNS cache entries
+   for(i = 0; i < DNS_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &dnsCache[i];
+
+      //Check whether the entry is currently in used or not
+      if(entry->state == DNS_STATE_NONE)
+      {
+         //Erase contents
+         memset(entry, 0, sizeof(DnsCacheEntry));
+         //Return a pointer to the DNS entry
+         return entry;
+      }
+
+      //Keep track of the oldest entry in the table
+      if(timeCompare(entry->timestamp, oldestEntry->timestamp) < 0)
+         oldestEntry = entry;
+   }
+
+   //The oldest entry is removed whenever the table runs out of space
+   dnsDeleteEntry(oldestEntry);
+   //Erase contents
+   memset(oldestEntry, 0, sizeof(DnsCacheEntry));
+   //Return a pointer to the DNS entry
+   return oldestEntry;
+}
+
+
+/**
+ * @brief Delete the specified DNS cache entry
+ * @param[in] entry Pointer to the DNS cache entry to be deleted
+ **/
+
+void dnsDeleteEntry(DnsCacheEntry *entry)
+{
+   //Make sure the specified entry is valid
+   if(entry != NULL)
+   {
+#if (DNS_CLIENT_SUPPORT == ENABLED)
+      //DNS resolver?
+      if(entry->protocol == HOST_NAME_RESOLVER_DNS)
+      {
+         //Name resolution in progress?
+         if(entry->state == DNS_STATE_IN_PROGRESS)
+         {
+            //Unregister user callback
+            udpDetachRxCallback(entry->interface, entry->port);
+         }
+      }
+#endif
+      //Delete DNS cache entry
+      entry->state = DNS_STATE_NONE;
+   }
+}
+
+
+/**
+ * @brief Search the DNS cache for a given domain name
+ * @param[in] interface Underlying network interface
+ * @param[in] name Domain name
+ * @param[in] type Host type (IPv4 or IPv6)
+ * @param[in] protocol Host name resolution protocol
+ * @return A pointer to the matching DNS entry is returned. NULL is returned
+ *   if the specified domain name could not be found in the DNS cache
+ **/
+
+DnsCacheEntry *dnsFindEntry(NetInterface *interface,
+   const char_t *name, HostType type, HostnameResolver protocol)
+{
+   uint_t i;
+   DnsCacheEntry *entry;
+
+   //Loop through DNS cache entries
+   for(i = 0; i < DNS_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &dnsCache[i];
+
+      //Make sure that the entry is currently in used
+      if(entry->state == DNS_STATE_NONE)
+         continue;
+
+      //Filter out entries that do not match the specified criteria
+      if(entry->interface != interface)
+         continue;
+      if(entry->type != type && type != HOST_TYPE_ANY)
+         continue;
+      if(entry->protocol != protocol && protocol != HOST_NAME_RESOLVER_ANY)
+         continue;
+
+      //Does the entry match the specified domain name?
+      if(name == NULL || !strcasecmp(entry->name, name))
+         return entry;
+   }
+
+   //No matching entry in the DNS cache...
+   return NULL;
+}
+
+
+/**
+ * @brief DNS timer handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * manage DNS cache
+ *
+ **/
+
+void dnsTick(void)
+{
+   error_t error;
+   uint_t i;
+   systime_t time;
+   DnsCacheEntry *entry;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Go through DNS cache
+   for(i = 0; i < DNS_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &dnsCache[i];
+
+      //Name resolution in progress?
+      if(entry->state == DNS_STATE_IN_PROGRESS)
+      {
+         //The request timed out?
+         if(timeCompare(time, entry->timestamp + entry->timeout) >= 0)
+         {
+            //Check whether the maximum number of retransmissions has been exceeded
+            if(entry->retransmitCount > 0)
+            {
+#if (DNS_CLIENT_SUPPORT == ENABLED)
+               //DNS resolver?
+               if(entry->protocol == HOST_NAME_RESOLVER_DNS)
+               {
+                  //Retransmit DNS query
+                  error = dnsSendQuery(entry);
+               }
+               else
+#endif
+#if (MDNS_CLIENT_SUPPORT == ENABLED)
+               //mDNS resolver?
+               if(entry->protocol == HOST_NAME_RESOLVER_MDNS)
+               {
+                  //Retransmit mDNS query
+                  error = mdnsClientSendQuery(entry);
+               }
+               else
+#endif
+#if (NBNS_CLIENT_SUPPORT == ENABLED && IPV4_SUPPORT == ENABLED)
+               //NetBIOS Name Service resolver?
+               if(entry->protocol == HOST_NAME_RESOLVER_NBNS)
+               {
+                  //Retransmit NBNS query
+                  error = nbnsSendQuery(entry);
+               }
+               else
+#endif
+               //Unknown protocol?
+               {
+                  error = ERROR_FAILURE;
+               }
+
+               //Query message successfully sent?
+               if(!error)
+               {
+                  //Save the time at which the query message was sent
+                  entry->timestamp = time;
+                  //The timeout value is doubled for each subsequent retransmission
+                  entry->timeout = MIN(entry->timeout * 2, entry->maxTimeout);
+                  //Decrement retransmission counter
+                  entry->retransmitCount--;
+               }
+               else
+               {
+                  //The entry should be deleted since name resolution has failed
+                  dnsDeleteEntry(entry);
+               }
+            }
+#if (DNS_CLIENT_SUPPORT == ENABLED)
+            //DNS resolver?
+            else if(entry->protocol == HOST_NAME_RESOLVER_DNS)
+            {
+               //Select the next DNS server
+               entry->dnsServerNum++;
+               //Initialize retransmission counter
+               entry->retransmitCount = DNS_CLIENT_MAX_RETRIES;
+               //Send DNS query
+               error = dnsSendQuery(entry);
+
+               //DNS message successfully sent?
+               if(!error)
+               {
+                  //Save the time at which the query message was sent
+                  entry->timestamp = time;
+                  //Set timeout value
+                  entry->timeout = DNS_CLIENT_INIT_TIMEOUT;
+                  //Decrement retransmission counter
+                  entry->retransmitCount--;
+               }
+               else
+               {
+                  //The entry should be deleted since name resolution has failed
+                  dnsDeleteEntry(entry);
+               }
+            }
+#endif
+            else
+            {
+               //The maximum number of retransmissions has been exceeded
+               dnsDeleteEntry(entry);
+            }
+         }
+      }
+      //Name successfully resolved?
+      else if(entry->state == DNS_STATE_RESOLVED)
+      {
+         //Check the lifetime of the current DNS cache entry
+         if(timeCompare(time, entry->timestamp + entry->timeout) >= 0)
+         {
+            //Periodically time out DNS cache entries
+            dnsDeleteEntry(entry);
+         }
+      }
+   }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dns/dns_cache.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,125 @@
+/**
+ * @file dns_cache.h
+ * @brief DNS cache management
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DNS_CACHE_H
+#define _DNS_CACHE_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/socket.h"
+
+//DNS tick interval
+#ifndef DNS_TICK_INTERVAL
+   #define DNS_TICK_INTERVAL 200
+#elif (DNS_TICK_INTERVAL < 10)
+   #error DNS_TICK_INTERVAL parameter is not valid
+#endif
+
+//Size of DNS cache
+#ifndef DNS_CACHE_SIZE
+   #define DNS_CACHE_SIZE 8
+#elif (DNS_CACHE_SIZE < 1)
+   #error DNS_CACHE_SIZE parameter is not valid
+#endif
+
+//Maximum length of domain names
+#ifndef DNS_MAX_NAME_LEN
+   #define DNS_MAX_NAME_LEN 63
+#elif (DNS_MAX_NAME_LEN < 1)
+   #error DNS_MAX_NAME_LEN parameter is not valid
+#endif
+
+//Initial polling interval
+#ifndef DNS_CACHE_INIT_POLLING_INTERVAL
+   #define DNS_CACHE_INIT_POLLING_INTERVAL 10
+#elif (DNS_CACHE_INIT_POLLING_INTERVAL < 1)
+   #error DNS_CACHE_INIT_POLLING_INTERVAL parameter is not valid
+#endif
+
+//Maximum polling interval
+#ifndef DNS_CACHE_MAX_POLLING_INTERVAL
+   #define DNS_CACHE_MAX_POLLING_INTERVAL 1000
+#elif (DNS_CACHE_MAX_POLLING_INTERVAL < 10)
+   #error DNS_CACHE_MAX_POLLING_INTERVAL parameter is not valid
+#endif
+
+
+/**
+ * @brief DNS cache entry states
+ **/
+
+typedef enum
+{
+   DNS_STATE_NONE        = 0,
+   DNS_STATE_IN_PROGRESS = 1,
+   DNS_STATE_RESOLVED    = 2,
+   DNS_STATE_PERMANENT   = 3
+} DnsState;
+
+
+/**
+ * @brief DNS cache entry
+ **/
+
+typedef struct
+{
+   DnsState state;                    ///<Entry state
+   HostType type;                     ///<IPv4 or IPv6 host?
+   HostnameResolver protocol;         ///<Name resolution protocol
+   NetInterface *interface;           ///<Underlying network interface
+   uint_t dnsServerNum;               ///<This parameter selects between the primary and secondary DNS server
+   uint16_t port;                     ///<Port number used by the resolver
+   uint16_t id;                       ///<Identifier used to match queries and responses
+   char_t name[DNS_MAX_NAME_LEN + 1]; ///<Domain name
+   IpAddr ipAddr;                     ///<IP address
+   systime_t timestamp;               ///<Time stamp to manage entry lifetime
+   systime_t timeout;                 ///<Retransmission timeout
+   systime_t maxTimeout;              ///<Maximum retransmission timeout
+   uint_t retransmitCount;            ///<Retransmission counter
+} DnsCacheEntry;
+
+
+//Global variables
+extern systime_t dnsTickCounter;
+extern DnsCacheEntry dnsCache[DNS_CACHE_SIZE];
+
+//DNS related functions
+error_t dnsInit(void);
+
+void dnsFlushCache(NetInterface *interface);
+
+DnsCacheEntry *dnsCreateEntry(void);
+void dnsDeleteEntry(DnsCacheEntry *entry);
+
+DnsCacheEntry *dnsFindEntry(NetInterface *interface,
+   const char_t *name, HostType type, HostnameResolver protocol);
+
+void dnsTick(void);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dns/dns_client.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,550 @@
+/**
+ * @file dns_client.c
+ * @brief DNS client (Domain Name System)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL DNS_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "dns/dns_cache.h"
+#include "dns/dns_client.h"
+#include "dns/dns_common.h"
+#include "dns/dns_debug.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (DNS_CLIENT_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Resolve a host name using DNS
+ * @param[in] interface Underlying network interface
+ * @param[in] name Name of the host to be resolved
+ * @param[in] type Host type (IPv4 or IPv6)
+ * @param[out] ipAddr IP address corresponding to the specified host name
+ **/
+
+error_t dnsResolve(NetInterface *interface,
+   const char_t *name, HostType type, IpAddr *ipAddr)
+{
+   error_t error;
+   DnsCacheEntry *entry;
+
+#if (NET_RTOS_SUPPORT == ENABLED)
+   systime_t delay;
+
+   //Debug message
+   TRACE_INFO("Resolving host name %s (DNS resolver)...\r\n", name);
+#endif
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Search the DNS cache for the specified host name
+   entry = dnsFindEntry(interface, name, type, HOST_NAME_RESOLVER_DNS);
+
+   //Check whether a matching entry has been found
+   if(entry)
+   {
+      //Host name already resolved?
+      if(entry->state == DNS_STATE_RESOLVED ||
+         entry->state == DNS_STATE_PERMANENT)
+      {
+         //Return the corresponding IP address
+         *ipAddr = entry->ipAddr;
+         //Successful host name resolution
+         error = NO_ERROR;
+      }
+      else
+      {
+         //Host name resolution is in progress...
+         error = ERROR_IN_PROGRESS;
+      }
+   }
+   else
+   {
+      //If no entry exists, then create a new one
+      entry = dnsCreateEntry();
+
+      //Record the host name whose IP address is unknown
+      strcpy(entry->name, name);
+
+      //Initialize DNS cache entry
+      entry->type = type;
+      entry->protocol = HOST_NAME_RESOLVER_DNS;
+      entry->interface = interface;
+      //Select primary DNS server
+      entry->dnsServerNum = 0;
+      //Get an ephemeral port number
+      entry->port = udpGetDynamicPort();
+
+      //An identifier is used by the DNS client to match replies
+      //with corresponding requests
+      entry->id = netGetRand();
+
+      //Callback function to be called when a DNS response is received
+      error = udpAttachRxCallback(interface, entry->port, dnsProcessResponse, NULL);
+
+      //Check status code
+      if(!error)
+      {
+         //Initialize retransmission counter
+         entry->retransmitCount = DNS_CLIENT_MAX_RETRIES;
+         //Send DNS query
+         error = dnsSendQuery(entry);
+
+         //DNS message successfully sent?
+         if(!error)
+         {
+            //Save the time at which the query message was sent
+            entry->timestamp = osGetSystemTime();
+            //Set timeout value
+            entry->timeout = DNS_CLIENT_INIT_TIMEOUT;
+            entry->maxTimeout = DNS_CLIENT_MAX_TIMEOUT;
+            //Decrement retransmission counter
+            entry->retransmitCount--;
+
+            //Switch state
+            entry->state = DNS_STATE_IN_PROGRESS;
+            //Host name resolution is in progress
+            error = ERROR_IN_PROGRESS;
+         }
+         else
+         {
+            //Unregister callback function
+            udpDetachRxCallback(interface, entry->port);
+         }
+      }
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+#if (NET_RTOS_SUPPORT == ENABLED)
+   //Set default polling interval
+   delay = DNS_CACHE_INIT_POLLING_INTERVAL;
+
+   //Wait the host name resolution to complete
+   while(error == ERROR_IN_PROGRESS)
+   {
+      //Wait until the next polling period
+      osDelayTask(delay);
+
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+
+      //Search the DNS cache for the specified host name
+      entry = dnsFindEntry(interface, name, type, HOST_NAME_RESOLVER_DNS);
+
+      //Check whether a matching entry has been found
+      if(entry)
+      {
+         //Host name successfully resolved?
+         if(entry->state == DNS_STATE_RESOLVED)
+         {
+            //Return the corresponding IP address
+            *ipAddr = entry->ipAddr;
+            //Successful host name resolution
+            error = NO_ERROR;
+         }
+      }
+      else
+      {
+         //Host name resolution failed
+         error = ERROR_FAILURE;
+      }
+
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+
+      //Backoff support for less aggressive polling
+      delay = MIN(delay * 2, DNS_CACHE_MAX_POLLING_INTERVAL);
+   }
+
+   //Check status code
+   if(error)
+   {
+      //Failed to resolve host name
+      TRACE_INFO("Host name resolution failed!\r\n");
+   }
+   else
+   {
+      //Successful host name resolution
+      TRACE_INFO("Host name resolved to %s...\r\n", ipAddrToString(ipAddr, NULL));
+   }
+#endif
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send a DNS query message
+ * @param[in] entry Pointer to a valid DNS cache entry
+ * @return Error code
+ **/
+
+error_t dnsSendQuery(DnsCacheEntry *entry)
+{
+   error_t error;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   DnsHeader *message;
+   DnsQuestion *dnsQuestion;
+   IpAddr destIpAddr;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //An IPv4 address is expected?
+   if(entry->type == HOST_TYPE_IPV4)
+   {
+      //Point to the IPv4 context
+      Ipv4Context *ipv4Context = &entry->interface->ipv4Context;
+
+      //Out of range index?
+      if(entry->dnsServerNum >= IPV4_DNS_SERVER_LIST_SIZE)
+         return ERROR_NO_DNS_SERVER;
+
+      //Select the relevant DNS server
+      destIpAddr.length = sizeof(Ipv4Addr);
+      destIpAddr.ipv4Addr = ipv4Context->dnsServerList[entry->dnsServerNum];
+
+      //Make sure the IP address is valid
+      if(destIpAddr.ipv4Addr == IPV4_UNSPECIFIED_ADDR)
+         return ERROR_NO_DNS_SERVER;
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //An IPv6 address is expected?
+   if(entry->type == HOST_TYPE_IPV6)
+   {
+      //Point to the IPv6 context
+      Ipv6Context *ipv6Context = &entry->interface->ipv6Context;
+
+      //Out of range index?
+      if(entry->dnsServerNum >= IPV6_DNS_SERVER_LIST_SIZE)
+         return ERROR_NO_DNS_SERVER;
+
+      //Select the relevant DNS server
+      destIpAddr.length = sizeof(Ipv6Addr);
+      destIpAddr.ipv6Addr = ipv6Context->dnsServerList[entry->dnsServerNum];
+
+      //Make sure the IP address is valid
+      if(ipv6CompAddr(&destIpAddr.ipv6Addr, &IPV6_UNSPECIFIED_ADDR))
+         return ERROR_NO_DNS_SERVER;
+   }
+   else
+#endif
+   //Invalid host type?
+   {
+      //Report an error
+      return ERROR_INVALID_PARAMETER;
+   }
+
+   //Allocate a memory buffer to hold the DNS query message
+   buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset);
+   //Failed to allocate buffer?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the DNS header
+   message = netBufferAt(buffer, offset);
+
+   //Format DNS query message
+   message->id = htons(entry->id);
+   message->qr = 0;
+   message->opcode = DNS_OPCODE_QUERY;
+   message->aa = 0;
+   message->tc = 0;
+   message->rd = 1;
+   message->ra = 0;
+   message->z = 0;
+   message->rcode = DNS_RCODE_NO_ERROR;
+
+   //The DNS query contains one question
+   message->qdcount = HTONS(1);
+   message->ancount = 0;
+   message->nscount = 0;
+   message->arcount = 0;
+
+   //Length of the DNS query message
+   length = sizeof(DnsHeader);
+
+   //Encode the host name using the DNS name notation
+   length += dnsEncodeName(entry->name, message->questions);
+
+   //Point to the corresponding question structure
+   dnsQuestion = DNS_GET_QUESTION(message, length);
+
+#if (IPV4_SUPPORT == ENABLED)
+   //An IPv4 address is expected?
+   if(entry->type == HOST_TYPE_IPV4)
+   {
+      //Fill in question structure
+      dnsQuestion->qtype = HTONS(DNS_RR_TYPE_A);
+      dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN);
+   }
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //An IPv6 address is expected?
+   if(entry->type == HOST_TYPE_IPV6)
+   {
+      //Fill in question structure
+      dnsQuestion->qtype = HTONS(DNS_RR_TYPE_AAAA);
+      dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN);
+   }
+#endif
+
+   //Update the length of the DNS query message
+   length += sizeof(DnsQuestion);
+
+   //Adjust the length of the multi-part buffer
+   netBufferSetLength(buffer, offset + length);
+
+   //Debug message
+   TRACE_INFO("Sending DNS message (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message
+   dnsDumpMessage(message, length);
+
+   //Send DNS query message
+   error = udpSendDatagramEx(entry->interface, entry->port,
+      &destIpAddr, DNS_PORT, buffer, offset, 0);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process incoming DNS response message
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader UDP pseudo header
+ * @param[in] udpHeader UDP header
+ * @param[in] buffer Multi-part buffer containing the incoming DNS message
+ * @param[in] offset Offset to the first byte of the DNS message
+ * @param[in] params Callback function parameter (not used)
+ **/
+
+void dnsProcessResponse(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
+   const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, void *params)
+{
+   uint_t i;
+   uint_t j;
+   size_t pos;
+   size_t length;
+   DnsHeader *message;
+   DnsQuestion *question;
+   DnsResourceRecord *record;
+   DnsCacheEntry *entry;
+
+   //Retrieve the length of the DNS message
+   length = netBufferGetLength(buffer) - offset;
+
+   //Ensure the DNS message is valid
+   if(length < sizeof(DnsHeader))
+      return;
+   if(length > DNS_MESSAGE_MAX_SIZE)
+      return;
+
+   //Point to the DNS message header
+   message = netBufferAt(buffer, offset);
+   //Sanity check
+   if(message == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("DNS message received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message
+   dnsDumpMessage(message, length);
+
+   //Check message type
+   if(!message->qr)
+      return;
+   //The DNS message shall contain one question
+   if(ntohs(message->qdcount) != 1)
+      return;
+
+   //Loop through DNS cache entries
+   for(i = 0; i < DNS_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &dnsCache[i];
+
+      //DNS name resolution in progress?
+      if(entry->state == DNS_STATE_IN_PROGRESS &&
+         entry->protocol == HOST_NAME_RESOLVER_DNS)
+      {
+         //Check destination port number
+         if(entry->port == ntohs(udpHeader->destPort))
+         {
+            //Compare identifier against the expected one
+            if(ntohs(message->id) != entry->id)
+               break;
+
+            //Point to the first question
+            pos = sizeof(DnsHeader);
+            //Parse domain name
+            pos = dnsParseName(message, length, pos, NULL, 0);
+
+            //Invalid name?
+            if(!pos)
+               break;
+            //Malformed DNS message?
+            if((pos + sizeof(DnsQuestion)) > length)
+               break;
+
+            //Compare domain name
+            if(dnsCompareName(message, length, sizeof(DnsHeader), entry->name, 0))
+               break;
+
+            //Point to the corresponding entry
+            question = DNS_GET_QUESTION(message, pos);
+
+            //Check the class of the query
+            if(ntohs(question->qclass) != DNS_RR_CLASS_IN)
+               break;
+
+            //Check the type of the query
+            if(entry->type == HOST_TYPE_IPV4 && ntohs(question->qtype) != DNS_RR_TYPE_A)
+               break;
+            if(entry->type == HOST_TYPE_IPV6 && ntohs(question->qtype) != DNS_RR_TYPE_AAAA)
+               break;
+
+            //Check return code
+            if(message->rcode != DNS_RCODE_NO_ERROR)
+            {
+               //The entry should be deleted since name resolution has failed
+               dnsDeleteEntry(entry);
+               //Exit immediately
+               break;
+            }
+
+            //Point to the first answer
+            pos += sizeof(DnsQuestion);
+
+            //Parse answer resource records
+            for(j = 0; j < ntohs(message->ancount); j++)
+            {
+               //Parse domain name
+               pos = dnsParseName(message, length, pos, NULL, 0);
+               //Invalid name?
+               if(!pos)
+                  break;
+
+               //Point to the associated resource record
+               record = DNS_GET_RESOURCE_RECORD(message, pos);
+               //Point to the resource data
+               pos += sizeof(DnsResourceRecord);
+
+               //Make sure the resource record is valid
+               if(pos > length)
+                  break;
+               if((pos + ntohs(record->rdlength)) > length)
+                  break;
+
+#if (IPV4_SUPPORT == ENABLED)
+               //IPv4 address expected?
+               if(entry->type == HOST_TYPE_IPV4)
+               {
+                  //A resource record found?
+                  if(ntohs(record->rtype) == DNS_RR_TYPE_A &&
+                     ntohs(record->rdlength) == sizeof(Ipv4Addr))
+                  {
+                     //Copy the IPv4 address
+                     entry->ipAddr.length = sizeof(Ipv4Addr);
+                     ipv4CopyAddr(&entry->ipAddr.ipv4Addr, record->rdata);
+
+                     //Save current time
+                     entry->timestamp = osGetSystemTime();
+                     //Save TTL value
+                     entry->timeout = ntohl(record->ttl) * 1000;
+
+                     //Limit the lifetime of the DNS cache entries
+                     if(entry->timeout >= DNS_MAX_LIFETIME)
+                        entry->timeout = DNS_MAX_LIFETIME;
+                     if(entry->timeout <= DNS_MIN_LIFETIME)
+                        entry->timeout = DNS_MIN_LIFETIME;
+
+                     //Unregister UDP callback function
+                     udpDetachRxCallback(interface, entry->port);
+                     //Host name successfully resolved
+                     entry->state = DNS_STATE_RESOLVED;
+                     //Exit immediately
+                     break;
+                  }
+               }
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+               //IPv6 address expected?
+               if(entry->type == HOST_TYPE_IPV6)
+               {
+                  //AAAA resource record found?
+                  if(ntohs(record->rtype) == DNS_RR_TYPE_AAAA &&
+                     ntohs(record->rdlength) == sizeof(Ipv6Addr))
+                  {
+                     //Copy the IPv6 address
+                     entry->ipAddr.length = sizeof(Ipv6Addr);
+                     ipv6CopyAddr(&entry->ipAddr.ipv6Addr, record->rdata);
+
+                     //Save current time
+                     entry->timestamp = osGetSystemTime();
+                     //Save TTL value
+                     entry->timeout = ntohl(record->ttl) * 1000;
+
+                     //Limit the lifetime of the DNS cache entries
+                     if(entry->timeout >= DNS_MAX_LIFETIME)
+                        entry->timeout = DNS_MAX_LIFETIME;
+                     if(entry->timeout <= DNS_MIN_LIFETIME)
+                        entry->timeout = DNS_MIN_LIFETIME;
+
+                     //Unregister UDP callback function
+                     udpDetachRxCallback(interface, entry->port);
+                     //Host name successfully resolved
+                     entry->state = DNS_STATE_RESOLVED;
+                     //Exit immediately
+                     break;
+                  }
+               }
+#endif
+               //Point to the next resource record
+               pos += ntohs(record->rdlength);
+            }
+
+            //We are done
+            break;
+         }
+      }
+   }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dns/dns_client.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,90 @@
+/**
+ * @file dns_client.h
+ * @brief DNS client (Domain Name System)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DNS_CLIENT_H
+#define _DNS_CLIENT_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/socket.h"
+#include "core/udp.h"
+#include "dns/dns_cache.h"
+
+//DNS client support
+#ifndef DNS_CLIENT_SUPPORT
+   #define DNS_CLIENT_SUPPORT ENABLED
+#elif (DNS_CLIENT_SUPPORT != ENABLED && DNS_CLIENT_SUPPORT != DISABLED)
+   #error DNS_CLIENT_SUPPORT parameter is not valid
+#endif
+
+//Maximum number of retransmissions of DNS queries
+#ifndef DNS_CLIENT_MAX_RETRIES
+   #define DNS_CLIENT_MAX_RETRIES 3
+#elif (DNS_CLIENT_MAX_RETRIES < 1)
+   #error DNS_CLIENT_MAX_RETRIES parameter is not valid
+#endif
+
+//Initial retransmission timeout
+#ifndef DNS_CLIENT_INIT_TIMEOUT
+   #define DNS_CLIENT_INIT_TIMEOUT 1000
+#elif (DNS_CLIENT_INIT_TIMEOUT < 1000)
+   #error DNS_CLIENT_INIT_TIMEOUT parameter is not valid
+#endif
+
+//Maximum retransmission timeout
+#ifndef DNS_CLIENT_MAX_TIMEOUT
+   #define DNS_CLIENT_MAX_TIMEOUT 5000
+#elif (DNS_CLIENT_MAX_TIMEOUT < 1000)
+   #error DNS_CLIENT_MAX_TIMEOUT parameter is not valid
+#endif
+
+//Minimum cache lifetime for DNS entries
+#ifndef DNS_MIN_LIFETIME
+   #define DNS_MIN_LIFETIME 1000
+#elif (DNS_MIN_LIFETIME < 0)
+   #error DNS_MIN_LIFETIME parameter is not valid
+#endif
+
+//Maximum cache lifetime for DNS entries
+#ifndef DNS_MAX_LIFETIME
+   #define DNS_MAX_LIFETIME 3600000
+#elif (DNS_MAX_LIFETIME < 1000 || DNS_MAX_LIFETIME < DNS_MIN_LIFETIME)
+   #error DNS_MAX_LIFETIME parameter is not valid
+#endif
+
+//DNS related functions
+error_t dnsResolve(NetInterface *interface,
+   const char_t *name, HostType type, IpAddr *ipAddr);
+
+error_t dnsSendQuery(DnsCacheEntry *entry);
+
+void dnsProcessResponse(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
+   const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, void *params);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dns/dns_common.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,460 @@
+/**
+ * @file dns_common.c
+ * @brief Common DNS routines
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL DNS_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "core/net.h"
+#include "dns/dns_client.h"
+#include "dns/dns_common.h"
+#include "mdns/mdns_client.h"
+#include "mdns/mdns_responder.h"
+#include "mdns/mdns_common.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (DNS_CLIENT_SUPPORT == ENABLED || MDNS_CLIENT_SUPPORT == ENABLED || \
+   MDNS_RESPONDER_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Encode a domain name using the DNS name notation
+ * @param[in] src Pointer to the domain name to encode
+ * @param[out] dest Pointer to the encoded domain name (optional parameter)
+ * @return Length of the encoded domain name
+ **/
+
+size_t dnsEncodeName(const char_t *src, uint8_t *dest)
+{
+   uint_t i = 0;
+   size_t length = 0;
+
+   //Parse input name
+   while(1)
+   {
+      //End of string detected?
+      if(src[i] == '\0')
+      {
+         //Check label length
+         if(i < 1 || i > DNS_LABEL_MAX_SIZE)
+            return 0;
+
+         //Save label length
+         if(dest != NULL)
+         {
+            dest[0] = i;
+            dest[i + 1] = 0;
+         }
+
+         //Adjust the length of the resulting string
+         length += i + 2;
+
+         //Stop parsing the input string
+         return length;
+      }
+      //Separator detected?
+      else if(src[i] == '.')
+      {
+         //Check label length
+         if(i < 1 || i > DNS_LABEL_MAX_SIZE)
+            return 0;
+
+         //Save label length
+         if(dest != NULL)
+            dest[0] = i;
+
+         //Adjust the length of the resulting string
+         length += i + 1;
+
+         //Advance write pointer
+         if(dest != NULL)
+            dest += i + 1;
+
+         //Prepare to decode the next label
+         src += i + 1;
+         i = 0;
+      }
+      //Any other character?
+      else
+      {
+         //Copy current character
+         if(dest != NULL)
+            dest[i + 1] = src[i];
+
+         //Point to the next character
+         i++;
+      }
+   }
+}
+
+
+/**
+ * @brief Decode a domain name that uses the DNS name encoding
+ * @param[in] message Pointer to the DNS message
+ * @param[in] length Length of the DNS message
+ * @param[in] pos Offset of the name to decode
+ * @param[out] dest Pointer to the decoded name (optional)
+ * @param[in] level Current level of recursion
+ * @return The position of the resource record that immediately follows the domain name
+ **/
+
+size_t dnsParseName(const DnsHeader *message,
+   size_t length, size_t pos, char_t *dest, uint_t level)
+{
+   size_t n;
+   size_t pointer;
+   uint8_t *src;
+
+   //Recursion limit exceeded?
+   if(level >= DNS_NAME_MAX_RECURSION)
+      return 0;
+
+   //Cast the input DNS message to byte array
+   src = (uint8_t *) message;
+
+   //Parse encoded domain name
+   while(pos < length)
+   {
+      //End marker found?
+      if(src[pos] == 0)
+      {
+         //Properly terminate the string
+         if(dest != NULL)
+            *dest = '\0';
+
+         //Return the position of the resource record that
+         //is immediately following the domain name
+         return (pos + 1);
+      }
+      //Compression tag found?
+      else if(src[pos] >= DNS_COMPRESSION_TAG)
+      {
+         //Malformed DNS message?
+         if((pos + 1) >= length)
+            return 0;
+
+         //Read the most significant byte of the pointer
+         pointer = (src[pos] & ~DNS_COMPRESSION_TAG) << 8;
+         //Read the least significant byte of the pointer
+         pointer |= src[pos + 1];
+
+         //Decode the remaining part of the domain name
+         if(!dnsParseName(message, length, pointer, dest, level + 1))
+         {
+            //Domain name decoding failed
+            return 0;
+         }
+
+         //Return the position of the resource record that
+         //is immediately following the domain name
+         return (pos + 2);
+      }
+      //Valid label length?
+      else if(src[pos] < DNS_LABEL_MAX_SIZE)
+      {
+         //Get the length of the current label
+         n = src[pos++];
+
+         //Malformed DNS message?
+         if((pos + n) > length)
+            return 0;
+
+         //The last parameter is optional
+         if(dest != NULL)
+         {
+            //Copy current label
+            memcpy(dest, src + pos, n);
+
+            //Advance read pointer
+            pos += n;
+            //Advance write pointer
+            dest += n;
+
+            //Append a separator if necessary
+            if(pos < length && src[pos] != '\0')
+               *(dest++) = '.';
+         }
+         else
+         {
+            //Advance read pointer
+            pos += n;
+         }
+      }
+      //Invalid label length?
+      else
+      {
+         //Properly terminate the string
+         if(dest != NULL)
+            *dest = '\0';
+         //Domain name decoding failed
+         return 0;
+      }
+   }
+
+   //Domain name decoding failed
+   return 0;
+}
+
+
+/**
+ * @brief Compare domain names
+ * @param[in] message Pointer to the DNS message
+ * @param[in] length Length of the DNS message
+ * @param[in] pos Offset of the encoded domain name
+ * @param[in] name NULL-terminated string that holds a domain name
+ * @param[in] level Current level of recursion
+ * @return The function returns 0 if the domain names match, -1 if the first
+ *   domain name lexicographically precedes the second name, or 1 if the
+ *   second domain name lexicographically precedes the first name
+ **/
+
+int_t dnsCompareName(const DnsHeader *message, size_t length,
+   size_t pos, const char_t *name, uint_t level)
+{
+   int_t res;
+   size_t n;
+   size_t pointer;
+   uint8_t *p;
+
+   //Recursion limit exceeded?
+   if(level >= DNS_NAME_MAX_RECURSION)
+      return -2;
+
+   //Cast the DNS message to byte array
+   p = (uint8_t *) message;
+
+   //Parse encoded domain name
+   while(pos < length)
+   {
+      //Retrieve the length of the current label
+      n = p[pos];
+
+      //End marker found?
+      if(n == 0)
+      {
+         //The domain name which still has remaining data is deemed
+         //lexicographically later
+         if(*name != '\0')
+            return -1;
+
+         //The domain names match each other
+         return 0;
+      }
+      //Compression tag found?
+      else if(n >= DNS_COMPRESSION_TAG)
+      {
+         //Malformed DNS message?
+         if((pos + 1) >= length)
+            return FALSE;
+
+         //Read the most significant byte of the pointer
+         pointer = (p[pos] & ~DNS_COMPRESSION_TAG) << 8;
+         //Read the least significant byte of the pointer
+         pointer |= p[pos + 1];
+
+         //Compare the remaining part
+         res = dnsCompareName(message, length, pointer, name, level + 1);
+
+         //Return comparison result
+         return res;
+      }
+      else
+      {
+         //Advance data pointer
+         pos++;
+
+         //Malformed DNS message?
+         if((pos + n) > length)
+            return -2;
+
+         //Compare current label
+         res = strncasecmp((char_t *) p + pos, name, n);
+         //Any mismatch?
+         if(res)
+            return res;
+
+         //Advance data pointer
+         pos += n;
+         name += n;
+
+         //The domain name which still has remaining data is deemed
+         //lexicographically later
+         if(*name != '\0' && *name != '.')
+            return -1;
+
+         //Skip the separator character, if any
+         if(*name == '.')
+            name++;
+      }
+   }
+
+   //Malformed DNS message
+   return -2;
+}
+
+
+/**
+ * @brief Compare domain names encoded with DNS notation
+ * @param[in] message1 Pointer to the first DNS message
+ * @param[in] length1 Length of the first DNS message
+ * @param[in] pos1 Offset of the encoded domain name within the first message
+ * @param[in] message2 Pointer to the second DNS message
+ * @param[in] length2 Length of the second DNS message
+ * @param[in] pos2 Offset of the encoded domain name within the second message
+ * @param[in] level Current level of recursion
+ * @return The function returns 0 if the domain names match, -1 if the first
+ *   domain name lexicographically precedes the second name, or 1 if the
+ *   second domain name lexicographically precedes the first name
+ **/
+
+int_t dnsCompareEncodedName(const DnsHeader *message1, size_t length1, size_t pos1,
+   const DnsHeader *message2, size_t length2, size_t pos2, uint_t level)
+{
+   int_t res;
+   size_t n;
+   size_t n1;
+   size_t n2;
+   size_t pointer1;
+   size_t pointer2;
+   uint8_t *p1;
+   uint8_t *p2;
+
+   //Recursion limit exceeded?
+   if(level >= DNS_NAME_MAX_RECURSION)
+      return -2;
+
+   //Cast DNS messages to byte array
+   p1 = (uint8_t *) message1;
+   p2 = (uint8_t *) message2;
+
+   //Compare encoded domain names
+   while(pos1 < length1 && pos2 < length2)
+   {
+      //Retrieve the length of each label
+      n1 = p1[pos1];
+      n2 = p2[pos2];
+
+      //End marker found?
+      if(n1 == 0 || n2 == 0)
+      {
+         //The domain name which still has remaining data is deemed
+         //lexicographically later
+         if(n1 < n2)
+            return -1;
+         else if(n1 > n2)
+            return 1;
+
+         //The domain names match each other
+         return 0;
+      }
+      //Compression tag found?
+      else if(n1 >= DNS_COMPRESSION_TAG || n2 >= DNS_COMPRESSION_TAG)
+      {
+         //First domain name compressed?
+         if(n1 >= DNS_COMPRESSION_TAG)
+         {
+            //Malformed DNS message?
+            if((pos1 + 1) >= length1)
+               return -2;
+
+            //Read the most significant byte of the pointer
+            pointer1 = (p1[pos1] & ~DNS_COMPRESSION_TAG) << 8;
+            //Read the least significant byte of the pointer
+            pointer1 |= p1[pos1 + 1];
+         }
+         else
+         {
+            //The first domain name is not compressed
+            pointer1 = pos1;
+         }
+
+         //Second domain name compressed?
+         if(n2 >= DNS_COMPRESSION_TAG)
+         {
+            //Malformed DNS message?
+            if((pos2 + 1) >= length2)
+               return -2;
+
+            //Read the most significant byte of the pointer
+            pointer2 = (p2[pos2] & ~DNS_COMPRESSION_TAG) << 8;
+            //Read the least significant byte of the pointer
+            pointer2 |= p2[pos2 + 1];
+         }
+         else
+         {
+            //The second domain name is not compressed
+            pointer2 = pos2;
+         }
+
+         //Compare the remaining part
+         res = dnsCompareEncodedName(message1, length1, pointer1,
+            message2, length2, pointer2, level + 1);
+
+         //Return comparison result
+         return res;
+      }
+      else
+      {
+         //Advance data pointer
+         pos1++;
+         pos2++;
+
+         //Malformed DNS message?
+         if((pos1 + n1) > length1 || (pos2 + n2) > length2)
+            return -2;
+
+         //Compare as much data as possible
+         n = MIN(n1, n2);
+
+         //Compare labels
+         res = strncasecmp((char_t *) p1 + pos1, (char_t *) p2 + pos2, n);
+         //Any mismatch?
+         if(res)
+            return res;
+
+         //The domain name which still has remaining data is deemed
+         //lexicographically later
+         if(n1 < n2)
+            return -1;
+         else if(n1 > n2)
+            return 1;
+
+         //Advance data pointer
+         pos1 += n1;
+         pos2 += n2;
+      }
+   }
+
+   //Malformed DNS message
+   return -2;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dns/dns_common.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,240 @@
+/**
+ * @file dns_common.h
+ * @brief Common DNS routines
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DNS_COMMON_H
+#define _DNS_COMMON_H
+
+//Dependencies
+#include "core/net.h"
+
+//Maximum recursion limit when parsing domain names
+#ifndef DNS_NAME_MAX_RECURSION
+   #define DNS_NAME_MAX_RECURSION 4
+#elif (DNS_NAME_MAX_RECURSION < 1 || DNS_NAME_MAX_RECURSION > 8)
+   #error DNS_NAME_MAX_RECURSION parameter is not valid
+#endif
+
+//Maximum size of DNS messages
+#define DNS_MESSAGE_MAX_SIZE 512
+//Maximum size of names
+#define DNS_NAME_MAX_SIZE 255
+//Maximum size of labels
+#define DNS_LABEL_MAX_SIZE 63
+
+//Maximum length of reverse DNS names (IPv4)
+#define DNS_MAX_IPV4_REVERSE_NAME_LEN 15
+//Maximum length of reverse DNS names (IPv6)
+#define DNS_MAX_IPV6_REVERSE_NAME_LEN 63
+
+//DNS port number
+#define DNS_PORT 53
+
+//Label compression tag
+#define DNS_COMPRESSION_TAG 0xC0
+
+//Macro definition
+#define DNS_GET_QUESTION(message, offset) (DnsQuestion *) ((uint8_t *) (message) + (offset))
+#define DNS_GET_RESOURCE_RECORD(message, offset) (DnsResourceRecord *) ((uint8_t *) (message) + (offset))
+
+#define DNS_SET_NSEC_BITMAP(bitmap, type) bitmap[(type) / 8] |= 0x80 >> ((type) % 8)
+#define DNS_CLR_NSEC_BITMAP(bitmap, type) bitmap[(type) / 8] &= ~(0x80 >> ((type) % 8))
+
+
+/**
+ * @brief DNS opcodes
+ **/
+
+typedef enum
+{
+   DNS_OPCODE_QUERY         = 0,
+   DNS_OPCODE_INVERSE_QUERY = 1,
+   DNS_OPCODE_STATUS        = 2,
+   DNS_OPCODE_NOTIFY        = 4,
+   DNS_OPCODE_UPDATE        = 5
+} DnsOpcode;
+
+
+/**
+ * @brief DNS return codes
+ **/
+
+typedef enum
+{
+   DNS_RCODE_NO_ERROR        = 0,
+   DNS_RCODE_FORMAT_ERROR    = 1,
+   DNS_RCODE_SERVER_FAILURE  = 2,
+   DNS_RCODE_NAME_ERROR      = 3,
+   DNS_RCODE_NOT_IMPLEMENTED = 4,
+   DNS_RCODE_QUERY_REFUSED   = 5
+}DnsReturnCode;
+
+
+/**
+ * @brief DNS resource record classes
+ **/
+
+typedef enum
+{
+   DNS_RR_CLASS_IN  = 1,  ///<Internet
+   DNS_RR_CLASS_CH  = 3,  ///<Chaos
+   DNS_RR_CLASS_HS  = 4,  ///<Hesiod
+   DNS_RR_CLASS_ANY = 255 ///<Any class
+} DnsResourceRecordClass;
+
+
+/**
+ * @brief DNS resource record types
+ **/
+
+typedef enum
+{
+   DNS_RR_TYPE_A     = 1,   ///<Host address
+   DNS_RR_TYPE_NS    = 2,   ///<Authoritative name server
+   DNS_RR_TYPE_CNAME = 5,   ///<Canonical name for an alias
+   DNS_RR_TYPE_SOA   = 6,   ///<Start of a zone of authority
+   DNS_RR_TYPE_WKS   = 11,  ///<Well known service description
+   DNS_RR_TYPE_PTR   = 12,  ///<Domain name pointer
+   DNS_RR_TYPE_HINFO = 13,  ///<Host information
+   DNS_RR_TYPE_MINFO = 14,  ///<Mailbox or mail list information
+   DNS_RR_TYPE_MX    = 15,  ///<Mail exchange
+   DNS_RR_TYPE_TXT   = 16,  ///<Text strings
+   DNS_RR_TYPE_AAAA  = 28,  ///<IPv6 address
+   DNS_RR_TYPE_NB    = 32,  ///<NetBIOS name service
+   DNS_RR_TYPE_SRV   = 33,  ///<Server selection
+   DNS_RR_TYPE_NAPTR = 35,  ///<Naming authority pointer
+   DNS_RR_TYPE_NSEC  = 47,  ///<NSEC record
+   DNS_RR_TYPE_EUI48 = 108, ///<EUI-48 address
+   DNS_RR_TYPE_EUI64 = 109, ///<EUI-64 address
+   DNS_RR_TYPE_AXFR  = 252, ///<Transfer of an entire zone
+   DNS_RR_TYPE_ANY   = 255, ///<A request for all records
+   DNS_RR_TYPE_URI   = 256  ///<Uniform resource identifier
+} DnsResourceRecordType;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief DNS message header
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t id;         //0-1
+#ifdef _CPU_BIG_ENDIAN
+   uint16_t qr : 1;     //2
+   uint16_t opcode : 4;
+   uint16_t aa : 1;
+   uint16_t tc : 1;
+   uint16_t rd : 1;
+   uint16_t ra : 1;     //3
+   uint16_t z : 3;
+   uint16_t rcode : 4;
+#else
+   uint16_t rd : 1;     //2
+   uint16_t tc : 1;
+   uint16_t aa : 1;
+   uint16_t opcode : 4;
+   uint16_t qr : 1;
+   uint16_t rcode : 4;  //3
+   uint16_t z : 3;
+   uint16_t ra : 1;
+#endif
+   uint16_t qdcount;    //4-5
+   uint16_t ancount;    //6-7
+   uint16_t nscount;    //8-9
+   uint16_t arcount;    //10-11
+   uint8_t questions[]; //12
+} __end_packed DnsHeader;
+
+
+/**
+ * @brief Question format
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t qtype;
+   uint16_t qclass;
+} __end_packed DnsQuestion;
+
+
+/**
+ * @brief Resource record format
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t rtype;    //0-1
+   uint16_t rclass;   //2-3
+   uint32_t ttl;      //4-7
+   uint16_t rdlength; //8-9
+   uint8_t rdata[];   //10
+} __end_packed DnsResourceRecord;
+
+
+/**
+ * @brief SRV resource record format
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t rtype;    //0-1
+   uint16_t rclass;   //2-3
+   uint32_t ttl;      //4-7
+   uint16_t rdlength; //8-9
+   uint16_t priority; //10-11
+   uint16_t weight;   //12-13
+   uint16_t port;     //14-15
+   uint8_t target[];  //16
+} __end_packed DnsSrvResourceRecord;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+//DNS related functions
+size_t dnsEncodeName(const char_t *src, uint8_t *dest);
+
+size_t dnsParseName(const DnsHeader *message,
+   size_t length, size_t pos, char_t *dest, uint_t level);
+
+int_t dnsCompareName(const DnsHeader *message, size_t length,
+   size_t pos, const char_t *name, uint_t level);
+
+int_t dnsCompareEncodedName(const DnsHeader *message1, size_t length1, size_t pos1,
+   const DnsHeader *message2, size_t length2, size_t pos2, uint_t level);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dns/dns_debug.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,334 @@
+/**
+ * @file dns_debug.c
+ * @brief Data logging functions for debugging purpose (DNS)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL DNS_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "dns/dns_debug.h"
+#include "netbios/nbns_client.h"
+#include "netbios/nbns_responder.h"
+#include "netbios/nbns_common.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (DNS_TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+
+
+/**
+ * @brief Dump DNS message for debugging purpose
+ * @param[in] message Pointer to the DNS message
+ * @param[in] length Length of the DNS message
+ **/
+
+void dnsDumpMessage(const DnsHeader *message, size_t length)
+{
+   uint_t i;
+   size_t pos;
+   char_t *buffer;
+
+   //Make sure the DNS message is valid
+   if(length >= sizeof(DnsHeader))
+   {
+      //Dump DNS message header
+      TRACE_DEBUG("  Identifier (ID) = %" PRIu16 "\r\n", ntohs(message->id));
+      TRACE_DEBUG("  Query Response (QR) = %" PRIu8 "\r\n", message->qr);
+      TRACE_DEBUG("  Opcode (OPCODE) = %" PRIu8 "\r\n", message->opcode);
+      TRACE_DEBUG("  Authoritative Answer (AA) = %" PRIu8 "\r\n", message->aa);
+      TRACE_DEBUG("  TrunCation (TC) = %" PRIu8 "\r\n", message->tc);
+      TRACE_DEBUG("  Recursion Desired (RD) = %" PRIu8 "\r\n", message->rd);
+      TRACE_DEBUG("  Recursion Available (RA) = %" PRIu8 "\r\n", message->ra);
+      TRACE_DEBUG("  Reserved (Z) = %" PRIu8 "\r\n", message->z);
+      TRACE_DEBUG("  Response Code (RCODE) = %" PRIu8 "\r\n", message->rcode);
+      TRACE_DEBUG("  Question Count (QDCOUNT) = %" PRIu8 "\r\n", ntohs(message->qdcount));
+      TRACE_DEBUG("  Answer Count (ANCOUNT) = %" PRIu8 "\r\n", ntohs(message->ancount));
+      TRACE_DEBUG("  Name Server Count (NSCOUNT) = %" PRIu8 "\r\n", ntohs(message->nscount));
+      TRACE_DEBUG("  Additional Record Count (ARCOUNT) = %" PRIu8 "\r\n", ntohs(message->arcount));
+
+      //Allocate a memory buffer to holds domain names
+      buffer = memPoolAlloc(DNS_NAME_MAX_SIZE);
+      //Failed to allocate memory
+      if(buffer == NULL)
+         return;
+
+      //Point to the first question
+      pos = sizeof(DnsHeader);
+
+      //Start of exception handling block
+      do
+      {
+         //Debug message
+         TRACE_DEBUG("  Questions\r\n");
+
+         //Parse questions
+         for(i = 0; i < ntohs(message->qdcount); i++)
+         {
+            //Dump current question
+            pos = dnsDumpQuestion(message, length, pos, buffer);
+            //Any error to report?
+            if(!pos)
+               break;
+         }
+
+         //Parsing error?
+         if(!pos)
+            break;
+
+         //Debug message
+         TRACE_DEBUG("  Answer RRs\r\n");
+
+         //Parse answer resource records
+         for(i = 0; i < ntohs(message->ancount); i++)
+         {
+            //Dump current resource record
+            pos = dnsDumpResourceRecord(message, length, pos, buffer);
+            //Any error to report?
+            if(!pos)
+               break;
+         }
+
+         //Parsing error?
+         if(!pos)
+            break;
+
+         //Debug message
+         TRACE_DEBUG("  Authority RRs\r\n");
+
+         //Parse authority resource records
+         for(i = 0; i < ntohs(message->nscount); i++)
+         {
+            //Dump current resource record
+            pos = dnsDumpResourceRecord(message, length, pos, buffer);
+            //Any error to report?
+            if(!pos)
+               break;
+         }
+
+         //Parsing error?
+         if(!pos)
+            break;
+
+         //Debug message
+         TRACE_DEBUG("  Additional RRs\r\n");
+
+         //Parse additional resource records
+         for(i = 0; i < ntohs(message->arcount); i++)
+         {
+            //Dump current resource record
+            pos = dnsDumpResourceRecord(message, length, pos, buffer);
+            //Any error to report?
+            if(!pos)
+               break;
+         }
+
+         //End of exception handling block
+      } while(0);
+
+      //Free previously allocated memory
+      memPoolFree(buffer);
+   }
+}
+
+
+/**
+ * @brief Dump DNS question for debugging purpose
+ * @param[in] message Pointer to the DNS message
+ * @param[in] length Length of the DNS message
+ * @param[in] pos Offset of the question to decode
+ * @param[in] buffer Memory buffer to holds domain names
+ * @return Offset to the next question
+ **/
+
+size_t dnsDumpQuestion(const DnsHeader *message, size_t length, size_t pos, char_t *buffer)
+{
+   size_t n;
+   DnsQuestion *question;
+
+   //Parse domain name
+   n = dnsParseName(message, length, pos, buffer, 0);
+   //Invalid name?
+   if(!n)
+      return 0;
+
+   //Make sure the DNS question is valid
+   if((n + sizeof(DnsQuestion)) > length)
+      return 0;
+
+   //Point to the corresponding entry
+   question = DNS_GET_QUESTION(message, n);
+
+   //NB question found?
+   if(ntohs(question->qtype) == DNS_RR_TYPE_NB)
+   {
+#if (NBNS_CLIENT_SUPPORT == ENABLED || NBNS_RESPONDER_SUPPORT == ENABLED)
+#if (IPV4_SUPPORT == ENABLED)
+      //Decode NetBIOS name
+      pos = nbnsParseName((NbnsHeader *) message, length, pos, buffer);
+      //Invalid NetBIOS name?
+      if(!pos)
+         return 0;
+#endif
+#endif
+   }
+
+   //Dump DNS question
+   TRACE_DEBUG("    Name (QNAME) = %s\r\n", buffer);
+   TRACE_DEBUG("      Query Type (QTYPE) = %" PRIu16 "\r\n", ntohs(question->qtype));
+   TRACE_DEBUG("      Query Class (QCLASS) = %" PRIu16 "\r\n", ntohs(question->qclass));
+
+   //Point to the next question
+   n += sizeof(DnsQuestion);
+   //Return the current position
+   return n;
+}
+
+
+/**
+ * @brief Dump DNS resource record for debugging purpose
+ * @param[in] message Pointer to the DNS message
+ * @param[in] length Length of the DNS message
+ * @param[in] pos Offset of the question to decode
+ * @param[in] buffer Memory buffer to holds domain names
+ * @return Offset to the next question
+ **/
+
+size_t dnsDumpResourceRecord(const DnsHeader *message, size_t length, size_t pos, char_t *buffer)
+{
+   size_t n;
+   DnsResourceRecord *record;
+   DnsSrvResourceRecord *srvRecord;
+
+   //Parse domain name
+   n = dnsParseName(message, length, pos, buffer, 0);
+   //Invalid name?
+   if(!n)
+      return 0;
+
+   //Point to the corresponding entry
+   record = DNS_GET_RESOURCE_RECORD(message, n);
+
+   //Make sure the resource record is valid
+   if((n + sizeof(DnsResourceRecord)) > length)
+      return 0;
+   if((n + sizeof(DnsResourceRecord) + ntohs(record->rdlength)) > length)
+      return 0;
+
+   //NB resource record found?
+   if(ntohs(record->rtype) == DNS_RR_TYPE_NB)
+   {
+#if (NBNS_CLIENT_SUPPORT == ENABLED || NBNS_RESPONDER_SUPPORT == ENABLED)
+#if (IPV4_SUPPORT == ENABLED)
+      //Decode NetBIOS name
+      pos = nbnsParseName((NbnsHeader *) message, length, pos, buffer);
+      //Invalid NetBIOS name?
+      if(!pos)
+         return 0;
+#endif
+#endif
+   }
+
+   //Dump DNS resource record
+   TRACE_DEBUG("    Name (NAME) = %s\r\n", buffer);
+   TRACE_DEBUG("      Query Type (TYPE) = %" PRIu16 "\r\n", ntohs(record->rtype));
+   TRACE_DEBUG("      Query Class (CLASS) = %" PRIu16 "\r\n", ntohs(record->rclass));
+   TRACE_DEBUG("      Time-To-Live (TTL) = %" PRIu32 "\r\n", ntohl(record->ttl));
+   TRACE_DEBUG("      Data Length (RDLENGTH) = %" PRIu16 "\r\n", ntohs(record->rdlength));
+
+   //Dump resource data
+#if (IPV4_SUPPORT == ENABLED)
+   if(ntohs(record->rtype) == DNS_RR_TYPE_A &&
+      ntohs(record->rdlength) == sizeof(Ipv4Addr))
+   {
+      Ipv4Addr ipAddr;
+
+      //Copy IPv4 address
+      ipv4CopyAddr(&ipAddr, record->rdata);
+      //Dump IPv4 address
+      TRACE_DEBUG("      Data (RDATA) = %s\r\n", ipv4AddrToString(ipAddr, NULL));
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   if(ntohs(record->rtype) == DNS_RR_TYPE_AAAA &&
+      ntohs(record->rdlength) == sizeof(Ipv6Addr))
+   {
+      Ipv6Addr ipAddr;
+
+      //Copy IPv6 address
+      ipv6CopyAddr(&ipAddr, record->rdata);
+      //Dump IPv6 address
+      TRACE_DEBUG("      Data (RDATA) = %s\r\n", ipv6AddrToString(&ipAddr, NULL));
+   }
+   else
+#endif
+   if(ntohs(record->rtype) == DNS_RR_TYPE_PTR)
+   {
+      //Decode domain name
+      pos = dnsParseName(message, length, n + sizeof(DnsResourceRecord), buffer, 0);
+      //Invalid domain name?
+      if(!pos)
+         return 0;
+
+      //Dump name
+      TRACE_DEBUG("      Domain Name (PTRDNAME) = %s\r\n", buffer);
+   }
+   else if(ntohs(record->rtype) == DNS_RR_TYPE_SRV)
+   {
+      //Cast resource record
+      srvRecord = (DnsSrvResourceRecord *) record;
+
+      //Dump SRV resource record
+      TRACE_DEBUG("      Priority = %" PRIu16 "\r\n", ntohs(srvRecord->priority));
+      TRACE_DEBUG("      Weight = %" PRIu16 "\r\n", ntohs(srvRecord->weight));
+      TRACE_DEBUG("      Port = %" PRIu16 "\r\n", ntohs(srvRecord->port));
+
+      //Decode target name
+      pos = dnsParseName(message, length, n + sizeof(DnsSrvResourceRecord), buffer, 0);
+      //Invalid domain name?
+      if(!pos)
+         return 0;
+
+      //Dump name
+      TRACE_DEBUG("      Target = %s\r\n", buffer);
+   }
+   else
+   {
+      //Dump resource data
+      TRACE_DEBUG("      Data (RDATA)\r\n");
+      TRACE_DEBUG_ARRAY("        ", record->rdata, ntohs(record->rdlength));
+   }
+
+   //Point to the next resource record
+   n += sizeof(DnsResourceRecord) + ntohs(record->rdlength);
+   //Return the current position
+   return n;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dns/dns_debug.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,49 @@
+/**
+ * @file dns_debug.h
+ * @brief Data logging functions for debugging purpose (DNS)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DNS_DEBUG_H
+#define _DNS_DEBUG_H
+
+//Dependencies
+#include "core/net.h"
+#include "dns/dns_common.h"
+#include "debug.h"
+
+//Check current trace level
+#if (DNS_TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+
+void dnsDumpMessage(const DnsHeader *message, size_t length);
+size_t dnsDumpQuestion(const DnsHeader *message, size_t length, size_t pos, char_t *buffer);
+size_t dnsDumpResourceRecord(const DnsHeader *message, size_t length, size_t pos, char_t *buffer);
+
+#else
+   #define dnsDumpMessage(message, length)
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dns_sd/dns_sd.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1998 @@
+/**
+ * @file dns_sd.c
+ * @brief DNS-SD (DNS-Based Service Discovery)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * DNS-SD allows clients to discover a list of named instances of that
+ * desired service, using standard DNS queries. Refer to the following
+ * RFCs for complete details:
+ * - RFC 6763: DNS-Based Service Discovery
+ * - RFC 2782: A DNS RR for specifying the location of services (DNS SRV)
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL DNS_SD_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include <ctype.h>
+#include "core/net.h"
+#include "mdns/mdns_common.h"
+#include "mdns/mdns_responder.h"
+#include "dns/dns_debug.h"
+#include "dns_sd/dns_sd.h"
+#include "str.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (DNS_SD_SUPPORT == ENABLED)
+
+//Tick counter to handle periodic operations
+systime_t dnsSdTickCounter;
+
+
+/**
+ * @brief Initialize settings with default values
+ * @param[out] settings Structure that contains DNS-SD settings
+ **/
+
+void dnsSdGetDefaultSettings(DnsSdSettings *settings)
+{
+   //Use default interface
+   settings->interface = netGetDefaultInterface();
+
+   //Number of announcement packets
+   settings->numAnnouncements = MDNS_ANNOUNCE_NUM;
+   //TTL resource record
+   settings->ttl = DNS_SD_DEFAULT_RR_TTL;
+   //FSM state change event
+   settings->stateChangeEvent = NULL;
+}
+
+
+/**
+ * @brief DNS-DS initialization
+ * @param[in] context Pointer to the DNS-SD context
+ * @param[in] settings DNS-SD specific settings
+ * @return Error code
+ **/
+
+error_t dnsSdInit(DnsSdContext *context, const DnsSdSettings *settings)
+{
+   NetInterface *interface;
+
+   //Debug message
+   TRACE_INFO("Initializing DNS-SD...\r\n");
+
+   //Ensure the parameters are valid
+   if(context == NULL || settings == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Invalid network interface?
+   if(settings->interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Point to the underlying network interface
+   interface = settings->interface;
+
+   //Clear the DNS-SD context
+   memset(context, 0, sizeof(DnsSdContext));
+   //Save user settings
+   context->settings = *settings;
+
+   //DNS-SD is currently suspended
+   context->running = FALSE;
+   //Initialize state machine
+   context->state = MDNS_STATE_INIT;
+
+   //Attach the DNS-SD context to the network interface
+   interface->dnsSdContext = context;
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Start mDNS responder
+ * @param[in] context Pointer to the DNS-SD context
+ * @return Error code
+ **/
+
+error_t dnsSdStart(DnsSdContext *context)
+{
+   //Check parameter
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Starting DNS-SD...\r\n");
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Start DNS-SD
+   context->running = TRUE;
+   //Initialize state machine
+   context->state = MDNS_STATE_INIT;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Stop mDNS responder
+ * @param[in] context Pointer to the DNS-SD context
+ * @return Error code
+ **/
+
+error_t dnsSdStop(DnsSdContext *context)
+{
+   //Check parameter
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Stopping DNS-SD...\r\n");
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Suspend DNS-SD
+   context->running = FALSE;
+   //Reinitialize state machine
+   context->state = MDNS_STATE_INIT;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Retrieve current state
+ * @param[in] context Pointer to the DNS-SD context
+ * @return Current DNS-SD state
+ **/
+
+MdnsState dnsSdGetState(DnsSdContext *context)
+{
+   MdnsState state;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Get current state
+   state = context->state;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return current state
+   return state;
+}
+
+
+/**
+ * @brief Set service instance name
+ * @param[in] context Pointer to the DNS-SD context
+ * @param[in] instanceName NULL-terminated string that contains the service
+ *   instance name
+ * @return Error code
+ **/
+
+error_t dnsSdSetInstanceName(DnsSdContext *context, const char_t *instanceName)
+{
+   NetInterface *interface;
+
+   //Check parameters
+   if(context == NULL || instanceName == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Any registered services?
+   if(dnsSdGetNumServices(context) > 0)
+   {
+      //Check whether the link is up
+      if(interface->linkState)
+      {
+         //Send a goodbye packet
+         dnsSdSendGoodbye(context, NULL);
+      }
+   }
+
+   //Set instance name
+   strSafeCopy(context->instanceName, instanceName,
+      DNS_SD_MAX_INSTANCE_NAME_LEN);
+
+   //Restart probing process
+   dnsSdStartProbing(context);
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Register a DNS-SD service
+ * @param[in] context Pointer to the DNS-SD context
+ * @param[in] serviceName NULL-terminated string that contains the name of the
+ *   service to be registered
+ * @param[in] priority Priority field
+ * @param[in] weight Weight field
+ * @param[in] port Port number
+ * @param[in] metadata NULL-terminated string that contains the discovery-time
+ *   metadata (TXT record)
+ * @return Error code
+ **/
+
+error_t dnsSdRegisterService(DnsSdContext *context, const char_t *serviceName,
+   uint16_t priority, uint16_t weight, uint16_t port, const char_t *metadata)
+{
+   error_t error;
+   size_t i;
+   size_t j;
+   size_t k;
+   size_t n;
+   DnsSdService *entry;
+   DnsSdService *firstFreeEntry;
+
+   //Check parameters
+   if(context == NULL || serviceName == NULL || metadata == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Keep track of the first free entry
+   firstFreeEntry = NULL;
+
+   //Loop through the list of registered services
+   for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &context->serviceList[i];
+
+      //Check if the entry is currently in use
+      if(entry->name[0] != '\0')
+      {
+         //Check whether the specified service is already registered
+         if(!strcasecmp(entry->name, serviceName))
+            break;
+      }
+      else
+      {
+         //Keep track of the first free entry
+         if(firstFreeEntry == NULL)
+            firstFreeEntry = entry;
+      }
+   }
+
+   //If the specified service is not yet registered, then a new
+   //entry should be created
+   if(i >= DNS_SD_SERVICE_LIST_SIZE)
+      entry = firstFreeEntry;
+
+   //Check whether the service list runs out of space
+   if(entry != NULL)
+   {
+      //Service name
+      strSafeCopy(entry->name, serviceName, DNS_SD_MAX_SERVICE_NAME_LEN);
+
+      //Priority field
+      entry->priority = priority;
+      //Weight field
+      entry->weight = weight;
+      //Port number
+      entry->port = port;
+
+      //Clear TXT record
+      entry->metadataLength = 0;
+
+      //Point to the beginning of the information string
+      i = 0;
+      j = 0;
+
+      //Point to the beginning of the resulting TXT record data
+      k = 0;
+
+      //Format TXT record
+      while(1)
+      {
+         //End of text data?
+         if(metadata[i] == '\0' || metadata[i] == ';')
+         {
+            //Calculate the length of the text data
+            n = MIN(i - j, UINT8_MAX);
+
+            //Check the length of the resulting TXT record
+            if((entry->metadataLength + n + 1) > DNS_SD_MAX_METADATA_LEN)
+               break;
+
+            //Write length field
+            entry->metadata[k] = n;
+            //Write text data
+            memcpy(entry->metadata + k + 1, metadata + j, n);
+
+            //Jump to the next text data
+            j = i + 1;
+            //Advance write index
+            k += n + 1;
+
+            //Update the length of the TXT record
+            entry->metadataLength += n + 1;
+
+            //End of string detected?
+            if(metadata[i] == '\0')
+               break;
+         }
+
+         //Advance read index
+         i++;
+      }
+
+      //Empty TXT record?
+      if(!entry->metadataLength)
+      {
+         //An empty TXT record shall contain a single zero byte
+         entry->metadata[0] = 0;
+         entry->metadataLength = 1;
+      }
+
+      //Restart probing process
+      dnsSdStartProbing(context);
+
+      //Successful processing
+      error = NO_ERROR;
+   }
+   else
+   {
+      //The service list is full
+      error = ERROR_FAILURE;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return error code
+   return error;
+}
+
+
+/**
+ * @brief Unregister a DNS-SD service
+ * @param[in] context Pointer to the DNS-SD context
+ * @param[in] serviceName NULL-terminated string that contains the name of the
+ *   service to be unregistered
+ * @return Error code
+ **/
+
+error_t dnsSdUnregisterService(DnsSdContext *context, const char_t *serviceName)
+{
+   uint_t i;
+   DnsSdService *entry;
+
+   //Check parameters
+   if(context == NULL || serviceName == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Loop through the list of registered services
+   for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &context->serviceList[i];
+
+      //Service name found?
+      if(!strcasecmp(entry->name, serviceName))
+      {
+         //Send a goodbye packet
+         dnsSdSendGoodbye(context, entry);
+         //Remove the service from the list
+         entry->name[0] = '\0';
+      }
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Get the number of registered services
+ * @param[in] context Pointer to the DNS-SD context
+ * @return Number of registered services
+ **/
+
+uint_t dnsSdGetNumServices(DnsSdContext *context)
+{
+   uint_t i;
+   uint_t n;
+
+   //Number of registered services
+   n = 0;
+
+   //Check parameter
+   if(context != NULL)
+   {
+      //Valid instance name?
+      if(context->instanceName[0] != '\0')
+      {
+         //Loop through the list of registered services
+         for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
+         {
+            //Check if the entry is currently in use
+            if(context->serviceList[i].name[0] != '\0')
+               n++;
+         }
+      }
+   }
+
+   //Return the number of registered services
+   return n;
+}
+
+
+/**
+ * @brief Restart probing process
+ * @param[in] context Pointer to the DNS-SD context
+ * @return Error code
+ **/
+
+error_t dnsSdStartProbing(DnsSdContext *context)
+{
+   //Check parameter
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Force DNS-SD to start probing again
+   context->state = MDNS_STATE_INIT;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief DNS-SD responder timer handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * manage DNS-SD operation
+ *
+ * @param[in] context Pointer to the DNS-SD context
+ **/
+
+void dnsSdTick(DnsSdContext *context)
+{
+   systime_t time;
+   systime_t delay;
+   NetInterface *interface;
+   MdnsState state;
+
+   //Make sure DNS-SD has been properly instantiated
+   if(context == NULL)
+      return;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check current state
+   if(context->state == MDNS_STATE_INIT)
+   {
+      //Check whether the mDNS responder has been properly instantiated
+      if(interface->mdnsResponderContext != NULL)
+         state = interface->mdnsResponderContext->state;
+      else
+         state = MDNS_STATE_INIT;
+
+      //Wait for mDNS probing to complete
+      if(state == MDNS_STATE_ANNOUNCING || state == MDNS_STATE_IDLE)
+      {
+         //Any registered services?
+         if(dnsSdGetNumServices(context) > 0)
+         {
+            //Initial random delay
+            delay = netGetRandRange(MDNS_RAND_DELAY_MIN, MDNS_RAND_DELAY_MAX);
+            //Perform probing
+            dnsSdChangeState(context, MDNS_STATE_PROBING, delay);
+         }
+      }
+   }
+   else if(context->state == MDNS_STATE_PROBING)
+   {
+      //Probing failed?
+      if(context->conflict && context->retransmitCount > 0)
+      {
+         //Programmatically change the service instance name
+         dnsSdChangeInstanceName(context);
+         //Probe again, and repeat as necessary until a unique name is found
+         dnsSdChangeState(context, MDNS_STATE_PROBING, 0);
+      }
+      //Tie-break lost?
+      else if(context->tieBreakLost && context->retransmitCount > 0)
+      {
+         //The host defers to the winning host by waiting one second, and
+         //then begins probing for this record again
+         dnsSdChangeState(context, MDNS_STATE_PROBING, MDNS_PROBE_DEFER);
+      }
+      else
+      {
+         //Check current time
+         if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+         {
+            //Probing is on-going?
+            if(context->retransmitCount < MDNS_PROBE_NUM)
+            {
+               //First probe?
+               if(context->retransmitCount == 0)
+               {
+                  //Apparently conflicting mDNS responses received before the
+                  //first probe packet is sent must be silently ignored
+                  context->conflict = FALSE;
+                  context->tieBreakLost = FALSE;
+               }
+
+               //Send probe packet
+               dnsSdSendProbe(context);
+
+               //Save the time at which the packet was sent
+               context->timestamp = time;
+               //Time interval between subsequent probe packets
+               context->timeout = MDNS_PROBE_DELAY;
+               //Increment retransmission counter
+               context->retransmitCount++;
+            }
+            //Probing is complete?
+            else
+            {
+               //The mDNS responder must send unsolicited mDNS responses
+               //containing all of its newly registered resource records
+               if(context->settings.numAnnouncements > 0)
+                  dnsSdChangeState(context, MDNS_STATE_ANNOUNCING, 0);
+               else
+                  dnsSdChangeState(context, MDNS_STATE_IDLE, 0);
+            }
+         }
+      }
+   }
+   else if(context->state == MDNS_STATE_ANNOUNCING)
+   {
+      //Whenever a mDNS responder receives any mDNS response (solicited or
+      //otherwise) containing a conflicting resource record, the conflict
+      //must be resolved
+      if(context->conflict)
+      {
+         //Probe again, and repeat as necessary until a unique name is found
+         dnsSdChangeState(context, MDNS_STATE_PROBING, 0);
+      }
+      else
+      {
+         //Check current time
+         if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+         {
+            //Send announcement packet
+            dnsSdSendAnnouncement(context);
+
+            //Save the time at which the packet was sent
+            context->timestamp = time;
+            //Increment retransmission counter
+            context->retransmitCount++;
+
+            //First announcement packet?
+            if(context->retransmitCount == 1)
+            {
+               //The mDNS responder must send at least two unsolicited
+               //responses, one second apart
+               context->timeout = MDNS_ANNOUNCE_DELAY;
+            }
+            else
+            {
+               //To provide increased robustness against packet loss, a mDNS
+               //responder may send up to eight unsolicited responses, provided
+               //that the interval between unsolicited responses increases by
+               //at least a factor of two with every response sent
+               context->timeout *= 2;
+            }
+
+            //Last announcement packet?
+            if(context->retransmitCount >= context->settings.numAnnouncements)
+            {
+               //A mDNS responder must not send regular periodic announcements
+               dnsSdChangeState(context, MDNS_STATE_IDLE, 0);
+            }
+         }
+      }
+   }
+   else if(context->state == MDNS_STATE_IDLE)
+   {
+      //Whenever a mDNS responder receives any mDNS response (solicited or
+      //otherwise) containing a conflicting resource record, the conflict
+      //must be resolved
+      if(context->conflict)
+      {
+         //Probe again, and repeat as necessary until a unique name is found
+         dnsSdChangeState(context, MDNS_STATE_PROBING, 0);
+      }
+   }
+}
+
+
+/**
+ * @brief Callback function for link change event
+ * @param[in] context Pointer to the DNS-SD context
+ **/
+
+void dnsSdLinkChangeEvent(DnsSdContext *context)
+{
+   //Make sure DNS-SD has been properly instantiated
+   if(context == NULL)
+      return;
+
+   //Whenever a mDNS responder receives an indication of a link
+   //change event, it must perform probing and announcing
+   dnsSdChangeState(context, MDNS_STATE_INIT, 0);
+}
+
+
+/**
+ * @brief Update FSM state
+ * @param[in] context Pointer to the DNS-SD context
+ * @param[in] newState New state to switch to
+ * @param[in] delay Initial delay
+ **/
+
+void dnsSdChangeState(DnsSdContext *context,
+   MdnsState newState, systime_t delay)
+{
+   NetInterface *interface;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Set time stamp
+   context->timestamp = osGetSystemTime();
+   //Set initial delay
+   context->timeout = delay;
+   //Reset retransmission counter
+   context->retransmitCount = 0;
+   //Switch to the new state
+   context->state = newState;
+
+   //Any registered callback?
+   if(context->settings.stateChangeEvent != NULL)
+   {
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+      //Invoke user callback function
+      context->settings.stateChangeEvent(context, interface, newState);
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+   }
+}
+
+
+/**
+ * @brief Programmatically change the service instance name
+ * @param[in] context Pointer to the DNS-SD context
+ **/
+
+void dnsSdChangeInstanceName(DnsSdContext *context)
+{
+   size_t i;
+   size_t m;
+   size_t n;
+   uint32_t index;
+   char_t s[16];
+
+   //Retrieve the length of the string
+   n = strlen(context->instanceName);
+
+   //Parse the string backwards
+   for(i = n; i > 0; i--)
+   {
+      //Last character?
+      if(i == n)
+      {
+         //Check whether the last character is a bracket
+         if(context->instanceName[i - 1] != ')')
+            break;
+      }
+      else
+      {
+         //Check whether the current character is a digit
+         if(!isdigit((uint8_t) context->instanceName[i - 1]))
+            break;
+      }
+   }
+
+   //Any number following the service instance name?
+   if(context->instanceName[i] != '\0')
+   {
+      //Retrieve the number at the end of the name
+      index = atoi(context->instanceName + i);
+      //Increment the value
+      index++;
+
+      //Check the length of the name
+      if(i >= 2)
+      {
+         //Discard any space and bracket that may precede the number
+         if(context->instanceName[i - 2] == ' ' &&
+            context->instanceName[i - 1] == '(')
+         {
+            i -= 2;
+         }
+      }
+
+      //Strip the digits
+      context->instanceName[i] = '\0';
+   }
+   else
+   {
+      //Append the digit "2" to the name
+      index = 2;
+   }
+
+   //Convert the number to a string of characters
+   m = sprintf(s, " (%" PRIu32 ")", index);
+
+   //Sanity check
+   if((i + m) <= DNS_SD_MAX_INSTANCE_NAME_LEN)
+   {
+      //Programmatically change the service instance name
+      strcat(context->instanceName, s);
+   }
+}
+
+
+/**
+ * @brief Send probe packet
+ * @param[in] context Pointer to the DNS-SD context
+ * @return Error code
+ **/
+
+error_t dnsSdSendProbe(DnsSdContext *context)
+{
+   error_t error;
+   uint_t i;
+   NetInterface *interface;
+   DnsQuestion *dnsQuestion;
+   DnsSdService *service;
+   MdnsMessage message;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Create an empty mDNS query message
+   error = mdnsCreateMessage(&message, FALSE);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Start of exception handling block
+   do
+   {
+      //For all those resource records that a mDNS responder desires to be
+      //unique on the local link, it must send a mDNS query asking for those
+      //resource records, to see if any of them are already in use
+      if(dnsSdGetNumServices(context) > 0)
+      {
+         //Loop through the list of registered services
+         for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
+         {
+            //Point to the current entry
+            service = &context->serviceList[i];
+
+            //Valid service?
+            if(service->name[0] != '\0')
+            {
+               //Encode the service name using DNS notation
+               message.length += mdnsEncodeName(context->instanceName, service->name,
+                  ".local", (uint8_t *) message.dnsHeader + message.length);
+
+               //Point to the corresponding question structure
+               dnsQuestion = DNS_GET_QUESTION(message.dnsHeader, message.length);
+
+               //The probes should be sent as QU questions with the unicast-response
+               //bit set, to allow a defending host to respond immediately via unicast
+               dnsQuestion->qtype = HTONS(DNS_RR_TYPE_ANY);
+               dnsQuestion->qclass = HTONS(MDNS_QCLASS_QU | DNS_RR_CLASS_IN);
+
+               //Update the length of the mDNS query message
+               message.length += sizeof(DnsQuestion);
+
+               //Number of questions in the Question Section
+               message.dnsHeader->qdcount++;
+            }
+         }
+      }
+
+      //A probe query can be distinguished from a normal query by the fact that
+      //a probe query contains a proposed record in the Authority Section that
+      //answers the question in the Question Section
+      if(dnsSdGetNumServices(context) > 0)
+      {
+         //Loop through the list of registered services
+         for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
+         {
+            //Point to the current entry
+            service = &context->serviceList[i];
+
+            //Valid service?
+            if(service->name[0] != '\0')
+            {
+               //Format SRV resource record
+               error = dnsSdAddSrvRecord(interface, &message,
+                  service, FALSE, DNS_SD_DEFAULT_RR_TTL);
+               //Any error to report?
+               if(error)
+                  break;
+
+               //Format TXT resource record
+               error = dnsSdAddTxtRecord(interface, &message,
+                  service, FALSE, DNS_SD_DEFAULT_RR_TTL);
+               //Any error to report?
+               if(error)
+                  break;
+            }
+         }
+      }
+
+      //Propagate exception if necessary
+      if(error)
+         break;
+
+      //Number of resource records in the Authority Section
+      message.dnsHeader->nscount = message.dnsHeader->ancount;
+      //Number of resource records in the Answer Section
+      message.dnsHeader->ancount = 0;
+
+      //Send mDNS message
+      error = mdnsSendMessage(interface, &message, NULL, MDNS_PORT);
+
+      //End of exception handling block
+   } while(0);
+
+   //Free previously allocated memory
+   mdnsDeleteMessage(&message);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send announcement packet
+ * @param[in] context Pointer to the DNS-SD context
+ * @return Error code
+ **/
+
+error_t dnsSdSendAnnouncement(DnsSdContext *context)
+{
+   error_t error;
+   uint_t i;
+   NetInterface *interface;
+   DnsSdService *service;
+   MdnsMessage message;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Create an empty mDNS response message
+   error = mdnsCreateMessage(&message, TRUE);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Start of exception handling block
+   do
+   {
+      //Send an unsolicited mDNS response containing, in the Answer Section,
+      //all of its newly registered resource records
+      if(dnsSdGetNumServices(context) > 0)
+      {
+         //Loop through the list of registered services
+         for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
+         {
+            //Point to the current entry
+            service = &context->serviceList[i];
+
+            //Valid service?
+            if(service->name[0] != '\0')
+            {
+               //Format PTR resource record (service type enumeration)
+               error = dnsSdAddServiceEnumPtrRecord(interface,
+                  &message, service, DNS_SD_DEFAULT_RR_TTL);
+               //Any error to report?
+               if(error)
+                  break;
+
+               //Format PTR resource record
+               error = dnsSdAddPtrRecord(interface, &message,
+                  service, DNS_SD_DEFAULT_RR_TTL);
+               //Any error to report?
+               if(error)
+                  break;
+
+               //Format SRV resource record
+               error = dnsSdAddSrvRecord(interface, &message,
+                  service, TRUE, DNS_SD_DEFAULT_RR_TTL);
+               //Any error to report?
+               if(error)
+                  break;
+
+               //Format TXT resource record
+               error = dnsSdAddTxtRecord(interface, &message,
+                  service, TRUE, DNS_SD_DEFAULT_RR_TTL);
+               //Any error to report?
+               if(error)
+                  break;
+            }
+         }
+      }
+
+      //Propagate exception if necessary
+      if(error)
+         break;
+
+      //Send mDNS message
+      error = mdnsSendMessage(interface, &message, NULL, MDNS_PORT);
+
+      //End of exception handling block
+   } while(0);
+
+   //Free previously allocated memory
+   mdnsDeleteMessage(&message);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send goodbye packet
+ * @param[in] context Pointer to the DNS-SD context
+ * @param[in] service Pointer to a DNS-SD service
+ * @return Error code
+ **/
+
+error_t dnsSdSendGoodbye(DnsSdContext *context, const DnsSdService *service)
+{
+   error_t error;
+   uint_t i;
+   NetInterface *interface;
+   DnsSdService *entry;
+   MdnsMessage message;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Create an empty mDNS response message
+   error = mdnsCreateMessage(&message, TRUE);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Loop through the list of registered services
+   for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &context->serviceList[i];
+
+      //Valid service?
+      if(entry->name[0] != '\0')
+      {
+         if(service == entry || service == NULL)
+         {
+            //Format PTR resource record (service type enumeration)
+            error = dnsSdAddServiceEnumPtrRecord(interface, &message, entry, 0);
+            //Any error to report?
+            if(error)
+               break;
+
+            //Format PTR resource record
+            error = dnsSdAddPtrRecord(interface, &message, entry, 0);
+            //Any error to report?
+            if(error)
+               break;
+
+            //Format SRV resource record
+            error = dnsSdAddSrvRecord(interface, &message, entry, TRUE, 0);
+            //Any error to report?
+            if(error)
+               break;
+
+            //Format TXT resource record
+            error = dnsSdAddTxtRecord(interface, &message, entry, TRUE, 0);
+            //Any error to report?
+            if(error)
+               break;
+         }
+      }
+   }
+
+   //Check status code
+   if(!error)
+   {
+      //Send mDNS message
+      error = mdnsSendMessage(interface, &message, NULL, MDNS_PORT);
+   }
+
+   //Free previously allocated memory
+   mdnsDeleteMessage(&message);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse a question
+ * @param[in] interface Underlying network interface
+ * @param[in] query Incoming mDNS query message
+ * @param[in] offset Offset to first byte of the question
+ * @param[in] question Pointer to the question
+ * @param[in,out] response mDNS response message
+ * @return Error code
+ **/
+
+error_t dnsSdParseQuestion(NetInterface *interface, const MdnsMessage *query,
+   size_t offset, const DnsQuestion *question, MdnsMessage *response)
+{
+   error_t error;
+   uint_t i;
+   uint16_t qclass;
+   uint16_t qtype;
+   uint32_t ttl;
+   bool_t cacheFlush;
+   DnsSdContext *context;
+   DnsSdService *service;
+
+   //Point to the DNS-SD context
+   context = interface->dnsSdContext;
+   //Make sure DNS-SD has been properly instantiated
+   if(context == NULL)
+      return NO_ERROR;
+
+   //Check the state of the mDNS responder
+   if(context->state != MDNS_STATE_ANNOUNCING &&
+      context->state != MDNS_STATE_IDLE)
+   {
+      //Do not respond to mDNS queries during probing
+      return NO_ERROR;
+   }
+
+   //Convert the query class to host byte order
+   qclass = ntohs(question->qclass);
+   //Discard QU flag
+   qclass &= ~MDNS_QCLASS_QU;
+
+   //Convert the query type to host byte order
+   qtype = ntohs(question->qtype);
+
+   //Get the TTL resource record
+   ttl = context->settings.ttl;
+
+   //Check whether the querier originating the query is a simple resolver
+   if(ntohs(query->udpHeader->srcPort) != MDNS_PORT)
+   {
+      //The resource record TTL given in a legacy unicast response should
+      //not be greater than ten seconds, even if the true TTL of the mDNS
+      //resource record is higher
+      ttl = MIN(ttl, MDNS_LEGACY_UNICAST_RR_TTL);
+
+      //The cache-flush bit must not be set in legacy unicast responses
+      cacheFlush = FALSE;
+   }
+   else
+   {
+      //The cache-bit should be set for unique resource records
+      cacheFlush = TRUE;
+   }
+
+   //Any registered services?
+   if(dnsSdGetNumServices(context) > 0)
+   {
+      //Loop through the list of registered services
+      for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
+      {
+         //Point to the current entry
+         service = &context->serviceList[i];
+
+         //Valid service?
+         if(service->name[0] != '\0')
+         {
+            //Check the class of the query
+            if(qclass == DNS_RR_CLASS_IN || qclass == DNS_RR_CLASS_ANY)
+            {
+               //Compare service name
+               if(!mdnsCompareName(query->dnsHeader, query->length,
+                  offset, "", "_services._dns-sd._udp", ".local", 0))
+               {
+                  //PTR query?
+                  if(qtype == DNS_RR_TYPE_PTR || qtype == DNS_RR_TYPE_ANY)
+                  {
+                     //Format PTR resource record (service type enumeration)
+                     error = dnsSdAddServiceEnumPtrRecord(interface,
+                        response, service, ttl);
+                     //Any error to report?
+                     if(error)
+                        return error;
+
+                     //Update the number of shared resource records
+                     response->sharedRecordCount++;
+                  }
+               }
+               else if(!mdnsCompareName(query->dnsHeader, query->length,
+                  offset, "", service->name, ".local", 0))
+               {
+                  //PTR query?
+                  if(qtype == DNS_RR_TYPE_PTR || qtype == DNS_RR_TYPE_ANY)
+                  {
+                     //Format PTR resource record
+                     error = dnsSdAddPtrRecord(interface, response,
+                        service, ttl);
+                     //Any error to report?
+                     if(error)
+                        return error;
+
+                     //Update the number of shared resource records
+                     response->sharedRecordCount++;
+                  }
+               }
+               else if(!mdnsCompareName(query->dnsHeader, query->length, offset,
+                  context->instanceName, service->name, ".local", 0))
+               {
+                  //SRV query?
+                  if(qtype == DNS_RR_TYPE_SRV || qtype == DNS_RR_TYPE_ANY)
+                  {
+                     //Format SRV resource record
+                     error = dnsSdAddSrvRecord(interface, response,
+                        service, cacheFlush, ttl);
+                     //Any error to report?
+                     if(error)
+                        return error;
+                  }
+
+                  //TXT query?
+                  if(qtype == DNS_RR_TYPE_TXT || qtype == DNS_RR_TYPE_ANY)
+                  {
+                     //Format TXT resource record
+                     error = dnsSdAddTxtRecord(interface, response,
+                        service, cacheFlush, ttl);
+                     //Any error to report?
+                     if(error)
+                        return error;
+                  }
+
+                  //NSEC query?
+                  if(qtype != DNS_RR_TYPE_SRV && qtype != DNS_RR_TYPE_TXT)
+                  {
+                     //Format NSEC resource record
+                     error = dnsSdAddNsecRecord(interface, response,
+                        service, cacheFlush, ttl);
+                     //Any error to report?
+                     if(error)
+                        return error;
+                  }
+               }
+            }
+         }
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+
+/**
+ * @brief Parse a resource record from the Authority Section
+ * @param[in] interface Underlying network interface
+ * @param[in] query Incoming mDNS query message
+ * @param[in] offset Offset to first byte of the resource record
+ * @param[in] record Pointer to the resource record
+ **/
+
+void dnsSdParseNsRecord(NetInterface *interface, const MdnsMessage *query,
+   size_t offset, const DnsResourceRecord *record)
+{
+   uint_t i;
+   uint16_t rclass;
+   DnsSdContext *context;
+   DnsSdService *service;
+   DnsSrvResourceRecord *srvRecord;
+
+   //Point to the DNS-SD context
+   context = interface->dnsSdContext;
+   //Make sure DNS-SD has been properly instantiated
+   if(context == NULL)
+      return;
+
+   //Any services registered?
+   if(dnsSdGetNumServices(context) > 0)
+   {
+      //Loop through the list of registered services
+      for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
+      {
+         //Point to the current entry
+         service = &context->serviceList[i];
+
+         //Valid service?
+         if(service->name[0] != '\0')
+         {
+            //Apply tie-breaking rules
+            if(!mdnsCompareName(query->dnsHeader, query->length, offset,
+               context->instanceName, service->name, ".local", 0))
+            {
+               //Convert the class to host byte order
+               rclass = ntohs(record->rclass);
+               //Discard Cache Flush flag
+               rclass &= ~MDNS_RCLASS_CACHE_FLUSH;
+
+               //Check the class of the resource record
+               if(rclass == DNS_RR_CLASS_IN)
+               {
+                  //SRV resource record found?
+                  if(ntohs(record->rtype) == DNS_RR_TYPE_SRV)
+                  {
+                     //Cast resource record
+                     srvRecord = (DnsSrvResourceRecord *) record;
+
+                     //Compare Priority fields
+                     if(ntohs(srvRecord->priority) > service->priority)
+                     {
+                        context->tieBreakLost = TRUE;
+                     }
+                     else if(ntohs(srvRecord->priority) == service->priority)
+                     {
+                        //Compare Weight fields
+                        if(ntohs(srvRecord->weight) > service->weight)
+                        {
+                           context->tieBreakLost = TRUE;
+                        }
+                        else if(ntohs(srvRecord->weight) == service->weight)
+                        {
+                           //Compare Port fields
+                           if(ntohs(srvRecord->port) > service->port)
+                           {
+                              context->tieBreakLost = TRUE;
+                           }
+                           else if(ntohs(srvRecord->port) == service->port)
+                           {
+                              //Compute the offset of the first byte of the target
+                              offset = srvRecord->target - (uint8_t *) query->dnsHeader;
+
+                              if(mdnsCompareName(query->dnsHeader, query->length, offset,
+                                 context->instanceName, "", ".local", 0) > 0)
+                              {
+                                 //The host has lost the tie-break
+                                 context->tieBreakLost = TRUE;
+                              }
+                           }
+                        }
+                     }
+                  }
+               }
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Parse a resource record from the Answer Section
+ * @param[in] interface Underlying network interface
+ * @param[in] response Incoming mDNS response message
+ * @param[in] offset Offset to first byte of the resource record to be checked
+ * @param[in] record Pointer to the resource record
+ **/
+
+void dnsSdParseAnRecord(NetInterface *interface, const MdnsMessage *response,
+   size_t offset, const DnsResourceRecord *record)
+{
+   uint_t i;
+   uint16_t rclass;
+   DnsSdContext *context;
+   DnsSdService *service;
+
+   //Point to the DNS-SD context
+   context = interface->dnsSdContext;
+   //Make sure DNS-SD has been properly instantiated
+   if(context == NULL)
+      return;
+
+   //Any services registered?
+   if(dnsSdGetNumServices(context) > 0)
+   {
+      //Loop through the list of registered services
+      for(i = 0; i < DNS_SD_SERVICE_LIST_SIZE; i++)
+      {
+         //Point to the current entry
+         service = &context->serviceList[i];
+
+         //Valid service?
+         if(service->name[0] != '\0')
+         {
+            //Check for conflicts
+            if(!mdnsCompareName(response->dnsHeader, response->length, offset,
+               context->instanceName, service->name, ".local", 0))
+            {
+               //Convert the class to host byte order
+               rclass = ntohs(record->rclass);
+               //Discard Cache Flush flag
+               rclass &= ~MDNS_RCLASS_CACHE_FLUSH;
+
+               //Check the class of the resource record
+               if(rclass == DNS_RR_CLASS_IN)
+               {
+                  //SRV resource record found?
+                  if(ntohs(record->rtype) == DNS_RR_TYPE_SRV)
+                  {
+                     //Compute the offset of the first byte of the rdata
+                     offset = record->rdata - (uint8_t *) response->dnsHeader;
+
+                     //A conflict occurs when a mDNS responder has a unique record for
+                     //which it is currently authoritative, and it receives a mDNS
+                     //response message containing a record with the same name, rrtype
+                     //and rrclass, but inconsistent rdata
+                     if(mdnsCompareName(response->dnsHeader, response->length, offset,
+                        context->instanceName, "", ".local", 0))
+                     {
+                        //The service instance name is already in use by some other host
+                        context->conflict = TRUE;
+                     }
+                  }
+               }
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Additional record generation
+ * @param[in] interface Underlying network interface
+ * @param[in,out] response mDNS response message
+ * @param[in] legacyUnicast This flag is set for legacy unicast responses
+ **/
+
+void dnsSdGenerateAdditionalRecords(NetInterface *interface,
+   MdnsMessage *response, bool_t legacyUnicast)
+{
+   error_t error;
+   uint_t i;
+   uint_t j;
+   size_t n;
+   size_t offset;
+   uint_t ancount;
+   uint16_t rclass;
+   uint32_t ttl;
+   bool_t cacheFlush;
+   DnsSdContext *context;
+   DnsSdService *service;
+   DnsResourceRecord *record;
+
+   //Point to the DNS-SD context
+   context = interface->dnsSdContext;
+   //Make sure DNS-SD has been properly instantiated
+   if(context == NULL)
+      return;
+
+   //No registered services?
+   if(dnsSdGetNumServices(context) == 0)
+      return;
+
+   //mDNS responses must not contain any questions in the Question Section
+   if(response->dnsHeader->qdcount != 0)
+      return;
+
+   //Get the TTL resource record
+   ttl = context->settings.ttl;
+
+   //Check whether the querier originating the query is a simple resolver
+   if(legacyUnicast)
+   {
+      //The resource record TTL given in a legacy unicast response should
+      //not be greater than ten seconds, even if the true TTL of the mDNS
+      //resource record is higher
+      ttl = MIN(ttl, MDNS_LEGACY_UNICAST_RR_TTL);
+
+      //The cache-flush bit must not be set in legacy unicast responses
+      cacheFlush = FALSE;
+   }
+   else
+   {
+      //The cache-bit should be set for unique resource records
+      cacheFlush = TRUE;
+   }
+
+   //Point to the first resource record
+   offset = sizeof(DnsHeader);
+
+   //Save the number of resource records in the Answer Section
+   ancount = response->dnsHeader->ancount;
+
+   //Parse the Answer Section
+   for(i = 0; i < ancount; i++)
+   {
+      //Parse resource record name
+      n = dnsParseName(response->dnsHeader, response->length, offset, NULL, 0);
+      //Invalid name?
+      if(!n)
+         break;
+
+      //Point to the associated resource record
+      record = DNS_GET_RESOURCE_RECORD(response->dnsHeader, n);
+      //Point to the resource data
+      n += sizeof(DnsResourceRecord);
+
+      //Make sure the resource record is valid
+      if(n > response->length)
+         break;
+      if((n + ntohs(record->rdlength)) > response->length)
+         break;
+
+      //Convert the record class to host byte order
+      rclass = ntohs(record->rclass);
+      //Discard the cache-flush bit
+      rclass &= ~MDNS_RCLASS_CACHE_FLUSH;
+
+      //Loop through the list of registered services
+      for(j = 0; j < DNS_SD_SERVICE_LIST_SIZE; j++)
+      {
+         //Point to the current entry
+         service = &context->serviceList[j];
+
+         //Valid service?
+         if(service->name[0] != '\0')
+         {
+            //Check the class of the resource record
+            if(rclass == DNS_RR_CLASS_IN)
+            {
+               //PTR record?
+               if(ntohs(record->rtype) == DNS_RR_TYPE_PTR)
+               {
+                  //Compare service name
+                  if(!mdnsCompareName(response->dnsHeader, response->length,
+                     offset, "", service->name, ".local", 0))
+                  {
+                     //Format SRV resource record
+                     error = dnsSdAddSrvRecord(interface,
+                        response, service, cacheFlush, ttl);
+                     //Any error to report?
+                     if(error)
+                        return;
+
+                     //Format TXT resource record
+                     error = dnsSdAddTxtRecord(interface,
+                        response, service, cacheFlush, ttl);
+                     //Any error to report?
+                     if(error)
+                        return;
+                  }
+               }
+               //SRV record?
+               else if(ntohs(record->rtype) == DNS_RR_TYPE_SRV)
+               {
+                  //Compare service name
+                  if(!mdnsCompareName(response->dnsHeader, response->length,
+                     offset, context->instanceName, service->name, ".local", 0))
+                  {
+                     //Format TXT resource record
+                     error = dnsSdAddTxtRecord(interface,
+                        response, service, cacheFlush, ttl);
+                     //Any error to report?
+                     if(error)
+                        return;
+                  }
+               }
+            }
+         }
+      }
+
+      //Point to the next resource record
+      offset = n + ntohs(record->rdlength);
+   }
+
+   //Number of resource records in the Additional Section
+   response->dnsHeader->arcount += response->dnsHeader->ancount - ancount;
+   //Number of resource records in the Answer Section
+   response->dnsHeader->ancount = ancount;
+}
+
+
+/**
+ * @brief Add PTR record to a mDNS message (in response to a meta-query)
+ * @param[in] interface Underlying network interface
+ * @param[in,out] message Pointer to the mDNS message
+ * @param[in] service Pointer to a DNS-SD service
+ * @param[in] ttl Resource record TTL (cache lifetime)
+ * @return Error code
+ **/
+
+error_t dnsSdAddServiceEnumPtrRecord(NetInterface *interface,
+   MdnsMessage *message, const DnsSdService *service, uint32_t ttl)
+{
+   size_t n;
+   size_t offset;
+   DnsResourceRecord *record;
+
+   //Set the position to the end of the buffer
+   offset = message->length;
+
+   //The first pass calculates the length of the DNS encoded service name
+   n = mdnsEncodeName("", "_services._dns-sd._udp", ".local", NULL);
+
+   //Check the length of the resulting mDNS message
+   if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+      return ERROR_MESSAGE_TOO_LONG;
+
+   //The second pass encodes the service name using the DNS name notation
+   offset += mdnsEncodeName("", "_services._dns-sd._udp",
+      ".local", (uint8_t *) message->dnsHeader + offset);
+
+   //Consider the length of the resource record itself
+   if((offset + sizeof(DnsResourceRecord)) > MDNS_MESSAGE_MAX_SIZE)
+      return ERROR_MESSAGE_TOO_LONG;
+
+   //Point to the corresponding resource record
+   record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset);
+
+   //Fill in resource record
+   record->rtype = HTONS(DNS_RR_TYPE_PTR);
+   record->rclass = HTONS(DNS_RR_CLASS_IN);
+   record->ttl = htonl(ttl);
+
+   //Advance write index
+   offset += sizeof(DnsResourceRecord);
+
+   //The first pass calculates the length of the DNS encoded service name
+   n = mdnsEncodeName("", service->name, ".local", NULL);
+
+   //Check the length of the resulting mDNS message
+   if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+      return ERROR_MESSAGE_TOO_LONG;
+
+   //The second pass encodes the service name using DNS notation
+   n = mdnsEncodeName("", service->name,
+      ".local", record->rdata);
+
+   //Convert length field to network byte order
+   record->rdlength = htons(n);
+
+   //Number of resource records in the answer section
+   message->dnsHeader->ancount++;
+   //Update the length of the DNS message
+   message->length = offset + n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Add PTR record to a mDNS message
+ * @param[in] interface Underlying network interface
+ * @param[in,out] message Pointer to the mDNS message
+ * @param[in] service Pointer to a DNS-SD service
+ * @param[in] ttl Resource record TTL (cache lifetime)
+ * @return Error code
+ **/
+
+error_t dnsSdAddPtrRecord(NetInterface *interface,
+   MdnsMessage *message, const DnsSdService *service, uint32_t ttl)
+{
+   size_t n;
+   size_t offset;
+   bool_t duplicate;
+   DnsSdContext *context;
+   DnsResourceRecord *record;
+
+   //Point to the DNS-SD context
+   context = interface->dnsSdContext;
+
+   //Check whether the resource record is already present in the Answer
+   //Section of the message
+   duplicate = mdnsCheckDuplicateRecord(message, "",
+      service->name, ".local", DNS_RR_TYPE_PTR);
+
+   //The duplicates should be suppressed and the resource record should
+   //appear only once in the list
+   if(!duplicate)
+   {
+      //Set the position to the end of the buffer
+      offset = message->length;
+
+      //The first pass calculates the length of the DNS encoded service name
+      n = mdnsEncodeName("", service->name, ".local", NULL);
+
+      //Check the length of the resulting mDNS message
+      if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+         return ERROR_MESSAGE_TOO_LONG;
+
+      //Encode the service name using the DNS name notation
+      offset += mdnsEncodeName("", service->name,
+         ".local", (uint8_t *) message->dnsHeader + offset);
+
+      //Consider the length of the resource record itself
+      if((offset + sizeof(DnsResourceRecord)) > MDNS_MESSAGE_MAX_SIZE)
+         return ERROR_MESSAGE_TOO_LONG;
+
+      //Point to the corresponding resource record
+      record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset);
+
+      //Fill in resource record
+      record->rtype = HTONS(DNS_RR_TYPE_PTR);
+      record->rclass = HTONS(DNS_RR_CLASS_IN);
+      record->ttl = htonl(ttl);
+
+      //Advance write index
+      offset += sizeof(DnsResourceRecord);
+
+      //The first pass calculates the length of the DNS encoded instance name
+      n = mdnsEncodeName(context->instanceName, service->name, ".local", NULL);
+
+      //Check the length of the resulting mDNS message
+      if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+         return ERROR_MESSAGE_TOO_LONG;
+
+      //The second pass encodes the instance name using DNS notation
+      n = mdnsEncodeName(context->instanceName,
+         service->name, ".local", record->rdata);
+
+      //Convert length field to network byte order
+      record->rdlength = htons(n);
+
+      //Number of resource records in the answer section
+      message->dnsHeader->ancount++;
+      //Update the length of the DNS message
+      message->length = offset + n;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Add SRV record to a mDNS message
+ * @param[in] interface Underlying network interface
+ * @param[in,out] message Pointer to the mDNS message
+ * @param[in] service Pointer to a DNS-SD service
+ * @param[in] cacheFlush Cache-flush bit
+ * @param[in] ttl Resource record TTL (cache lifetime)
+ * @return Error code
+ **/
+
+error_t dnsSdAddSrvRecord(NetInterface *interface, MdnsMessage *message,
+   const DnsSdService *service, bool_t cacheFlush, uint32_t ttl)
+{
+   size_t n;
+   size_t offset;
+   bool_t duplicate;
+   MdnsResponderContext *mdnsResponderContext;
+   DnsSdContext *dnsSdContext;
+   DnsSrvResourceRecord *record;
+
+   //Point to the mDNS responder context
+   mdnsResponderContext = interface->mdnsResponderContext;
+   //Point to the DNS-SD context
+   dnsSdContext = interface->dnsSdContext;
+
+   //Check whether the resource record is already present in the Answer
+   //Section of the message
+   duplicate = mdnsCheckDuplicateRecord(message, dnsSdContext->instanceName,
+      service->name, ".local", DNS_RR_TYPE_SRV);
+
+   //The duplicates should be suppressed and the resource record should
+   //appear only once in the list
+   if(!duplicate)
+   {
+      //Set the position to the end of the buffer
+      offset = message->length;
+
+      //The first pass calculates the length of the DNS encoded instance name
+      n = mdnsEncodeName(dnsSdContext->instanceName,
+         service->name, ".local", NULL);
+
+      //Check the length of the resulting mDNS message
+      if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+         return ERROR_MESSAGE_TOO_LONG;
+
+      //The second pass encodes the instance name using DNS notation
+      offset += mdnsEncodeName(dnsSdContext->instanceName,
+         service->name, ".local", (uint8_t *) message->dnsHeader + offset);
+
+      //Consider the length of the resource record itself
+      if((offset + sizeof(DnsSrvResourceRecord)) > MDNS_MESSAGE_MAX_SIZE)
+         return ERROR_MESSAGE_TOO_LONG;
+
+      //Point to the corresponding resource record
+      record = (DnsSrvResourceRecord *) DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset);
+
+      //Fill in resource record
+      record->rtype = HTONS(DNS_RR_TYPE_SRV);
+      record->rclass = HTONS(DNS_RR_CLASS_IN);
+      record->ttl = htonl(ttl);
+      record->priority = htons(service->priority);
+      record->weight = htons(service->weight);
+      record->port = htons(service->port);
+
+      //Check whether the cache-flush bit should be set
+      if(cacheFlush)
+         record->rclass |= HTONS(MDNS_RCLASS_CACHE_FLUSH);
+
+      //Advance write index
+      offset += sizeof(DnsSrvResourceRecord);
+
+      //The first pass calculates the length of the DNS encoded target name
+      n = mdnsEncodeName("", mdnsResponderContext->hostname,
+         ".local", NULL);
+
+      //Check the length of the resulting mDNS message
+      if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+         return ERROR_MESSAGE_TOO_LONG;
+
+      //The second pass encodes the target name using DNS notation
+      n = mdnsEncodeName("", mdnsResponderContext->hostname,
+         ".local", record->target);
+
+      //Calculate data length
+      record->rdlength = htons(sizeof(DnsSrvResourceRecord) -
+         sizeof(DnsResourceRecord) + n);
+
+      //Number of resource records in the answer section
+      message->dnsHeader->ancount++;
+      //Update the length of the DNS message
+      message->length = offset + n;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Add TXT record to a mDNS message
+ * @param[in] interface Underlying network interface
+ * @param[in,out] message Pointer to the mDNS message
+ * @param[in] service Pointer to a DNS-SD service
+ * @param[in] cacheFlush Cache-flush bit
+ * @param[in] ttl Resource record TTL (cache lifetime)
+ * @return Error code
+ **/
+
+error_t dnsSdAddTxtRecord(NetInterface *interface, MdnsMessage *message,
+   const DnsSdService *service, bool_t cacheFlush, uint32_t ttl)
+{
+   size_t n;
+   size_t offset;
+   bool_t duplicate;
+   DnsSdContext *context;
+   DnsResourceRecord *record;
+
+   //Point to the DNS-SD context
+   context = interface->dnsSdContext;
+
+   //Check whether the resource record is already present in the Answer
+   //Section of the message
+   duplicate = mdnsCheckDuplicateRecord(message, context->instanceName,
+      service->name, ".local", DNS_RR_TYPE_TXT);
+
+   //The duplicates should be suppressed and the resource record should
+   //appear only once in the list
+   if(!duplicate)
+   {
+      //Set the position to the end of the buffer
+      offset = message->length;
+
+      //The first pass calculates the length of the DNS encoded instance name
+      n = mdnsEncodeName(context->instanceName, service->name, ".local", NULL);
+
+      //Check the length of the resulting mDNS message
+      if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+         return ERROR_MESSAGE_TOO_LONG;
+
+      //The second pass encodes the instance name using DNS notation
+      offset += mdnsEncodeName(context->instanceName,
+         service->name, ".local", (uint8_t *) message->dnsHeader + offset);
+
+      //Consider the length of the resource record itself
+      if((offset + sizeof(DnsResourceRecord)) > MDNS_MESSAGE_MAX_SIZE)
+         return ERROR_MESSAGE_TOO_LONG;
+
+      //Point to the corresponding resource record
+      record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset);
+
+      //Fill in resource record
+      record->rtype = HTONS(DNS_RR_TYPE_TXT);
+      record->rclass = HTONS(DNS_RR_CLASS_IN);
+      record->ttl = htonl(ttl);
+      record->rdlength = htons(service->metadataLength);
+
+      //Check whether the cache-flush bit should be set
+      if(cacheFlush)
+         record->rclass |= HTONS(MDNS_RCLASS_CACHE_FLUSH);
+
+      //Advance write index
+      offset += sizeof(DnsResourceRecord);
+
+      //Check the length of the resulting mDNS message
+      if((offset + service->metadataLength) > MDNS_MESSAGE_MAX_SIZE)
+         return ERROR_MESSAGE_TOO_LONG;
+
+      //Copy metadata
+      memcpy(record->rdata, service->metadata, service->metadataLength);
+
+      //Update the length of the DNS message
+      message->length = offset + service->metadataLength;
+      //Number of resource records in the answer section
+      message->dnsHeader->ancount++;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Add NSEC record to a mDNS message
+ * @param[in] interface Underlying network interface
+ * @param[in,out] message Pointer to the mDNS message
+ * @param[in] service Pointer to a DNS-SD service
+ * @param[in] cacheFlush Cache-flush bit
+ * @param[in] ttl Resource record TTL (cache lifetime)
+ * @return Error code
+ **/
+
+error_t dnsSdAddNsecRecord(NetInterface *interface, MdnsMessage *message,
+   const DnsSdService *service, bool_t cacheFlush, uint32_t ttl)
+{
+   size_t n;
+   size_t offset;
+   bool_t duplicate;
+   size_t bitmapLength;
+   uint8_t bitmap[8];
+   DnsSdContext *context;
+   DnsResourceRecord *record;
+
+   //Point to the DNS-SD context
+   context = interface->dnsSdContext;
+
+   //Check whether the resource record is already present in the Answer
+   //Section of the message
+   duplicate = mdnsCheckDuplicateRecord(message, context->instanceName,
+      service->name, ".local", DNS_RR_TYPE_NSEC);
+
+   //The duplicates should be suppressed and the resource record should
+   //appear only once in the list
+   if(!duplicate)
+   {
+      //The bitmap identifies the resource record types that exist
+      memset(bitmap, 0, sizeof(bitmap));
+
+      //TXT resource record is supported
+      DNS_SET_NSEC_BITMAP(bitmap, DNS_RR_TYPE_TXT);
+      //SRV resource record is supported
+      DNS_SET_NSEC_BITMAP(bitmap, DNS_RR_TYPE_SRV);
+
+      //Compute the length of the bitmap
+      for(bitmapLength = sizeof(bitmap); bitmapLength > 0; bitmapLength--)
+      {
+         //Trailing zero octets in the bitmap must be omitted...
+         if(bitmap[bitmapLength - 1] != 0x00)
+            break;
+      }
+
+      //Set the position to the end of the buffer
+      offset = message->length;
+
+      //The first pass calculates the length of the DNS encoded instance name
+      n = mdnsEncodeName(context->instanceName, service->name, ".local", NULL);
+
+      //Check the length of the resulting mDNS message
+      if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+         return ERROR_MESSAGE_TOO_LONG;
+
+      //The second pass encodes the instance name using the DNS name notation
+      offset += mdnsEncodeName(context->instanceName, service->name,
+         ".local", (uint8_t *) message->dnsHeader + offset);
+
+      //Consider the length of the resource record itself
+      if((offset + sizeof(DnsResourceRecord)) > MDNS_MESSAGE_MAX_SIZE)
+         return ERROR_MESSAGE_TOO_LONG;
+
+      //Point to the corresponding resource record
+      record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset);
+
+      //Fill in resource record
+      record->rtype = HTONS(DNS_RR_TYPE_NSEC);
+      record->rclass = HTONS(DNS_RR_CLASS_IN);
+      record->ttl = htonl(ttl);
+
+      //Check whether the cache-flush bit should be set
+      if(cacheFlush)
+         record->rclass |= HTONS(MDNS_RCLASS_CACHE_FLUSH);
+
+      //Advance write index
+      offset += sizeof(DnsResourceRecord);
+
+      //Check the length of the resulting mDNS message
+      if((offset + n + 2) > MDNS_MESSAGE_MAX_SIZE)
+         return ERROR_MESSAGE_TOO_LONG;
+
+      //The Next Domain Name field contains the record's own name
+      mdnsEncodeName(context->instanceName, service->name,
+         ".local", record->rdata);
+
+      //DNS NSEC record is limited to Window Block number zero
+      record->rdata[n++] = 0;
+      //The Bitmap Length is a value in the range 1-32
+      record->rdata[n++] = bitmapLength;
+
+      //The Bitmap data identifies the resource record types that exist
+      memcpy(record->rdata + n, bitmap, bitmapLength);
+
+      //Convert length field to network byte order
+      record->rdlength = htons(n + bitmapLength);
+
+      //Number of resource records in the answer section
+      message->dnsHeader->ancount++;
+      //Update the length of the DNS message
+      message->length = offset + n + bitmapLength;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/dns_sd/dns_sd.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,206 @@
+/**
+ * @file dns_sd.h
+ * @brief DNS-SD (DNS-Based Service Discovery)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DNS_SD_H
+#define _DNS_SD_H
+
+//Dependencies
+#include "core/net.h"
+#include "dns/dns_common.h"
+#include "mdns/mdns_common.h"
+
+//DNS-SD support
+#ifndef DNS_SD_SUPPORT
+   #define DNS_SD_SUPPORT DISABLED
+#elif (DNS_SD_SUPPORT != ENABLED && DNS_SD_SUPPORT != DISABLED)
+   #error DNS_SD_SUPPORT parameter is not valid
+#endif
+
+//DNS-SD tick interval
+#ifndef DNS_SD_TICK_INTERVAL
+   #define DNS_SD_TICK_INTERVAL 250
+#elif (DNS_SD_TICK_INTERVAL < 10)
+   #error DNS_SD_TICK_INTERVAL parameter is not valid
+#endif
+
+//Maximum number of registered services
+#ifndef DNS_SD_SERVICE_LIST_SIZE
+   #define DNS_SD_SERVICE_LIST_SIZE 2
+#elif (DNS_SD_SERVICE_LIST_SIZE < 1)
+   #error DNS_SD_SERVICE_LIST_SIZE parameter is not valid
+#endif
+
+//Maximum length of service name
+#ifndef DNS_SD_MAX_SERVICE_NAME_LEN
+   #define DNS_SD_MAX_SERVICE_NAME_LEN 16
+#elif (DNS_SD_MAX_SERVICE_NAME_LEN < 1)
+   #error DNS_SD_MAX_SERVICE_NAME_LEN parameter is not valid
+#endif
+
+//Maximum length of instance name
+#ifndef DNS_SD_MAX_INSTANCE_NAME_LEN
+   #define DNS_SD_MAX_INSTANCE_NAME_LEN 32
+#elif (DNS_SD_MAX_INSTANCE_NAME_LEN < 1)
+   #error DNS_SD_MAX_INSTANCE_NAME_LEN parameter is not valid
+#endif
+
+//Maximum length of the discovery-time metadata (TXT record)
+#ifndef DNS_SD_MAX_METADATA_LEN
+   #define DNS_SD_MAX_METADATA_LEN 128
+#elif (DNS_SD_MAX_METADATA_LEN < 1)
+   #error DNS_SD_MAX_METADATA_LEN parameter is not valid
+#endif
+
+//Default resource record TTL (cache lifetime)
+#ifndef DNS_SD_DEFAULT_RR_TTL
+   #define DNS_SD_DEFAULT_RR_TTL 120
+#elif (DNS_SD_DEFAULT_RR_TTL < 1)
+   #error DNS_SD_DEFAULT_RR_TTL parameter is not valid
+#endif
+
+//Forward declaration of DnsSdContext structure
+struct _DnsSdContext;
+#define DnsSdContext struct _DnsSdContext
+
+
+/**
+ * @brief FSM state change callback
+ **/
+
+typedef void (*DnsSdStateChangeCallback)(DnsSdContext *context,
+   NetInterface *interface, MdnsState state);
+
+
+/**
+ * @brief DNS-SD settings
+ **/
+
+typedef struct
+{
+   NetInterface *interface;                   ///<Underlying network interface
+   uint_t numAnnouncements;                   ///<Number of announcement packets
+   uint32_t ttl;                              ///<TTL resource record
+   DnsSdStateChangeCallback stateChangeEvent; ///<FSM state change event
+} DnsSdSettings;
+
+
+/**
+ * @brief DNS-SD service descriptor
+ **/
+
+typedef struct
+{
+   char_t name[DNS_SD_MAX_SERVICE_NAME_LEN + 1]; ///<Service name
+   uint16_t priority;                            ///<Priority of the target host
+   uint16_t weight;                              ///<Server selection mechanism
+   uint16_t port;                                ///<Port on the target host of this service
+   uint8_t metadata[DNS_SD_MAX_METADATA_LEN];    ///<Discovery-time metadata (TXT record)
+   size_t metadataLength;                        ///<Length of the metadata
+} DnsSdService;
+
+
+/**
+ * @brief DNS-SD context
+ **/
+
+struct _DnsSdContext
+{
+   DnsSdSettings settings;                                ///<DNS-SD settings
+   bool_t running;                                        ///<DNS-SD is currently running
+   MdnsState state;                                       ///<FSM state
+   bool_t conflict;                                       ///<Conflict detected
+   bool_t tieBreakLost;                                   ///<Tie-break lost
+   systime_t timestamp;                                   ///<Timestamp to manage retransmissions
+   systime_t timeout;                                     ///<Timeout value
+   uint_t retransmitCount;                                ///<Retransmission counter
+   char_t instanceName[DNS_SD_MAX_INSTANCE_NAME_LEN + 1]; ///<Service instance name
+   DnsSdService serviceList[DNS_SD_SERVICE_LIST_SIZE];    ///<List of registered services
+};
+
+
+//Tick counter to handle periodic operations
+extern systime_t dnsSdTickCounter;
+
+//DNS-SD related functions
+void dnsSdGetDefaultSettings(DnsSdSettings *settings);
+error_t dnsSdInit(DnsSdContext *context, const DnsSdSettings *settings);
+error_t dnsSdStart(DnsSdContext *context);
+error_t dnsSdStop(DnsSdContext *context);
+MdnsState dnsSdGetState(DnsSdContext *context);
+
+error_t dnsSdSetInstanceName(DnsSdContext *context, const char_t *instanceName);
+
+error_t dnsSdRegisterService(DnsSdContext *context, const char_t *serviceName,
+   uint16_t priority, uint16_t weight, uint16_t port, const char_t *metadata);
+
+error_t dnsSdUnregisterService(DnsSdContext *context, const char_t *serviceName);
+
+uint_t dnsSdGetNumServices(DnsSdContext *context);
+error_t dnsSdStartProbing(DnsSdContext *context);
+
+void dnsSdTick(DnsSdContext *interface);
+void dnsSdLinkChangeEvent(DnsSdContext *interface);
+
+void dnsSdChangeState(DnsSdContext *context,
+   MdnsState newState, systime_t delay);
+
+void dnsSdChangeInstanceName(DnsSdContext *context);
+
+error_t dnsSdSendProbe(DnsSdContext *context);
+error_t dnsSdSendAnnouncement(DnsSdContext *context);
+error_t dnsSdSendGoodbye(DnsSdContext *context, const DnsSdService *service);
+
+error_t dnsSdParseQuestion(NetInterface *interface, const MdnsMessage *query,
+   size_t offset, const DnsQuestion *question, MdnsMessage *response);
+
+void dnsSdParseNsRecord(NetInterface *interface, const MdnsMessage *query,
+   size_t offset, const DnsResourceRecord *record);
+
+void dnsSdParseAnRecord(NetInterface *interface, const MdnsMessage *response,
+   size_t offset, const DnsResourceRecord *record);
+
+void dnsSdGenerateAdditionalRecords(NetInterface *interface,
+   MdnsMessage *response, bool_t legacyUnicast);
+
+error_t dnsSdAddServiceEnumPtrRecord(NetInterface *interface,
+   MdnsMessage *message, const DnsSdService *service, uint32_t ttl);
+
+error_t dnsSdAddPtrRecord(NetInterface *interface,
+   MdnsMessage *message, const DnsSdService *service, uint32_t ttl);
+
+error_t dnsSdAddSrvRecord(NetInterface *interface, MdnsMessage *message,
+   const DnsSdService *service, bool_t cacheFlush, uint32_t ttl);
+
+error_t dnsSdAddTxtRecord(NetInterface *interface, MdnsMessage *message,
+   const DnsSdService *service, bool_t cacheFlush, uint32_t ttl);
+
+error_t dnsSdAddNsecRecord(NetInterface *interface, MdnsMessage *message,
+   const DnsSdService *service, bool_t cacheFlush, uint32_t ttl);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/a2fxxxm3_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,753 @@
+/**
+ * @file a2fxxxm3_eth.c
+ * @brief SmartFusion (A2FxxxM3) Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "a2fxxxm3.h"
+#include "drivers/mss_ethernet_mac/mss_ethernet_mac_regs.h"
+#include "drivers/mss_ethernet_mac/mss_ethernet_mac_desc.h"
+#include "core/net.h"
+#include "drivers/a2fxxxm3_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+static uint8_t txBuffer[A2FXXXM3_ETH_TX_BUFFER_COUNT][A2FXXXM3_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+static uint8_t rxBuffer[A2FXXXM3_ETH_RX_BUFFER_COUNT][A2FXXXM3_ETH_RX_BUFFER_SIZE];
+//Transmit DMA descriptors
+#pragma data_alignment = 4
+static A2fxxxm3TxDmaDesc txDmaDesc[A2FXXXM3_ETH_TX_BUFFER_COUNT];
+//Receive DMA descriptors
+#pragma data_alignment = 4
+static A2fxxxm3RxDmaDesc rxDmaDesc[A2FXXXM3_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[A2FXXXM3_ETH_TX_BUFFER_COUNT][A2FXXXM3_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[A2FXXXM3_ETH_RX_BUFFER_COUNT][A2FXXXM3_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Transmit DMA descriptors
+static A2fxxxm3TxDmaDesc txDmaDesc[A2FXXXM3_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive DMA descriptors
+static A2fxxxm3RxDmaDesc rxDmaDesc[A2FXXXM3_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+
+#endif
+
+//Pointer to the current TX DMA descriptor
+static A2fxxxm3TxDmaDesc *txCurDmaDesc;
+//Pointer to the current RX DMA descriptor
+static A2fxxxm3RxDmaDesc *rxCurDmaDesc;
+
+
+/**
+ * @brief A2FxxxM3 Ethernet MAC driver
+ **/
+
+const NicDriver a2fxxxm3EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   a2fxxxm3EthInit,
+   a2fxxxm3EthTick,
+   a2fxxxm3EthEnableIrq,
+   a2fxxxm3EthDisableIrq,
+   a2fxxxm3EthEventHandler,
+   a2fxxxm3EthSendPacket,
+   a2fxxxm3EthSetMulticastFilter,
+   a2fxxxm3EthUpdateMacConfig,
+   a2fxxxm3EthWritePhyReg,
+   a2fxxxm3EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief A2FxxxM3 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t a2fxxxm3EthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing A2FxxxM3 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Perform a software reset
+   MAC->CSR0 |= CSR0_SWR_MASK;
+   //Wait for the reset to complete
+   while(MAC->CSR0 & CSR0_SWR_MASK);
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Enable store and forward mode
+   MAC->CSR6 |= CSR6_SF_MASK;
+
+   //Initialize DMA descriptor lists
+   a2fxxxm3EthInitDmaDesc(interface);
+
+   //Enable the desired Ethernet interrupts
+   MAC->CSR7 |= CSR7_NIE_MASK | CSR7_RIE_MASK | CSR7_TIE_MASK;
+
+   //Set priority grouping (5 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(A2FXXXM3_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure Ethernet interrupt priority
+   NVIC_SetPriority(EthernetMAC_IRQn, NVIC_EncodePriority(A2FXXXM3_ETH_IRQ_PRIORITY_GROUPING,
+      A2FXXXM3_ETH_IRQ_GROUP_PRIORITY, A2FXXXM3_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable transmission and reception
+   MAC->CSR6 |= CSR6_ST_MASK | CSR6_SR_MASK;
+
+   //Set MAC address
+   error = a2fxxxm3EthSendSetup(interface);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void a2fxxxm3EthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX DMA descriptor list
+   for(i = 0; i < A2FXXXM3_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the user
+      txDmaDesc[i].tdes0 = 0;
+      //Use chain structure rather than ring structure
+      txDmaDesc[i].tdes1 = TDES1_TCH;
+      //Transmit buffer address
+      txDmaDesc[i].tdes2 = (uint32_t) txBuffer[i];
+      //Next descriptor address
+      txDmaDesc[i].tdes3 = (uint32_t) &txDmaDesc[i + 1];
+   }
+
+   //The last descriptor is chained to the first entry
+   txDmaDesc[i - 1].tdes3 = (uint32_t) &txDmaDesc[0];
+   //Point to the very first descriptor
+   txCurDmaDesc = &txDmaDesc[0];
+
+   //Initialize RX DMA descriptor list
+   for(i = 0; i < A2FXXXM3_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the DMA
+      rxDmaDesc[i].rdes0 = RDES0_OWN;
+      //Use chain structure rather than ring structure
+      rxDmaDesc[i].rdes1 = RDES1_RCH | (A2FXXXM3_ETH_RX_BUFFER_SIZE & RDES1_RBS1_MASK);
+      //Receive buffer address
+      rxDmaDesc[i].rdes2 = (uint32_t) rxBuffer[i];
+      //Next descriptor address
+      rxDmaDesc[i].rdes3 = (uint32_t) &rxDmaDesc[i + 1];
+   }
+
+   //The last descriptor is chained to the first entry
+   rxDmaDesc[i - 1].rdes3 = (uint32_t) &rxDmaDesc[0];
+   //Point to the very first descriptor
+   rxCurDmaDesc = &rxDmaDesc[0];
+
+   //Start location of the TX descriptor list
+   MAC->CSR4 = (uint32_t) txDmaDesc;
+   //Start location of the RX descriptor list
+   MAC->CSR3 = (uint32_t) rxDmaDesc;
+}
+
+
+/**
+ * @brief A2FxxxM3 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void a2fxxxm3EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void a2fxxxm3EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(EthernetMAC_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void a2fxxxm3EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(EthernetMAC_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief A2FxxxM3 Ethernet MAC interrupt service routine
+ **/
+
+void EthernetMAC_IRQHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read interrupt status register
+   status = MAC->CSR5;
+
+   //A packet has been transmitted?
+   if(status & CSR5_TI_MASK)
+   {
+      //Clear TI interrupt flag
+      MAC->CSR5 = CSR5_TI_MASK;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurDmaDesc->tdes0 & TDES0_OWN))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & CSR5_RI_MASK)
+   {
+      //Disable RIE interrupt
+      MAC->CSR7 &= ~CSR7_RIE_MASK;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Clear NIS interrupt flag
+   MAC->CSR5 = CSR5_NIS_MASK;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief A2FxxxM3 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void a2fxxxm3EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(MAC->CSR5 & CSR5_RI_MASK)
+   {
+      //Clear interrupt flag
+      MAC->CSR5 = CSR5_RI_MASK;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = a2fxxxm3EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable Ethernet interrupts
+   MAC->CSR7 |= CSR7_NIE_MASK | CSR7_RIE_MASK | CSR7_TIE_MASK;
+}
+
+
+/**
+ * @brief Send a setup frame
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t a2fxxxm3EthSendSetup(NetInterface *interface)
+{
+   A2fxxxm3HashTableSetupFrame *setupFrame;
+
+   //Make sure the current buffer is available for writing
+   if(txCurDmaDesc->tdes0 & TDES0_OWN)
+      return ERROR_FAILURE;
+
+   //Point to the buffer where to format the setup frame
+   setupFrame = (A2fxxxm3HashTableSetupFrame *) txCurDmaDesc->tdes2;
+
+   //Clear contents
+   memset(setupFrame, 0, sizeof(A2fxxxm3HashTableSetupFrame));
+
+   //Set MAC address
+   setupFrame->physicalAddr[0] = interface->macAddr.w[0];
+   setupFrame->physicalAddr[1] = interface->macAddr.w[1];
+   setupFrame->physicalAddr[2] = interface->macAddr.w[2];
+
+   //Write the number of bytes to send
+   txCurDmaDesc->tdes1 = sizeof(A2fxxxm3HashTableSetupFrame) & TDES1_TBS1_MASK;
+   //The SET flag indicates that this is a setup frame descriptor
+   txCurDmaDesc->tdes1 |= TDES1_SET | TDES1_TCH | TDES1_FT0;
+   //Give the ownership of the descriptor to the DMA
+   txCurDmaDesc->tdes0 |= TDES0_OWN;
+
+   //Instruct the DMA to poll the transmit descriptor list
+   MAC->CSR1 = 1;
+
+   //Point to the next descriptor in the list
+   txCurDmaDesc = (A2fxxxm3TxDmaDesc *) txCurDmaDesc->tdes3;
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t a2fxxxm3EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > A2FXXXM3_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurDmaDesc->tdes0 & TDES0_OWN)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txCurDmaDesc->tdes2, buffer, offset, length);
+
+   //Write the number of bytes to send
+   txCurDmaDesc->tdes1 = length & TDES1_TBS1_MASK;
+   //Set LS and FS flags as the data fits in a single buffer
+   txCurDmaDesc->tdes1 |= TDES1_IC | TDES1_LS | TDES1_FS | TDES1_TCH;
+   //Give the ownership of the descriptor to the DMA
+   txCurDmaDesc->tdes0 |= TDES0_OWN;
+
+   //Instruct the DMA to poll the transmit descriptor list
+   MAC->CSR1 = 1;
+
+   //Point to the next descriptor in the list
+   txCurDmaDesc = (A2fxxxm3TxDmaDesc *) txCurDmaDesc->tdes3;
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurDmaDesc->tdes0 & TDES0_OWN))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t a2fxxxm3EthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxCurDmaDesc->rdes0 & RDES0_OWN))
+   {
+      //FS and LS flags should be set
+      if((rxCurDmaDesc->rdes0 & RDES0_FS) && (rxCurDmaDesc->rdes0 & RDES0_LS))
+      {
+         //Make sure no error occurred
+         if(!(rxCurDmaDesc->rdes0 & RDES0_ES))
+         {
+            //Retrieve the length of the frame
+            n = (rxCurDmaDesc->rdes0 >> RDES0_FL_OFFSET) & RDES0_FL_MASK;
+            //Limit the number of data to read
+            n = MIN(n, A2FXXXM3_ETH_RX_BUFFER_SIZE);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, (uint8_t *) rxCurDmaDesc->rdes2, n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Give the ownership of the descriptor back to the DMA
+      rxCurDmaDesc->rdes0 = RDES0_OWN;
+      //Point to the next descriptor in the list
+      rxCurDmaDesc = (A2fxxxm3RxDmaDesc *) rxCurDmaDesc->rdes3;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Instruct the DMA to poll the receive descriptor list
+   MAC->CSR2 = 1;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t a2fxxxm3EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   bool_t acceptMulticast;
+
+   //This flag will be set if multicast addresses should be accepted
+   acceptMulticast = FALSE;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Valid entry?
+      if(interface->macMulticastFilter[i].refCount > 0)
+      {
+         //Accept multicast addresses
+         acceptMulticast = TRUE;
+         //We are done
+         break;
+      }
+   }
+
+   //Enable the reception of multicast frames if necessary
+   if(acceptMulticast)
+      MAC->CSR6 |= CSR6_PM_MASK;
+   else
+      MAC->CSR6 &= ~CSR6_PM_MASK;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t a2fxxxm3EthUpdateMacConfig(NetInterface *interface)
+{
+   //Stop transmission
+   while(((MAC->CSR5 & CSR5_TS_MASK) >> CSR5_TS_SHIFT) != CSR5_TS_STOPPED)
+      MAC->CSR6 &= ~CSR6_ST_MASK;
+
+   //Stop reception
+   while(((MAC->CSR5 & CSR5_RS_MASK) >> CSR5_RS_SHIFT) != CSR5_RS_STOPPED)
+      MAC->CSR6 &= ~CSR6_SR_MASK;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      MAC->CSR6 |= CSR6_TTM_MASK;
+   else
+      MAC->CSR6 &= ~CSR6_TTM_MASK;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      MAC->CSR6 |= CSR6_FD_MASK;
+   else
+      MAC->CSR6 &= ~CSR6_FD_MASK;
+
+   //Restart transmission and reception
+   MAC->CSR6 |= CSR6_ST_MASK | CSR6_SR_MASK;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void a2fxxxm3EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   //Synchronization pattern
+   a2fxxxm3EthWriteSmi(SMI_SYNC, 32);
+   //Start of frame
+   a2fxxxm3EthWriteSmi(SMI_START, 2);
+   //Set up a write operation
+   a2fxxxm3EthWriteSmi(SMI_WRITE, 2);
+   //Write PHY address
+   a2fxxxm3EthWriteSmi(phyAddr, 5);
+   //Write register address
+   a2fxxxm3EthWriteSmi(regAddr, 5);
+   //Turnaround
+   a2fxxxm3EthWriteSmi(SMI_TA, 2);
+   //Write register value
+   a2fxxxm3EthWriteSmi(data, 16);
+   //Release MDIO
+   a2fxxxm3EthReadSmi(1);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t a2fxxxm3EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint16_t data;
+
+   //Synchronization pattern
+   a2fxxxm3EthWriteSmi(SMI_SYNC, 32);
+   //Start of frame
+   a2fxxxm3EthWriteSmi(SMI_START, 2);
+   //Set up a read operation
+   a2fxxxm3EthWriteSmi(SMI_READ, 2);
+   //Write PHY address
+   a2fxxxm3EthWriteSmi(phyAddr, 5);
+   //Write register address
+   a2fxxxm3EthWriteSmi(regAddr, 5);
+   //Turnaround to avoid contention
+   a2fxxxm3EthReadSmi(1);
+   //Read register value
+   data = a2fxxxm3EthReadSmi(16);
+   //Force the PHY to release the MDIO pin
+   a2fxxxm3EthReadSmi(1);
+
+   //Return PHY register contents
+   return data;
+}
+
+
+/**
+ * @brief SMI write operation
+ * @param[in] data Raw data to be written
+ * @param[in] length Number of bits to be written
+ **/
+
+void a2fxxxm3EthWriteSmi(uint32_t data, uint_t length)
+{
+   //Skip the most significant bits since they are meaningless
+   data <<= 32 - length;
+
+   //Configure MDIO as an output
+   MAC->CSR9 |= CSR9_MDEN_MASK;
+
+   //Write the specified number of bits
+   while(length--)
+   {
+      //Write MDIO
+      if(data & 0x80000000)
+         MAC->CSR9 |= CSR9_MDO_MASK;
+      else
+         MAC->CSR9 &= ~CSR9_MDO_MASK;
+
+      //Assert MDC
+      usleep(1);
+      MAC->CSR9 |= CSR9_MDC_MASK;
+      //Deassert MDC
+      usleep(1);
+      MAC->CSR9 &= ~CSR9_MDC_MASK;
+
+      //Rotate data
+      data <<= 1;
+   }
+}
+
+
+/**
+ * @brief SMI read operation
+ * @param[in] length Number of bits to be read
+ * @return Data resulting from the MDIO read operation
+ **/
+
+uint32_t a2fxxxm3EthReadSmi(uint_t length)
+{
+   uint32_t data = 0;
+
+   //Configure MDIO as an input
+   MAC->CSR9 &= ~CSR9_MDEN_MASK;
+
+   //Read the specified number of bits
+   while(length--)
+   {
+      //Rotate data
+      data <<= 1;
+
+      //Assert MDC
+      MAC->CSR9 |= CSR9_MDC_MASK;
+      usleep(1);
+      //Deassert MDC
+      MAC->CSR9 &= ~CSR9_MDC_MASK;
+      usleep(1);
+
+      //Check MDIO state
+      if(MAC->CSR9 & CSR9_MDI_MASK)
+         data |= 0x00000001;
+   }
+
+   //Return the received data
+   return data;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t a2fxxxm3EthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //Update CRC value
+      crc ^= p[i];
+
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(crc & 0x00000001)
+            crc = (crc >> 1) ^ 0xEDB88320;
+         else
+            crc = crc >> 1;
+      }
+   }
+
+   //Return CRC value
+   return crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/a2fxxxm3_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,168 @@
+/**
+ * @file a2fxxxm3_eth.h
+ * @brief SmartFusion (A2FxxxM3) Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _A2FXXXM3_ETH_H
+#define _A2FXXXM3_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef A2FXXXM3_ETH_TX_BUFFER_COUNT
+   #define A2FXXXM3_ETH_TX_BUFFER_COUNT 2
+#elif (A2FXXXM3_ETH_TX_BUFFER_COUNT < 1)
+   #error A2FXXXM3_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef A2FXXXM3_ETH_TX_BUFFER_SIZE
+   #define A2FXXXM3_ETH_TX_BUFFER_SIZE 1536
+#elif (A2FXXXM3_ETH_TX_BUFFER_SIZE != 1536)
+   #error A2FXXXM3_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef A2FXXXM3_ETH_RX_BUFFER_COUNT
+   #define A2FXXXM3_ETH_RX_BUFFER_COUNT 4
+#elif (A2FXXXM3_ETH_RX_BUFFER_COUNT < 1)
+   #error A2FXXXM3_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef A2FXXXM3_ETH_RX_BUFFER_SIZE
+   #define A2FXXXM3_ETH_RX_BUFFER_SIZE 1536
+#elif (A2FXXXM3_ETH_RX_BUFFER_SIZE != 1536)
+   #error A2FXXXM3_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef A2FXXXM3_ETH_IRQ_PRIORITY_GROUPING
+   #define A2FXXXM3_ETH_IRQ_PRIORITY_GROUPING 2
+#elif (A2FXXXM3_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error A2FXXXM3_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef A2FXXXM3_ETH_IRQ_GROUP_PRIORITY
+   #define A2FXXXM3_ETH_IRQ_GROUP_PRIORITY 24
+#elif (A2FXXXM3_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error A2FXXXM3_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef A2FXXXM3_ETH_IRQ_SUB_PRIORITY
+   #define A2FXXXM3_ETH_IRQ_SUB_PRIORITY 0
+#elif (A2FXXXM3_ETH_IRQ_SUB_PRIORITY < 0)
+   #error A2FXXXM3_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//MDEN bit definition
+#ifndef CSR9_MDEN_MASK
+   #define CSR9_MDEN_MASK CSR9_MII_MASK
+#endif
+
+//Serial Management Interface
+#define SMI_SYNC  0xFFFFFFFF
+#define SMI_START 0x00000001
+#define SMI_WRITE 0x00000001
+#define SMI_READ  0x00000002
+#define SMI_TA    0x00000002
+
+
+/**
+ * @brief Transmit DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t tdes0;
+   uint32_t tdes1;
+   uint32_t tdes2;
+   uint32_t tdes3;
+} A2fxxxm3TxDmaDesc;
+
+
+/**
+ * @brief Receive DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t rdes0;
+   uint32_t rdes1;
+   uint32_t rdes2;
+   uint32_t rdes3;
+} A2fxxxm3RxDmaDesc;
+
+
+/**
+ * @brief Hash table setup frame
+ **/
+
+typedef struct
+{
+   uint32_t hashFilter[32];  //0-127
+   uint32_t reserved1[7];    //128-155
+   uint32_t physicalAddr[3]; //156-167
+   uint32_t reserved2[6];    //168-191
+} A2fxxxm3HashTableSetupFrame;
+
+
+//A2FxxxM3 Ethernet MAC driver
+extern const NicDriver a2fxxxm3EthDriver;
+
+//A2FxxxM3 Ethernet MAC related functions
+error_t a2fxxxm3EthInit(NetInterface *interface);
+void a2fxxxm3EthInitDmaDesc(NetInterface *interface);
+
+void a2fxxxm3EthTick(NetInterface *interface);
+
+void a2fxxxm3EthEnableIrq(NetInterface *interface);
+void a2fxxxm3EthDisableIrq(NetInterface *interface);
+void a2fxxxm3EthEventHandler(NetInterface *interface);
+
+error_t a2fxxxm3EthSendSetup(NetInterface *interface);
+
+error_t a2fxxxm3EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t a2fxxxm3EthReceivePacket(NetInterface *interface);
+
+error_t a2fxxxm3EthSetMulticastFilter(NetInterface *interface);
+error_t a2fxxxm3EthUpdateMacConfig(NetInterface *interface);
+
+void a2fxxxm3EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t a2fxxxm3EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+void a2fxxxm3EthWriteSmi(uint32_t data, uint_t length);
+uint32_t a2fxxxm3EthReadSmi(uint_t length);
+
+uint32_t a2fxxxm3EthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/am335x_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1753 @@
+/**
+ * @file am335x_eth.c
+ * @brief Sitara AM335x Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "soc_am335x.h"
+#include "hw_types.h"
+#include "hw_cm_per.h"
+#include "hw_control_am335x.h"
+#include "hw_cpsw_ale.h"
+#include "hw_cpsw_cpdma.h"
+#include "hw_cpsw_port.h"
+#include "hw_cpsw_sl.h"
+#include "hw_cpsw_ss.h"
+#include "hw_cpsw_wr.h"
+#include "hw_mdio.h"
+#include "interrupt.h"
+#include "core/net.h"
+#include "drivers/am335x_eth.h"
+#include "debug.h"
+
+//MDIO input clock frequency
+#define MDIO_INPUT_CLK 125000000
+//MDIO output clock frequency
+#define MDIO_OUTPUT_CLK 1000000
+
+//Underlying network interface (port 1)
+static NetInterface *nicDriverInterface1 = NULL;
+//Underlying network interface (port 2)
+static NetInterface *nicDriverInterface2 = NULL;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer (port 1)
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static uint8_t txBuffer1[AM335X_ETH_TX_BUFFER_COUNT][AM335X_ETH_TX_BUFFER_SIZE];
+//Transmit buffer (port 2)
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static uint8_t txBuffer2[AM335X_ETH_TX_BUFFER_COUNT][AM335X_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static uint8_t rxBuffer[AM335X_ETH_RX_BUFFER_COUNT][AM335X_ETH_RX_BUFFER_SIZE];
+//Transmit buffer descriptors (port 1)
+#pragma data_alignment = 4
+#pragma location = ".ram_cppi"
+static Am335xTxBufferDesc txBufferDesc1[AM335X_ETH_TX_BUFFER_COUNT];
+//Transmit buffer descriptors (port 2)
+#pragma data_alignment = 4
+#pragma location = ".ram_cppi"
+static Am335xTxBufferDesc txBufferDesc2[AM335X_ETH_TX_BUFFER_COUNT];
+//Receive buffer descriptors
+#pragma data_alignment = 4
+#pragma location = ".ram_cppi"
+static Am335xRxBufferDesc rxBufferDesc[AM335X_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer (port 1)
+static uint8_t txBuffer1[AM335X_ETH_TX_BUFFER_COUNT][AM335X_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+//Transmit buffer (port 2)
+static uint8_t txBuffer2[AM335X_ETH_TX_BUFFER_COUNT][AM335X_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+//Receive buffer
+static uint8_t rxBuffer[AM335X_ETH_RX_BUFFER_COUNT][AM335X_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+//Transmit buffer descriptors (port 1)
+static Am335xTxBufferDesc txBufferDesc1[AM335X_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".ram_cppi")));
+//Transmit buffer descriptors (port 2)
+static Am335xTxBufferDesc txBufferDesc2[AM335X_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".ram_cppi")));
+//Receive buffer descriptors
+static Am335xRxBufferDesc rxBufferDesc[AM335X_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".ram_cppi")));
+
+#endif
+
+//Pointer to the current TX buffer descriptor (port1)
+static Am335xTxBufferDesc *txCurBufferDesc1;
+//Pointer to the current TX buffer descriptor (port 2)
+static Am335xTxBufferDesc *txCurBufferDesc2;
+//Pointer to the current RX buffer descriptor
+static Am335xRxBufferDesc *rxCurBufferDesc;
+
+
+/**
+ * @brief AM335x Ethernet MAC driver (port1)
+ **/
+
+const NicDriver am335xEthPort1Driver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   am335xEthInitPort1,
+   am335xEthTick,
+   am335xEthEnableIrq,
+   am335xEthDisableIrq,
+   am335xEthEventHandler,
+   am335xEthSendPacketPort1,
+   am335xEthSetMulticastFilter,
+   am335xEthUpdateMacConfig,
+   am335xEthWritePhyReg,
+   am335xEthReadPhyReg,
+   FALSE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief AM335x Ethernet MAC driver (port2)
+ **/
+
+const NicDriver am335xEthPort2Driver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   am335xEthInitPort2,
+   am335xEthTick,
+   am335xEthEnableIrq,
+   am335xEthDisableIrq,
+   am335xEthEventHandler,
+   am335xEthSendPacketPort2,
+   am335xEthSetMulticastFilter,
+   am335xEthUpdateMacConfig,
+   am335xEthWritePhyReg,
+   am335xEthReadPhyReg,
+   FALSE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief AM335x Ethernet MAC initialization (port 1)
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t am335xEthInitPort1(NetInterface *interface)
+{
+   error_t error;
+   uint32_t temp;
+
+   //Debug message
+   TRACE_INFO("Initializing AM335x Ethernet MAC (port 1)...\r\n");
+
+   //Initialize CPSW instance
+   am335xEthInitInstance(interface);
+
+   //Save underlying network interface
+   nicDriverInterface1 = interface;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Unspecifield MAC address?
+   if(macCompAddr(&interface->macAddr, &MAC_UNSPECIFIED_ADDR))
+   {
+      //Use the factory preprogrammed MAC address
+      interface->macAddr.b[0] = CONTROL_MAC_ID_HI_R(0) >> CONTROL_MAC_ID0_HI_MACADDR_47_40_SHIFT;
+      interface->macAddr.b[1] = CONTROL_MAC_ID_HI_R(0) >> CONTROL_MAC_ID0_HI_MACADDR_39_32_SHIFT;
+      interface->macAddr.b[2] = CONTROL_MAC_ID_HI_R(0) >> CONTROL_MAC_ID0_HI_MACADDR_31_24_SHIFT;
+      interface->macAddr.b[3] = CONTROL_MAC_ID_HI_R(0) >> CONTROL_MAC_ID0_HI_MACADDR_23_16_SHIFT;
+      interface->macAddr.b[4] = CONTROL_MAC_ID_LO_R(0) >> CONTROL_MAC_ID0_LO_MACADDR_15_8_SHIFT;
+      interface->macAddr.b[5] = CONTROL_MAC_ID_LO_R(0) >> CONTROL_MAC_ID0_LO_MACADDR_7_0_SHIFT;
+
+      //Generate the 64-bit interface identifier
+      macAddrToEui64(&interface->macAddr, &interface->eui64);
+   }
+
+   //Set port state to forward
+   temp = CPSW_ALE_PORTCTL_R(1) & ~CPSW_ALE_PORTCTL1_PORT_STATE;
+   CPSW_ALE_PORTCTL_R(1) = temp | CPSW_ALE_PORTCTL_PORT_STATE_FORWARD;
+
+   //Set the MAC address
+   CPSW_PORT1_SA_HI_R = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   CPSW_PORT1_SA_LO_R = interface->macAddr.w[2];
+
+   //Configure VLAN identifier and VLAN priority
+   CPSW_PORT1_PORT_VLAN_R = (0 << CPSW_PORT_P1_PORT_VLAN_PORT_PRI_SHIFT) |
+      (CPSW_PORT1 << CPSW_PORT_P1_PORT_VLAN_PORT_VID_SHIFT);
+
+   //Add a VLAN entry in the ALE table
+   am335xEthAddVlanEntry(CPSW_PORT1, CPSW_PORT1);
+
+   //Add a VLAN/unicast address entry in the ALE table
+   am335xEthAddVlanAddrEntry(CPSW_PORT1, CPSW_PORT1, &interface->macAddr);
+
+   //Enable CPSW statistics
+   CPSW_SS_STAT_PORT_EN_R |= CPSW_SS_STAT_PORT_EN_P1_STAT_EN;
+
+   //Enable TX and RX
+   CPSW_SL1_MACCONTROL_R = CPSW_SL_MACCONTROL_GMII_EN;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief AM335x Ethernet MAC initialization (port 2)
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t am335xEthInitPort2(NetInterface *interface)
+{
+   error_t error;
+   uint32_t temp;
+
+   //Debug message
+   TRACE_INFO("Initializing AM335x Ethernet MAC (port 2)...\r\n");
+
+   //Initialize CPSW instance
+   am335xEthInitInstance(interface);
+
+   //Save underlying network interface
+   nicDriverInterface2 = interface;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Unspecifield MAC address?
+   if(macCompAddr(&interface->macAddr, &MAC_UNSPECIFIED_ADDR))
+   {
+      //Use the factory preprogrammed MAC address
+      interface->macAddr.b[0] = CONTROL_MAC_ID_HI_R(1) >> CONTROL_MAC_ID1_HI_MACADDR_47_40_SHIFT;
+      interface->macAddr.b[1] = CONTROL_MAC_ID_HI_R(1) >> CONTROL_MAC_ID1_HI_MACADDR_39_32_SHIFT;
+      interface->macAddr.b[2] = CONTROL_MAC_ID_HI_R(1) >> CONTROL_MAC_ID1_HI_MACADDR_31_24_SHIFT;
+      interface->macAddr.b[3] = CONTROL_MAC_ID_HI_R(1) >> CONTROL_MAC_ID1_HI_MACADDR_23_16_SHIFT;
+      interface->macAddr.b[4] = CONTROL_MAC_ID_LO_R(1) >> CONTROL_MAC_ID1_LO_MACADDR_15_8_SHIFT;
+      interface->macAddr.b[5] = CONTROL_MAC_ID_LO_R(1) >> CONTROL_MAC_ID1_LO_MACADDR_7_0_SHIFT;
+
+      //Generate the 64-bit interface identifier
+      macAddrToEui64(&interface->macAddr, &interface->eui64);
+   }
+
+   //Set port state to forward
+   temp = CPSW_ALE_PORTCTL_R(2) & ~CPSW_ALE_PORTCTL2_PORT_STATE;
+   CPSW_ALE_PORTCTL_R(2) = temp | CPSW_ALE_PORTCTL_PORT_STATE_FORWARD;
+
+   //Set the MAC address
+   CPSW_PORT2_SA_HI_R = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   CPSW_PORT2_SA_LO_R = interface->macAddr.w[2];
+
+   //Configure VLAN identifier and VLAN priority
+   CPSW_PORT2_PORT_VLAN_R = (0 << CPSW_PORT_P2_PORT_VLAN_PORT_PRI_SHIFT) |
+      (CPSW_PORT2 << CPSW_PORT_P2_PORT_VLAN_PORT_VID_SHIFT);
+
+   //Add a VLAN entry in the ALE table
+   am335xEthAddVlanEntry(CPSW_PORT2, CPSW_PORT2);
+
+   //Add a VLAN/unicast address entry in the ALE table
+   am335xEthAddVlanAddrEntry(CPSW_PORT2, CPSW_PORT2, &interface->macAddr);
+
+   //Enable CPSW statistics
+   CPSW_SS_STAT_PORT_EN_R |= CPSW_SS_STAT_PORT_EN_P2_STAT_EN;
+
+   //Enable TX and RX
+   CPSW_SL2_MACCONTROL_R = CPSW_SL_MACCONTROL_GMII_EN;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize CPSW instance
+ * @param[in] interface Underlying network interface
+ **/
+
+void am335xEthInitInstance(NetInterface *interface)
+{
+   uint_t i;
+   uint32_t temp;
+#ifdef ti_sysbios_BIOS___VERS
+   Hwi_Params hwiParams;
+#endif
+
+   //Initialization sequence is performed once
+   if(nicDriverInterface1 == NULL && nicDriverInterface2 == NULL)
+   {
+      //Select the interface mode (MII/RMII/RGMII) and configure pin muxing
+      am335xEthInitGpio(interface);
+
+      //Enable the CPSW subsystem clocks
+      CM_PER_CPGMAC0_CLKCTRL_R = CM_PER_CPGMAC0_CLKCTRL_MODULEMODE_ENABLE;
+
+      //Wait for the CPSW module to be fully functional
+      do
+      {
+         //Get module idle status
+         temp = (CM_PER_CPGMAC0_CLKCTRL_R & CM_PER_CPGMAC0_CLKCTRL_IDLEST) >>
+            CM_PER_CPGMAC0_CLKCTRL_IDLEST_SHIFT;
+
+         //Keep looping as long as the module is not fully functional
+      } while(temp != CM_PER_CPGMAC0_CLKCTRL_IDLEST_FUNC);
+
+      //Start a software forced wake-up transition
+      CM_PER_CPSW_CLKSTCTRL_R = CM_PER_CPSW_CLKSTCTRL_CLKTRCTRL_SW_WKUP;
+
+      //Wait for the CPSW 125 MHz OCP clock to be active
+      do
+      {
+         //Get the state of the CPSW 125 MHz OCP clock
+         temp = (CM_PER_CPSW_CLKSTCTRL_R & CM_PER_CPSW_CLKSTCTRL_CLKACTIVITY_CPSW_125MHZ_GCLK) >>
+           CM_PER_CPSW_CLKSTCTRL_CLKACTIVITY_CPSW_125MHZ_GCLK_SHIFT;
+
+         //Keep looping as long as the clock is inactive
+      } while(temp != CM_PER_CPSW_CLKSTCTRL_CLKACTIVITY_CPSW_125MHZ_GCLK_ACT);
+
+      //Reset CPSW subsystem
+      CPSW_SS_SOFT_RESET_R = CPSW_SS_SOFT_RESET_SOFT_RESET;
+      //Wait for the reset to complete
+      while(CPSW_SS_SOFT_RESET_R & CPSW_SS_SOFT_RESET_SOFT_RESET);
+
+      //Reset CPSW wrapper module
+      CPSW_WR_SOFT_RESET_R = CPSW_WR_SOFT_RESET_SOFT_RESET;
+      //Wait for the reset to complete
+      while(CPSW_WR_SOFT_RESET_R & CPSW_WR_SOFT_RESET_SOFT_RESET);
+
+      //Reset CPSW sliver 1 logic
+      CPSW_SL1_SOFT_RESET_R = CPSW_SL_SOFT_RESET_SOFT_RESET;
+      //Wait for the reset to complete
+      while(CPSW_SL1_SOFT_RESET_R & CPSW_SL_SOFT_RESET_SOFT_RESET);
+
+      //Reset CPSW sliver 2 logic
+      CPSW_SL2_SOFT_RESET_R = CPSW_SL_SOFT_RESET_SOFT_RESET;
+      //Wait for the reset to complete
+      while(CPSW_SL2_SOFT_RESET_R & CPSW_SL_SOFT_RESET_SOFT_RESET);
+
+      //Reset CPSW CPDMA module
+      CPSW_CPDMA_CPDMA_SOFT_RESET_R = CPSW_CPDMA_CPDMA_SOFT_RESET_SOFT_RESET;
+      //Wait for the reset to complete
+      while(CPSW_CPDMA_CPDMA_SOFT_RESET_R & CPSW_CPDMA_CPDMA_SOFT_RESET_SOFT_RESET);
+
+      //Initialize the HDPs and the CPs to NULL
+      for(i = CPSW_CH0; i <= CPSW_CH7; i++)
+      {
+         //TX head descriptor pointer
+         CPSW_CPDMA_TX_HDP_R(i) = 0;
+         //TX completion pointer
+         CPSW_CPDMA_TX_CP_R(i) = 0;
+         //RX head descriptor pointer
+         CPSW_CPDMA_RX_HDP_R(i) = 0;
+         //RX completion pointer
+         CPSW_CPDMA_RX_CP_R(i) = 0;
+      }
+
+      //Enable ALE and clear ALE address table
+      CPSW_ALE_CONTROL_R = CPSW_ALE_CONTROL_ENABLE_ALE |
+         CPSW_ALE_CONTROL_CLEAR_TABLE;
+
+      //For dual MAC mode, configure VLAN aware mode
+      CPSW_ALE_CONTROL_R |= CPSW_ALE_CONTROL_ALE_VLAN_AWARE;
+
+      //Set dual MAC mode for port 0
+      temp = CPSW_PORT0_TX_IN_CTL_R & ~CPSW_PORT_P0_TX_IN_CTL_TX_IN_SEL;
+      CPSW_PORT0_TX_IN_CTL_R = temp | CPSW_PORT_P0_TX_IN_CTL_TX_IN_DUAL_MAC;
+
+      //Set port 0 state to forward
+      temp = CPSW_ALE_PORTCTL_R(0) & ~CPSW_ALE_PORTCTL0_PORT_STATE;
+      CPSW_ALE_PORTCTL_R(0) = temp | CPSW_ALE_PORTCTL_PORT_STATE_FORWARD;
+
+      //Enable CPSW statistics
+      CPSW_SS_STAT_PORT_EN_R = CPSW_SS_STAT_PORT_EN_P0_STAT_EN;
+
+      //Configure TX and RX buffer descriptors
+      am335xEthInitBufferDesc(interface);
+
+      //Acknowledge TX and interrupts for proper interrupt pulsing
+      CPSW_CPDMA_CPDMA_EOI_VECTOR_R = CPSW_CPDMA_EOI_VECTOR_TX_PULSE;
+      CPSW_CPDMA_CPDMA_EOI_VECTOR_R = CPSW_CPDMA_EOI_VECTOR_RX_PULSE;
+
+      //Enable channel 1 and 2 interrupts of the DMA engine
+      CPSW_CPDMA_TX_INTMASK_SET_R = (1 << CPSW_CH1) | (1 << CPSW_CH2);
+      //Enable TX completion interrupts
+      CPSW_WR_C_TX_EN_R(CPSW_CORE0) |= (1 << CPSW_CH1) | (1 << CPSW_CH2);
+
+      //Enable channel 0 interrupts of the DMA engine
+      CPSW_CPDMA_RX_INTMASK_SET_R = (1 << CPSW_CH0);
+      //Enable RX completion interrupts
+      CPSW_WR_C_RX_EN_R(CPSW_CORE0) |= (1 << CPSW_CH0);
+
+#ifdef ti_sysbios_BIOS___VERS
+      //Configure TX interrupt
+      Hwi_Params_init(&hwiParams);
+      hwiParams.enableInt = FALSE;
+      hwiParams.priority = AM335X_ETH_IRQ_PRIORITY;
+
+      //Register TX interrupt handler
+      Hwi_create(SYS_INT_3PGSWTXINT0, (Hwi_FuncPtr) am335xEthTxIrqHandler,
+         &hwiParams, NULL);
+
+      //Configure RX interrupt
+      Hwi_Params_init(&hwiParams);
+      hwiParams.enableInt = FALSE;
+      hwiParams.priority = AM335X_ETH_IRQ_PRIORITY;
+
+      //Register RX interrupt handler
+      Hwi_create(SYS_INT_3PGSWRXINT0, (Hwi_FuncPtr) am335xEthRxIrqHandler,
+         &hwiParams, NULL);
+#else
+      //Register interrupt handlers
+      IntRegister(SYS_INT_3PGSWTXINT0, am335xEthTxIrqHandler);
+      IntRegister(SYS_INT_3PGSWRXINT0, am335xEthRxIrqHandler);
+
+      //Configure TX interrupt priority
+      IntPrioritySet(SYS_INT_3PGSWTXINT0, AM335X_ETH_IRQ_PRIORITY,
+         AINTC_HOSTINT_ROUTE_IRQ);
+
+      //Configure RX interrupt priority
+      IntPrioritySet(SYS_INT_3PGSWRXINT0, AM335X_ETH_IRQ_PRIORITY,
+         AINTC_HOSTINT_ROUTE_IRQ);
+#endif
+
+      //Enable the transmission and reception
+      CPSW_CPDMA_TX_CONTROL_R = CPSW_CPDMA_TX_CONTROL_TX_EN;
+      CPSW_CPDMA_RX_CONTROL_R = CPSW_CPDMA_RX_CONTROL_RX_EN;
+
+      //Calculate the MDC clock divider to be used
+      temp = (MDIO_INPUT_CLK / MDIO_OUTPUT_CLK) - 1;
+
+      //Initialize MDIO interface
+      MDIO_CONTROL_R = MDIO_CONTROL_ENABLE |
+         MDIO_CONTROL_FAULTENB | (temp & MDIO_CONTROL_CLKDIV);
+   }
+}
+
+
+//BeagleBone Black TMDSSK3358 or SBC DIVA board?
+#if defined(USE_BEAGLEBONE_BLACK) || defined(USE_TMDSSK3358) || defined(USE_SBC_DIVA)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void am335xEthInitGpio(NetInterface *interface)
+{
+//BeagleBone Black board?
+#if defined(USE_BEAGLEBONE_BLACK)
+   //Select MII interface mode for port 1
+   CONTROL_GMII_SEL_R = CONTROL_GMII_SEL_GMII1_SEL_MII;
+
+   //Configure MII1_TX_CLK (GPIO3_9)
+   CONTROL_CONF_MII1_TXCLK_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(0);
+   //Configure MII1_TX_EN (GPIO3_3)
+   CONTROL_CONF_MII1_TXEN_R = CONTROL_CONF_MUXMODE(0);
+   //Configure MII1_TXD0 (GPIO0_28)
+   CONTROL_CONF_MII1_TXD0_R = CONTROL_CONF_MUXMODE(0);
+   //Configure MII1_TXD1 (GPIO0_21)
+   CONTROL_CONF_MII1_TXD1_R = CONTROL_CONF_MUXMODE(0);
+   //Configure MII1_TXD2 (GPIO0_17)
+   CONTROL_CONF_MII1_TXD2_R = CONTROL_CONF_MUXMODE(0);
+   //Configure MII1_TXD3 (GPIO0_16)
+   CONTROL_CONF_MII1_TXD3_R = CONTROL_CONF_MUXMODE(0);
+
+   //Configure MII1_RX_CLK (GPIO3_10)
+   CONTROL_CONF_MII1_RXCLK_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(0);
+   //Configure MII1_RXD0 (GPIO2_21)
+   CONTROL_CONF_MII1_RXD0_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(0);
+   //Configure MII1_RXD1 (GPIO2_20)
+   CONTROL_CONF_MII1_RXD1_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(0);
+   //Configure MII1_RXD2 (GPIO2_19)
+   CONTROL_CONF_MII1_RXD2_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(0);
+   //Configure MII1_RXD3 (GPIO2_18)
+   CONTROL_CONF_MII1_RXD3_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(0);
+   //Configure MII1_COL (GPIO3_0)
+   CONTROL_CONF_MII1_COL_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(0);
+   //Configure MII1_CRS (GPIO3_1)
+   CONTROL_CONF_MII1_CRS_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(0);
+   //Configure MII1_RX_ER (GPIO3_2)
+   CONTROL_CONF_MII1_RXERR_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(0);
+   //Configure MII1_RX_DV (GPIO3_4)
+   CONTROL_CONF_MII1_RXDV_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(0);
+
+   //Configure MDIO (GPIO0_0)
+   CONTROL_CONF_MDIO_DATA_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_PULLUPSEL |
+      CONTROL_CONF_MUXMODE(0);
+
+   //Configure MDC (GPIO0_1)
+   CONTROL_CONF_MDIO_CLK_R = CONTROL_CONF_PULLUPSEL | CONTROL_CONF_MUXMODE(0);
+
+//TMDSSK3358 board?
+#elif defined(USE_TMDSSK3358)
+   //Select RGMII interface mode for both port 1 and port 2
+   CONTROL_GMII_SEL_R = CONTROL_GMII_SEL_GMII1_SEL_RGMII |
+      CONTROL_GMII_SEL_GMII2_SEL_RGMII;
+
+   //Configure RGMII1_TCLK (GPIO3_9)
+   CONTROL_CONF_MII1_TXCLK_R = CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII1_TCTL (GPIO3_3)
+   CONTROL_CONF_MII1_TXEN_R = CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII1_TD0 (GPIO0_28)
+   CONTROL_CONF_MII1_TXD0_R = CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII1_TD1 (GPIO0_21)
+   CONTROL_CONF_MII1_TXD1_R = CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII1_TD2 (GPIO0_17)
+   CONTROL_CONF_MII1_TXD2_R = CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII1_TD3 (GPIO0_16)
+   CONTROL_CONF_MII1_TXD3_R = CONTROL_CONF_MUXMODE(2);
+
+   //Configure RGMII1_RCLK (GPIO3_10)
+   CONTROL_CONF_MII1_RXCLK_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII1_RCTL (GPIO3_4)
+   CONTROL_CONF_MII1_RXDV_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII1_RD0 (GPIO2_21)
+   CONTROL_CONF_MII1_RXD0_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(2);
+   //Configure /RGMII1_RD1 (GPIO2_20)
+   CONTROL_CONF_MII1_RXD1_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII1_RD2 (GPIO2_19)
+   CONTROL_CONF_MII1_RXD2_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII1_RD3 (GPIO2_18)
+   CONTROL_CONF_MII1_RXD3_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(2);
+
+   //Configure RGMII2_TCLK (GPIO1_22/GPMC_A6)
+   CONTROL_CONF_GPMC_A_R(6) = CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_TCTL (GPIO1_16/GPMC_A0)
+   CONTROL_CONF_GPMC_A_R(0) = CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_TD0 (GPIO1_21/GPMC_A5)
+   CONTROL_CONF_GPMC_A_R(5) = CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_TD1 (GPIO1_20/GPMC_A4)
+   CONTROL_CONF_GPMC_A_R(4) = CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_TD2 (GPIO1_19/GPMC_A3)
+   CONTROL_CONF_GPMC_A_R(3) = CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_TD3 (GPIO1_18/GPMC_A2)
+   CONTROL_CONF_GPMC_A_R(2) = CONTROL_CONF_MUXMODE(2);
+
+   //Configure RGMII2_RCLK (GPIO1_23/GPMC_A7)
+   CONTROL_CONF_GPMC_A_R(7) = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_RCTL (GPIO1_17/GPMC_A1)
+   CONTROL_CONF_GPMC_A_R(1) = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_RD0 (GPIO1_27/GPMC_A11)
+   CONTROL_CONF_GPMC_A_R(11) = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_RD1 (GPIO1_26/GPMC_A10)
+   CONTROL_CONF_GPMC_A_R(10) = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_RD2 (GPIO1_25/GPMC_A9)
+   CONTROL_CONF_GPMC_A_R(9) = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_RD3 (GPIO1_24/GPMC_A8)
+   CONTROL_CONF_GPMC_A_R(8) = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(2);
+
+   //Configure MDIO (GPIO0_0)
+   CONTROL_CONF_MDIO_DATA_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_PULLUPSEL |
+      CONTROL_CONF_MUXMODE(0);
+
+   //Configure MDC (GPIO0_1)
+   CONTROL_CONF_MDIO_CLK_R = CONTROL_CONF_PULLUPSEL | CONTROL_CONF_MUXMODE(0);
+
+//SBC DIVA board?
+#elif defined(USE_SBC_DIVA)
+   //Select RMII interface mode for port 1 and RGMII interface mode for port 2
+   CONTROL_GMII_SEL_R = CONTROL_GMII_SEL_RMII1_IO_CLK_EN |
+     CONTROL_GMII_SEL_GMII1_SEL_RMII | CONTROL_GMII_SEL_GMII2_SEL_RGMII;
+
+   //Configure RMII1_REF_CLK (GPIO0_29)
+   CONTROL_CONF_RMII1_REFCLK_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(0);
+
+   //Configure RMII1_TX_EN (GPIO3_3)
+   CONTROL_CONF_MII1_TXEN_R = CONTROL_CONF_MUXMODE(1);
+   //Configure RMII1_TXD0 (GPIO0_28)
+   CONTROL_CONF_MII1_TXD0_R = CONTROL_CONF_MUXMODE(1);
+   //Configure RMII1_TXD1 (GPIO0_21)
+   CONTROL_CONF_MII1_TXD1_R = CONTROL_CONF_MUXMODE(1);
+
+   //Configure RMII1_RXD0 (GPIO2.21)
+   CONTROL_CONF_MII1_RXD0_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(1);
+   //Configure RMII1_RXD1 (GPIO2.20)
+   CONTROL_CONF_MII1_RXD1_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(1);
+   //Configure RMII1_CRS_DV (GPIO3_1)
+   CONTROL_CONF_MII1_CRS_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(1);
+   //Configure RMII1_RX_ER (GPIO3_2)
+   CONTROL_CONF_MII1_RXERR_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(1);
+
+   //Configure RGMII2_TCLK (GPIO1_22/GPMC_A6)
+   CONTROL_CONF_GPMC_A_R(6) = CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_TCTL (GPIO1_16/GPMC_A0)
+   CONTROL_CONF_GPMC_A_R(0) = CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_TD0 (GPIO1_21/GPMC_A5)
+   CONTROL_CONF_GPMC_A_R(5) = CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_TD1 (GPIO1_20/GPMC_A4)
+   CONTROL_CONF_GPMC_A_R(4) = CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_TD2 (GPIO1_19/GPMC_A3)
+   CONTROL_CONF_GPMC_A_R(3) = CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_TD3 (GPIO1_18/GPMC_A2)
+   CONTROL_CONF_GPMC_A_R(2) = CONTROL_CONF_MUXMODE(2);
+
+   //Configure RGMII2_RCLK (GPIO1_23/GPMC_A7)
+   CONTROL_CONF_GPMC_A_R(7) = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_RCTL (GPIO1_17/GPMC_A1)
+   CONTROL_CONF_GPMC_A_R(1) = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_RD0 (GPIO1_27/GPMC_A11)
+   CONTROL_CONF_GPMC_A_R(11) = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_RD1 (GPIO1_26/GPMC_A10)
+   CONTROL_CONF_GPMC_A_R(10) = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_RD2 (GPIO1_25/GPMC_A9)
+   CONTROL_CONF_GPMC_A_R(9) = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(2);
+   //Configure RGMII2_RD3 (GPIO1_24/GPMC_A8)
+   CONTROL_CONF_GPMC_A_R(8) = CONTROL_CONF_RXACTIVE | CONTROL_CONF_MUXMODE(2);
+
+   //Configure MDIO (GPIO0_0)
+   CONTROL_CONF_MDIO_DATA_R = CONTROL_CONF_RXACTIVE | CONTROL_CONF_PULLUPSEL |
+      CONTROL_CONF_MUXMODE(0);
+
+   //Configure MDC (GPIO0_1)
+   CONTROL_CONF_MDIO_CLK_R = CONTROL_CONF_PULLUPSEL | CONTROL_CONF_MUXMODE(0);
+#endif
+}
+
+#endif
+
+
+/**
+ * @brief Initialize buffer descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void am335xEthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+   uint_t nextIndex;
+   uint_t prevIndex;
+
+   //Initialize TX buffer descriptor list (port 1)
+   for(i = 0; i < AM335X_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Index of the next buffer
+      nextIndex = (i + 1) % AM335X_ETH_TX_BUFFER_COUNT;
+      //Index of the previous buffer
+      prevIndex = (i + AM335X_ETH_TX_BUFFER_COUNT - 1) % AM335X_ETH_TX_BUFFER_COUNT;
+
+      //Next descriptor pointer
+      txBufferDesc1[i].word0 = (uint32_t) NULL;
+      //Buffer pointer
+      txBufferDesc1[i].word1 = (uint32_t) txBuffer1[i];
+      //Buffer offset and buffer length
+      txBufferDesc1[i].word2 = 0;
+      //Status flags and packet length
+      txBufferDesc1[i].word3 = 0;
+
+      //Form a doubly linked list
+      txBufferDesc1[i].next = &txBufferDesc1[nextIndex];
+      txBufferDesc1[i].prev = &txBufferDesc1[prevIndex];
+   }
+
+   //Point to the very first descriptor
+   txCurBufferDesc1 = &txBufferDesc1[0];
+
+   //Mark the end of the queue
+   txCurBufferDesc1->prev->word3 = CPSW_TX_WORD3_SOP |
+      CPSW_TX_WORD3_EOP | CPSW_TX_WORD3_EOQ;
+
+   //Initialize TX buffer descriptor list (port 2)
+   for(i = 0; i < AM335X_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Index of the next buffer
+      nextIndex = (i + 1) % AM335X_ETH_TX_BUFFER_COUNT;
+      //Index of the previous buffer
+      prevIndex = (i + AM335X_ETH_TX_BUFFER_COUNT - 1) % AM335X_ETH_TX_BUFFER_COUNT;
+
+      //Next descriptor pointer
+      txBufferDesc2[i].word0 = (uint32_t) NULL;
+      //Buffer pointer
+      txBufferDesc2[i].word1 = (uint32_t) txBuffer2[i];
+      //Buffer offset and buffer length
+      txBufferDesc2[i].word2 = 0;
+      //Status flags and packet length
+      txBufferDesc2[i].word3 = 0;
+
+      //Form a doubly linked list
+      txBufferDesc2[i].next = &txBufferDesc2[nextIndex];
+      txBufferDesc2[i].prev = &txBufferDesc2[prevIndex];
+   }
+
+   //Point to the very first descriptor
+   txCurBufferDesc2 = &txBufferDesc2[0];
+
+   //Mark the end of the queue
+   txCurBufferDesc2->prev->word3 = CPSW_TX_WORD3_SOP |
+      CPSW_TX_WORD3_EOP | CPSW_TX_WORD3_EOQ;
+
+   //Initialize RX buffer descriptor list
+   for(i = 0; i < AM335X_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Index of the next buffer
+      nextIndex = (i + 1) % AM335X_ETH_RX_BUFFER_COUNT;
+      //Index of the previous buffer
+      prevIndex = (i + AM335X_ETH_RX_BUFFER_COUNT - 1) % AM335X_ETH_RX_BUFFER_COUNT;
+
+      //Next descriptor pointer
+      rxBufferDesc[i].word0 = (uint32_t) &rxBufferDesc[nextIndex];
+      //Buffer pointer
+      rxBufferDesc[i].word1 = (uint32_t) rxBuffer[i];
+      //Buffer offset and buffer length
+      rxBufferDesc[i].word2 = AM335X_ETH_RX_BUFFER_SIZE;
+      //Status flags and packet length
+      rxBufferDesc[i].word3 = CPSW_RX_WORD3_OWNER;
+
+      //Form a doubly linked list
+      rxBufferDesc[i].next = &rxBufferDesc[nextIndex];
+      rxBufferDesc[i].prev = &rxBufferDesc[prevIndex];
+   }
+
+   //Point to the very first descriptor
+   rxCurBufferDesc = &rxBufferDesc[0];
+
+   //Mark the end of the queue
+   rxCurBufferDesc->prev->word0 = (uint32_t) NULL;
+
+   //Write the RX DMA head descriptor pointer
+   CPSW_CPDMA_RX_HDP_R(CPSW_CH0) = (uint32_t) rxCurBufferDesc;
+}
+
+
+/**
+ * @brief AM335x Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void am335xEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+
+   //Misqueued buffer condition?
+   if(rxCurBufferDesc->word3 & CPSW_RX_WORD3_OWNER)
+   {
+      if(CPSW_CPDMA_RX_HDP_R(CPSW_CH0) == 0)
+      {
+         //The host acts on the misqueued buffer condition by writing the added
+         //buffer descriptor address to the appropriate RX DMA head descriptor
+         //pointer
+         CPSW_CPDMA_RX_HDP_R(CPSW_CH0) = (uint32_t) rxCurBufferDesc;
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void am335xEthEnableIrq(NetInterface *interface)
+{
+#ifdef ti_sysbios_BIOS___VERS
+   //Enable Ethernet MAC interrupts
+   Hwi_enableInterrupt(SYS_INT_3PGSWTXINT0);
+   Hwi_enableInterrupt(SYS_INT_3PGSWRXINT0);
+#else
+   //Enable Ethernet MAC interrupts
+   IntSystemEnable(SYS_INT_3PGSWTXINT0);
+   IntSystemEnable(SYS_INT_3PGSWRXINT0);
+#endif
+
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void am335xEthDisableIrq(NetInterface *interface)
+{
+#ifdef ti_sysbios_BIOS___VERS
+   //Disable Ethernet MAC interrupts
+   Hwi_disableInterrupt(SYS_INT_3PGSWTXINT0);
+   Hwi_disableInterrupt(SYS_INT_3PGSWRXINT0);
+#else
+   //Disable Ethernet MAC interrupts
+   IntSystemDisable(SYS_INT_3PGSWTXINT0);
+   IntSystemDisable(SYS_INT_3PGSWRXINT0);
+#endif
+
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief Ethernet MAC transmit interrupt
+ **/
+
+void am335xEthTxIrqHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+   uint32_t temp;
+   Am335xTxBufferDesc *p;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read the TX_STAT register to determine which channels caused the interrupt
+   status = CPSW_WR_C_TX_STAT_R(CPSW_CORE0);
+
+   //Packet transmitted on channel 1?
+   if(status & (1 << CPSW_CH1))
+   {
+      //Point to the buffer descriptor
+      p = (Am335xTxBufferDesc *) CPSW_CPDMA_TX_CP_R(CPSW_CH1);
+
+      //Read the status flags
+      temp = p->word3 & (CPSW_TX_WORD3_SOP | CPSW_TX_WORD3_EOP |
+         CPSW_TX_WORD3_OWNER | CPSW_TX_WORD3_EOQ);
+
+      //Misqueued buffer condition?
+      if(temp == (CPSW_TX_WORD3_SOP | CPSW_TX_WORD3_EOP | CPSW_TX_WORD3_EOQ))
+      {
+         //Check whether the next descriptor pointer is non-zero
+         if(p->word0 != 0)
+         {
+            //The host corrects the misqueued buffer condition by writing the
+            //misqueued packet’s buffer descriptor address to the appropriate
+            //TX DMA head descriptor pointer
+            CPSW_CPDMA_TX_HDP_R(CPSW_CH1) = (uint32_t) p->word0;
+         }
+      }
+
+      //Write the TX completion pointer
+      CPSW_CPDMA_TX_CP_R(CPSW_CH1) = (uint32_t) p;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurBufferDesc1->word3 & CPSW_TX_WORD3_OWNER))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface1->nicTxEvent);
+      }
+   }
+
+   //Packet transmitted on channel 2?
+   if(status & (1 << CPSW_CH2))
+   {
+      //Point to the buffer descriptor
+      p = (Am335xTxBufferDesc *) CPSW_CPDMA_TX_CP_R(CPSW_CH2);
+
+      //Read the status flags
+      temp = p->word3 & (CPSW_TX_WORD3_SOP | CPSW_TX_WORD3_EOP |
+         CPSW_TX_WORD3_OWNER | CPSW_TX_WORD3_EOQ);
+
+      //Misqueued buffer condition?
+      if(temp == (CPSW_TX_WORD3_SOP | CPSW_TX_WORD3_EOP | CPSW_TX_WORD3_EOQ))
+      {
+         //Check whether the next descriptor pointer is non-zero
+         if(p->word0 != 0)
+         {
+            //The host corrects the misqueued buffer condition by writing the
+            //misqueued packet’s buffer descriptor address to the appropriate
+            //TX DMA head descriptor pointer
+            CPSW_CPDMA_TX_HDP_R(CPSW_CH2) = (uint32_t) p->word0;
+         }
+      }
+
+      //Write the TX completion pointer
+      CPSW_CPDMA_TX_CP_R(CPSW_CH2) = (uint32_t) p;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurBufferDesc2->word3 & CPSW_TX_WORD3_OWNER))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface2->nicTxEvent);
+      }
+   }
+
+   //Writes the DMA end of interrupt vector
+   CPSW_CPDMA_CPDMA_EOI_VECTOR_R = CPSW_CPDMA_EOI_VECTOR_TX_PULSE;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief Ethernet MAC receive interrupt
+ **/
+
+void am335xEthRxIrqHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read the RX_STAT register to determine which channels caused the interrupt
+   status = CPSW_WR_C_RX_STAT_R(CPSW_CORE0);
+
+   //Packet received on channel 0?
+   if(status & (1 << CPSW_CH0))
+   {
+      //Disable RX interrupts
+      CPSW_WR_C_RX_EN_R(CPSW_CORE0) &= ~(1 << CPSW_CH0);
+
+      //Set event flag
+      if(nicDriverInterface1 != NULL)
+         nicDriverInterface1->nicEvent = TRUE;
+      else if(nicDriverInterface2 != NULL)
+         nicDriverInterface2->nicEvent = TRUE;
+
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Writes the DMA end of interrupt vector
+   CPSW_CPDMA_CPDMA_EOI_VECTOR_R = CPSW_CPDMA_EOI_VECTOR_RX_PULSE;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief AM335x Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void am335xEthEventHandler(NetInterface *interface)
+{
+   static uint8_t buffer[AM335X_ETH_RX_BUFFER_SIZE];
+   error_t error;
+   size_t n;
+   uint32_t temp;
+
+   //Process all pending packets
+   do
+   {
+      //The current buffer is available for reading?
+      if(!(rxCurBufferDesc->word3 & CPSW_RX_WORD3_OWNER))
+      {
+         //SOP and EOP flags should be set
+         if((rxCurBufferDesc->word3 & CPSW_RX_WORD3_SOP) &&
+            (rxCurBufferDesc->word3 & CPSW_RX_WORD3_EOP))
+         {
+            //Make sure no error occurred
+            if(!(rxCurBufferDesc->word3 & CPSW_RX_WORD3_PKT_ERROR))
+            {
+               //Check the port on which the packet was received
+               switch(rxCurBufferDesc->word3 & CPSW_RX_WORD3_FROM_PORT)
+               {
+               //Port 1?
+               case CPSW_RX_WORD3_FROM_PORT_1:
+                  interface = nicDriverInterface1;
+                  break;
+               //Port 1?
+               case CPSW_RX_WORD3_FROM_PORT_2:
+                  interface = nicDriverInterface2;
+                  break;
+               //Invalid port number?
+               default:
+                  interface = NULL;
+                  break;
+               }
+
+               //Retrieve the length of the frame
+               n = rxCurBufferDesc->word3 & CPSW_RX_WORD3_PACKET_LENGTH;
+               //Limit the number of data to read
+               n = MIN(n, AM335X_ETH_RX_BUFFER_SIZE);
+
+               //Sanity check
+               if(interface != NULL)
+               {
+                  //Copy data from the receive buffer
+                  memcpy(buffer, (uint8_t *) rxCurBufferDesc->word1, n);
+
+                  //Packet successfully received
+                  error = NO_ERROR;
+               }
+               else
+               {
+                  //The port number is invalid
+                  error = ERROR_INVALID_PACKET;
+               }
+            }
+            else
+            {
+               //The received packet contains an error
+               error = ERROR_INVALID_PACKET;
+            }
+         }
+         else
+         {
+            //The packet is not valid
+            error = ERROR_INVALID_PACKET;
+         }
+
+         //Mark the end of the queue with a NULL pointer
+         rxCurBufferDesc->word0 = (uint32_t) NULL;
+         //Restore the length of the buffer
+         rxCurBufferDesc->word2 = AM335X_ETH_RX_BUFFER_SIZE;
+         //Give the ownership of the descriptor back to the DMA
+         rxCurBufferDesc->word3 = CPSW_RX_WORD3_OWNER;
+
+         //Link the current descriptor to the previous descriptor
+         rxCurBufferDesc->prev->word0 = (uint32_t) rxCurBufferDesc;
+
+         //Read the status flags of the previous descriptor
+         temp = rxCurBufferDesc->prev->word3 & (CPSW_RX_WORD3_SOP |
+            CPSW_RX_WORD3_EOP | CPSW_RX_WORD3_OWNER | CPSW_RX_WORD3_EOQ);
+
+         //Misqueued buffer condition?
+         if(temp == (CPSW_RX_WORD3_SOP | CPSW_RX_WORD3_EOP | CPSW_RX_WORD3_EOQ))
+         {
+            //The host acts on the misqueued buffer condition by writing the added
+            //buffer descriptor address to the appropriate RX DMA head descriptor
+            //pointer
+            CPSW_CPDMA_RX_HDP_R(CPSW_CH0) = (uint32_t) rxCurBufferDesc;
+         }
+
+         //Write the RX completion pointer
+         CPSW_CPDMA_RX_CP_R(CPSW_CH0) = (uint32_t) rxCurBufferDesc;
+
+         //Point to the next descriptor in the list
+         rxCurBufferDesc = rxCurBufferDesc->next;
+      }
+      else
+      {
+         //No more data in the receive buffer
+         error = ERROR_BUFFER_EMPTY;
+      }
+
+      //Check whether a valid packet has been received
+      if(!error)
+      {
+         //Pass the packet to the upper layer
+         nicProcessPacket(interface, buffer, n);
+      }
+
+      //No more data in the receive buffer?
+   } while(error != ERROR_BUFFER_EMPTY);
+
+   //Re-enable RX interrupts
+   CPSW_WR_C_RX_EN_R(CPSW_CORE0) |= (1 << CPSW_CH0);
+}
+
+
+/**
+ * @brief Send a packet (port 1)
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t am335xEthSendPacketPort1(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+   uint32_t temp;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > AM335X_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurBufferDesc1->word3 & CPSW_TX_WORD3_OWNER)
+      return ERROR_FAILURE;
+
+   //Mark the end of the queue with a NULL pointer
+   txCurBufferDesc1->word0 = (uint32_t) NULL;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txCurBufferDesc1->word1, buffer, offset, length);
+
+   //Set the length of the buffer
+   txCurBufferDesc1->word2 = length & CPSW_TX_WORD2_BUFFER_LENGTH;
+
+   //Set the length of the packet
+   temp = length & CPSW_TX_WORD3_PACKET_LENGTH;
+   //Set SOP and EOP flags as the data fits in a single buffer
+   temp |= CPSW_TX_WORD3_SOP | CPSW_TX_WORD3_EOP;
+   //Redirect the packet to the relevant port number
+   temp |= CPSW_TX_WORD3_TO_PORT_EN | CPSW_TX_WORD3_TO_PORT_1;
+
+   //Give the ownership of the descriptor to the DMA
+   txCurBufferDesc1->word3 = CPSW_TX_WORD3_OWNER | temp;
+
+   //Link the current descriptor to the previous descriptor
+   txCurBufferDesc1->prev->word0 = (uint32_t) txCurBufferDesc1;
+
+   //Read the status flags of the previous descriptor
+   temp = txCurBufferDesc1->prev->word3 & (CPSW_TX_WORD3_SOP |
+      CPSW_TX_WORD3_EOP | CPSW_TX_WORD3_OWNER | CPSW_TX_WORD3_EOQ);
+
+   //Misqueued buffer condition?
+   if(temp == (CPSW_TX_WORD3_SOP | CPSW_TX_WORD3_EOP | CPSW_TX_WORD3_EOQ))
+   {
+      //Clear the misqueued buffer condition
+      txCurBufferDesc1->prev->word3 = 0;
+
+      //The host corrects the misqueued buffer condition by writing the
+      //misqueued packet’s buffer descriptor address to the appropriate
+      //TX DMA head descriptor pointer
+      CPSW_CPDMA_TX_HDP_R(CPSW_CH1) = (uint32_t) txCurBufferDesc1;
+   }
+
+   //Point to the next descriptor in the list
+   txCurBufferDesc1 = txCurBufferDesc1->next;
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurBufferDesc1->word3 & CPSW_TX_WORD3_OWNER))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Send a packet (port 2)
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t am335xEthSendPacketPort2(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+   uint32_t temp;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > AM335X_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurBufferDesc2->word3 & CPSW_TX_WORD3_OWNER)
+      return ERROR_FAILURE;
+
+   //Mark the end of the queue with a NULL pointer
+   txCurBufferDesc2->word0 = (uint32_t) NULL;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txCurBufferDesc2->word1, buffer, offset, length);
+
+   //Set the length of the buffer
+   txCurBufferDesc2->word2 = length & CPSW_TX_WORD2_BUFFER_LENGTH;
+
+   //Set the length of the packet
+   temp = length & CPSW_TX_WORD3_PACKET_LENGTH;
+   //Set SOP and EOP flags as the data fits in a single buffer
+   temp |= CPSW_TX_WORD3_SOP | CPSW_TX_WORD3_EOP;
+   //Redirect the packet to the relevant port number
+   temp |= CPSW_TX_WORD3_TO_PORT_EN | CPSW_TX_WORD3_TO_PORT_2;
+
+   //Give the ownership of the descriptor to the DMA
+   txCurBufferDesc2->word3 = CPSW_TX_WORD3_OWNER | temp;
+
+   //Link the current descriptor to the previous descriptor
+   txCurBufferDesc2->prev->word0 = (uint32_t) txCurBufferDesc2;
+
+   //Read the status flags of the previous descriptor
+   temp = txCurBufferDesc2->prev->word3 & (CPSW_TX_WORD3_SOP |
+      CPSW_TX_WORD3_EOP | CPSW_TX_WORD3_OWNER | CPSW_TX_WORD3_EOQ);
+
+   //Misqueued buffer condition?
+   if(temp == (CPSW_TX_WORD3_SOP | CPSW_TX_WORD3_EOP | CPSW_TX_WORD3_EOQ))
+   {
+      //Clear the misqueued buffer condition
+      txCurBufferDesc2->prev->word3 = 0;
+
+      //The host corrects the misqueued buffer condition by writing the
+      //misqueued packet’s buffer descriptor address to the appropriate
+      //TX DMA head descriptor pointer
+      CPSW_CPDMA_TX_HDP_R(CPSW_CH2) = (uint32_t) txCurBufferDesc2;
+   }
+
+   //Point to the next descriptor in the list
+   txCurBufferDesc2 = txCurBufferDesc2->next;
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurBufferDesc2->word3 & CPSW_TX_WORD3_OWNER))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t am335xEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t port;
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating AM335x ALE table...\r\n");
+
+   //Select the relevant port number
+   if(interface == nicDriverInterface1)
+      port = CPSW_PORT1;
+   else if(interface == nicDriverInterface2)
+      port = CPSW_PORT2;
+   else
+      port = CPSW_PORT0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Check whether the ALE table should be updated for the
+      //current multicast address
+      if(!macCompAddr(&entry->addr, &MAC_UNSPECIFIED_ADDR))
+      {
+         if(entry->addFlag)
+         {
+            //Add VLAN/multicast address entry to the ALE table
+            am335xEthAddVlanAddrEntry(port, port, &entry->addr);
+         }
+         else if(entry->deleteFlag)
+         {
+            //Remove VLAN/multicast address entry from the ALE table
+            am335xEthDeleteVlanAddrEntry(port, port, &entry->addr);
+         }
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t am335xEthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config = 0;
+
+   //Read MAC control register
+   if(interface == nicDriverInterface1)
+      config = CPSW_SL1_MACCONTROL_R;
+   else if(interface == nicDriverInterface2)
+      config = CPSW_SL2_MACCONTROL_R;
+
+   //1000BASE-T operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_1GBPS)
+   {
+      config |= CPSW_SL_MACCONTROL_GIG;
+      config &= ~(CPSW_SL_MACCONTROL_IFCTL_A | CPSW_SL_MACCONTROL_IFCTL_B);
+   }
+   //100BASE-TX operation mode?
+   else if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+   {
+      config &= ~CPSW_SL_MACCONTROL_GIG;
+      config |= CPSW_SL_MACCONTROL_IFCTL_A | CPSW_SL_MACCONTROL_IFCTL_B;
+   }
+   //10BASE-T operation mode?
+   else
+   {
+      config &= ~CPSW_SL_MACCONTROL_GIG;
+      config &= ~(CPSW_SL_MACCONTROL_IFCTL_A | CPSW_SL_MACCONTROL_IFCTL_B);
+   }
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= CPSW_SL_MACCONTROL_FULLDUPLEX;
+   else
+      config &= ~CPSW_SL_MACCONTROL_FULLDUPLEX;
+
+   //Update MAC control register
+   if(interface == nicDriverInterface1)
+      CPSW_SL1_MACCONTROL_R = config;
+   else if(interface == nicDriverInterface2)
+      CPSW_SL2_MACCONTROL_R = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void am335xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Set up a write operation
+   value = MDIO_USERACCESS0_GO | MDIO_USERACCESS0_WRITE;
+   //PHY address
+   value |= (phyAddr << MDIO_USERACCESS0_PHYADR_SHIFT) & MDIO_USERACCESS0_PHYADR;
+   //Register address
+   value |= (regAddr << MDIO_USERACCESS0_REGADR_SHIFT) & MDIO_USERACCESS0_REGADR;
+   //Register value
+   value |= data & MDIO_USERACCESS0_DATA;
+
+   //Start a write operation
+   MDIO_USERACCESS0_R = value;
+   //Wait for the write to complete
+   while(MDIO_USERACCESS0_R & MDIO_USERACCESS0_GO);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t am335xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Set up a read operation
+   value = MDIO_USERACCESS0_GO | MDIO_USERACCESS0_READ;
+   //PHY address
+   value |= (phyAddr << MDIO_USERACCESS0_PHYADR_SHIFT) & MDIO_USERACCESS0_PHYADR;
+   //Register address
+   value |= (regAddr << MDIO_USERACCESS0_REGADR_SHIFT) & MDIO_USERACCESS0_REGADR;
+
+   //Start a read operation
+   MDIO_USERACCESS0_R = value;
+   //Wait for the read to complete
+   while(MDIO_USERACCESS0_R & MDIO_USERACCESS0_GO);
+
+   //Return PHY register contents
+   return MDIO_USERACCESS0_R & MDIO_USERACCESS0_DATA;
+}
+
+
+/**
+ * @brief Write an ALE table entry
+ * @param[in] index Entry index
+ * @param[in] entry Pointer to the ALE table entry
+ **/
+
+void am335xEthWriteEntry(uint_t index, const Am335xAleEntry *entry)
+{
+   //Copy the content of the entry to be written
+   CPSW_ALE_TBLW_R(2) = entry->word2;
+   CPSW_ALE_TBLW_R(1) = entry->word1;
+   CPSW_ALE_TBLW_R(0) = entry->word0;
+
+   //Write the ALE entry at the specified index
+   CPSW_ALE_TBLCTL_R = CPSW_ALE_TBLCTL_WRITE_RDZ | index;
+}
+
+
+/**
+ * @brief Read an ALE table entry
+ * @param[in] index Entry index
+ * @param[out] entry Pointer to the ALE table entry
+ **/
+
+void am335xEthReadEntry(uint_t index, Am335xAleEntry *entry)
+{
+   //Read the ALE entry at the specified index
+   CPSW_ALE_TBLCTL_R = index;
+
+   //Copy the content of the entry
+   entry->word2 = CPSW_ALE_TBLW_R(2);
+   entry->word1 = CPSW_ALE_TBLW_R(1);
+   entry->word0 = CPSW_ALE_TBLW_R(0);
+}
+
+
+/**
+ * @brief Find a free entry in the ALE table
+ * @return Index of the first free entry
+ **/
+
+uint_t am335xEthFindFreeEntry(void)
+{
+   uint_t index;
+   uint32_t type;
+   Am335xAleEntry entry;
+
+   //Loop through the ALE table entries
+   for(index = 0; index < CPSW_ALE_MAX_ENTRIES; index++)
+   {
+      //Read the current entry
+      am335xEthReadEntry(index, &entry);
+
+      //Retrieve the type of the ALE entry
+      type = entry.word1 & CPSW_ALE_WORD1_ENTRY_TYPE_MASK;
+
+      //Free entry?
+      if(type == CPSW_ALE_WORD1_ENTRY_TYPE_FREE)
+      {
+         //Exit immediately
+         break;
+      }
+   }
+
+   //Return the index of the entry
+   return index;
+}
+
+
+/**
+ * @brief Search the ALE table for the specified VLAN entry
+ * @param[in] vlanId VLAN identifier
+ * @return Index of the matching entry
+ **/
+
+uint_t am335xEthFindVlanEntry(uint_t vlanId)
+{
+   uint_t index;
+   uint32_t value;
+   Am335xAleEntry entry;
+
+   //Loop through the ALE table entries
+   for(index = 0; index < CPSW_ALE_MAX_ENTRIES; index++)
+   {
+      //Read the current entry
+      am335xEthReadEntry(index, &entry);
+
+      //Retrieve the type of the ALE entry
+      value = entry.word1 & CPSW_ALE_WORD1_ENTRY_TYPE_MASK;
+
+      //Check the type of the ALE entry
+      if(value == CPSW_ALE_WORD1_ENTRY_TYPE_VLAN_ADDR)
+      {
+         //Get the VLAN identifier
+         value = entry.word1 & CPSW_ALE_WORD1_VLAN_ID_MASK;
+
+         //Compare the VLAN identifier
+         if(value == CPSW_ALE_WORD1_VLAN_ID(vlanId))
+         {
+            //Matching ALE entry found
+            break;
+         }
+      }
+   }
+
+   //Return the index of the entry
+   return index;
+}
+
+
+/**
+ * @brief Search the ALE table for the specified VLAN/address entry
+ * @param[in] vlanId VLAN identifier
+ * @param[in] macAddr MAC address
+ * @return Index of the matching entry
+ **/
+
+uint_t am335xEthFindVlanAddrEntry(uint_t vlanId, MacAddr *macAddr)
+{
+   uint_t index;
+   uint32_t value;
+   Am335xAleEntry entry;
+
+   //Loop through the ALE table entries
+   for(index = 0; index < CPSW_ALE_MAX_ENTRIES; index++)
+   {
+      //Read the current entry
+      am335xEthReadEntry(index, &entry);
+
+      //Retrieve the type of the ALE entry
+      value = entry.word1 & CPSW_ALE_WORD1_ENTRY_TYPE_MASK;
+
+      //Check the type of the ALE entry
+      if(value == CPSW_ALE_WORD1_ENTRY_TYPE_VLAN_ADDR)
+      {
+         //Get the VLAN identifier
+         value = entry.word1 & CPSW_ALE_WORD1_VLAN_ID_MASK;
+
+         //Compare the VLAN identifier
+         if(value == CPSW_ALE_WORD1_VLAN_ID(vlanId))
+         {
+            //Compare the MAC address
+            if(macAddr->b[0] == (uint8_t) (entry.word1 >> 8) &&
+               macAddr->b[1] == (uint8_t) (entry.word1 >> 0) &&
+               macAddr->b[2] == (uint8_t) (entry.word0 >> 24) &&
+               macAddr->b[3] == (uint8_t) (entry.word0 >> 16) &&
+               macAddr->b[4] == (uint8_t) (entry.word0 >> 8) &&
+               macAddr->b[5] == (uint8_t) (entry.word0 >> 0))
+            {
+               //Matching ALE entry found
+               break;
+            }
+         }
+      }
+   }
+
+   //Return the index of the entry
+   return index;
+}
+
+
+/**
+ * @brief Add a VLAN entry in the ALE table
+ * @param[in] port Port number
+ * @param[in] vlanId VLAN identifier
+ * @return Error code
+ **/
+
+error_t am335xEthAddVlanEntry(uint_t port, uint_t vlanId)
+{
+   error_t error;
+   uint_t index;
+   Am335xAleEntry entry;
+
+   //Ensure that there are not duplicate address entries in the ALE table
+   index = am335xEthFindVlanEntry(vlanId);
+
+   //No matching entry found?
+   if(index >= CPSW_ALE_MAX_ENTRIES)
+   {
+      //Find a free entry in the ALE table
+      index = am335xEthFindFreeEntry();
+   }
+
+   //Sanity check
+   if(index < CPSW_ALE_MAX_ENTRIES)
+   {
+      //Set up a VLAN table entry
+      entry.word2 = 0;
+      entry.word1 = CPSW_ALE_WORD1_ENTRY_TYPE_VLAN;
+      entry.word0 = 0;
+
+      //Set VLAN identifier
+      entry.word1 |= CPSW_ALE_WORD1_VLAN_ID(vlanId);
+
+      //Force the packet VLAN tag to be removed on egress
+      entry.word0 |= CPSW_ALE_WORD0_FORCE_UNTAG_EGRESS(1 << port) |
+         CPSW_ALE_WORD0_FORCE_UNTAG_EGRESS(1 << CPSW_PORT0);
+
+      //Set VLAN member list
+      entry.word0 |= CPSW_ALE_WORD0_VLAN_MEMBER_LIST(1 << port) |
+         CPSW_ALE_WORD0_VLAN_MEMBER_LIST(1 << CPSW_PORT0);
+
+      //Add a new entry to the ALE table
+      am335xEthWriteEntry(index, &entry);
+
+      //Sucessful processing
+      error = NO_ERROR;
+   }
+   else
+   {
+      //The ALE table is full
+      error = ERROR_FAILURE;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Add a VLAN/address entry in the ALE table
+ * @param[in] port Port number
+ * @param[in] vlanId VLAN identifier
+ * @param[in] macAddr MAC address
+ * @return Error code
+ **/
+
+error_t am335xEthAddVlanAddrEntry(uint_t port, uint_t vlanId, MacAddr *macAddr)
+{
+   error_t error;
+   uint_t index;
+   Am335xAleEntry entry;
+
+   //Ensure that there are not duplicate address entries in the ALE table
+   index = am335xEthFindVlanAddrEntry(vlanId, macAddr);
+
+   //No matching entry found?
+   if(index >= CPSW_ALE_MAX_ENTRIES)
+   {
+      //Find a free entry in the ALE table
+      index = am335xEthFindFreeEntry();
+   }
+
+   //Sanity check
+   if(index < CPSW_ALE_MAX_ENTRIES)
+   {
+      //Set up a VLAN/address table entry
+      entry.word2 = 0;
+      entry.word1 = CPSW_ALE_WORD1_ENTRY_TYPE_VLAN_ADDR;
+      entry.word0 = 0;
+
+      //Multicast address?
+      if(macIsMulticastAddr(macAddr))
+      {
+         //Set port mask
+         entry.word2 |= CPSW_ALE_WORD2_SUPER |
+            CPSW_ALE_WORD2_PORT_LIST(1 << port) |
+            CPSW_ALE_WORD2_PORT_LIST(1 << CPSW_CH0);
+
+         //Set multicast forward state
+         entry.word1 |= CPSW_ALE_WORD1_MCAST_FWD_STATE(0);
+      }
+
+      //Set VLAN identifier
+      entry.word1 |= CPSW_ALE_WORD1_VLAN_ID(vlanId);
+
+      //Copy the upper 16 bits of the unicast address
+      entry.word1 |= (macAddr->b[0] << 8) | macAddr->b[1];
+
+      //Copy the lower 32 bits of the unicast address
+      entry.word0 |= (macAddr->b[2] << 24) | (macAddr->b[3] << 16) |
+         (macAddr->b[4] << 8) | macAddr->b[5];
+
+      //Add a new entry to the ALE table
+      am335xEthWriteEntry(index, &entry);
+
+      //Sucessful processing
+      error = NO_ERROR;
+   }
+   else
+   {
+      //The ALE table is full
+      error = ERROR_FAILURE;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Remove a VLAN/address entry from the ALE table
+ * @param[in] port Port number
+ * @param[in] vlanId VLAN identifier
+ * @param[in] macAddr MAC address
+ * @return Error code
+ **/
+
+error_t am335xEthDeleteVlanAddrEntry(uint_t port, uint_t vlanId, MacAddr *macAddr)
+{
+   error_t error;
+   uint_t index;
+   Am335xAleEntry entry;
+
+   //Search the ALE table for the specified VLAN/address entry
+   index = am335xEthFindVlanAddrEntry(vlanId, macAddr);
+
+   //Matching ALE entry found?
+   if(index < CPSW_ALE_MAX_ENTRIES)
+   {
+      //Clear the contents of the entry
+      entry.word2 = 0;
+      entry.word1 = 0;
+      entry.word0 = 0;
+
+      //Update the ALE table
+      am335xEthWriteEntry(index, &entry);
+
+      //Sucessful processing
+      error = NO_ERROR;
+   }
+   else
+   {
+      //Entry not found
+      error = ERROR_NOT_FOUND;
+   }
+
+   //Return status code
+   return error;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/am335x_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,461 @@
+/**
+ * @file am335x_eth.h
+ * @brief Sitara AM335x Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _AM335X_ETH_H
+#define _AM335X_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef AM335X_ETH_TX_BUFFER_COUNT
+   #define AM335X_ETH_TX_BUFFER_COUNT 16
+#elif (AM335X_ETH_TX_BUFFER_COUNT < 1)
+   #error AM335X_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef AM335X_ETH_TX_BUFFER_SIZE
+   #define AM335X_ETH_TX_BUFFER_SIZE 1536
+#elif (AM335X_ETH_TX_BUFFER_SIZE != 1536)
+   #error AM335X_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef AM335X_ETH_RX_BUFFER_COUNT
+   #define AM335X_ETH_RX_BUFFER_COUNT 16
+#elif (AM335X_ETH_RX_BUFFER_COUNT < 1)
+   #error AM335X_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef AM335X_ETH_RX_BUFFER_SIZE
+   #define AM335X_ETH_RX_BUFFER_SIZE 1536
+#elif (AM335X_ETH_RX_BUFFER_SIZE != 1536)
+   #error AM335X_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Ethernet interrupt priority
+#ifndef AM335X_ETH_IRQ_PRIORITY
+   #define AM335X_ETH_IRQ_PRIORITY 1
+#elif (AM335X_ETH_IRQ_PRIORITY < 0)
+   #error AM335X_ETH_IRQ_PRIORITY parameter is not valid
+#endif
+
+//CPSW cores
+#define CPSW_CORE0 0
+#define CPSW_CORE1 1
+#define CPSW_CORE2 2
+
+//CPSW ports
+#define CPSW_PORT0 0
+#define CPSW_PORT1 1
+#define CPSW_PORT2 2
+
+//CPSW channels
+#define CPSW_CH0 0
+#define CPSW_CH1 1
+#define CPSW_CH2 2
+#define CPSW_CH3 3
+#define CPSW_CH4 4
+#define CPSW_CH5 5
+#define CPSW_CH6 6
+#define CPSW_CH7 7
+
+//PRCM registers
+#define CM_PER_CPGMAC0_CLKCTRL_R          HWREG(SOC_PRCM_REGS + CM_PER_CPGMAC0_CLKCTRL)
+#define CM_PER_CPSW_CLKSTCTRL_R           HWREG(SOC_PRCM_REGS + CM_PER_CPSW_CLKSTCTRL)
+
+//CONTROL registers
+#define CONTROL_MAC_ID_LO_R(n)            HWREG(SOC_CONTROL_REGS + CONTROL_MAC_ID_LO(n))
+#define CONTROL_MAC_ID_HI_R(n)            HWREG(SOC_CONTROL_REGS + CONTROL_MAC_ID_HI(n))
+#define CONTROL_GMII_SEL_R                HWREG(SOC_CONTROL_REGS + CONTROL_GMII_SEL)
+#define CONTROL_CONF_GPMC_A_R(n)          HWREG(SOC_CONTROL_REGS + CONTROL_CONF_GPMC_A(n))
+#define CONTROL_CONF_MII1_COL_R           HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_COL)
+#define CONTROL_CONF_MII1_CRS_R           HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_CRS)
+#define CONTROL_CONF_MII1_RXERR_R         HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_RXERR)
+#define CONTROL_CONF_MII1_TXEN_R          HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_TXEN)
+#define CONTROL_CONF_MII1_RXDV_R          HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_RXDV)
+#define CONTROL_CONF_MII1_TXD3_R          HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_TXD3)
+#define CONTROL_CONF_MII1_TXD2_R          HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_TXD2)
+#define CONTROL_CONF_MII1_TXD1_R          HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_TXD1)
+#define CONTROL_CONF_MII1_TXD0_R          HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_TXD0)
+#define CONTROL_CONF_MII1_TXCLK_R         HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_TXCLK)
+#define CONTROL_CONF_MII1_RXCLK_R         HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_RXCLK)
+#define CONTROL_CONF_MII1_RXD3_R          HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_RXD3)
+#define CONTROL_CONF_MII1_RXD2_R          HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_RXD2)
+#define CONTROL_CONF_MII1_RXD1_R          HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_RXD1)
+#define CONTROL_CONF_MII1_RXD0_R          HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_RXD0)
+#define CONTROL_CONF_RMII1_REFCLK_R       HWREG(SOC_CONTROL_REGS + CONTROL_CONF_RMII1_REFCLK)
+#define CONTROL_CONF_MDIO_DATA_R          HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MDIO_DATA)
+#define CONTROL_CONF_MDIO_CLK_R           HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MDIO_CLK)
+
+//CPSW_ALE registers
+#define CPSW_ALE_IDVER_R                  HWREG(SOC_CPSW_ALE_REGS + CPSW_ALE_IDVER)
+#define CPSW_ALE_CONTROL_R                HWREG(SOC_CPSW_ALE_REGS + CPSW_ALE_CONTROL)
+#define CPSW_ALE_PRESCALE_R               HWREG(SOC_CPSW_ALE_REGS + CPSW_ALE_PRESCALE)
+#define CPSW_ALE_UNKNOWN_VLAN_R           HWREG(SOC_CPSW_ALE_REGS + CPSW_ALE_UNKNOWN_VLAN)
+#define CPSW_ALE_TBLCTL_R                 HWREG(SOC_CPSW_ALE_REGS + CPSW_ALE_TBLCTL)
+#define CPSW_ALE_TBLW_R(n)                HWREG(SOC_CPSW_ALE_REGS + CPSW_ALE_TBLW(n))
+#define CPSW_ALE_PORTCTL_R(n)             HWREG(SOC_CPSW_ALE_REGS + CPSW_ALE_PORTCTL(n))
+
+//CPSW_CPDMA registers
+#define CPSW_CPDMA_TX_IDVER_R             HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_TX_IDVER)
+#define CPSW_CPDMA_TX_CONTROL_R           HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_TX_CONTROL)
+#define CPSW_CPDMA_TX_TEARDOWN_R          HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_TX_TEARDOWN)
+#define CPSW_CPDMA_RX_IDVER_R             HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_RX_IDVER)
+#define CPSW_CPDMA_RX_CONTROL_R           HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_RX_CONTROL)
+#define CPSW_CPDMA_RX_TEARDOWN_R          HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_RX_TEARDOWN)
+#define CPSW_CPDMA_CPDMA_SOFT_RESET_R     HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_CPDMA_SOFT_RESET)
+#define CPSW_CPDMA_DMACONTROL_R           HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_DMACONTROL)
+#define CPSW_CPDMA_DMASTATUS_R            HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_DMASTATUS)
+#define CPSW_CPDMA_RX_BUFFER_OFFSET_R     HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_RX_BUFFER_OFFSET)
+#define CPSW_CPDMA_EMCONTROL_R            HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_EMCONTROL)
+#define CPSW_CPDMA_TX_PRI_RATE_R(n)       HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_TX_PRI_RATE(n))
+#define CPSW_CPDMA_TX_INTSTAT_RAW_R       HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_TX_INTSTAT_RAW)
+#define CPSW_CPDMA_TX_INTSTAT_MASKED_R    HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_TX_INTSTAT_MASKED)
+#define CPSW_CPDMA_TX_INTMASK_SET_R       HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_TX_INTMASK_SET)
+#define CPSW_CPDMA_TX_INTMASK_CLEAR_R     HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_TX_INTMASK_CLEAR)
+#define CPSW_CPDMA_CPDMA_IN_VECTOR_R      HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_CPDMA_IN_VECTOR)
+#define CPSW_CPDMA_CPDMA_EOI_VECTOR_R     HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_CPDMA_EOI_VECTOR)
+#define CPSW_CPDMA_RX_INTSTAT_RAW_R       HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_RX_INTSTAT_RAW)
+#define CPSW_CPDMA_RX_INTSTAT_MASKED_R    HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_RX_INTSTAT_MASKED)
+#define CPSW_CPDMA_RX_INTMASK_SET_R       HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_RX_INTMASK_SET)
+#define CPSW_CPDMA_RX_INTMASK_CLEAR_R     HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_RX_INTMASK_CLEAR)
+#define CPSW_CPDMA_DMA_INTSTAT_RAW_R      HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_DMA_INTSTAT_RAW)
+#define CPSW_CPDMA_DMA_INTSTAT_MASKED_R   HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_DMA_INTSTAT_MASKED)
+#define CPSW_CPDMA_DMA_INTMASK_SET_R      HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_DMA_INTMASK_SET)
+#define CPSW_CPDMA_DMA_INTMASK_CLEAR_R    HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_DMA_INTMASK_CLEAR)
+#define CPSW_CPDMA_RX_PENDTHRESH_R(n)     HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_RX_PENDTHRESH(n))
+#define CPSW_CPDMA_RX_FREEBUFFER_R(n)     HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_RX_FREEBUFFER(n))
+#define CPSW_CPDMA_TX_HDP_R(n)            HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_TX_HDP(n))
+#define CPSW_CPDMA_RX_HDP_R(n)            HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_RX_HDP(n))
+#define CPSW_CPDMA_TX_CP_R(n)             HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_TX_CP(n))
+#define CPSW_CPDMA_RX_CP_R(n)             HWREG(SOC_CPSW_CPDMA_REGS + CPSW_CPDMA_RX_CP(n))
+
+//CPSW_PORT registers
+#define CPSW_PORT0_CONTROL_R              HWREG(SOC_CPSW_PORT_0_REGS + CPSW_PORT_CONTROL)
+#define CPSW_PORT0_MAX_BLKS_R             HWREG(SOC_CPSW_PORT_0_REGS + CPSW_PORT_MAX_BLKS)
+#define CPSW_PORT0_BLK_CNT_R              HWREG(SOC_CPSW_PORT_0_REGS + CPSW_PORT_BLK_CNT)
+#define CPSW_PORT0_TX_IN_CTL_R            HWREG(SOC_CPSW_PORT_0_REGS + CPSW_PORT_TX_IN_CTL)
+#define CPSW_PORT0_PORT_VLAN_R            HWREG(SOC_CPSW_PORT_0_REGS + CPSW_PORT_PORT_VLAN)
+#define CPSW_PORT0_TX_PRI_MAP_R           HWREG(SOC_CPSW_PORT_0_REGS + CPSW_PORT_TX_PRI_MAP)
+#define CPSW_PORT0_CPDMA_TX_PRI_MAP0_R    HWREG(SOC_CPSW_PORT_0_REGS + CPSW_PORT_CPDMA_TX_PRI_MAP0)
+#define CPSW_PORT0_CPDMA_RX_CH_MAP0_R     HWREG(SOC_CPSW_PORT_0_REGS + CPSW_PORT_CPDMA_RX_CH_MAP0)
+#define CPSW_PORT0_RX_DSCP_PRI_MAP_R(n)   HWREG(SOC_CPSW_PORT_0_REGS + CPSW_PORT_RX_DSCP_PRI_MAP(n))
+#define CPSW_PORT0_TS_SEQ_MTYPE_R         HWREG(SOC_CPSW_PORT_0_REGS + CPSW_PORT_TS_SEQ_MTYPE)
+#define CPSW_PORT0_SA_LO_R                HWREG(SOC_CPSW_PORT_0_REGS + CPSW_PORT_SA_LO)
+#define CPSW_PORT0_SA_HI_R                HWREG(SOC_CPSW_PORT_0_REGS + CPSW_PORT_SA_HI)
+#define CPSW_PORT0_SEND_PERCENT_R         HWREG(SOC_CPSW_PORT_0_REGS + CPSW_PORT_SEND_PERCENT)
+
+#define CPSW_PORT1_CONTROL_R              HWREG(SOC_CPSW_PORT_1_REGS + CPSW_PORT_CONTROL)
+#define CPSW_PORT1_MAX_BLKS_R             HWREG(SOC_CPSW_PORT_1_REGS + CPSW_PORT_MAX_BLKS)
+#define CPSW_PORT1_BLK_CNT_R              HWREG(SOC_CPSW_PORT_1_REGS + CPSW_PORT_BLK_CNT)
+#define CPSW_PORT1_TX_IN_CTL_R            HWREG(SOC_CPSW_PORT_1_REGS + CPSW_PORT_TX_IN_CTL)
+#define CPSW_PORT1_PORT_VLAN_R            HWREG(SOC_CPSW_PORT_1_REGS + CPSW_PORT_PORT_VLAN)
+#define CPSW_PORT1_TX_PRI_MAP_R           HWREG(SOC_CPSW_PORT_1_REGS + CPSW_PORT_TX_PRI_MAP)
+#define CPSW_PORT1_CPDMA_TX_PRI_MAP0_R    HWREG(SOC_CPSW_PORT_1_REGS + CPSW_PORT_CPDMA_TX_PRI_MAP0)
+#define CPSW_PORT1_CPDMA_RX_CH_MAP0_R     HWREG(SOC_CPSW_PORT_1_REGS + CPSW_PORT_CPDMA_RX_CH_MAP0)
+#define CPSW_PORT1_RX_DSCP_PRI_MAP_R(n)   HWREG(SOC_CPSW_PORT_1_REGS + CPSW_PORT_RX_DSCP_PRI_MAP(n))
+#define CPSW_PORT1_TS_SEQ_MTYPE_R         HWREG(SOC_CPSW_PORT_1_REGS + CPSW_PORT_TS_SEQ_MTYPE)
+#define CPSW_PORT1_SA_LO_R                HWREG(SOC_CPSW_PORT_1_REGS + CPSW_PORT_SA_LO)
+#define CPSW_PORT1_SA_HI_R                HWREG(SOC_CPSW_PORT_1_REGS + CPSW_PORT_SA_HI)
+#define CPSW_PORT1_SEND_PERCENT_R         HWREG(SOC_CPSW_PORT_1_REGS + CPSW_PORT_SEND_PERCENT)
+
+#define CPSW_PORT2_CONTROL_R              HWREG(SOC_CPSW_PORT_2_REGS + CPSW_PORT_CONTROL)
+#define CPSW_PORT2_MAX_BLKS_R             HWREG(SOC_CPSW_PORT_2_REGS + CPSW_PORT_MAX_BLKS)
+#define CPSW_PORT2_BLK_CNT_R              HWREG(SOC_CPSW_PORT_2_REGS + CPSW_PORT_BLK_CNT)
+#define CPSW_PORT2_TX_IN_CTL_R            HWREG(SOC_CPSW_PORT_2_REGS + CPSW_PORT_TX_IN_CTL)
+#define CPSW_PORT2_PORT_VLAN_R            HWREG(SOC_CPSW_PORT_2_REGS + CPSW_PORT_PORT_VLAN)
+#define CPSW_PORT2_TX_PRI_MAP_R           HWREG(SOC_CPSW_PORT_2_REGS + CPSW_PORT_TX_PRI_MAP)
+#define CPSW_PORT2_CPDMA_TX_PRI_MAP0_R    HWREG(SOC_CPSW_PORT_2_REGS + CPSW_PORT_CPDMA_TX_PRI_MAP0)
+#define CPSW_PORT2_CPDMA_RX_CH_MAP0_R     HWREG(SOC_CPSW_PORT_2_REGS + CPSW_PORT_CPDMA_RX_CH_MAP0)
+#define CPSW_PORT2_RX_DSCP_PRI_MAP_R(n)   HWREG(SOC_CPSW_PORT_2_REGS + CPSW_PORT_RX_DSCP_PRI_MAP(n))
+#define CPSW_PORT2_TS_SEQ_MTYPE_R         HWREG(SOC_CPSW_PORT_2_REGS + CPSW_PORT_TS_SEQ_MTYPE)
+#define CPSW_PORT2_SA_LO_R                HWREG(SOC_CPSW_PORT_2_REGS + CPSW_PORT_SA_LO)
+#define CPSW_PORT2_SA_HI_R                HWREG(SOC_CPSW_PORT_2_REGS + CPSW_PORT_SA_HI)
+#define CPSW_PORT2_SEND_PERCENT_R         HWREG(SOC_CPSW_PORT_2_REGS + CPSW_PORT_SEND_PERCENT)
+
+//CPSW_SL registers
+#define CPSW_SL1_IDVER_R                  HWREG(SOC_CPSW_SLIVER_1_REGS + CPSW_SL_IDVER)
+#define CPSW_SL1_MACCONTROL_R             HWREG(SOC_CPSW_SLIVER_1_REGS + CPSW_SL_MACCONTROL)
+#define CPSW_SL1_MACSTATUS_R              HWREG(SOC_CPSW_SLIVER_1_REGS + CPSW_SL_MACSTATUS)
+#define CPSW_SL1_SOFT_RESET_R             HWREG(SOC_CPSW_SLIVER_1_REGS + CPSW_SL_SOFT_RESET)
+#define CPSW_SL1_RX_MAXLEN_R              HWREG(SOC_CPSW_SLIVER_1_REGS + CPSW_SL_RX_MAXLEN)
+#define CPSW_SL1_BOFFTEST_R               HWREG(SOC_CPSW_SLIVER_1_REGS + CPSW_SL_BOFFTEST)
+#define CPSW_SL1_RX_PAUSE_R               HWREG(SOC_CPSW_SLIVER_1_REGS + CPSW_SL_RX_PAUSE)
+#define CPSW_SL1_TX_PAUSE_R               HWREG(SOC_CPSW_SLIVER_1_REGS + CPSW_SL_TX_PAUSE)
+#define CPSW_SL1_EMCONTROL_R              HWREG(SOC_CPSW_SLIVER_1_REGS + CPSW_SL_EMCONTROL)
+#define CPSW_SL1_RX_PRI_MAP_R             HWREG(SOC_CPSW_SLIVER_1_REGS + CPSW_SL_RX_PRI_MAP)
+#define CPSW_SL1_TX_GAP_R                 HWREG(SOC_CPSW_SLIVER_1_REGS + CPSW_SL_TX_GAP)
+
+#define CPSW_SL2_IDVER_R                  HWREG(SOC_CPSW_SLIVER_2_REGS + CPSW_SL_IDVER)
+#define CPSW_SL2_MACCONTROL_R             HWREG(SOC_CPSW_SLIVER_2_REGS + CPSW_SL_MACCONTROL)
+#define CPSW_SL2_MACSTATUS_R              HWREG(SOC_CPSW_SLIVER_2_REGS + CPSW_SL_MACSTATUS)
+#define CPSW_SL2_SOFT_RESET_R             HWREG(SOC_CPSW_SLIVER_2_REGS + CPSW_SL_SOFT_RESET)
+#define CPSW_SL2_RX_MAXLEN_R              HWREG(SOC_CPSW_SLIVER_2_REGS + CPSW_SL_RX_MAXLEN)
+#define CPSW_SL2_BOFFTEST_R               HWREG(SOC_CPSW_SLIVER_2_REGS + CPSW_SL_BOFFTEST)
+#define CPSW_SL2_RX_PAUSE_R               HWREG(SOC_CPSW_SLIVER_2_REGS + CPSW_SL_RX_PAUSE)
+#define CPSW_SL2_TX_PAUSE_R               HWREG(SOC_CPSW_SLIVER_2_REGS + CPSW_SL_TX_PAUSE)
+#define CPSW_SL2_EMCONTROL_R              HWREG(SOC_CPSW_SLIVER_2_REGS + CPSW_SL_EMCONTROL)
+#define CPSW_SL2_RX_PRI_MAP_R             HWREG(SOC_CPSW_SLIVER_2_REGS + CPSW_SL_RX_PRI_MAP)
+#define CPSW_SL2_TX_GAP_R                 HWREG(SOC_CPSW_SLIVER_2_REGS + CPSW_SL_TX_GAP)
+
+//CPSW_SS registers
+#define CPSW_SS_ID_VER_R                  HWREG(SOC_CPSW_SS_REGS + CPSW_SS_ID_VER)
+#define CPSW_SS_CONTROL_R                 HWREG(SOC_CPSW_SS_REGS + CPSW_SS_CONTROL)
+#define CPSW_SS_SOFT_RESET_R              HWREG(SOC_CPSW_SS_REGS + CPSW_SS_SOFT_RESET)
+#define CPSW_SS_STAT_PORT_EN_R            HWREG(SOC_CPSW_SS_REGS + CPSW_SS_STAT_PORT_EN)
+#define CPSW_SS_PTYPE_R                   HWREG(SOC_CPSW_SS_REGS + CPSW_SS_PTYPE)
+#define CPSW_SS_SOFT_IDLE_R               HWREG(SOC_CPSW_SS_REGS + CPSW_SS_SOFT_IDLE)
+#define CPSW_SS_THRU_RATE_R               HWREG(SOC_CPSW_SS_REGS + CPSW_SS_THRU_RATE)
+#define CPSW_SS_GAP_THRESH_R              HWREG(SOC_CPSW_SS_REGS + CPSW_SS_GAP_THRESH)
+#define CPSW_SS_TX_START_WDS_R            HWREG(SOC_CPSW_SS_REGS + CPSW_SS_TX_START_WDS)
+#define CPSW_SS_FLOW_CONTROL_R            HWREG(SOC_CPSW_SS_REGS + CPSW_SS_FLOW_CONTROL)
+#define CPSW_SS_VLAN_LTYPE_R              HWREG(SOC_CPSW_SS_REGS + CPSW_SS_VLAN_LTYPE)
+#define CPSW_SS_TS_LTYPE_R                HWREG(SOC_CPSW_SS_REGS + CPSW_SS_TS_LTYPE)
+#define CPSW_SS_DLR_LTYPE_R               HWREG(SOC_CPSW_SS_REGS + CPSW_SS_DLR_LTYPE)
+
+//CPSW_WR registers
+#define CPSW_WR_IDVER_R                   HWREG(SOC_CPSW_WR_REGS + CPSW_WR_IDVER)
+#define CPSW_WR_SOFT_RESET_R              HWREG(SOC_CPSW_WR_REGS + CPSW_WR_SOFT_RESET)
+#define CPSW_WR_CONTROL_R                 HWREG(SOC_CPSW_WR_REGS + CPSW_WR_CONTROL)
+#define CPSW_WR_INT_CONTROL_R             HWREG(SOC_CPSW_WR_REGS + CPSW_WR_INT_CONTROL)
+#define CPSW_WR_C_RX_THRESH_EN_R(n)       HWREG(SOC_CPSW_WR_REGS + CPSW_WR_C_RX_THRESH_EN(n))
+#define CPSW_WR_C_RX_EN_R(n)              HWREG(SOC_CPSW_WR_REGS + CPSW_WR_C_RX_EN(n))
+#define CPSW_WR_C_TX_EN_R(n)              HWREG(SOC_CPSW_WR_REGS + CPSW_WR_C_TX_EN(n))
+#define CPSW_WR_C_MISC_EN_R(n)            HWREG(SOC_CPSW_WR_REGS + CPSW_WR_C_MISC_EN(n))
+#define CPSW_WR_C_RX_THRESH_STAT_R(n)     HWREG(SOC_CPSW_WR_REGS +CPSW_WR_C_RX_THRESH_STAT(n))
+#define CPSW_WR_C_RX_STAT_R(n)            HWREG(SOC_CPSW_WR_REGS + CPSW_WR_C_RX_STAT(n))
+#define CPSW_WR_C_TX_STAT_R(n)            HWREG(SOC_CPSW_WR_REGS + CPSW_WR_C_TX_STAT(n))
+#define CPSW_WR_C_MISC_STAT_R(n)          HWREG(SOC_CPSW_WR_REGS + CPSW_WR_C_MISC_STAT(n))
+#define CPSW_WR_C_RX_IMAX_R(n)            HWREG(SOC_CPSW_WR_REGS + CPSW_WR_C_RX_IMAX(n))
+#define CPSW_WR_C_TX_IMAX_R(n)            HWREG(SOC_CPSW_WR_REGS + CPSW_WR_C_TX_IMAX(n))
+#define CPSW_WR_RGMII_CTL_R               HWREG(SOC_CPSW_WR_REGS + CPSW_WR_RGMII_CTL)
+
+//MDIO registers
+#define MDIO_REVID_R                      HWREG(SOC_CPSW_MDIO_REGS + MDIO_REVID)
+#define MDIO_CONTROL_R                    HWREG(SOC_CPSW_MDIO_REGS + MDIO_CONTROL)
+#define MDIO_ALIVE_R                      HWREG(SOC_CPSW_MDIO_REGS + MDIO_ALIVE)
+#define MDIO_LINK_R                       HWREG(SOC_CPSW_MDIO_REGS + MDIO_LINK)
+#define MDIO_LINKINTRAW_R                 HWREG(SOC_CPSW_MDIO_REGS + MDIO_LINKINTRAW)
+#define MDIO_LINKINTMASKED_R              HWREG(SOC_CPSW_MDIO_REGS + MDIO_LINKINTMASKED)
+#define MDIO_USERINTRAW_R                 HWREG(SOC_CPSW_MDIO_REGS + MDIO_USERINTRAW)
+#define MDIO_USERINTMASKED_R              HWREG(SOC_CPSW_MDIO_REGS + MDIO_USERINTMASKED)
+#define MDIO_USERINTMASKSET_R             HWREG(SOC_CPSW_MDIO_REGS + MDIO_USERINTMASKSET)
+#define MDIO_USERINTMASKCLEAR_R           HWREG(SOC_CPSW_MDIO_REGS + MDIO_USERINTMASKCLEAR)
+#define MDIO_USERACCESS0_R                HWREG(SOC_CPSW_MDIO_REGS + MDIO_USERACCESS0)
+#define MDIO_USERPHYSEL0_R                HWREG(SOC_CPSW_MDIO_REGS + MDIO_USERPHYSEL0)
+#define MDIO_USERACCESS1_R                HWREG(SOC_CPSW_MDIO_REGS + MDIO_USERACCESS1)
+#define MDIO_USERPHYSEL1_R                HWREG(SOC_CPSW_MDIO_REGS + MDIO_USERPHYSEL1)
+
+//GMII_SEL register
+#define CONTROL_GMII_SEL_GMII2_SEL_MII        0x00000000
+#define CONTROL_GMII_SEL_GMII2_SEL_RMII       0x00000004
+#define CONTROL_GMII_SEL_GMII2_SEL_RGMII      0x00000008
+#define CONTROL_GMII_SEL_GMII1_SEL_MII        0x00000000
+#define CONTROL_GMII_SEL_GMII1_SEL_RMII       0x00000001
+#define CONTROL_GMII_SEL_GMII1_SEL_RGMII      0x00000002
+
+//ALE_PORTCTL register
+#define CPSW_ALE_PORTCTL_PORT_STATE_DISABLED  0x00000000
+#define CPSW_ALE_PORTCTL_PORT_STATE_BLOCKED   0x00000001
+#define CPSW_ALE_PORTCTL_PORT_STATE_LEARN     0x00000002
+#define CPSW_ALE_PORTCTL_PORT_STATE_FORWARD   0x00000003
+
+//CPDMA_EOI_VECTOR register
+#define CPSW_CPDMA_EOI_VECTOR_RX_THRESH_PULSE 0x00000000
+#define CPSW_CPDMA_EOI_VECTOR_RX_PULSE        0x00000001
+#define CPSW_CPDMA_EOI_VECTOR_TX_PULSE        0x00000002
+#define CPSW_CPDMA_EOI_VECTOR_MISC_PULSE      0x00000003
+
+//TX buffer descriptor flags
+#define CPSW_TX_WORD0_NEXT_DESC_POINTER       0xFFFFFFFF
+#define CPSW_TX_WORD1_BUFFER_POINTER          0xFFFFFFFF
+#define CPSW_TX_WORD2_BUFFER_OFFSET           0xFFFF0000
+#define CPSW_TX_WORD2_BUFFER_LENGTH           0x0000FFFF
+#define CPSW_TX_WORD3_SOP                     0x80000000
+#define CPSW_TX_WORD3_EOP                     0x40000000
+#define CPSW_TX_WORD3_OWNER                   0x20000000
+#define CPSW_TX_WORD3_EOQ                     0x10000000
+#define CPSW_TX_WORD3_TDOWN_CMPLT             0x08000000
+#define CPSW_TX_WORD3_PASS_CRC                0x04000000
+#define CPSW_TX_WORD3_TO_PORT_EN              0x00100000
+#define CPSW_TX_WORD3_TO_PORT                 0x00030000
+#define CPSW_TX_WORD3_TO_PORT_1               0x00010000
+#define CPSW_TX_WORD3_TO_PORT_2               0x00020000
+#define CPSW_TX_WORD3_PACKET_LENGTH           0x000007FF
+
+//RX buffer descriptor flags
+#define CPSW_RX_WORD0_NEXT_DESC_POINTER       0xFFFFFFFF
+#define CPSW_RX_WORD1_BUFFER_POINTER          0xFFFFFFFF
+#define CPSW_RX_WORD2_BUFFER_OFFSET           0x07FF0000
+#define CPSW_RX_WORD2_BUFFER_LENGTH           0x000007FF
+#define CPSW_RX_WORD3_SOP                     0x80000000
+#define CPSW_RX_WORD3_EOP                     0x40000000
+#define CPSW_RX_WORD3_OWNER                   0x20000000
+#define CPSW_RX_WORD3_EOQ                     0x10000000
+#define CPSW_RX_WORD3_TDOWN_CMPLT             0x08000000
+#define CPSW_RX_WORD3_PASS_CRC                0x04000000
+#define CPSW_RX_WORD3_LONG                    0x02000000
+#define CPSW_RX_WORD3_SHORT                   0x01000000
+#define CPSW_RX_WORD3_CONTROL                 0x00800000
+#define CPSW_RX_WORD3_OVERRUN                 0x00400000
+#define CPSW_RX_WORD3_PKT_ERROR               0x00300000
+#define CPSW_RX_WORD3_RX_VLAN_ENCAP           0x000C0000
+#define CPSW_RX_WORD3_FROM_PORT               0x00030000
+#define CPSW_RX_WORD3_FROM_PORT_1             0x00010000
+#define CPSW_RX_WORD3_FROM_PORT_2             0x00020000
+#define CPSW_RX_WORD3_PACKET_LENGTH           0x000007FF
+
+//Number of entries in the ALE table
+#define CPSW_ALE_MAX_ENTRIES                   1024
+
+//ALE table entry
+#define CPSW_ALE_WORD1_ENTRY_TYPE_MASK         (3 << 28)
+#define CPSW_ALE_WORD1_ENTRY_TYPE_FREE         (0 << 28)
+#define CPSW_ALE_WORD1_ENTRY_TYPE_ADDR         (1 << 28)
+#define CPSW_ALE_WORD1_ENTRY_TYPE_VLAN         (2 << 28)
+#define CPSW_ALE_WORD1_ENTRY_TYPE_VLAN_ADDR    (3 << 28)
+#define CPSW_ALE_WORD1_MULTICAST               (1 << 8)
+
+//Unicast address table entry
+#define CPSW_ALE_WORD2_DLR_UNICAST             (1 << 5)
+#define CPSW_ALE_WORD2_PORT_NUMBER_MASK        (3 << 2)
+#define CPSW_ALE_WORD2_PORT_NUMBER(n)          ((n) << 2)
+#define CPSW_ALE_WORD2_BLOCK                   (1 << 1)
+#define CPSW_ALE_WORD2_SECURE                  (1 << 0)
+#define CPSW_ALE_WORD1_UNICAST_TYPE_MASK       (3 << 30)
+#define CPSW_ALE_WORD1_UNICAST_TYPE(n)         ((n) << 30)
+
+//Multicast address table entry
+#define CPSW_ALE_WORD2_PORT_LIST_MASK          (3 << 2)
+#define CPSW_ALE_WORD2_PORT_LIST(n)            ((n) << 2)
+#define CPSW_ALE_WORD2_SUPER                   (1 << 1)
+#define CPSW_ALE_WORD1_MCAST_FWD_STATE_MASK    (3 << 30)
+#define CPSW_ALE_WORD1_MCAST_FWD_STATE(n)      ((n) << 30)
+
+//VLAN table entry
+#define CPSW_ALE_WORD1_VLAN_ID_MASK            (4095 << 16)
+#define CPSW_ALE_WORD1_VLAN_ID(n)              ((n) << 16)
+#define CPSW_ALE_WORD0_FORCE_UNTAG_EGRESS_MASK (7 << 24)
+#define CPSW_ALE_WORD0_FORCE_UNTAG_EGRESS(n)   ((n) << 24)
+#define CPSW_ALE_WORD0_REG_MCAST_FLOOD_MASK    (7 << 16)
+#define CPSW_ALE_WORD0_REG_MCAST_FLOOD(n)      ((n) << 16)
+#define CPSW_ALE_WORD0_UNREG_MCAST_FLOOD_MASK  (7 << 8)
+#define CPSW_ALE_WORD0_UNREG_MCAST_FLOOD(n)    ((n) << 8)
+#define CPSW_ALE_WORD0_VLAN_MEMBER_LIST_MASK   (7 << 0)
+#define CPSW_ALE_WORD0_VLAN_MEMBER_LIST(n)     ((n) << 0)
+
+
+/**
+ * @brief ALE table entry
+ **/
+
+typedef struct
+{
+   uint32_t word2;
+   uint32_t word1;
+   uint32_t word0;
+} Am335xAleEntry;
+
+
+/**
+ * @brief TX buffer descriptor
+ **/
+
+typedef struct _Am335xTxBufferDesc
+{
+   uint32_t word0;
+   uint32_t word1;
+   uint32_t word2;
+   uint32_t word3;
+   struct _Am335xTxBufferDesc *next;
+   struct _Am335xTxBufferDesc *prev;
+} Am335xTxBufferDesc;
+
+
+/**
+ * @brief RX buffer descriptor
+ **/
+
+typedef struct _Am335xRxBufferDesc
+{
+   uint32_t word0;
+   uint32_t word1;
+   uint32_t word2;
+   uint32_t word3;
+   struct _Am335xRxBufferDesc *next;
+   struct _Am335xRxBufferDesc *prev;
+} Am335xRxBufferDesc;
+
+
+//AM335x Ethernet MAC driver
+extern const NicDriver am335xEthPort1Driver;
+extern const NicDriver am335xEthPort2Driver;
+
+//AM335x Ethernet MAC related functions
+error_t am335xEthInitPort1(NetInterface *interface);
+error_t am335xEthInitPort2(NetInterface *interface);
+void am335xEthInitInstance(NetInterface *interface);
+void am335xEthInitGpio(NetInterface *interface);
+void am335xEthInitBufferDesc(NetInterface *interface);
+
+void am335xEthTick(NetInterface *interface);
+
+void am335xEthEnableIrq(NetInterface *interface);
+void am335xEthDisableIrq(NetInterface *interface);
+void am335xEthTxIrqHandler(void);
+void am335xEthRxIrqHandler(void);
+void am335xEthEventHandler(NetInterface *interface);
+
+error_t am335xEthSendPacketPort1(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t am335xEthSendPacketPort2(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t am335xEthSetMulticastFilter(NetInterface *interface);
+error_t am335xEthUpdateMacConfig(NetInterface *interface);
+
+void am335xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t am335xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+void am335xEthWriteEntry(uint_t index, const Am335xAleEntry *entry);
+void am335xEthReadEntry(uint_t index, Am335xAleEntry *entry);
+
+uint_t am335xEthFindFreeEntry(void);
+uint_t am335xEthFindVlanEntry(uint_t vlanId);
+uint_t am335xEthFindVlanAddrEntry(uint_t vlanId, MacAddr *macAddr);
+
+error_t am335xEthAddVlanEntry(uint_t port, uint_t vlanId);
+error_t am335xEthAddVlanAddrEntry(uint_t port, uint_t vlanId, MacAddr *macAddr);
+error_t am335xEthDeleteVlanAddrEntry(uint_t port, uint_t vlanId, MacAddr *macAddr);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/aps3_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,652 @@
+/**
+ * @file aps3_eth.c
+ * @brief Cortus APS3 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include <machine/sfradr.h>
+#include <machine/sfradr_eth.h>
+#include <machine/ethernet.h>
+#include <machine/ic.h>
+#undef _ETHERNET_H
+#include "core/net.h"
+#include "drivers/aps3_eth.h"
+#include "debug.h"
+
+//Transmit buffer
+#define txBuffer ((uint8_t *) SFRADR_ETH_TX_MEM_BOTTOM_AD)
+//Receive buffer
+#define rxBuffer ((uint8_t *) SFRADR_ETH_RX_MEM_BOTTOM_AD)
+
+//Transmit DMA descriptors
+#define txDmaDesc ((Aps3TxDmaDesc *) (SFRADR_ETH_TX_MEM_BOTTOM_AD + \
+   APS3_ETH_TX_BUFFER_COUNT * APS3_ETH_TX_BUFFER_SIZE))
+
+//Receive DMA descriptors
+#define rxDmaDesc ((Aps3RxDmaDesc *) (SFRADR_ETH_RX_MEM_BOTTOM_AD + \
+   APS3_ETH_RX_BUFFER_COUNT * APS3_ETH_RX_BUFFER_SIZE))
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+
+/**
+ * @brief Cortus APS3 Ethernet MAC driver
+ **/
+
+const NicDriver aps3EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   aps3EthInit,
+   aps3EthTick,
+   aps3EthEnableIrq,
+   aps3EthDisableIrq,
+   aps3EthEventHandler,
+   aps3EthSendPacket,
+   aps3EthSetMulticastFilter,
+   aps3EthUpdateMacConfig,
+   aps3EthWritePhyReg,
+   aps3EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief Cortus APS3 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t aps3EthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing Cortus APS3 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Adjust MDC clock range
+   eth_miim->miim_clock_divider = 32;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Reset Ethernet MAC peripheral
+   eth_mac->sw_reset = 1;
+
+   //Set the MAC address
+   eth_mac->addr_low = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   eth_mac->addr_high = interface->macAddr.w[2];
+
+   //Initialize hash table
+   eth_mac->hash_filter_low = 0;
+   eth_mac->hash_filter_high = 0;
+
+   //Configure the receive filter
+   eth_mac->unicast = 1;
+   eth_mac->multicast = 0;
+   eth_mac->broadcast = 1;
+   eth_mac->hash = 1;
+   eth_mac->exact_addr = 1;
+
+   //Default duplex mode
+   eth_mac->full_duplex = 0;
+
+   //Automatic padding and CRC generation
+   eth_mac->no_padding = 0;
+   eth_mac->crc_disable = 0;
+
+   //Set the maximum frame length
+   eth_mac->max_frame_size = 1518;
+
+   //Set transmit and receive thresholds
+   eth_tx->tx_threshold = 0;
+   eth_rx->rx_threshold = 0;
+
+   //Disable indefinite deferral
+   eth_mac->indefinite_deferral = 0;
+   //Number of attempts to transmit a frame before aborting
+   eth_mac->max_deferral = 15;
+
+   //Use default collision window (112 half-octets)
+   eth_mac->collision_window = 111;
+   //Maximum Number of Collisions
+   eth_mac->max_collision = 15;
+
+   //Automatic backoff on collision
+   eth_mac->no_backoff = 0;
+
+   //Use the default interframe gap (24 half-octets or 96 bits)
+   eth_mac->interframe_gap = 23;
+
+   //Initialize DMA descriptor lists
+   aps3EthInitDmaDesc(interface);
+
+   //Configure TX interrupts
+   eth_tx->tx_irq_mask = TX_IRQ_MASK_MEMORY_AVAILABLE;
+   //Configure RX interrupts
+   eth_rx->rx_irq_mask = RX_IRQ_MASK_FRAME_READY;
+
+   //Configure TX interrupt priority
+   irq[IRQ_ETH_TX].ipl = APS3_ETH_IRQ_PRIORITY;
+   //Configure RX interrupt priority
+   irq[IRQ_ETH_RX].ipl = APS3_ETH_IRQ_PRIORITY;
+
+   //Enable transmission and reception
+   eth_tx->tx_enable = 1;
+   eth_rx->rx_enable = 1;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void aps3EthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX DMA descriptor list
+   for(i = 0; i < APS3_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Transmit buffer address
+      txDmaDesc[i].addr = (uint32_t) txBuffer + (APS3_ETH_TX_BUFFER_SIZE * i);
+      //Transmit buffer size
+      txDmaDesc[i].size = 0;
+      //Transmit status
+      txDmaDesc[i].status = 0;
+   }
+
+   //Initialize RX DMA descriptor list
+   for(i = 0; i < APS3_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Receive buffer address
+      rxDmaDesc[i].addr = (uint32_t) rxBuffer + (APS3_ETH_RX_BUFFER_SIZE * i);
+      //Receive buffer size
+      rxDmaDesc[i].size = 0;
+      //Receive status
+      rxDmaDesc[i].status = 0;
+   }
+
+   //Start location of the TX descriptor list
+   eth_tx->tx_desc_base_addr = (uint32_t) txDmaDesc;
+   //Number of TX descriptors
+   eth_tx->tx_desc_number = APS3_ETH_TX_BUFFER_COUNT - 1;
+
+   //Start location of the RX descriptor list
+   eth_rx->rx_desc_base_addr = (uint32_t) rxDmaDesc;
+   //Number of RX descriptors
+   eth_rx->rx_desc_number = APS3_ETH_RX_BUFFER_COUNT - 1;
+}
+
+
+/**
+ * @brief Cortus APS3 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void aps3EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void aps3EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   irq[IRQ_ETH_TX].ien = 1;
+   irq[IRQ_ETH_RX].ien = 1;
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void aps3EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   irq[IRQ_ETH_TX].ien = 0;
+   irq[IRQ_ETH_RX].ien = 0;
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief Ethernet MAC transmit interrupt service routine
+ **/
+
+void aps3EthTxIrqHandler(void)
+{
+   bool_t flag;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Check interrupt flag
+   if(eth_tx->tx_status & TX_IRQ_MASK_MEMORY_AVAILABLE)
+   {
+      //Disable TX interrupts
+      eth_tx->tx_irq_mask = 0;
+
+      //Check whether the TX buffer is available for writing
+      if(!(eth_tx->tx_desc_status))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag = osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief Ethernet MAC receive interrupt service routine
+ **/
+
+void aps3EthRxIrqHandler(void)
+{
+   bool_t flag;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Disable RX interrupts
+   eth_rx->rx_irq_mask = 0;
+
+   //Set event flag
+   nicDriverInterface->nicEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   flag = osSetEventFromIsr(&netEvent);
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief Cortus APS3 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void aps3EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //A packet has been received?
+   if(eth_rx->rx_status & RX_IRQ_MASK_FRAME_READY)
+   {
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = aps3EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable RX interrupts
+   eth_rx->rx_irq_mask = RX_IRQ_MASK_FRAME_READY;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t aps3EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   uint_t i;
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > APS3_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(eth_tx->tx_desc_status)
+   {
+      //Re-enable TX interrupts
+      eth_tx->tx_irq_mask = TX_IRQ_MASK_MEMORY_AVAILABLE;
+      //Report an error
+      return ERROR_FAILURE;
+   }
+
+   //Get the index of the current descriptor
+   i = eth_tx->tx_desc_produce;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txDmaDesc[i].addr, buffer, offset, length);
+   //Write the number of bytes to send
+   txDmaDesc[i].size = length;
+
+   //Start transmission
+   eth_tx->tx_sw_done = 1;
+
+   //Check whether the next buffer is available for writing
+   if(!eth_tx->tx_desc_status)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+   else
+   {
+      //Re-enable TX interrupts
+      eth_tx->tx_irq_mask = TX_IRQ_MASK_MEMORY_AVAILABLE;
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t aps3EthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   uint_t i;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(eth_rx->rx_desc_status))
+   {
+      //Point to the current descriptor
+      i = eth_rx->rx_desc_consume;
+
+      //Make sure no error occurred
+      if(!(rxDmaDesc[i].status & RX_DESC_RECEIVE_ERROR))
+      {
+         //Retrieve the length of the frame
+         n = rxDmaDesc[i].size;
+         //Limit the number of data to read
+         n = MIN(n, APS3_ETH_RX_BUFFER_SIZE);
+
+         //Pass the packet to the upper layer
+         nicProcessPacket(interface, (uint8_t *) rxDmaDesc[i].addr, n);
+
+         //Valid packet received
+         error = NO_ERROR;
+      }
+      else
+      {
+         //The received packet contains an error
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //The frame has been has been processed by the software
+      //and is no longer needed
+      eth_rx->rx_sw_done = 1;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t aps3EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating Cortus APS3 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = aps3EthCalcCrc(&entry->addr, sizeof(MacAddr));
+         //Calculate the corresponding index in the table
+         k = (crc >> 23) & 0x3F;
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Disable transmission and reception
+   eth_tx->tx_enable = 0;
+   eth_rx->rx_enable = 0;
+
+   //Write the hash table
+   eth_mac->hash_filter_low = hashTable[0];
+   eth_mac->hash_filter_high = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  hash_filter_low = %08" PRIX32 "\r\n", hashTable[0]);
+   TRACE_DEBUG("  hash_filter_high = %08" PRIX32 "\r\n", hashTable[1]);
+
+   //Re-enable transmission and reception
+   eth_tx->tx_enable = 1;
+   eth_rx->rx_enable = 1;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t aps3EthUpdateMacConfig(NetInterface *interface)
+{
+   //Disable transmission and reception
+   eth_tx->tx_enable = 0;
+   eth_rx->rx_enable = 0;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      eth_mac->full_duplex = 1;
+   else
+      eth_mac->full_duplex = 0;
+
+   //Re-enable transmission and reception
+   eth_tx->tx_enable = 1;
+   eth_rx->rx_enable = 1;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void aps3EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   //Wait for the MII management module to be ready
+   while(!eth_miim->miim_status);
+
+   //PHY address
+   eth_miim->miim_phy_addr = phyAddr;
+   //Register address
+   eth_miim->miim_phy_register_addr = regAddr;
+   //Data to be written in the PHY register
+   eth_miim->miim_data = data;
+
+   //Start a write operation
+   eth_miim->miim_read_write = 0;
+   //Wait for the write to complete
+   while(!eth_miim->miim_status);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t aps3EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   //Wait for the MII management module to be ready
+   while(!eth_miim->miim_status);
+
+   //PHY address
+   eth_miim->miim_phy_addr = phyAddr;
+   //Register address
+   eth_miim->miim_phy_register_addr = regAddr;
+
+   //Start a read operation
+   eth_miim->miim_read_write = 1;
+   //Wait for the read to complete
+   while(!eth_miim->miim_status);
+
+   //Return PHY register contents
+   return eth_miim->miim_data;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t aps3EthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //Update CRC value
+      crc ^= p[i];
+
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(crc & 0x00000001)
+            crc = (crc >> 1) ^ 0xEDB88320;
+         else
+            crc = crc >> 1;
+      }
+   }
+
+   //Return CRC value
+   return ~crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/aps3_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,172 @@
+/**
+ * @file aps3_eth.h
+ * @brief Cortus APS3 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _APS3_ETH_H
+#define _APS3_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef APS3_ETH_TX_BUFFER_COUNT
+   #define APS3_ETH_TX_BUFFER_COUNT 4
+#elif (APS3_ETH_TX_BUFFER_COUNT < 1)
+   #error APS3_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef APS3_ETH_TX_BUFFER_SIZE
+   #define APS3_ETH_TX_BUFFER_SIZE 1536
+#elif (APS3_ETH_TX_BUFFER_SIZE != 1536)
+   #error APS3_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef APS3_ETH_RX_BUFFER_COUNT
+   #define APS3_ETH_RX_BUFFER_COUNT 4
+#elif (APS3_ETH_RX_BUFFER_COUNT < 1)
+   #error APS3_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef APS3_ETH_RX_BUFFER_SIZE
+   #define APS3_ETH_RX_BUFFER_SIZE 1536
+#elif (APS3_ETH_RX_BUFFER_SIZE != 1536)
+   #error APS3_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Ethernet interrupt priority
+#ifndef APS3_ETH_IRQ_PRIORITY
+   #define APS3_ETH_IRQ_PRIORITY 0
+#elif (APS3_ETH_IRQ_PRIORITY < 0)
+   #error APS3_ETH_IRQ_PRIORITY parameter is not valid
+#endif
+
+//tx_irq_mask register
+#define TX_IRQ_MASK_TRANSMIT_ERROR      0x0001
+#define TX_IRQ_MASK_EXCESSIVE_DEFERRAL  0x0002
+#define TX_IRQ_MASK_EXCESSIVE_COLLISION 0x0004
+#define TX_IRQ_MASK_LATE_COLLISION      0x0008
+#define TX_IRQ_MASK_FRAME_TOO_LONG      0x0010
+#define TX_IRQ_MASK_MEMORY_ERROR        0x0020
+#define TX_IRQ_MASK_FRAME_SENT          0x0040
+#define TX_IRQ_MASK_MEMORY_AVAILABLE    0x0080
+#define TX_IRQ_MASK_THRESHOLD_REACHED   0x0100
+#define TX_IRQ_MASK_MEMORY_EMPTY        0x0200
+
+//rx_irq_mask register
+#define RX_IRQ_MASK_RECEIVE_ERROR       0x0001
+#define RX_IRQ_MASK_LENGTH_FIELD_ERROR  0x0002
+#define RX_IRQ_MASK_FRAME_TOO_LONG      0x0004
+#define RX_IRQ_MASK_SHORT_FRAME         0x0008
+#define RX_IRQ_MASK_ODD_NIBBLE_COUNT    0x0010
+#define RX_IRQ_MASK_INVALID_ADDRESS     0x0020
+#define RX_IRQ_MASK_PHY_ERROR           0x0040
+#define RX_IRQ_MASK_CRC_ERROR           0x0080
+#define RX_IRQ_MASK_MEMORY_ERROR        0x0100
+#define RX_IRQ_MASK_WAKEUP_ON_LAN       0x0200
+#define RX_IRQ_MASK_FRAME_READY         0x0400
+#define RX_IRQ_MASK_THRESHOLD_REACHED   0x0800
+#define RX_IRQ_MASK_FRAME_OVERFLOW      0x1000
+
+//Transmit DMA descriptor flags
+#define TX_DESC_TRANSMIT_ERROR          0x0001
+#define TX_DESC_EXCESSIVE_DEFERRAL      0x0002
+#define TX_DESC_EXCESSIVE_COLLISION     0x0003
+#define TX_DESC_LATE_COLLISION          0x0004
+#define TX_DESC_FRAME_TOO_LONG          0x0010
+#define TX_DESC_MEMORY_ERROR            0x0020
+
+//Receive DMA descriptor flags
+#define RX_DESC_RECEIVE_ERROR           0x0001
+#define RX_DESC_LENGTH_FIELD_ERROR      0x0002
+#define RX_DESC_FRAME_TOO_LONG          0x0003
+#define RX_DESC_SHORT_FRAME             0x0004
+#define RX_DESC_ODD_NIBBLE_COUNT        0x0010
+#define RX_DESC_INVALID_ADDRESS         0x0020
+#define RX_DESC_PHY_ERROR               0x0040
+#define RX_DESC_CRC_ERROR               0x0080
+#define RX_DESC_MEMORY_ERROR            0x0100
+
+
+/**
+ * @brief TX DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t addr;
+   uint32_t size : 16;
+   uint32_t status : 16;
+} Aps3TxDmaDesc;
+
+
+/**
+ * @brief RX DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t addr;
+   uint32_t size : 16;
+   uint32_t status : 16;
+} Aps3RxDmaDesc;
+
+
+//Cortus APS3 Ethernet MAC driver
+extern const NicDriver aps3EthDriver;
+
+//Cortus APS3 Ethernet MAC related functions
+error_t aps3EthInit(NetInterface *interface);
+void aps3EthInitDmaDesc(NetInterface *interface);
+
+void aps3EthTick(NetInterface *interface);
+
+void aps3EthEnableIrq(NetInterface *interface);
+void aps3EthDisableIrq(NetInterface *interface);
+
+void aps3EthTxIrqHandler(void) __attribute__((noinline));
+void aps3EthRxIrqHandler(void) __attribute__((noinline));
+
+void aps3EthEventHandler(NetInterface *interface);
+
+error_t aps3EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t aps3EthReceivePacket(NetInterface *interface);
+
+error_t aps3EthSetMulticastFilter(NetInterface *interface);
+error_t aps3EthUpdateMacConfig(NetInterface *interface);
+
+void aps3EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t aps3EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t aps3EthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ar8031.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,305 @@
+/**
+ * @file ar8031.c
+ * @brief AR8031 Gigabit Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/ar8031.h"
+#include "debug.h"
+
+
+/**
+ * @brief AR8031 Ethernet PHY driver
+ **/
+
+const PhyDriver ar8031PhyDriver =
+{
+   ar8031Init,
+   ar8031Tick,
+   ar8031EnableIrq,
+   ar8031DisableIrq,
+   ar8031EventHandler,
+};
+
+
+/**
+ * @brief AR8031 PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ar8031Init(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing AR8031...\r\n");
+
+   //Initialize external interrupt line driver
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->init();
+
+   //Reset PHY transceiver
+   ar8031WritePhyReg(interface, AR8031_PHY_REG_BMCR, BMCR_RESET);
+   //Wait for the reset to complete
+   while(ar8031ReadPhyReg(interface, AR8031_PHY_REG_BMCR) & BMCR_RESET);
+
+   //Chip configuration register
+   ar8031WritePhyReg(interface, AR8031_PHY_REG_CHIP_CONFIG,
+      CHIP_CONFIG_BT_BX_REG_SEL | CHIP_CONFIG_PRIORITY_SEL);
+
+   //Basic mode control register
+   ar8031WritePhyReg(interface, AR8031_PHY_REG_BMCR,
+      BMCR_SPEED_SEL_LSB | BMCR_AN_EN | BMCR_DUPLEX_MODE);
+
+   //Auto-negotiation advertisement register
+   ar8031WritePhyReg(interface, AR8031_PHY_REG_ANAR,
+      ANAR_XNP_ABLE | ANAR_ASYMMETRIC_PAUSE | ANAR_PAUSE | ANAR_100BTX_FD |
+      ANAR_100BTX_HD | ANAR_10BT_FD | ANAR_10BT_HD | ANAR_SELECTOR0);
+
+   //1000 BASE-T control register
+   ar8031WritePhyReg(interface, AR8031_PHY_REG_1000BT_CTRL,
+      _1000BT_CTRL_1000BT_FD);
+
+   //Function control register
+   ar8031WritePhyReg(interface, AR8031_PHY_REG_FUNCTION_CTRL,
+      FUNCTION_ASSERT_CRS_ON_TX | FUNCTION_MDI_CROSSOVER_MODE1 |
+      FUNCTION_MDI_CROSSOVER_MODE0 | FUNCTION_POLARITY_REVERSAL);
+
+   //Dump PHY registers for debugging purpose
+   ar8031DumpPhyReg(interface);
+
+   //The PHY will generate interrupts when link status changes are detected
+   ar8031WritePhyReg(interface, AR8031_PHY_REG_INT_EN,
+      INT_STATUS_LINK_FAIL | INT_STATUS_LINK_SUCCESS);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief AR8031 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ar8031Tick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //No external interrupt line driver?
+   if(interface->extIntDriver == NULL)
+   {
+      //Read basic status register
+      value = ar8031ReadPhyReg(interface, AR8031_PHY_REG_BMSR);
+      //Retrieve current link state
+      linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+      //Link up event?
+      if(linkState && !interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+      //Link down event?
+      else if(!linkState && interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ar8031EnableIrq(NetInterface *interface)
+{
+   //Enable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ar8031DisableIrq(NetInterface *interface)
+{
+   //Disable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief AR8031 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ar8031EventHandler(NetInterface *interface)
+{
+   uint16_t status;
+
+   //Read status register to acknowledge the interrupt
+   status = ar8031ReadPhyReg(interface, AR8031_PHY_REG_INT_STATUS);
+
+   //Link status change?
+   if(status & (INT_STATUS_LINK_FAIL | INT_STATUS_LINK_SUCCESS))
+   {
+      //Read PHY status register
+      status = ar8031ReadPhyReg(interface, AR8031_PHY_REG_PHY_STATUS);
+
+      //Link is up?
+      if(status & PHY_STATUS_LINK)
+      {
+         //Check current speed
+         switch(status & PHY_STATUS_SPEED_MASK)
+         {
+         //10BASE-T
+         case PHY_STATUS_SPEED_10:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            break;
+         //100BASE-TX
+         case PHY_STATUS_SPEED_100:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            break;
+         //1000BASE-T
+         case PHY_STATUS_SPEED_1000:
+            interface->linkSpeed = NIC_LINK_SPEED_1GBPS;
+            break;
+         //Unknown speed
+         default:
+            //Debug message
+            TRACE_WARNING("Invalid speed\r\n");
+            break;
+         }
+
+         //Check current duplex mode
+         if(status & PHY_STATUS_DUPLEX)
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+         else
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+
+         //Update link state
+         interface->linkState = TRUE;
+
+         //Adjust MAC configuration parameters for proper operation
+         interface->nicDriver->updateMacConfig(interface);
+      }
+      else
+      {
+         //Update link state
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void ar8031WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = AR8031_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t ar8031ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = AR8031_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void ar8031DumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, ar8031ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ar8031.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,359 @@
+/**
+ * @file ar8031.h
+ * @brief AR8031 Gigabit Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _AR8031_H
+#define _AR8031_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#ifndef AR8031_PHY_ADDR
+   #define AR8031_PHY_ADDR 0
+#elif (AR8031_PHY_ADDR < 0 || AR8031_PHY_ADDR > 31)
+   #error AR8031_PHY_ADDR parameter is not valid
+#endif
+
+//AR8031 registers
+#define AR8031_PHY_REG_BMCR              0x00
+#define AR8031_PHY_REG_BMSR              0x01
+#define AR8031_PHY_REG_PHYIDR1           0x02
+#define AR8031_PHY_REG_PHYIDR2           0x03
+#define AR8031_PHY_REG_ANAR              0x04
+#define AR8031_PHY_REG_ANLPAR            0x05
+#define AR8031_PHY_REG_ANER              0x06
+#define AR8031_PHY_REG_ANNPTR            0x07
+#define AR8031_PHY_REG_LPNPAR            0x08
+#define AR8031_PHY_REG_1000BT_CTRL       0x09
+#define AR8031_PHY_REG_1000BT_STATUS     0x0A
+#define AR8031_PHY_REG_MMD_CTRL          0x0D
+#define AR8031_PHY_REG_MMD_DATA          0x0E
+#define AR8031_PHY_REG_EXT_STATUS        0x0F
+#define AR8031_PHY_REG_FUNCTION_CTRL     0x10
+#define AR8031_PHY_REG_PHY_STATUS        0x11
+#define AR8031_PHY_REG_INT_EN            0x12
+#define AR8031_PHY_REG_INT_STATUS        0x13
+#define AR8031_PHY_REG_SMART_SPEED       0x14
+#define AR8031_PHY_REG_CDT_CTRL          0x16
+#define AR8031_PHY_REG_LED_CTRL          0x18
+#define AR8031_PHY_REG_MAN_LED_OVERRIDE  0x19
+#define AR8031_PHY_REG_CDT_STATUS        0x1C
+#define AR8031_PHY_REG_DBG_PORT          0x1D
+#define AR8031_PHY_REG_DBG_PORT2         0x1E
+#define AR8031_PHY_REG_CHIP_CONFIG       0x1F
+
+//BMCR register
+#define BMCR_RESET                       (1 << 15)
+#define BMCR_LOOPBACK                    (1 << 14)
+#define BMCR_SPEED_SEL_LSB               (1 << 13)
+#define BMCR_AN_EN                       (1 << 12)
+#define BMCR_POWER_DOWN                  (1 << 11)
+#define BMCR_ISOLATE                     (1 << 10)
+#define BMCR_RESTART_AN                  (1 << 9)
+#define BMCR_DUPLEX_MODE                 (1 << 8)
+#define BMCR_COL_TEST                    (1 << 7)
+#define BMCR_SPEED_SEL_MSB               (1 << 6)
+
+//BMSR register
+#define BMSR_100BT4                      (1 << 15)
+#define BMSR_100BTX_FD                   (1 << 14)
+#define BMSR_100BTX_HD                   (1 << 13)
+#define BMSR_10BT_FD                     (1 << 12)
+#define BMSR_10BT_HD                     (1 << 11)
+#define BMSR_100BT2_FD                   (1 << 10)
+#define BMSR_100BT2_HD                   (1 << 9)
+#define BMSR_EXTENDED_STATUS             (1 << 8)
+#define BMSR_NO_PREAMBLE                 (1 << 6)
+#define BMSR_AN_COMPLETE                 (1 << 5)
+#define BMSR_REMOTE_FAULT                (1 << 4)
+#define BMSR_AN_ABLE                     (1 << 3)
+#define BMSR_LINK_STATUS                 (1 << 2)
+#define BMSR_JABBER_DETECT               (1 << 1)
+#define BMSR_EXTENDED_CAP                (1 << 0)
+
+//ANAR register
+#define ANAR_NEXT_PAGE                   (1 << 15)
+#define ANAR_ACK                         (1 << 14)
+#define ANAR_REMOTE_FAULT                (1 << 13)
+#define ANAR_XNP_ABLE                    (1 << 12)
+#define ANAR_ASYMMETRIC_PAUSE            (1 << 11)
+#define ANAR_PAUSE                       (1 << 10)
+#define ANAR_100BT4                      (1 << 9)
+#define ANAR_100BTX_FD                   (1 << 8)
+#define ANAR_100BTX_HD                   (1 << 7)
+#define ANAR_10BT_FD                     (1 << 6)
+#define ANAR_10BT_HD                     (1 << 5)
+#define ANAR_SELECTOR4                   (1 << 4)
+#define ANAR_SELECTOR3                   (1 << 3)
+#define ANAR_SELECTOR2                   (1 << 2)
+#define ANAR_SELECTOR1                   (1 << 1)
+#define ANAR_SELECTOR0                   (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NEXT_PAGE                 (1 << 15)
+#define ANLPAR_ACK                       (1 << 14)
+#define ANLPAR_REMOTE_FAULT              (1 << 13)
+#define ANLPAR_ASYMMETRIC_PAUSE          (1 << 11)
+#define ANLPAR_PAUSE                     (1 << 10)
+#define ANLPAR_100BT4                    (1 << 9)
+#define ANLPAR_100BTX_FD                 (1 << 8)
+#define ANLPAR_100BTX_HD                 (1 << 7)
+#define ANLPAR_10BT_FD                   (1 << 6)
+#define ANLPAR_10BT_HD                   (1 << 5)
+#define ANLPAR_SELECTOR4                 (1 << 4)
+#define ANLPAR_SELECTOR3                 (1 << 3)
+#define ANLPAR_SELECTOR2                 (1 << 2)
+#define ANLPAR_SELECTOR1                 (1 << 1)
+#define ANLPAR_SELECTOR0                 (1 << 0)
+
+//ANER register
+#define ANER_PAR_DET_FAULT               (1 << 4)
+#define ANER_LP_NEXT_PAGE_ABLE           (1 << 3)
+#define ANER_NEXT_PAGE_ABLE              (1 << 2)
+#define ANER_PAGE_RECEIVED               (1 << 1)
+#define ANER_LP_AN_ABLE                  (1 << 0)
+
+//ANNPTR register
+#define ANNPTR_NEXT_PAGE                 (1 << 15)
+#define ANNPTR_MSG_PAGE                  (1 << 13)
+#define ANNPTR_ACK2                      (1 << 12)
+#define ANNPTR_TOGGLE                    (1 << 11)
+#define ANNPTR_MESSAGE10                 (1 << 10)
+#define ANNPTR_MESSAGE9                  (1 << 9)
+#define ANNPTR_MESSAGE8                  (1 << 8)
+#define ANNPTR_MESSAGE7                  (1 << 7)
+#define ANNPTR_MESSAGE6                  (1 << 6)
+#define ANNPTR_MESSAGE5                  (1 << 5)
+#define ANNPTR_MESSAGE4                  (1 << 4)
+#define ANNPTR_MESSAGE3                  (1 << 3)
+#define ANNPTR_MESSAGE2                  (1 << 2)
+#define ANNPTR_MESSAGE1                  (1 << 1)
+#define ANNPTR_MESSAGE0                  (1 << 0)
+
+//LPNPAR register
+#define LPNPAR_NEXT_PAGE                 (1 << 15)
+#define LPNPAR_MSG_PAGE                  (1 << 13)
+#define LPNPAR_ACK2                      (1 << 12)
+#define LPNPAR_TOGGLE                    (1 << 11)
+#define LPNPAR_MESSAGE10                 (1 << 10)
+#define LPNPAR_MESSAGE9                  (1 << 9)
+#define LPNPAR_MESSAGE8                  (1 << 8)
+#define LPNPAR_MESSAGE7                  (1 << 7)
+#define LPNPAR_MESSAGE6                  (1 << 6)
+#define LPNPAR_MESSAGE5                  (1 << 5)
+#define LPNPAR_MESSAGE4                  (1 << 4)
+#define LPNPAR_MESSAGE3                  (1 << 3)
+#define LPNPAR_MESSAGE2                  (1 << 2)
+#define LPNPAR_MESSAGE1                  (1 << 1)
+#define LPNPAR_MESSAGE0                  (1 << 0)
+
+//1000BT_CTRL register
+#define _1000BT_CTRL_TEST_MODE2          (1 << 15)
+#define _1000BT_CTRL_TEST_MODE1          (1 << 14)
+#define _1000BT_CTRL_TEST_MODE0          (1 << 13)
+#define _1000BT_CTRL_MS_MAN_CONF_EN      (1 << 12)
+#define _1000BT_CTRL_MS_MAN_CONF_VAL     (1 << 11)
+#define _1000BT_CTRL_PORT_TYPE           (1 << 10)
+#define _1000BT_CTRL_1000BT_FD           (1 << 9)
+#define _1000BT_CTRL_1000BT_HD           (1 << 8)
+
+//1000BT_STATUS register
+#define _1000BT_STATUS_MS_CONF_FAULT     (1 << 15)
+#define _1000BT_STATUS_MS_CONF_RES       (1 << 14)
+#define _1000BT_STATUS_LOC_REC_STATUS    (1 << 13)
+#define _1000BT_STATUS_REM_REC_STATUS    (1 << 12)
+#define _1000BT_STATUS_LP_1000BT_FD      (1 << 11)
+#define _1000BT_STATUS_LP_1000BT_HD      (1 << 10)
+#define _1000BT_STATUS_IDLE_ERR_CTR7     (1 << 7)
+#define _1000BT_STATUS_IDLE_ERR_CTR6     (1 << 6)
+#define _1000BT_STATUS_IDLE_ERR_CTR5     (1 << 5)
+#define _1000BT_STATUS_IDLE_ERR_CTR4     (1 << 4)
+#define _1000BT_STATUS_IDLE_ERR_CTR3     (1 << 3)
+#define _1000BT_STATUS_IDLE_ERR_CTR2     (1 << 2)
+#define _1000BT_STATUS_IDLE_ERR_CTR1     (1 << 1)
+#define _1000BT_STATUS_IDLE_ERR_CTR0     (1 << 0)
+
+//MMD_CTRL register
+#define MMD_CTRL_FUNCTION1               (1 << 15)
+#define MMD_CTRL_FUNCTION0               (1 << 14)
+#define MMD_CTRL_DEVAD4                  (1 << 4)
+#define MMD_CTRL_DEVAD3                  (1 << 3)
+#define MMD_CTRL_DEVAD2                  (1 << 2)
+#define MMD_CTRL_DEVAD1                  (1 << 1)
+#define MMD_CTRL_DEVAD0                  (1 << 0)
+
+//EXT_STATUS register
+#define EXT_STATUS_1000BX_FD             (1 << 15)
+#define EXT_STATUS_1000BX_HD             (1 << 14)
+#define EXT_STATUS_1000BT_FD             (1 << 13)
+#define EXT_STATUS_1000BT_HD             (1 << 12)
+
+//FUNCTION register
+#define FUNCTION_ASSERT_CRS_ON_TX        (1 << 11)
+#define FUNCTION_FORCE_LINK              (1 << 10)
+#define FUNCTION_MDI_CROSSOVER_MODE1     (1 << 6)
+#define FUNCTION_MDI_CROSSOVER_MODE0     (1 << 5)
+#define FUNCTION_SQE_TEST                (1 << 2)
+#define FUNCTION_POLARITY_REVERSAL       (1 << 1)
+#define FUNCTION_DISABLE_JABBER          (1 << 0)
+
+//PHY_STATUS register
+#define PHY_STATUS_SPEED1                (1 << 15)
+#define PHY_STATUS_SPEED0                (1 << 14)
+#define PHY_STATUS_DUPLEX                (1 << 13)
+#define PHY_STATUS_PAGE_RECEIVED         (1 << 12)
+#define PHY_STATUS_SPEED_DUPLEX_RESOLVED (1 << 11)
+#define PHY_STATUS_LINK                  (1 << 10)
+#define PHY_STATUS_MDI_CROSSOVER_STATUS  (1 << 6)
+#define PHY_STATUS_WIRESPEED_DOWNGRADE   (1 << 5)
+#define PHY_STATUS_TX_PAUSE_ENABLED      (1 << 3)
+#define PHY_STATUS_RX_PAUSE_ENABLED      (1 << 2)
+#define PHY_STATUS_POLARITY              (1 << 1)
+#define PHY_STATUS_JABBER                (1 << 0)
+
+//Speed
+#define PHY_STATUS_SPEED_MASK            (3 << 14)
+#define PHY_STATUS_SPEED_10              (0 << 14)
+#define PHY_STATUS_SPEED_100             (1 << 14)
+#define PHY_STATUS_SPEED_1000            (2 << 14)
+
+//INT_EN register
+#define INT_EN_AN_ERROR                  (1 << 15)
+#define INT_EN_SPEED_CHANGED             (1 << 14)
+#define INT_EN_PAGE_RECEIVED             (1 << 12)
+#define INT_EN_LINK_FAIL                 (1 << 11)
+#define INT_EN_LINK_SUCCESS              (1 << 10)
+#define INT_EN_FAST_LINK_DOWN1           (1 << 9)
+#define INT_EN_LINK_FAIL_BX              (1 << 8)
+#define INT_EN_LINK_SUCCESS_BX           (1 << 7)
+#define INT_EN_FAST_LINK_DOWN0           (1 << 6)
+#define INT_EN_WIRESPEED_DOWNGRADE       (1 << 5)
+#define INT_EN_10MS_PTP                  (1 << 4)
+#define INT_EN_RX_PTP                    (1 << 3)
+#define INT_EN_TX_PTP                    (1 << 2)
+#define INT_EN_POLARITY_CHANGED          (1 << 1)
+#define INT_EN_WOL_PTP                   (1 << 0)
+
+//INT_STATUS register
+#define INT_STATUS_AN_ERROR              (1 << 15)
+#define INT_STATUS_SPEED_CHANGED         (1 << 14)
+#define INT_STATUS_PAGE_RECEIVED         (1 << 12)
+#define INT_STATUS_LINK_FAIL             (1 << 11)
+#define INT_STATUS_LINK_SUCCESS          (1 << 10)
+#define INT_STATUS_FAST_LINK_DOWN1       (1 << 9)
+#define INT_STATUS_LINK_FAIL_BX          (1 << 8)
+#define INT_STATUS_LINK_SUCCESS_BX       (1 << 7)
+#define INT_STATUS_FAST_LINK_DOWN0       (1 << 6)
+#define INT_STATUS_WIRESPEED_DOWNGRADE   (1 << 5)
+#define INT_STATUS_10MS_PTP              (1 << 4)
+#define INT_STATUS_RX_PTP                (1 << 3)
+#define INT_STATUS_TX_PTP                (1 << 2)
+#define INT_STATUS_POLARITY_CHANGED      (1 << 1)
+#define INT_STATUS_WOL_PTP               (1 << 0)
+
+//SMART_SPEED register
+#define SMART_SPEED_EN                   (1 << 5)
+#define SMART_SPEED_RETRY_LIMIT2         (1 << 4)
+#define SMART_SPEED_RETRY_LIMIT1         (1 << 3)
+#define SMART_SPEED_RETRY_LIMIT0         (1 << 2)
+#define SMART_SPEED_TIMER                (1 << 1)
+
+//CDT_CTRL register
+#define CDT_CTRL_MDI_PAIR_SELECT1        (1 << 9)
+#define CDT_CTRL_MDI_PAIR_SELECT0        (1 << 8)
+#define CDT_CTRL_ENABLE_TEST             (1 << 0)
+
+//LED_CTRL register
+#define LED_CTRL_DISABLE_LED             (1 << 15)
+#define LED_CTRL_LED_ON_TIME2            (1 << 14)
+#define LED_CTRL_LED_ON_TIME1            (1 << 13)
+#define LED_CTRL_LED_ON_TIME0            (1 << 12)
+#define LED_CTRL_LED_OFF_TIME2           (1 << 10)
+#define LED_CTRL_LED_OFF_TIME1           (1 << 9)
+#define LED_CTRL_LED_OFF_TIME0           (1 << 8)
+#define LED_CTRL_LED_LINK_CTRL1          (1 << 4)
+#define LED_CTRL_LED_LINK_CTRL0          (1 << 3)
+#define LED_CTRL_LED_ACT_CTRL            (1 << 1)
+
+//MAN_LED_OVERRIDE register
+#define MAN_LED_OVERRIDE_LED_ACT_CTRL    (1 << 12)
+#define MAN_LED_OVERRIDE_LED_LINK_CTRL1  (1 << 7)
+#define MAN_LED_OVERRIDE_LED_LINK_CTRL0  (1 << 6)
+#define MAN_LED_OVERRIDE_LED_RX_CTRL1    (1 << 3)
+#define MAN_LED_OVERRIDE_LED_RX_CTRL0    (1 << 2)
+#define MAN_LED_OVERRIDE_LED_TX_CTRL1    (1 << 1)
+#define MAN_LED_OVERRIDE_LED_TX_CTRL0    (1 << 0)
+
+//CDT_STATUS register
+#define CDT_STATUS_STATUS1               (1 << 9)
+#define CDT_STATUS_STATUS0               (1 << 8)
+#define CDT_STATUS_DELTA_TIME7           (1 << 7)
+#define CDT_STATUS_DELTA_TIME6           (1 << 6)
+#define CDT_STATUS_DELTA_TIME5           (1 << 5)
+#define CDT_STATUS_DELTA_TIME4           (1 << 4)
+#define CDT_STATUS_DELTA_TIME3           (1 << 3)
+#define CDT_STATUS_DELTA_TIME2           (1 << 2)
+#define CDT_STATUS_DELTA_TIME1           (1 << 1)
+#define CDT_STATUS_DELTA_TIME0           (1 << 0)
+
+//CHIP_CONF register
+#define CHIP_CONFIG_BT_BX_REG_SEL        (1 << 15)
+#define CHIP_CONFIG_SMII_IMP_50_75_AUTO  (1 << 14)
+#define CHIP_CONFIG_SGMII_RXIMP_50_75    (1 << 13)
+#define CHIP_CONFIG_SGMII_TXIMP_50_75    (1 << 12)
+#define CHIP_CONFIG_PRIORITY_SEL         (1 << 10)
+#define CHIP_CONFIG_FIBER_MODE_AUTO      (1 << 8)
+#define CHIP_CONFIG_MODE_CFG_QUAL3       (1 << 7)
+#define CHIP_CONFIG_MODE_CFG_QUAL2       (1 << 6)
+#define CHIP_CONFIG_MODE_CFG_QUAL1       (1 << 5)
+#define CHIP_CONFIG_MODE_CFG_QUAL0       (1 << 4)
+#define CHIP_CONFIG_MODE_CFG3            (1 << 3)
+#define CHIP_CONFIG_MODE_CFG2            (1 << 2)
+#define CHIP_CONFIG_MODE_CFG1            (1 << 1)
+#define CHIP_CONFIG_MODE_CFG0            (1 << 0)
+
+//AR8031 Ethernet PHY driver
+extern const PhyDriver ar8031PhyDriver;
+
+//AR8031 related functions
+error_t ar8031Init(NetInterface *interface);
+
+void ar8031Tick(NetInterface *interface);
+
+void ar8031EnableIrq(NetInterface *interface);
+void ar8031DisableIrq(NetInterface *interface);
+
+void ar8031EventHandler(NetInterface *interface);
+
+void ar8031WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t ar8031ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void ar8031DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/avr32_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,733 @@
+/**
+ * @file avr32_eth.c
+ * @brief AVR32 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include <limits.h>
+#include <avr32/io.h>
+#include "interrupt.h"
+#include "intc.h"
+#include "core/net.h"
+#include "drivers/avr32_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//TX buffer
+#pragma data_alignment = 4
+static uint8_t txBuffer[AVR32_ETH_TX_BUFFER_COUNT][AVR32_ETH_TX_BUFFER_SIZE];
+//RX buffer
+#pragma data_alignment = 4
+static uint8_t rxBuffer[AVR32_ETH_RX_BUFFER_COUNT][AVR32_ETH_RX_BUFFER_SIZE];
+//TX buffer descriptors
+#pragma data_alignment = 8
+static Avr32TxBufferDesc txBufferDesc[AVR32_ETH_TX_BUFFER_COUNT];
+//RX buffer descriptors
+#pragma data_alignment = 8
+static Avr32RxBufferDesc rxBufferDesc[AVR32_ETH_RX_BUFFER_COUNT];
+
+//GCC compiler?
+#else
+
+//TX buffer
+static uint8_t txBuffer[AVR32_ETH_TX_BUFFER_COUNT][AVR32_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//RX buffer
+static uint8_t rxBuffer[AVR32_ETH_RX_BUFFER_COUNT][AVR32_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//TX buffer descriptors
+static Avr32TxBufferDesc txBufferDesc[AVR32_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(8)));
+//RX buffer descriptors
+static Avr32RxBufferDesc rxBufferDesc[AVR32_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(8)));
+
+#endif
+
+//TX buffer index
+static uint_t txBufferIndex;
+//RX buffer index
+static uint_t rxBufferIndex;
+
+
+/**
+ * @brief AVR32 Ethernet MAC driver
+ **/
+
+const NicDriver avr32EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   avr32EthInit,
+   avr32EthTick,
+   avr32EthEnableIrq,
+   avr32EthDisableIrq,
+   avr32EthEventHandler,
+   avr32EthSendPacket,
+   avr32EthSetMulticastFilter,
+   avr32EthUpdateMacConfig,
+   avr32EthWritePhyReg,
+   avr32EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief AVR32 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t avr32EthInit(NetInterface *interface)
+{
+   error_t error;
+   volatile uint32_t status;
+
+   //Debug message
+   TRACE_INFO("Initializing AVR32 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //GPIO configuration
+   avr32EthInitGpio(interface);
+
+   //Configure MDC clock speed
+   AVR32_MACB.ncfgr = AVR32_MACB_NCFGR_CLK_DIV64;
+   //Enable management port (MDC and MDIO)
+   AVR32_MACB.ncr |= AVR32_MACB_NCR_MPE_MASK;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Set the MAC address
+   AVR32_MACB.sa1b = interface->macAddr.b[0] |
+      (interface->macAddr.b[1] << 8) |
+      (interface->macAddr.b[2] << 16) |
+      (interface->macAddr.b[3] << 24);
+
+   AVR32_MACB.sa1t = interface->macAddr.b[4] |
+      (interface->macAddr.b[5] << 8);
+
+   //Configure the receive filter
+   AVR32_MACB.ncfgr |= AVR32_MACB_NCFGR_UNI_MASK | AVR32_MACB_NCFGR_MTI_MASK;
+
+   //Initialize hash table
+   AVR32_MACB.hrb = 0;
+   AVR32_MACB.hrt = 0;
+
+   //Initialize buffer descriptors
+   avr32EthInitBufferDesc(interface);
+
+   //Clear transmit status register
+   AVR32_MACB.tsr = AVR32_MACB_TSR_UND_MASK | AVR32_MACB_TSR_COMP_MASK | AVR32_MACB_TSR_BEX_MASK |
+      AVR32_MACB_TSR_TGO_MASK | AVR32_MACB_TSR_RLE_MASK | AVR32_MACB_TSR_COL_MASK | AVR32_MACB_TSR_UBR_MASK;
+   //Clear receive status register
+   AVR32_MACB.rsr = AVR32_MACB_RSR_OVR_MASK | AVR32_MACB_RSR_REC_MASK | AVR32_MACB_RSR_BNA_MASK;
+
+   //First disable all EMAC interrupts
+   AVR32_MACB.idr = 0xFFFFFFFF;
+   //Only the desired ones are enabled
+   AVR32_MACB.ier = AVR32_MACB_IER_ROVR_MASK | AVR32_MACB_IER_TCOMP_MASK | AVR32_MACB_IER_TXERR_MASK |
+      AVR32_MACB_IER_RLE_MASK | AVR32_MACB_IER_TUND_MASK | AVR32_MACB_IER_RXUBR_MASK | AVR32_MACB_IER_RCOMP_MASK;
+
+   //Read EMAC ISR register to clear any pending interrupt
+   status = AVR32_MACB.isr;
+
+   //Register interrupt handler
+   INTC_register_interrupt(avr32EthIrqWrapper, AVR32_MACB_IRQ, AVR32_ETH_IRQ_PRIORITY);
+
+   //Enable the EMAC to transmit and receive data
+   AVR32_MACB.ncr |= AVR32_MACB_NCR_TE_MASK | AVR32_MACB_NCR_RE_MASK;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//EVK1105 evaluation board?
+#if defined(USE_EVK1105)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void avr32EthInitGpio(NetInterface *interface)
+{
+   //Assign RMII pins to peripheral A function
+   AVR32_GPIO.port[1].pmr0c = MACB_RMII_MASK;
+   AVR32_GPIO.port[1].pmr1c =MACB_RMII_MASK;
+
+   //Disable the PIO from controlling the corresponding pins
+   AVR32_GPIO.port[1].gperc = MACB_RMII_MASK;
+
+   //Select RMII operation mode
+   AVR32_MACB.usrio &= ~AVR32_MACB_USRIO_RMII_MASK;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize buffer descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void avr32EthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+   uint32_t address;
+
+   //Initialize TX buffer descriptors
+   for(i = 0; i < AVR32_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current TX buffer
+      address = (uint32_t) txBuffer[i];
+      //Write the address to the descriptor entry
+      txBufferDesc[i].address = address;
+      //Initialize status field
+      txBufferDesc[i].status = MACB_TX_USED;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   txBufferDesc[i - 1].status |= MACB_TX_WRAP;
+   //Initialize TX buffer index
+   txBufferIndex = 0;
+
+   //Initialize RX buffer descriptors
+   for(i = 0; i < AVR32_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current RX buffer
+      address = (uint32_t) rxBuffer[i];
+      //Write the address to the descriptor entry
+      rxBufferDesc[i].address = address & MACB_RX_ADDRESS;
+      //Clear status field
+      rxBufferDesc[i].status = 0;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   rxBufferDesc[i - 1].address |= MACB_RX_WRAP;
+   //Initialize RX buffer index
+   rxBufferIndex = 0;
+
+   //Start location of the TX descriptor list
+   AVR32_MACB.tbqp = (uint32_t) txBufferDesc;
+   //Start location of the RX descriptor list
+   AVR32_MACB.rbqp = (uint32_t) rxBufferDesc;
+}
+
+
+/**
+ * @brief AVR32 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void avr32EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void avr32EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   Enable_global_interrupt();
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void avr32EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   Disable_global_interrupt();
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief AVR32 Ethernet MAC interrupt wrapper
+ **/
+
+__attribute__((naked)) void avr32EthIrqWrapper(void)
+{
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //Call Ethernet MAC interrupt handler
+   avr32EthIrqHandler();
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief AVR32 Ethernet MAC interrupt service routine
+ * @return TRUE if a higher priority task must be woken. Else FALSE is returned
+ **/
+
+bool_t avr32EthIrqHandler(void)
+{
+   bool_t flag;
+   volatile uint32_t isr;
+   volatile uint32_t tsr;
+   volatile uint32_t rsr;
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Each time the software reads EMAC_ISR, it has to check the
+   //contents of EMAC_TSR, EMAC_RSR and EMAC_NSR
+   isr = AVR32_MACB.isr;
+   tsr = AVR32_MACB.tsr;
+   rsr = AVR32_MACB.rsr;
+
+   //A packet has been transmitted?
+   if(tsr & (AVR32_MACB_TSR_UND_MASK | AVR32_MACB_TSR_COMP_MASK | AVR32_MACB_TSR_BEX_MASK |
+      AVR32_MACB_TSR_TGO_MASK | AVR32_MACB_TSR_RLE_MASK | AVR32_MACB_TSR_COL_MASK | AVR32_MACB_TSR_UBR_MASK))
+   {
+      //Only clear TSR flags that are currently set
+      AVR32_MACB.tsr = tsr;
+
+      //Check whether the TX buffer is available for writing
+      if(txBufferDesc[txBufferIndex].status & MACB_TX_USED)
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(rsr & (AVR32_MACB_RSR_OVR_MASK | AVR32_MACB_RSR_REC_MASK | AVR32_MACB_RSR_BNA_MASK))
+   {
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //A higher priority task must be woken?
+   return flag;
+}
+
+
+/**
+ * @brief AVR32 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void avr32EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint32_t rsr;
+
+   //Read receive status
+   rsr = AVR32_MACB.rsr;
+
+   //Packet received?
+   if(rsr & (AVR32_MACB_RSR_OVR_MASK | AVR32_MACB_RSR_REC_MASK | AVR32_MACB_RSR_BNA_MASK))
+   {
+      //Only clear RSR flags that are currently set
+      AVR32_MACB.rsr = rsr;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = avr32EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t avr32EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > AVR32_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(!(txBufferDesc[txBufferIndex].status & MACB_TX_USED))
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(txBuffer[txBufferIndex], buffer, offset, length);
+
+   //Set the necessary flags in the descriptor entry
+   if(txBufferIndex < (AVR32_ETH_TX_BUFFER_COUNT - 1))
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status =
+         MACB_TX_LAST | (length & MACB_TX_LENGTH);
+
+      //Point to the next buffer
+      txBufferIndex++;
+   }
+   else
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status = MACB_TX_WRAP |
+         MACB_TX_LAST | (length & MACB_TX_LENGTH);
+
+      //Wrap around
+      txBufferIndex = 0;
+   }
+
+   //Set the TSTART bit to initiate transmission
+   AVR32_MACB.ncr |= AVR32_MACB_NCR_TSTART_MASK;
+
+   //Check whether the next buffer is available for writing
+   if(txBufferDesc[txBufferIndex].status & MACB_TX_USED)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+uint_t avr32EthReceivePacket(NetInterface *interface)
+{
+   static uint8_t temp[ETH_MAX_FRAME_SIZE];
+   error_t error;
+   uint_t i;
+   uint_t j;
+   uint_t sofIndex;
+   uint_t eofIndex;
+   size_t n;
+   size_t size;
+   size_t length;
+
+   //Initialize SOF and EOF indices
+   sofIndex = UINT_MAX;
+   eofIndex = UINT_MAX;
+
+   //Search for SOF and EOF flags
+   for(i = 0; i < AVR32_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Point to the current entry
+      j = rxBufferIndex + i;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(j >= AVR32_ETH_RX_BUFFER_COUNT)
+         j -= AVR32_ETH_RX_BUFFER_COUNT;
+
+      //No more entries to process?
+      if(!(rxBufferDesc[j].address & MACB_RX_OWNERSHIP))
+      {
+         //Stop processing
+         break;
+      }
+      //A valid SOF has been found?
+      if(rxBufferDesc[j].status & MACB_RX_SOF)
+      {
+         //Save the position of the SOF
+         sofIndex = i;
+      }
+      //A valid EOF has been found?
+      if((rxBufferDesc[j].status & MACB_RX_EOF) && sofIndex != UINT_MAX)
+      {
+         //Save the position of the EOF
+         eofIndex = i;
+         //Retrieve the length of the frame
+         size = rxBufferDesc[j].status & MACB_RX_LENGTH;
+         //Limit the number of data to read
+         size = MIN(size, ETH_MAX_FRAME_SIZE);
+         //Stop processing since we have reached the end of the frame
+         break;
+      }
+   }
+
+   //Determine the number of entries to process
+   if(eofIndex != UINT_MAX)
+      j = eofIndex + 1;
+   else if(sofIndex != UINT_MAX)
+      j = sofIndex;
+   else
+      j = i;
+
+   //Total number of bytes that have been copied from the receive buffer
+   length = 0;
+
+   //Process incoming frame
+   for(i = 0; i < j; i++)
+   {
+      //Any data to copy from current buffer?
+      if(eofIndex != UINT_MAX && i >= sofIndex && i <= eofIndex)
+      {
+         //Calculate the number of bytes to read at a time
+         n = MIN(size, AVR32_ETH_RX_BUFFER_SIZE);
+         //Copy data from receive buffer
+         memcpy(temp + length, rxBuffer[rxBufferIndex], n);
+         //Update byte counters
+         length += n;
+         size -= n;
+      }
+
+      //Mark the current buffer as free
+      rxBufferDesc[rxBufferIndex].address &= ~MACB_RX_OWNERSHIP;
+
+      //Point to the following entry
+      rxBufferIndex++;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(rxBufferIndex >= AVR32_ETH_RX_BUFFER_COUNT)
+         rxBufferIndex = 0;
+   }
+
+   //Any packet to process?
+   if(length > 0)
+   {
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, temp, length);
+      //Valid packet received
+      error = NO_ERROR;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t avr32EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint8_t *p;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating AVR32 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Point to the MAC address
+         p = entry->addr.b;
+
+         //Apply the hash function
+         k = (p[0] >> 6) ^ p[0];
+         k ^= (p[1] >> 4) ^ (p[1] << 2);
+         k ^= (p[2] >> 2) ^ (p[2] << 4);
+         k ^= (p[3] >> 6) ^ p[3];
+         k ^= (p[4] >> 4) ^ (p[4] << 2);
+         k ^= (p[5] >> 2) ^ (p[5] << 4);
+
+         //The hash value is reduced to a 6-bit index
+         k &= 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   AVR32_MACB.hrb = hashTable[0];
+   AVR32_MACB.hrt = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HRB = %08" PRIX32 "\r\n", AVR32_MACB.hrb);
+   TRACE_DEBUG("  HRT = %08" PRIX32 "\r\n", AVR32_MACB.hrt);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t avr32EthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read network configuration register
+   config = AVR32_MACB.ncfgr;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= AVR32_MACB_NCFGR_SPD_MASK;
+   else
+      config &= ~AVR32_MACB_NCFGR_SPD_MASK;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= AVR32_MACB_NCFGR_FD_MASK;
+   else
+      config &= ~AVR32_MACB_NCFGR_FD_MASK;
+
+   //Write configuration value back to NCFGR register
+   AVR32_MACB.ncfgr = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void avr32EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Set up a write operation
+   value = MACB_MAN_SOF_01 | MACB_MAN_RW_01 | MACB_MAN_CODE_10;
+   //PHY address
+   value |= (phyAddr << AVR32_MACB_MAN_PHYA_OFFSET) & AVR32_MACB_MAN_PHYA_MASK;
+   //Register address
+   value |= (regAddr << AVR32_MACB_MAN_REGA_OFFSET) & AVR32_MACB_MAN_REGA_MASK;
+   //Register value
+   value |= data & AVR32_MACB_MAN_DATA_MASK;
+
+   //Start a write operation
+   AVR32_MACB.man = value;
+   //Wait for the write to complete
+   while(!(AVR32_MACB.nsr & AVR32_MACB_NSR_IDLE_MASK));
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t avr32EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Set up a read operation
+   value = MACB_MAN_SOF_01 | MACB_MAN_RW_10 | MACB_MAN_CODE_10;
+   //PHY address
+   value |= (phyAddr << AVR32_MACB_MAN_PHYA_OFFSET) & AVR32_MACB_MAN_PHYA_MASK;
+   //Register address
+   value |= (regAddr << AVR32_MACB_MAN_REGA_OFFSET) & AVR32_MACB_MAN_REGA_MASK;
+
+   //Start a read operation
+   AVR32_MACB.man = value;
+   //Wait for the read to complete
+   while(!(AVR32_MACB.nsr & AVR32_MACB_NSR_IDLE_MASK));
+
+   //Return PHY register contents
+   return AVR32_MACB.man & AVR32_MACB_MAN_DATA_MASK;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/avr32_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,173 @@
+/**
+ * @file avr32_eth.h
+ * @brief AVR32 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _AVR32_ETH_H
+#define _AVR32_ETH_H
+
+//Number of TX buffers
+#ifndef AVR32_ETH_TX_BUFFER_COUNT
+   #define AVR32_ETH_TX_BUFFER_COUNT 2
+#elif (AVR32_ETH_TX_BUFFER_COUNT < 1)
+   #error AVR32_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef AVR32_ETH_TX_BUFFER_SIZE
+   #define AVR32_ETH_TX_BUFFER_SIZE 1536
+#elif (AVR32_ETH_TX_BUFFER_SIZE != 1536)
+   #error AVR32_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef AVR32_ETH_RX_BUFFER_COUNT
+   #define AVR32_ETH_RX_BUFFER_COUNT 48
+#elif (AVR32_ETH_RX_BUFFER_COUNT < 12)
+   #error AVR32_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef AVR32_ETH_RX_BUFFER_SIZE
+   #define AVR32_ETH_RX_BUFFER_SIZE 128
+#elif (AVR32_ETH_RX_BUFFER_SIZE != 128)
+   #error AVR32_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Ethernet interrupt priority
+#ifndef AVR32_ETH_IRQ_PRIORITY
+   #define AVR32_ETH_IRQ_PRIORITY 2
+#elif (AVR32_ETH_IRQ_PRIORITY < 0 || AVR32_ETH_IRQ_PRIORITY > 3)
+   #error AVR32_ETH_IRQ_PRIORITY parameter is not valid
+#endif
+
+//RMII pin definition
+#define MACB_RMII_EREFCK_MASK (1 << (AVR32_MACB_TX_CLK_0_PIN - 32))
+#define MACB_RMII_ETXEN_MASK  (1 << (AVR32_MACB_TX_EN_0_PIN - 32))
+#define MACB_RMII_ETX0_MASK   (1 << (AVR32_MACB_TXD_0_PIN - 32))
+#define MACB_RMII_ETX1_MASK   (1 << (AVR32_MACB_TXD_1_PIN - 32))
+#define MACB_RMII_ERX0_MASK   (1 << (AVR32_MACB_RXD_0_PIN - 32))
+#define MACB_RMII_ERX1_MASK   (1 << (AVR32_MACB_RXD_1_PIN - 32))
+#define MACB_RMII_ERXER_MASK  (1 << (AVR32_MACB_RX_ER_0_PIN - 32))
+#define MACB_RMII_ECRSDV_MASK (1 << (AVR32_MACB_RX_DV_0_PIN - 32))
+#define MACB_RMII_MDC_MASK    (1 << (AVR32_MACB_MDC_0_PIN - 32))
+#define MACB_RMII_MDIO_MASK   (1 << (AVR32_MACB_MDIO_0_PIN - 32))
+
+//RMII signals
+#define MACB_RMII_MASK (MACB_RMII_EREFCK_MASK | MACB_RMII_ETXEN_MASK | \
+   MACB_RMII_ETX0_MASK | MACB_RMII_ETX1_MASK | MACB_RMII_ERX0_MASK | MACB_RMII_ERX1_MASK | \
+   MACB_RMII_ERXER_MASK | MACB_RMII_ECRSDV_MASK | MACB_RMII_MDC_MASK | MACB_RMII_MDIO_MASK)
+
+//PHY maintenance register (MAN)
+#define MACB_MAN_SOF_01  (1 << AVR32_MACB_MAN_SOF_OFFSET)
+#define MACB_MAN_RW_01   (1 << AVR32_MACB_MAN_RW_OFFSET)
+#define MACB_MAN_RW_10   (2 << AVR32_MACB_MAN_RW_OFFSET)
+#define MACB_MAN_CODE_10 (2 << AVR32_MACB_MAN_CODE_OFFSET)
+
+//TX buffer descriptor flags
+#define MACB_TX_USED           0x80000000
+#define MACB_TX_WRAP           0x40000000
+#define MACB_TX_ERROR          0x20000000
+#define MACB_TX_UNDERRUN       0x10000000
+#define MACB_TX_EXHAUSTED      0x08000000
+#define MACB_TX_NO_CRC         0x00010000
+#define MACB_TX_LAST           0x00008000
+#define MACB_TX_LENGTH         0x000007FF
+
+//RX buffer descriptor flags
+#define MACB_RX_ADDRESS        0xFFFFFFFC
+#define MACB_RX_WRAP           0x00000002
+#define MACB_RX_OWNERSHIP      0x00000001
+#define MACB_RX_BROADCAST      0x80000000
+#define MACB_RX_MULTICAST_HASH 0x40000000
+#define MACB_RX_UNICAST_HASH   0x20000000
+#define MACB_RX_EXT_ADDR       0x10000000
+#define MACB_RX_SAR1           0x04000000
+#define MACB_RX_SAR2           0x02000000
+#define MACB_RX_SAR3           0x01000000
+#define MACB_RX_SAR4           0x00800000
+#define MACB_RX_TYPE_ID        0x00400000
+#define MACB_RX_VLAN_TAG       0x00200000
+#define MACB_RX_PRIORITY_TAG   0x00100000
+#define MACB_RX_VLAN_PRIORITY  0x000E0000
+#define MACB_RX_CFI            0x00010000
+#define MACB_RX_EOF            0x00008000
+#define MACB_RX_SOF            0x00004000
+#define MACB_RX_OFFSET         0x00003000
+#define MACB_RX_LENGTH         0x00000FFF
+
+
+/**
+ * @brief Transmit buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Avr32TxBufferDesc;
+
+
+/**
+ * @brief Receive buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Avr32RxBufferDesc;
+
+
+//AVR32 Ethernet MAC driver
+extern const NicDriver avr32EthDriver;
+
+//AVR32 Ethernet MAC related functions
+error_t avr32EthInit(NetInterface *interface);
+void avr32EthInitGpio(NetInterface *interface);
+void avr32EthInitBufferDesc(NetInterface *interface);
+
+void avr32EthTick(NetInterface *interface);
+
+void avr32EthEnableIrq(NetInterface *interface);
+void avr32EthDisableIrq(NetInterface *interface);
+void avr32EthIrqWrapper(void);
+bool_t avr32EthIrqHandler(void);
+void avr32EthEventHandler(NetInterface *interface);
+
+error_t avr32EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t avr32EthReceivePacket(NetInterface *interface);
+
+error_t avr32EthSetMulticastFilter(NetInterface *interface);
+error_t avr32EthUpdateMacConfig(NetInterface *interface);
+
+void avr32EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t avr32EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/bcm43362_driver.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,544 @@
+/**
+ * @file bcm43362_driver.c
+ * @brief BCM43362 Wi-Fi controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "bcm43362_driver.h"
+#include "debug.h"
+
+//WICED dependencies
+#include "platform_init.h"
+#include "platform_mcu_peripheral.h"
+#include "wwd_constants.h"
+#include "wwd_structures.h"
+#include "wwd_buffer.h"
+#include "wwd_events.h"
+#include "wwd_management.h"
+#include "wwd_poll.h"
+#include "wwd_wifi.h"
+#include "wwd_buffer_interface.h"
+#include "wwd_bus_protocol_interface.h"
+#include "wwd_network_constants.h"
+#include "wwd_network_interface.h"
+
+//Underlying network interface
+static NetInterface *bcm43362StaInterface = NULL;
+static NetInterface *bcm43362ApInterface = NULL;
+
+//RX queue
+QueueHandle_t wwdRxQueue;
+
+//Regitered Wi-Fi events
+static const wwd_event_num_t app_wifi_events[] =
+{
+   WLC_E_IF,
+   WLC_E_LINK,
+   WLC_E_ASSOC_IND,
+   WLC_E_DISASSOC_IND,
+   WLC_E_NONE
+};
+
+//Forward declaration of functions
+void *app_wifi_event_handler(const wwd_event_header_t *event_header,
+   const uint8_t *event_data, void *handler_user_data);
+
+
+/**
+ * @brief BCM43362 driver (STA mode)
+ **/
+
+const NicDriver bcm43362StaDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   bcm43362Init,
+   bcm43362Tick,
+   bcm43362EnableIrq,
+   bcm43362DisableIrq,
+   bcm43362EventHandler,
+   bcm43362SendPacket,
+   bcm43362SetMulticastFilter,
+   NULL,
+   NULL,
+   NULL,
+   TRUE,
+   TRUE,
+   TRUE,
+   TRUE
+};
+
+
+/**
+ * @brief BCM43362 driver (AP mode)
+ **/
+
+const NicDriver bcm43362ApDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   bcm43362Init,
+   bcm43362Tick,
+   bcm43362EnableIrq,
+   bcm43362DisableIrq,
+   bcm43362EventHandler,
+   bcm43362SendPacket,
+   bcm43362SetMulticastFilter,
+   NULL,
+   NULL,
+   NULL,
+   TRUE,
+   TRUE,
+   TRUE,
+   TRUE
+};
+
+
+/**
+ * @brief BCM43362 initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t bcm43362Init(NetInterface *interface)
+{
+   wwd_result_t ret;
+   //MacAddr macAddr;
+
+   //STA or AP mode?
+   if(interface->nicDriver == &bcm43362StaDriver)
+   {
+      //Debug message
+      TRACE_INFO("Initializing BCM43362 (STA mode)...\r\n");
+   }
+   else
+   {
+      //Debug message
+      TRACE_INFO("Initializing BCM43362 (AP mode)...\r\n");
+   }
+
+   //Start of exception handling block
+   do
+   {
+      //Initialization sequence is performed once at startup
+      if(bcm43362StaInterface == NULL && bcm43362ApInterface == NULL)
+      {
+         platform_init_mcu_infrastructure();
+         wwd_buffer_init(NULL);
+
+         //Create TX queue
+         wwdRxQueue = xQueueCreate(16, sizeof(wiced_buffer_t));
+
+         //Initialize Wi-Fi controller
+         ret = wwd_management_wifi_on(WICED_COUNTRY_FRANCE);
+         TRACE_INFO("wwd_management_wifi_on=%d (0x%04X)\r\n", ret, ret);
+
+         ret = wwd_management_set_event_handler(app_wifi_events, app_wifi_event_handler, NULL, WWD_AP_INTERFACE);
+         TRACE_INFO("wwd_management_set_event_handler=%d (0x%04X)\r\n", ret, ret);
+      }
+      else
+      {
+         //Initialization was already done
+         ret = WWD_SUCCESS;
+      }
+
+      //STA or AP mode?
+      if(interface->nicDriver == &bcm43362StaDriver)
+      {
+         //Save underlying network interface (STA mode)
+         bcm43362StaInterface = interface;
+
+         //Optionally set the station MAC address
+         //if(macCompAddr(&interface->macAddr, &MAC_UNSPECIFIED_ADDR))
+         {
+            //Use the factory preprogrammed station address
+            ret = wwd_wifi_get_mac_address((wiced_mac_t *) &interface->macAddr, WWD_STA_INTERFACE);
+            TRACE_INFO("wwd_wifi_get_mac_address=%d (0x%04X)\r\n", ret, ret);
+            TRACE_INFO("MAC=%s\r\n", macAddrToString(&interface->macAddr, NULL));
+
+            //Generate the 64-bit interface identifier
+            macAddrToEui64(&interface->macAddr, &interface->eui64);
+         }
+      }
+      else
+      {
+         //Save underlying network interface (AP mode)
+         bcm43362ApInterface = interface;
+
+         //Optionally set the station MAC address
+         //if(macCompAddr(&interface->macAddr, &MAC_UNSPECIFIED_ADDR))
+         {
+            //Use the factory preprogrammed station address
+            ret = wwd_wifi_get_mac_address((wiced_mac_t *) &interface->macAddr, WWD_STA_INTERFACE);
+            TRACE_INFO("wwd_wifi_get_mac_address=%d (0x%04X)\r\n", ret, ret);
+            TRACE_INFO("MAC=%s\r\n", macAddrToString(&interface->macAddr, NULL));
+
+            //Generate the 64-bit interface identifier
+            macAddrToEui64(&interface->macAddr, &interface->eui64);
+         }
+      }
+
+      //End of exception handling block
+   } while(0);
+
+   //BCM43362 is now ready to send
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief BCM43362 timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void bcm43362Tick(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void bcm43362EnableIrq(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void bcm43362DisableIrq(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief BCM43362 interrupt service routine
+ * @return TRUE if a higher priority task must be woken. Else FALSE is returned
+ **/
+
+bool_t bcm43362IrqHandler(void)
+{
+   bool_t flag;
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //STA and/or AP mode?
+   if(bcm43362StaInterface != NULL)
+      bcm43362StaInterface->nicEvent = TRUE;
+   else if(bcm43362ApInterface != NULL)
+      bcm43362ApInterface->nicEvent = TRUE;
+
+   //Notify the TCP/IP stack of the event
+   flag = osSetEventFromIsr(&netEvent);
+
+   //A higher priority task must be woken?
+   return flag;
+}
+
+
+/**
+ * @brief BCM43362 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void bcm43362EventHandler(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t bcm43362SendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   wwd_result_t ret;
+   wiced_buffer_t packet;
+   size_t length;
+   uint8_t *p;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Allocate a network buffer
+   ret = host_buffer_get(&packet, WWD_NETWORK_TX, length + WICED_LINK_OVERHEAD_BELOW_ETHERNET_FRAME_MAX, FALSE);
+
+   //Check status code
+   if(ret == WWD_SUCCESS)
+   {
+      //Make room for additional headers
+      host_buffer_add_remove_at_front(&packet, WICED_LINK_OVERHEAD_BELOW_ETHERNET_FRAME_MAX);
+
+      //Point to the data payload
+      p = host_buffer_get_current_piece_data_pointer(packet);
+
+      //Copy user data
+      netBufferRead(p, buffer, offset, length);
+
+      //Adjust the length of the buffer
+      host_buffer_set_size(packet, length);
+
+      //STA or AP mode?
+      if(interface == bcm43362StaInterface)
+      {
+         //Send packet
+         wwd_network_send_ethernet_data(packet, WWD_STA_INTERFACE);
+      }
+      else
+      {
+         //Send packet
+         wwd_network_send_ethernet_data(packet, WWD_AP_INTERFACE);
+      }
+   }
+   else
+   {
+      TRACE_ERROR("##### bcm43362SendPacket ALLOC FAILED ####\r\n");
+   }
+
+   //The transmitter can accept another packet
+   osSetEvent(&interface->nicTxEvent);
+
+   //Return status code
+   if(ret == WWD_SUCCESS)
+      return NO_ERROR;
+   else
+      return ERROR_FAILURE;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t bcm43362SetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   wwd_result_t ret;
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_INFO("Updating BCM43362 multicast filter...\r\n");
+
+   //STA interface?
+   if(interface == bcm43362StaInterface)
+   {
+      //The MAC filter table contains the multicast MAC addresses
+      //to accept when receiving an Ethernet frame
+      for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+      {
+         //Point to the current entry
+         entry = &interface->macMulticastFilter[i];
+
+         //Check whether the MAC filter table should be updated for the
+         //current multicast address
+         if(!macCompAddr(&entry->addr, &MAC_UNSPECIFIED_ADDR))
+         {
+            if(entry->addFlag)
+            {
+               //Add a new entry to the MAC filter table
+               //ret = wwd_wifi_register_multicast_address((wiced_mac_t *) entry->addr.b);
+               //TRACE_ERROR("wwd_wifi_register_multicast_address=%d (0x%04X)\r\n", ret, ret);
+            }
+            else if(entry->deleteFlag)
+            {
+               //Remove the current entry from the MAC filter table
+               //ret = wwd_wifi_unregister_multicast_address((wiced_mac_t *) entry->addr.b);
+               //TRACE_ERROR("wwd_wifi_unregister_multicast_address=%d (0x%04X)\r\n", ret, ret);
+            }
+         }
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Callback function that handles Wi-Fi events
+ **/
+
+void *app_wifi_event_handler(const wwd_event_header_t *event_header, const uint8_t *event_data, void *handler_user_data)
+{
+   //Check event type
+   switch(event_header->event_type)
+   {
+   //I/F change?
+   case WLC_E_IF:
+      TRACE_INFO("### app_wifi_event_handler: WLC_E_IF\r\n");
+      break;
+
+   //802.11 ASSOC indication?
+   case WLC_E_ASSOC_IND:
+      TRACE_INFO("### app_wifi_event_handler: WLC_E_ASSOC_IND\r\n");
+      break;
+
+   //802.11 DISASSOC indication?
+   case WLC_E_DISASSOC_IND:
+      TRACE_INFO("### app_wifi_event_handler: WLC_E_DISASSOC_IND\r\n");
+      break;
+
+   //Generic link indication?
+   case WLC_E_LINK:
+      //Debug message
+      TRACE_INFO("### app_wifi_event_handler: WLC_E_LINK\r\n");
+
+      //STA interface?
+      if(event_header->interface == WWD_STA_INTERFACE)
+      {
+         if(bcm43362StaInterface != NULL)
+         {
+            //Check link state
+            if(event_header->flags & 0x01)
+               bcm43362StaInterface->linkState = TRUE;
+            else
+               bcm43362StaInterface->linkState = FALSE;
+
+            //Get exclusive access
+            osAcquireMutex(&netMutex);
+            //Process link state change event
+            nicNotifyLinkChange(bcm43362StaInterface);
+            //Release exclusive access
+            osReleaseMutex(&netMutex);
+         }
+      }
+      //AP interface?
+      else if(event_header->interface == WWD_AP_INTERFACE)
+      {
+         if(bcm43362ApInterface != NULL)
+         {
+            //Check link state
+            if(event_header->flags & 0x01)
+               bcm43362ApInterface->linkState = TRUE;
+            else
+               bcm43362ApInterface->linkState = FALSE;
+
+            //Get exclusive access
+            osAcquireMutex(&netMutex);
+            //Process link state change event
+            nicNotifyLinkChange(bcm43362ApInterface);
+            //Release exclusive access
+            osReleaseMutex(&netMutex);
+         }
+      }
+
+      break;
+
+   //Unknown event?
+   default:
+      TRACE_INFO("### app_wifi_event_handler: Unknown event\r\n");
+      break;
+   }
+
+   return handler_user_data;
+}
+
+
+/**
+ * @brief Callback function that handles incoming packets
+ **/
+
+void host_network_process_ethernet_data(wiced_buffer_t buffer, wwd_interface_t interface)
+{
+   size_t n;
+   uint8_t *p;
+
+   //Point to the incoming packet
+   p = host_buffer_get_current_piece_data_pointer(buffer);
+   //Retrieve the length of the packet
+   n = host_buffer_get_current_piece_size(buffer);
+
+   //Valid packet received?
+   if(p != NULL && n > 0)
+   {
+      if(interface == WWD_STA_INTERFACE)
+      {
+         if(bcm43362StaInterface != NULL)
+         {
+            //Get exclusive access
+            osAcquireMutex(&netMutex);
+            //Process link state change event
+            nicProcessPacket(bcm43362StaInterface, p, n);
+            //Release exclusive access
+            osReleaseMutex(&netMutex);
+         }
+      }
+      else if(interface == WWD_AP_INTERFACE)
+      {
+         if(bcm43362ApInterface != NULL)
+         {
+            //Get exclusive access
+            osAcquireMutex(&netMutex);
+            //Process link state change event
+            nicProcessPacket(bcm43362ApInterface, p, n);
+            //Release exclusive access
+            osReleaseMutex(&netMutex);
+         }
+      }
+   }
+
+   //Release network buffer
+   host_buffer_release(buffer, WWD_NETWORK_RX);
+}
+
+
+//Miscellaneous WICED dependencies
+signed int xTaskIsTaskFinished(void *xTask)
+{
+   TRACE_INFO("### xTaskIsTaskFinished\r\n");
+   return pdTRUE;
+}
+
+portBASE_TYPE vTaskFreeTerminated(TaskHandle_t xTask)
+{
+   TRACE_INFO("### vTaskFreeTerminated\r\n");
+   return pdTRUE;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/bcm43362_driver.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,59 @@
+/**
+ * @file bcm43362_driver.h
+ * @brief BCM43362 Wi-Fi controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _BCM43362_DRIVER_H
+#define _BCM43362_DRIVER_H
+
+//Dependencies
+#include "core/nic.h"
+
+//BCM43362 driver (STA mode)
+extern const NicDriver bcm43362StaDriver;
+//BCM43362 driver (AP mode)
+extern const NicDriver bcm43362ApDriver;
+
+//BCM43362 related functions
+error_t bcm43362Init(NetInterface *interface);
+
+void bcm43362Tick(NetInterface *interface);
+
+void bcm43362EnableIrq(NetInterface *interface);
+void bcm43362DisableIrq(NetInterface *interface);
+bool_t bcm43362IrqHandler(void);
+void bcm43362EventHandler(NetInterface *interface);
+
+error_t bcm43362SendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t bcm43362SetMulticastFilter(NetInterface *interface);
+
+void bcm43362AppWifiEvent(uint8_t msgType, void *msg);
+void bcm43362AppEthEvent(uint8_t msgType, void *msg, void *ctrlBuf);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/dm9000.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,698 @@
+/**
+ * @file dm9000.c
+ * @brief DM9000A/B Ethernet controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "core/ethernet.h"
+#include "drivers/dm9000.h"
+#include "debug.h"
+
+
+/**
+ * @brief DM9000 driver
+ **/
+
+const NicDriver dm9000Driver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   dm9000Init,
+   dm9000Tick,
+   dm9000EnableIrq,
+   dm9000DisableIrq,
+   dm9000EventHandler,
+   dm9000SendPacket,
+   dm9000SetMulticastFilter,
+   NULL,
+   NULL,
+   NULL,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief DM9000 controller initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t dm9000Init(NetInterface *interface)
+{
+   uint_t i;
+   uint16_t vendorId;
+   uint16_t productId;
+   uint8_t chipRevision;
+   Dm9000Context *context;
+
+   //Debug message
+   TRACE_INFO("Initializing DM9000 Ethernet controller...\r\n");
+
+   //Initialize external interrupt line
+   interface->extIntDriver->init();
+
+   //Point to the driver context
+   context = (Dm9000Context *) interface->nicContext;
+
+   //Initialize driver specific variables
+   context->queuedPackets = 0;
+
+   //Allocate TX and RX buffers
+   context->txBuffer = memPoolAlloc(ETH_MAX_FRAME_SIZE);
+   context->rxBuffer = memPoolAlloc(ETH_MAX_FRAME_SIZE);
+
+   //Failed to allocate memory?
+   if(context->txBuffer == NULL || context->rxBuffer == NULL)
+   {
+      //Clean up side effects
+      memPoolFree(context->txBuffer);
+      memPoolFree(context->rxBuffer);
+
+      //Report an error
+      return ERROR_OUT_OF_MEMORY;
+   }
+
+   //Retrieve vendorID, product ID and chip revision
+   vendorId = (dm9000ReadReg(DM9000_REG_VIDH) << 8) | dm9000ReadReg(DM9000_REG_VIDL);
+   productId = (dm9000ReadReg(DM9000_REG_PIDH) << 8) | dm9000ReadReg(DM9000_REG_PIDL);
+   chipRevision = dm9000ReadReg(DM9000_REG_CHIPR);
+
+   //Check vendor ID and product ID
+   if(vendorId != DM9000_VID || productId != DM9000_PID)
+      return ERROR_WRONG_IDENTIFIER;
+   //Check chip revision
+   if(chipRevision != DM9000A_CHIP_REV && chipRevision != DM9000B_CHIP_REV)
+      return ERROR_WRONG_IDENTIFIER;
+
+   //Power up the internal PHY by clearing PHYPD
+   dm9000WriteReg(DM9000_REG_GPR, 0x00);
+   //Wait for the PHY to be ready
+   sleep(10);
+
+   //Software reset
+   dm9000WriteReg(DM9000_REG_NCR, NCR_RST);
+   //Wait for the reset to complete
+   while(dm9000ReadReg(DM9000_REG_NCR) & NCR_RST);
+
+   //PHY software reset
+   dm9000WritePhyReg(DM9000_PHY_REG_BMCR, BMCR_RST);
+   //Wait for the PHY reset to complete
+   while(dm9000ReadPhyReg(DM9000_PHY_REG_BMCR) & BMCR_RST);
+
+   //Debug message
+   TRACE_INFO("  VID = 0x%04" PRIX16 "\r\n", vendorId);
+   TRACE_INFO("  PID = 0x%04" PRIX16 "\r\n", productId);
+   TRACE_INFO("  CHIPR = 0x%02" PRIX8 "\r\n", chipRevision);
+   TRACE_INFO("  PHYIDR1 = 0x%04" PRIX16 "\r\n", dm9000ReadPhyReg(DM9000_PHY_REG_PHYIDR1));
+   TRACE_INFO("  PHYIDR2 = 0x%04" PRIX16 "\r\n", dm9000ReadPhyReg(DM9000_PHY_REG_PHYIDR2));
+
+   //Enable loopback mode?
+#if (DM9000_LOOPBACK_MODE == ENABLED)
+   dm9000WriteReg(DM9000_REG_NCR, DM9000_LBK_PHY);
+   dm9000WritePhyReg(DM9000_PHY_REG_BMCR, BMCR_LOOPBACK | BMCR_SPEED_SEL | BMCR_AN_EN | BMCR_DUPLEX_MODE);
+#endif
+
+   //Set host MAC address
+   for(i = 0; i < 6; i++)
+      dm9000WriteReg(DM9000_REG_PAR0 + i, interface->macAddr.b[i]);
+
+   //Initialize hash table
+   for(i = 0; i < 8; i++)
+      dm9000WriteReg(DM9000_REG_MAR0 + i, 0x00);
+
+   //Always accept broadcast packets
+   dm9000WriteReg(DM9000_REG_MAR7, 0x80);
+
+   //Enable the Pointer Auto Return function
+   dm9000WriteReg(DM9000_REG_IMR, IMR_PAR);
+   //Clear NSR status bits
+   dm9000WriteReg(DM9000_REG_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);
+   //Clear interrupt flags
+   dm9000WriteReg(DM9000_REG_ISR, ISR_LNKCHG | ISR_UDRUN | ISR_ROO | ISR_ROS | ISR_PT | ISR_PR);
+   //Enable interrupts
+   dm9000WriteReg(DM9000_REG_IMR, IMR_PAR | IMR_LNKCHGI | IMR_PTI | IMR_PRI);
+   //Enable the receiver by setting RXEN
+   dm9000WriteReg(DM9000_REG_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->nicEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief DM9000 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void dm9000Tick(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void dm9000EnableIrq(NetInterface *interface)
+{
+   //Enable interrupts
+   interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void dm9000DisableIrq(NetInterface *interface)
+{
+   //Disable interrupts
+   interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief DM9000 interrupt service routine
+ * @param[in] interface Underlying network interface
+ * @return TRUE if a higher priority task must be woken. Else FALSE is returned
+ **/
+
+bool_t dm9000IrqHandler(NetInterface *interface)
+{
+   bool_t flag;
+   uint8_t status;
+   uint8_t mask;
+   Dm9000Context *context;
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Point to the driver context
+   context = (Dm9000Context *) interface->nicContext;
+
+   //Read interrupt status register
+   status = dm9000ReadReg(DM9000_REG_ISR);
+
+   //Link status change?
+   if(status & ISR_LNKCHG)
+   {
+      //Read interrupt mask register
+      mask = dm9000ReadReg(DM9000_REG_IMR);
+      //Disable LNKCHGI interrupt
+      dm9000WriteReg(DM9000_REG_IMR, mask & ~IMR_LNKCHGI);
+
+      //Set event flag
+      interface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Packet transmission complete?
+   if(status & ISR_PT)
+   {
+      //Check TX complete status bits
+      if(dm9000ReadReg(DM9000_REG_NSR) & (NSR_TX2END | NSR_TX1END))
+      {
+         //The transmission of the current packet is complete
+         if(context->queuedPackets > 0)
+            context->queuedPackets--;
+
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&interface->nicTxEvent);
+      }
+
+      //Clear interrupt flag
+      dm9000WriteReg(DM9000_REG_ISR, ISR_PT);
+   }
+
+   //Packet received?
+   if(status & ISR_PR)
+   {
+      //Read interrupt mask register
+      mask = dm9000ReadReg(DM9000_REG_IMR);
+      //Disable PRI interrupt
+      dm9000WriteReg(DM9000_REG_IMR, mask & ~IMR_PRI);
+
+      //Set event flag
+      interface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //A higher priority task must be woken?
+   return flag;
+}
+
+
+/**
+ * @brief DM9000 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void dm9000EventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint8_t status;
+
+   //Read interrupt status register
+   status = dm9000ReadReg(DM9000_REG_ISR);
+
+   //Check whether the link status has changed?
+   if(status & ISR_LNKCHG)
+   {
+      //Clear interrupt flag
+      dm9000WriteReg(DM9000_REG_ISR, ISR_LNKCHG);
+      //Read network status register
+      status = dm9000ReadReg(DM9000_REG_NSR);
+
+      //Check link state
+      if(status & NSR_LINKST)
+      {
+         //Get current speed
+         if(status & NSR_SPEED)
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+         else
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+
+         //Read network control register
+         status = dm9000ReadReg(DM9000_REG_NCR);
+
+         //Determine the new duplex mode
+         if(status & NCR_FDX)
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+         else
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+
+         //Link is up
+         interface->linkState = TRUE;
+      }
+      else
+      {
+         //Link is down
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+
+   //Check whether a packet has been received?
+   if(status & ISR_PR)
+   {
+      //Clear interrupt flag
+      dm9000WriteReg(DM9000_REG_ISR, ISR_PR);
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = dm9000ReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable LNKCHGI and PRI interrupts
+   dm9000WriteReg(DM9000_REG_IMR, IMR_PAR | IMR_LNKCHGI | IMR_PTI | IMR_PRI);
+}
+
+
+/**
+ * @brief Send a packet to DM9000
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t dm9000SendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t i;
+   size_t length;
+   uint16_t *p;
+   Dm9000Context *context;
+
+   //Point to the driver context
+   context = (Dm9000Context *) interface->nicContext;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > ETH_MAX_FRAME_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Copy user data
+   netBufferRead(context->txBuffer, buffer, offset, length);
+
+   //A dummy write is required before accessing FIFO
+   dm9000WriteReg(DM9000_REG_MWCMDX, 0);
+   //Select MWCMD register
+   DM9000_INDEX_REG = DM9000_REG_MWCMD;
+
+   //Point to the beginning of the buffer
+   p = (uint16_t *) context->txBuffer;
+
+   //Write data to the FIFO using 16-bit mode
+   for(i = length; i > 1; i -= 2)
+      DM9000_DATA_REG = *(p++);
+
+   //Odd number of bytes?
+   if(i > 0)
+      DM9000_DATA_REG = *((uint8_t *) p);
+
+   //Write the number of bytes to send
+   dm9000WriteReg(DM9000_REG_TXPLL, LSB(length));
+   dm9000WriteReg(DM9000_REG_TXPLH, MSB(length));
+
+   //Clear interrupt flag
+   dm9000WriteReg(DM9000_REG_ISR, ISR_PT);
+   //Start data transfer
+   dm9000WriteReg(DM9000_REG_TCR, TCR_TXREQ);
+
+   //The packet was successfully written to FIFO
+   context->queuedPackets++;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t dm9000ReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t i;
+   size_t n;
+   size_t length;
+   volatile uint8_t status;
+   volatile uint16_t data;
+   Dm9000Context *context;
+
+   //Point to the driver context
+   context = (Dm9000Context *) interface->nicContext;
+
+   //A dummy read is required before accessing the 4-byte header
+   data = dm9000ReadReg(DM9000_REG_MRCMDX);
+
+   //Select MRCMDX1 register
+   DM9000_INDEX_REG = DM9000_REG_MRCMDX1;
+   //Read the first byte of the header
+   status = LSB(DM9000_DATA_REG);
+
+   //The first byte indicates if a packet has been received
+   if(status == 0x01)
+   {
+      //Select MRCMD register
+      DM9000_INDEX_REG = DM9000_REG_MRCMD;
+      //The second byte is the RX status byte
+      status = MSB(DM9000_DATA_REG);
+
+      //Retrieve packet length
+      length = DM9000_DATA_REG;
+      //Limit the number of data to read
+      n = MIN(length, ETH_MAX_FRAME_SIZE);
+
+      //Point to the beginning of the buffer
+      i = 0;
+
+      //Make sure no error occurred
+      if(!(status & (RSR_LCS | RSR_RWTO | RSR_PLE | RSR_AE | RSR_CE | RSR_FOE)))
+      {
+         //Read data from FIFO using 16-bit mode
+         while((i + 1) < n)
+         {
+            data = DM9000_DATA_REG;
+            context->rxBuffer[i++] = LSB(data);
+            context->rxBuffer[i++] = MSB(data);
+         }
+
+         //Odd number of bytes to read?
+         if((i + 1) == n)
+         {
+            data = DM9000_DATA_REG;
+            context->rxBuffer[i] = LSB(data);
+            i += 2;
+         }
+
+         //Valid packet received
+         error = NO_ERROR;
+      }
+      else
+      {
+         //The received packet contains an error
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Flush remaining bytes
+      while(i < length)
+      {
+         data = DM9000_DATA_REG;
+         i += 2;
+      }
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Check whether a valid packet has been received
+   if(!error)
+   {
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, context->rxBuffer, n);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t dm9000SetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint8_t hashTable[8];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating DM9000 hash table...\r\n");
+
+   //Clear hash table
+   memset(hashTable, 0, sizeof(hashTable));
+   //Always accept broadcast packets regardless of the MAC filter table
+   hashTable[7] = 0x80;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = dm9000CalcCrc(&entry->addr, sizeof(MacAddr));
+         //Calculate the corresponding index in the table
+         k = crc & 0x3F;
+         //Update hash table contents
+         hashTable[k / 8] |= (1 << (k % 8));
+      }
+   }
+
+   //Write the hash table to the DM9000 controller
+   for(i = 0; i < 8; i++)
+      dm9000WriteReg(DM9000_REG_MAR0 + i, hashTable[i]);
+
+   //Debug message
+   TRACE_DEBUG("  MAR = %02" PRIX8 " %02" PRIX8 " %02" PRIX8 " %02" PRIX8 " "
+      "%02" PRIX8 " %02" PRIX8 " %02" PRIX8 " %02" PRIX8 "\r\n",
+      dm9000ReadReg(DM9000_REG_MAR0), dm9000ReadReg(DM9000_REG_MAR1),
+      dm9000ReadReg(DM9000_REG_MAR2), dm9000ReadReg(DM9000_REG_MAR3),
+      dm9000ReadReg(DM9000_REG_MAR4), dm9000ReadReg(DM9000_REG_MAR5),
+      dm9000ReadReg(DM9000_REG_MAR6), dm9000ReadReg(DM9000_REG_MAR7));
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write DM9000 register
+ * @param[in] address Register address
+ * @param[in] data Register value
+ **/
+
+void dm9000WriteReg(uint8_t address, uint8_t data)
+{
+   //Write register address to INDEX register
+   DM9000_INDEX_REG = address;
+   //Write register value to DATA register
+   DM9000_DATA_REG = data;
+}
+
+
+/**
+ * @brief Read DM9000 register
+ * @param[in] address Register address
+ * @return Register value
+ **/
+
+uint8_t dm9000ReadReg(uint8_t address)
+{
+   //Write register address to INDEX register
+   DM9000_INDEX_REG = address;
+   //Read register value from DATA register
+   return DM9000_DATA_REG;
+}
+
+
+/**
+ * @brief Write DM9000 PHY register
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void dm9000WritePhyReg(uint8_t address, uint16_t data)
+{
+   //Write PHY register address
+   dm9000WriteReg(DM9000_REG_EPAR, 0x40 | address);
+   //Write register value
+   dm9000WriteReg(DM9000_REG_EPDRL, LSB(data));
+   dm9000WriteReg(DM9000_REG_EPDRH, MSB(data));
+
+   //Start the write operation
+   dm9000WriteReg(DM9000_REG_EPCR, EPCR_EPOS | EPCR_ERPRW);
+   //PHY access is still in progress?
+   while(dm9000ReadReg(DM9000_REG_EPCR) & EPCR_ERRE);
+
+   //Wait 5us minimum
+   usleep(5);
+   //Clear command register
+   dm9000WriteReg(DM9000_REG_EPCR, EPCR_EPOS);
+}
+
+
+/**
+ * @brief Read DM9000 PHY register
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t dm9000ReadPhyReg(uint8_t address)
+{
+   //Write PHY register address
+   dm9000WriteReg(DM9000_REG_EPAR, 0x40 | address);
+
+   //Start the read operation
+   dm9000WriteReg(DM9000_REG_EPCR, EPCR_EPOS | EPCR_ERPRR);
+   //PHY access is still in progress?
+   while(dm9000ReadReg(DM9000_REG_EPCR) & EPCR_ERRE);
+
+   //Clear command register
+   dm9000WriteReg(DM9000_REG_EPCR, EPCR_EPOS);
+   //Wait 5us minimum
+   usleep(5);
+
+   //Return register value
+   return (dm9000ReadReg(DM9000_REG_EPDRH) << 8) | dm9000ReadReg(DM9000_REG_EPDRL);
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t dm9000CalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //Update CRC value
+      crc ^= p[i];
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         if(crc & 0x00000001)
+            crc = (crc >> 1) ^ 0xEDB88320;
+         else
+            crc = crc >> 1;
+      }
+   }
+
+   //Return CRC value
+   return crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/dm9000.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,397 @@
+/**
+ * @file dm9000.h
+ * @brief DM9000A/B Ethernet controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DM9000_H
+#define _DM9000_H
+
+//Dependencies
+#include "core/ethernet.h"
+
+//Loopback mode
+#ifndef DM9000_LOOPBACK_MODE
+   #define DM9000_LOOPBACK_MODE DISABLED
+#elif (DM9000_LOOPBACK_MODE != ENABLED && DM9000_LOOPBACK_MODE != DISABLED)
+   #error DM9000_LOOPBACK_MODE parameter is not valid
+#endif
+
+//DM9000 index register
+#ifndef DM9000_INDEX_REG
+   #define DM9000_INDEX_REG *((volatile uint16_t *) 0x30000000)
+#endif
+
+//DM9000 data register
+#ifndef DM9000_DATA_REG
+   #define DM9000_DATA_REG *((volatile uint16_t *) 0x30001000)
+#endif
+
+//DM9000 bus timing
+#define AT91C_SMC2_NWS_2        (2 << 0)
+#define AT91C_SMC2_TDF_2        (2 << 8)
+#define AT91C_SMC2_BAT_16       (1 << 12)
+#define AT91C_SMC2_DRP_STANDARD (0 << 15)
+#define AT91C_SMC2_RWSETUP_1    (1 << 24)
+#define AT91C_SMC2_RWHOLD_1     (1 << 28)
+
+//DM9000 identifiers
+#define DM9000_VID             0x0A46
+#define DM9000_PID             0x9000
+#define DM9000A_CHIP_REV       0x19
+#define DM9000B_CHIP_REV       0x1A
+
+//DM9000 registers
+#define DM9000_REG_NCR         0x00
+#define DM9000_REG_NSR         0x01
+#define DM9000_REG_TCR         0x02
+#define DM9000_REG_TSR1        0x03
+#define DM9000_REG_TSR2        0x04
+#define DM9000_REG_RCR         0x05
+#define DM9000_REG_RSR         0x06
+#define DM9000_REG_ROCR        0x07
+#define DM9000_REG_BPTR        0x08
+#define DM9000_REG_FCTR        0x09
+#define DM9000_REG_FCR         0x0A
+#define DM9000_REG_EPCR        0x0B
+#define DM9000_REG_EPAR        0x0C
+#define DM9000_REG_EPDRL       0x0D
+#define DM9000_REG_EPDRH       0x0E
+#define DM9000_REG_WCR         0x0F
+#define DM9000_REG_PAR0        0x10
+#define DM9000_REG_PAR1        0x11
+#define DM9000_REG_PAR2        0x12
+#define DM9000_REG_PAR3        0x13
+#define DM9000_REG_PAR4        0x14
+#define DM9000_REG_PAR5        0x15
+#define DM9000_REG_MAR0        0x16
+#define DM9000_REG_MAR1        0x17
+#define DM9000_REG_MAR2        0x18
+#define DM9000_REG_MAR3        0x19
+#define DM9000_REG_MAR4        0x1A
+#define DM9000_REG_MAR5        0x1B
+#define DM9000_REG_MAR6        0x1C
+#define DM9000_REG_MAR7        0x1D
+#define DM9000_REG_GPCR        0x1E
+#define DM9000_REG_GPR         0x1F
+#define DM9000_REG_TRPAL       0x22
+#define DM9000_REG_TRPAH       0x23
+#define DM9000_REG_RWPAL       0x24
+#define DM9000_REG_RWPAH       0x25
+#define DM9000_REG_VIDL        0x28
+#define DM9000_REG_VIDH        0x29
+#define DM9000_REG_PIDL        0x2A
+#define DM9000_REG_PIDH        0x2B
+#define DM9000_REG_CHIPR       0x2C
+#define DM9000_REG_TCR2        0x2D
+#define DM9000_REG_OCR         0x2E
+#define DM9000_REG_SMCR        0x2F
+#define DM9000_REG_ETXCSR      0x30
+#define DM9000_REG_TCSCR       0x31
+#define DM9000_REG_RCSCSR      0x32
+#define DM9000_REG_MPAR        0x33
+#define DM9000_REG_LEDCR       0x34
+#define DM9000_REG_BUSCR       0x38
+#define DM9000_REG_INTCR       0x39
+#define DM9000_REG_SCCR        0x50
+#define DM9000_REG_RSCCR       0x51
+#define DM9000_REG_MRCMDX      0xF0
+#define DM9000_REG_MRCMDX1     0xF1
+#define DM9000_REG_MRCMD       0xF2
+#define DM9000_REG_MRRL        0xF4
+#define DM9000_REG_MRRH        0xF5
+#define DM9000_REG_MWCMDX      0xF6
+#define DM9000_REG_MWCMD       0xF8
+#define DM9000_REG_MWRL        0xFA
+#define DM9000_REG_MWRH        0xFB
+#define DM9000_REG_TXPLL       0xFC
+#define DM9000_REG_TXPLH       0xFD
+#define DM9000_REG_ISR         0xFE
+#define DM9000_REG_IMR         0xFF
+
+//DM9000 PHY registers
+#define DM9000_PHY_REG_BMCR    0x00
+#define DM9000_PHY_REG_BMSR    0x01
+#define DM9000_PHY_REG_PHYIDR1 0x02
+#define DM9000_PHY_REG_PHYIDR2 0x03
+#define DM9000_PHY_REG_ANAR    0x04
+#define DM9000_PHY_REG_ANLPAR  0x05
+#define DM9000_PHY_REG_ANER    0x06
+#define DM9000_PHY_REG_DSCR    0x10
+#define DM9000_PHY_REG_DSCSR   0x11
+#define DM9000_PHY_REG_10BTCSR 0x12
+#define DM9000_PHY_REG_PWDOR   0x13
+#define DM9000_PHY_REG_SCR     0x14
+#define DM9000_PHY_REG_DSP     0x1B
+#define DM9000_PHY_REG_PSCR    0x1D
+
+//NCR register
+#define NCR_WAKEEN         (1 << 6)
+#define NCR_FCOL           (1 << 4)
+#define NCR_FDX            (1 << 3)
+#define NCR_LBK            (3 << 1)
+#define NCR_RST            (1 << 0)
+
+//NSR register
+#define NSR_SPEED          (1 << 7)
+#define NSR_LINKST         (1 << 6)
+#define NSR_WAKEST         (1 << 5)
+#define NSR_TX2END         (1 << 3)
+#define NSR_TX1END         (1 << 2)
+#define NSR_RXOV           (1 << 1)
+
+//TCR register
+#define TCR_TJDIS          (1 << 6)
+#define TCR_EXCECM         (1 << 5)
+#define TCR_PAD_DIS2       (1 << 4)
+#define TCR_CRC_DIS2       (1 << 3)
+#define TCR_PAD_DIS1       (1 << 2)
+#define TCR_CRC_DIS1       (1 << 1)
+#define TCR_TXREQ          (1 << 0)
+
+//TSR1 and TSR2 registers
+#define TSR_TJTO           (1 << 7)
+#define TSR_LC             (1 << 6)
+#define TSR_NC             (1 << 5)
+#define TSR_LCOL           (1 << 4)
+#define TSR_COL            (1 << 3)
+#define TSR_EC             (1 << 2)
+
+//RCR register
+#define RCR_WTDIS          (1 << 6)
+#define RCR_DIS_LONG       (1 << 5)
+#define RCR_DIS_CRC        (1 << 4)
+#define RCR_ALL            (1 << 3)
+#define RCR_RUNT           (1 << 2)
+#define RCR_PRMSC          (1 << 1)
+#define RCR_RXEN           (1 << 0)
+
+//RSR register
+#define RSR_RF             (1 << 7)
+#define RSR_MF             (1 << 6)
+#define RSR_LCS            (1 << 5)
+#define RSR_RWTO           (1 << 4)
+#define RSR_PLE            (1 << 3)
+#define RSR_AE             (1 << 2)
+#define RSR_CE             (1 << 1)
+#define RSR_FOE            (1 << 0)
+
+//ROCR register
+#define ROCR_ROC           (127 << 0)
+#define ROCR_RXFU          (1 << 7)
+
+//BPTR register
+#define BPTR_BPHW          (15 << 4)
+#define BPTR_JPT           (15 << 0)
+
+//FCTR register
+#define FCTR_HWOT          (15 << 4)
+#define FCTR_LWOT          (15 << 0)
+
+//FCR register
+#define FCR_TXP0           (1 << 7)
+#define FCR_TXPF           (1 << 6)
+#define FCR_TXPEN          (1 << 5)
+#define FCR_BKPA           (1 << 4)
+#define FCR_BKPM           (1 << 3)
+#define FCR_RXPS           (1 << 2)
+#define FCR_RXPCS          (1 << 1)
+#define FCR_FLCE           (1 << 0)
+
+//EPCR register
+#define EPCR_REEP          (1 << 5)
+#define EPCR_WEP           (1 << 4)
+#define EPCR_EPOS          (1 << 3)
+#define EPCR_ERPRR         (1 << 2)
+#define EPCR_ERPRW         (1 << 1)
+#define EPCR_ERRE          (1 << 0)
+
+//EPAR register
+#define EPAR_PHY_ADR       (3 << 6)
+#define EPAR_EROA          (31 << 0)
+
+//WCR register
+#define WCR_LINKEN         (1 << 5)
+#define WCR_SAMPLEEN       (1 << 4)
+#define WCR_MAGICEN        (1 << 3)
+#define WCR_LINKST         (1 << 2)
+#define WCR_SAMPLEST       (1 << 1)
+#define WCR_MAGICST        (1 << 0)
+
+//GPCR register
+#define GPCR_GPC6          (1 << 6)
+#define GPCR_GPC5          (1 << 5)
+#define GPCR_GPC4          (1 << 4)
+#define GPCR_GPC3          (1 << 3)
+#define GPCR_GPC2          (1 << 2)
+#define GPCR_GPC1          (1 << 1)
+
+//GPR register
+#define GPR_GPO6           (1 << 6)
+#define GPR_GPO5           (1 << 5)
+#define GPR_GPO4           (1 << 4)
+#define GPR_GPIO3          (1 << 3)
+#define GPR_GPIO2          (1 << 2)
+#define GPR_GPIO1          (1 << 1)
+#define GPR_PHYPD          (1 << 0)
+
+//TCR2 register
+#define TCR2_LED           (1 << 7)
+#define TCR2_RLCP          (1 << 6)
+#define TCR2_DTU           (1 << 5)
+#define TCR2_ONEPM         (1 << 4)
+#define TCR2_IFGS          (15 << 0)
+
+//OCR register
+#define OCR_SCC            (3 << 6)
+#define OCR_SOE            (1 << 4)
+#define OCR_SCS            (1 << 3)
+#define OCR_PHYOP          (7 << 0)
+
+//SMCR register
+#define SMCR_SM_EN         (1 << 7)
+#define SMCR_FLC           (1 << 2)
+#define SMCR_FB1           (1 << 1)
+#define SMCR_FB0           (1 << 0)
+
+//ETXCSR register
+#define ETXCSR_ETE         (1 << 7)
+#define ETXCSR_ETS2        (1 << 6)
+#define ETXCSR_ETS1        (1 << 5)
+#define ETXCSR_ETT         (3 << 0)
+
+//TCSCR register
+#define TCSCR_UDPCSE       (1 << 2)
+#define TCSCR_TCPCSE       (1 << 1)
+#define TCSCR_IPCSE        (1 << 0)
+
+//RCSCSR register
+#define RCSCSR_UDPS        (1 << 7)
+#define RCSCSR_TCPS        (1 << 6)
+#define RCSCSR_IPS         (1 << 5)
+#define RCSCSR_UDPP        (1 << 4)
+#define RCSCSR_TCPP        (1 << 3)
+#define RCSCSR_IPP         (1 << 2)
+#define RCSCSR_RCSEN       (1 << 1)
+#define RCSCSR_DCSE        (1 << 0)
+
+//MPAR register
+#define MPAR_ADR_EN        (1 << 7)
+#define MPAR_EPHYADR       (31 << 0)
+
+//LEDC register
+#define LEDCR_GPIO         (1 << 1)
+#define LEDCR_MII          (1 << 0)
+
+//BUSCR register
+#define BUSCR_CURR         (3 << 5)
+#define BUSCR_EST          (1 << 3)
+#define BUSCR_IOW_SPIKE    (1 << 1)
+#define BUSCR_IOR_SPIKE    (1 << 0)
+
+//INTCR register
+#define INTCR_INT_TYPE     (1 << 1)
+#define INTCR_INT_POL      (1 << 0)
+
+//SCCR register
+#define SCCR_DIS_CLK       (1 << 0)
+
+//ISR register
+#define ISR_IOMODE         (1 << 7)
+#define ISR_LNKCHG         (1 << 5)
+#define ISR_UDRUN          (1 << 4)
+#define ISR_ROO            (1 << 3)
+#define ISR_ROS            (1 << 2)
+#define ISR_PT             (1 << 1)
+#define ISR_PR             (1 << 0)
+
+//IMR register
+#define IMR_PAR            (1 << 7)
+#define IMR_LNKCHGI        (1 << 5)
+#define IMR_UDRUNI         (1 << 4)
+#define IMR_ROOI           (1 << 3)
+#define IMR_ROI            (1 << 2)
+#define IMR_PTI            (1 << 1)
+#define IMR_PRI            (1 << 0)
+
+//PHY BMCR register
+#define BMCR_RST           (1 << 15)
+#define BMCR_LOOPBACK      (1 << 14)
+#define BMCR_SPEED_SEL     (1 << 13)
+#define BMCR_AN_EN         (1 << 12)
+#define BMCR_PD            (1 << 11)
+#define BMCR_ISOLATE       (1 << 10)
+#define BMCR_RESTART_AN    (1 << 9)
+#define BMCR_DUPLEX_MODE   (1 << 8)
+#define BMCR_COL_TEST      (1 << 7)
+
+//Loopback mode
+#define DM9000_LBK_NORMAL  (0 << 1)
+#define DM9000_LBK_MAC     (1 << 1)
+#define DM9000_LBK_PHY     (2 << 1)
+
+
+/**
+ * @brief DM9000 driver context
+ **/
+
+typedef struct
+{
+   uint_t queuedPackets; ///<Number of packets in transmission buffer
+   uint8_t *txBuffer;    ///<Transmit buffer
+   uint8_t *rxBuffer;    ///<Receive buffer
+} Dm9000Context;
+
+
+//DM9000 driver
+extern const NicDriver dm9000Driver;
+
+//DM9000 related functions
+error_t dm9000Init(NetInterface *interface);
+
+void dm9000Tick(NetInterface *interface);
+
+void dm9000EnableIrq(NetInterface *interface);
+void dm9000DisableIrq(NetInterface *interface);
+bool_t dm9000IrqHandler(NetInterface *interface);
+void dm9000EventHandler(NetInterface *interface);
+
+error_t dm9000SendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t dm9000ReceivePacket(NetInterface *interface);
+
+error_t dm9000SetMulticastFilter(NetInterface *interface);
+
+void dm9000WriteReg(uint8_t address, uint8_t data);
+uint8_t dm9000ReadReg(uint8_t address);
+
+void dm9000WritePhyReg(uint8_t address, uint16_t data);
+uint16_t dm9000ReadPhyReg(uint8_t address);
+
+uint32_t dm9000CalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/dm9161.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,318 @@
+/**
+ * @file dm9161.c
+ * @brief DM9161 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/dm9161.h"
+#include "debug.h"
+
+
+/**
+ * @brief DM9161 Ethernet PHY driver
+ **/
+
+const PhyDriver dm9161PhyDriver =
+{
+   dm9161Init,
+   dm9161Tick,
+   dm9161EnableIrq,
+   dm9161DisableIrq,
+   dm9161EventHandler,
+};
+
+
+/**
+ * @brief DM9161 PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t dm9161Init(NetInterface *interface)
+{
+   volatile uint32_t status;
+
+   //Debug message
+   TRACE_INFO("Initializing DM9161...\r\n");
+
+   //Initialize external interrupt line driver
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->init();
+
+   //Reset PHY transceiver
+   dm9161WritePhyReg(interface, DM9161_PHY_REG_BMCR, BMCR_RESET);
+   //Wait for the reset to complete
+   while(dm9161ReadPhyReg(interface, DM9161_PHY_REG_BMCR) & BMCR_RESET);
+
+   //Dump PHY registers for debugging purpose
+   dm9161DumpPhyReg(interface);
+
+   //The PHY will generate interrupts when link status changes are detected
+   dm9161WritePhyReg(interface, DM9161_PHY_REG_MDINTR, ~(MDINTR_LINK_MASK | MDINTR_INTR_MASK));
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief DM9161 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void dm9161Tick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //No external interrupt line driver?
+   if(interface->extIntDriver == NULL)
+   {
+      //Read basic status register
+      value = dm9161ReadPhyReg(interface, DM9161_PHY_REG_BMSR);
+      //Retrieve current link state
+      linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+      //Link up event?
+      if(linkState && !interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+      //Link down event?
+      else if(!linkState && interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void dm9161EnableIrq(NetInterface *interface)
+{
+   //Enable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void dm9161DisableIrq(NetInterface *interface)
+{
+   //Disable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief DM9161 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void dm9161EventHandler(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t end;
+
+   //Read status register to acknowledge the interrupt
+   value = dm9161ReadPhyReg(interface, DM9161_PHY_REG_MDINTR);
+
+   //Link status change?
+   if(value & MDINTR_LINK_CHANGE)
+   {
+      //Any link failure condition is latched in the BMSR register... Reading
+      //the register twice will always return the actual link status
+      value = dm9161ReadPhyReg(interface, DM9161_PHY_REG_BMSR);
+      value = dm9161ReadPhyReg(interface, DM9161_PHY_REG_BMSR);
+
+      //Link is up?
+      if(value & BMSR_LINK_STATUS)
+      {
+         //Wait for the auto-negotiation to complete
+         do
+         {
+            //Read DSCSR register
+            value = dm9161ReadPhyReg(interface, DM9161_PHY_REG_DSCSR);
+
+            //Check current state
+            switch(value & DSCSR_ANMB_MASK)
+            {
+            //Auto-negotiation is still in progress?
+            case DSCSR_ANMB_ABILITY_MATCH:
+            case DSCSR_ANMB_ACK_MATCH:
+            case DSCSR_ANMB_CONSIST_MATCH:
+            case DSCSR_ANMB_SIGNAL_LINK_READY:
+               end = FALSE;
+               break;
+            //Auto-negotiation is complete?
+            default:
+               end = TRUE;
+               break;
+            }
+
+            //Check loop condition variable
+         } while(!end);
+
+         //Read DSCSR register
+         value = dm9161ReadPhyReg(interface, DM9161_PHY_REG_DSCSR);
+
+         //Check current operation mode
+         if(value & DSCSR_10HDX)
+         {
+            //10BASE-T half-duplex
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+         }
+         else if(value & DSCSR_10FDX)
+         {
+            //10BASE-T full-duplex
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+         }
+         else if(value & DSCSR_100HDX)
+         {
+            //100BASE-TX half-duplex
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+         }
+         else if(value & DSCSR_100FDX)
+         {
+            //100BASE-TX full-duplex
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+         }
+         else
+         {
+            //Debug message
+            TRACE_WARNING("Invalid Duplex mode\r\n");
+         }
+
+         //Update link state
+         interface->linkState = TRUE;
+
+         //Adjust MAC configuration parameters for proper operation
+         interface->nicDriver->updateMacConfig(interface);
+      }
+      else
+      {
+         //Update link state
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void dm9161WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = DM9161_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t dm9161ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = DM9161_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void dm9161DumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, dm9161ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/dm9161.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,221 @@
+/**
+ * @file dm9161.h
+ * @brief DM9161 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DM9161_H
+#define _DM9161_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#ifndef DM9161_PHY_ADDR
+   #define DM9161_PHY_ADDR 0
+#elif (DM9161_PHY_ADDR < 0 || DM9161_PHY_ADDR > 31)
+   #error DM9161_PHY_ADDR parameter is not valid
+#endif
+
+//DM9161 registers
+#define DM9161_PHY_REG_BMCR      0x00
+#define DM9161_PHY_REG_BMSR      0x01
+#define DM9161_PHY_REG_PHYIDR1   0x02
+#define DM9161_PHY_REG_PHYIDR2   0x03
+#define DM9161_PHY_REG_ANAR      0x04
+#define DM9161_PHY_REG_ANLPAR    0x05
+#define DM9161_PHY_REG_ANER      0x06
+#define DM9161_PHY_REG_DSCR      0x10
+#define DM9161_PHY_REG_DSCSR     0x11
+#define DM9161_PHY_REG_10BTCSR   0x12
+#define DM9161_PHY_REG_MDINTR    0x15
+#define DM9161_PHY_REG_RECR      0x16
+#define DM9161_PHY_REG_DISCR     0x17
+#define DM9161_PHY_REG_RLSR      0x18
+
+//BMCR register
+#define BMCR_RESET               (1 << 15)
+#define BMCR_LOOPBACK            (1 << 14)
+#define BMCR_SPEED_SEL           (1 << 13)
+#define BMCR_AN_EN               (1 << 12)
+#define BMCR_POWER_DOWN          (1 << 11)
+#define BMCR_ISOLATE             (1 << 10)
+#define BMCR_RESTART_AN          (1 << 9)
+#define BMCR_DUPLEX_MODE         (1 << 8)
+#define BMCR_COL_TEST            (1 << 7)
+
+//BMSR register
+#define BMSR_100BT4              (1 << 15)
+#define BMSR_100BTX_FD           (1 << 14)
+#define BMSR_100BTX              (1 << 13)
+#define BMSR_10BT_FD             (1 << 12)
+#define BMSR_10BT                (1 << 11)
+#define BMSR_NO_PREAMBLE         (1 << 6)
+#define BMSR_AN_COMPLETE         (1 << 5)
+#define BMSR_REMOTE_FAULT        (1 << 4)
+#define BMSR_AN_ABLE             (1 << 3)
+#define BMSR_LINK_STATUS         (1 << 2)
+#define BMSR_JABBER_DETECT       (1 << 1)
+#define BMSR_EXTENDED_CAP        (1 << 0)
+
+//ANAR register
+#define ANAR_NP                  (1 << 15)
+#define ANAR_ACK                 (1 << 14)
+#define ANAR_RF                  (1 << 13)
+#define ANAR_FCS                 (1 << 10)
+#define ANAR_100BT4              (1 << 9)
+#define ANAR_100BTX_FD           (1 << 8)
+#define ANAR_100BTX              (1 << 7)
+#define ANAR_10BT_FD             (1 << 6)
+#define ANAR_10BT                (1 << 5)
+#define ANAR_SELECTOR4           (1 << 4)
+#define ANAR_SELECTOR3           (1 << 3)
+#define ANAR_SELECTOR2           (1 << 2)
+#define ANAR_SELECTOR1           (1 << 1)
+#define ANAR_SELECTOR0           (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NP                (1 << 15)
+#define ANLPAR_ACK               (1 << 14)
+#define ANLPAR_RF                (1 << 13)
+#define ANLPAR_FCS               (1 << 10)
+#define ANLPAR_100BT4            (1 << 9)
+#define ANLPAR_100BTX_FD         (1 << 8)
+#define ANLPAR_100BTX            (1 << 7)
+#define ANLPAR_10BT_FD           (1 << 6)
+#define ANLPAR_10BT              (1 << 5)
+#define ANLPAR_SELECTOR4         (1 << 4)
+#define ANLPAR_SELECTOR3         (1 << 3)
+#define ANLPAR_SELECTOR2         (1 << 2)
+#define ANLPAR_SELECTOR1         (1 << 1)
+#define ANLPAR_SELECTOR0         (1 << 0)
+
+//ANER register
+#define ANER_PDF                 (1 << 4)
+#define ANER_LP_NP_ABLE          (1 << 3)
+#define ANER_NP_ABLE             (1 << 2)
+#define ANER_PAGE_RX             (1 << 1)
+#define ANER_LP_AN_ABLE          (1 << 0)
+
+//DSCR register
+#define DSCR_BP_4B5B             (1 << 15)
+#define DSCR_BP_SCR              (1 << 14)
+#define DSCR_BP_ALIGN            (1 << 13)
+#define DSCR_BP_ADPOK            (1 << 12)
+#define DSCR_REPEATER            (1 << 11)
+#define DSCR_TX                  (1 << 10)
+#define DSCR_FEF                 (1 << 9)
+#define DSCR_RMII_EN             (1 << 8)
+#define DSCR_F_LINK_100          (1 << 7)
+#define DSCR_SPLED_CTL           (1 << 6)
+#define DSCR_COLLED_CTL          (1 << 5)
+#define DSCR_RPDCTR_EN           (1 << 4)
+#define DSCR_SMRST               (1 << 3)
+#define DSCR_MFPSC               (1 << 2)
+#define DSCR_SLEEP               (1 << 1)
+#define DSCR_RLOUT               (1 << 0)
+
+//DSCSR register
+#define DSCSR_100FDX             (1 << 15)
+#define DSCSR_100HDX             (1 << 14)
+#define DSCSR_10FDX              (1 << 13)
+#define DSCSR_10HDX              (1 << 12)
+#define DSCSR_PHYADR4            (1 << 8)
+#define DSCSR_PHYADR3            (1 << 7)
+#define DSCSR_PHYADR2            (1 << 6)
+#define DSCSR_PHYADR1            (1 << 5)
+#define DSCSR_PHYADR0            (1 << 4)
+#define DSCSR_ANMB3              (1 << 3)
+#define DSCSR_ANMB2              (1 << 2)
+#define DSCSR_ANMB1              (1 << 1)
+#define DSCSR_ANMB0              (1 << 0)
+
+//10BTCSR register
+#define _10BTCSR_LP_EN           (1 << 14)
+#define _10BTCSR_HBE             (1 << 13)
+#define _10BTCSR_SQUELCH         (1 << 12)
+#define _10BTCSR_JABEN           (1 << 11)
+#define _10BTCSR_10BT_SER        (1 << 10)
+#define _10BTCSR_POLR            (1 << 0)
+
+//MDINTR register
+#define MDINTR_INTR_PEND         (1 << 15)
+#define MDINTR_FDX_MASK          (1 << 11)
+#define MDINTR_SPD_MASK          (1 << 10)
+#define MDINTR_LINK_MASK         (1 << 9)
+#define MDINTR_INTR_MASK         (1 << 8)
+#define MDINTR_FDX_CHANGE        (1 << 4)
+#define MDINTR_SPD_CHANGE        (1 << 3)
+#define MDINTR_LINK_CHANGE       (1 << 2)
+#define MDINTR_INTR_STATUS       (1 << 0)
+
+//RLSR register
+#define RLSR_LH_LEDST            (1 << 13)
+#define RLSR_LH_CSTS             (1 << 12)
+#define RLSR_LH_RMII             (1 << 11)
+#define RLSR_LH_SCRAM            (1 << 10)
+#define RLSR_LH_REPTR            (1 << 9)
+#define RLSR_LH_TSTMOD           (1 << 8)
+#define RLSR_LH_OP2              (1 << 7)
+#define RLSR_LH_OP1              (1 << 6)
+#define RLSR_LH_OP0              (1 << 5)
+#define RLSR_LH_PH4              (1 << 4)
+#define RLSR_LH_PH3              (1 << 3)
+#define RLSR_LH_PH2              (1 << 2)
+#define RLSR_LH_PH1              (1 << 1)
+#define RLSR_LH_PH0              (1 << 0)
+
+//Auto-negotiation state machine
+#define DSCSR_ANMB_MASK                      0x000F
+#define DSCSR_ANMB_IDLE                      0x0000
+#define DSCSR_ANMB_ABILITY_MATCH             0x0001
+#define DSCSR_ANMB_ACK_MATCH                 0x0002
+#define DSCSR_ANMB_ACK_MATCH_FAILED          0x0003
+#define DSCSR_ANMB_CONSIST_MATCH             0x0004
+#define DSCSR_ANMB_CONSIST_MATCH_FAILED      0x0005
+#define DSCSR_ANMB_SIGNAL_LINK_READY         0x0006
+#define DSCSR_ANMB_SIGNAL_LINK_READY_FAILED  0x0007
+#define DSCSR_ANMB_AN_SUCCESS                0x0008
+
+//DM9161 Ethernet PHY driver
+extern const PhyDriver dm9161PhyDriver;
+
+//DM9161 related functions
+error_t dm9161Init(NetInterface *interface);
+
+void dm9161Tick(NetInterface *interface);
+
+void dm9161EnableIrq(NetInterface *interface);
+void dm9161DisableIrq(NetInterface *interface);
+
+void dm9161EventHandler(NetInterface *interface);
+
+void dm9161WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t dm9161ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void dm9161DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/dp83620.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,268 @@
+/**
+ * @file dp83620.c
+ * @brief DP83620 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/dp83620.h"
+#include "debug.h"
+
+
+/**
+ * @brief DP83620 Ethernet PHY driver
+ **/
+
+const PhyDriver dp83620PhyDriver =
+{
+   dp83620Init,
+   dp83620Tick,
+   dp83620EnableIrq,
+   dp83620DisableIrq,
+   dp83620EventHandler,
+};
+
+
+/**
+ * @brief DP83620 PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t dp83620Init(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing DP83620...\r\n");
+
+   //Initialize external interrupt line driver
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->init();
+
+   //A software reset is accomplished by setting the RESET bit of the BMCR register
+   dp83620WritePhyReg(interface, DP83620_PHY_REG_BMCR, BMCR_RESET);
+   //Wait for the reset to complete
+   while(dp83620ReadPhyReg(interface, DP83620_PHY_REG_BMCR) & BMCR_RESET);
+
+   //Dump PHY registers for debugging purpose
+   dp83620DumpPhyReg(interface);
+
+   //Configure PWR_DOWN/INT pin as an interrupt output
+   dp83620WritePhyReg(interface, DP83620_PHY_REG_MICR, MICR_INTEN | MICR_INT_OE);
+   //The PHY will generate interrupts when link status changes are detected
+   dp83620WritePhyReg(interface, DP83620_PHY_REG_MISR, MISR_LINK_INT_EN);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief DP83620 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void dp83620Tick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //No external interrupt line driver?
+   if(interface->extIntDriver == NULL)
+   {
+      //Read basic status register
+      value = dp83620ReadPhyReg(interface, DP83620_PHY_REG_BMSR);
+      //Retrieve current link state
+      linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+      //Link up event?
+      if(linkState && !interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+      //Link down event?
+      else if(!linkState && interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void dp83620EnableIrq(NetInterface *interface)
+{
+   //Enable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void dp83620DisableIrq(NetInterface *interface)
+{
+   //Disable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief DP83620 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void dp83620EventHandler(NetInterface *interface)
+{
+   uint16_t status;
+
+   //Read status register to acknowledge the interrupt
+   status = dp83620ReadPhyReg(interface, DP83620_PHY_REG_MISR);
+
+   //Link status change?
+   if(status & MISR_LINK_INT)
+   {
+      //Read PHY status register
+      status = dp83620ReadPhyReg(interface, DP83620_PHY_REG_PHYSTS);
+
+      //Link is up?
+      if(status & PHYSTS_LINK_STATUS)
+      {
+         //Check current speed
+         if(status & PHYSTS_SPEED_STATUS)
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+         else
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+
+         //Check duplex mode
+         if(status & PHYSTS_DUPLEX_STATUS)
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+         else
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+
+         //Update link state
+         interface->linkState = TRUE;
+
+         //Adjust MAC configuration parameters for proper operation
+         interface->nicDriver->updateMacConfig(interface);
+      }
+      else
+      {
+         //Update link state
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void dp83620WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = DP83620_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t dp83620ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = DP83620_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void dp83620DumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, dp83620ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/dp83620.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,532 @@
+/**
+ * @file dp83620.h
+ * @brief DP83620 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DP83620_H
+#define _DP83620_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#ifndef DP83620_PHY_ADDR
+   #define DP83620_PHY_ADDR 1
+#elif (DP83620_PHY_ADDR < 0 || DP83620_PHY_ADDR > 31)
+   #error DP83620_PHY_ADDR parameter is not valid
+#endif
+
+//DP83620 registers
+#define DP83620_PHY_REG_BMCR       0x00
+#define DP83620_PHY_REG_BMSR       0x01
+#define DP83620_PHY_REG_PHYIDR1    0x02
+#define DP83620_PHY_REG_PHYIDR2    0x03
+#define DP83620_PHY_REG_ANAR       0x04
+#define DP83620_PHY_REG_ANLPAR     0x05
+#define DP83620_PHY_REG_ANER       0x06
+#define DP83620_PHY_REG_ANNPTR     0x07
+#define DP83620_PHY_REG_PHYSTS     0x10
+#define DP83620_PHY_REG_MICR       0x11
+#define DP83620_PHY_REG_MISR       0x12
+#define DP83620_PHY_REG_PAGESEL    0x13
+
+//Extended registers (page 0)
+#define DP83620_PHY_REG_FCSCR      0x14
+#define DP83620_PHY_REG_RECR       0x15
+#define DP83620_PHY_REG_PCSR       0x16
+#define DP83620_PHY_REG_RBR        0x17
+#define DP83620_PHY_REG_LEDCR      0x18
+#define DP83620_PHY_REG_PHYCR      0x19
+#define DP83620_PHY_REG_10BTSCR    0x1A
+#define DP83620_PHY_REG_CDCTRL1    0x1B
+#define DP83620_PHY_REG_PHYCR2     0x1C
+#define DP83620_PHY_REG_EDCR       0x1D
+#define DP83620_PHY_REG_PCFCR      0x1F
+
+//Extended registers (page 1)
+#define DP83620_PHY_REG_SD_CNFG    0x1E
+
+//Extended registers (page 2)
+#define DP83620_PHY_REG_LEN100_DET 0x14
+#define DP83620_PHY_REG_FREQ100    0x15
+#define DP83620_PHY_REG_TDR_CTRL   0x16
+#define DP83620_PHY_REG_TDR_WIN    0x17
+#define DP83620_PHY_REG_TDR_PEAK   0x18
+#define DP83620_PHY_REG_TDR_THR    0x19
+#define DP83620_PHY_REG_VAR_CTRL   0x1A
+#define DP83620_PHY_REG_VAR_DAT    0x1B
+#define DP83620_PHY_REG_LQMR       0x1D
+#define DP83620_PHY_REG_LQDR       0x1E
+#define DP83620_PHY_REG_LQMR2      0x1F
+
+//Extended registers (page 5)
+#define DP83620_PHY_REG_PSF_CFG    0x18
+
+//BMCR register
+#define BMCR_RESET                 (1 << 15)
+#define BMCR_LOOPBACK              (1 << 14)
+#define BMCR_SPEED_SEL             (1 << 13)
+#define BMCR_AN_EN                 (1 << 12)
+#define BMCR_POWER_DOWN            (1 << 11)
+#define BMCR_ISOLATE               (1 << 10)
+#define BMCR_RESTART_AN            (1 << 9)
+#define BMCR_DUPLEX_MODE           (1 << 8)
+#define BMCR_COL_TEST              (1 << 7)
+#define BMCR_UNIDIRECTIONAL_EN     (1 << 5)
+
+//BMSR register
+#define BMSR_100BT4                (1 << 15)
+#define BMSR_100BTX_FD             (1 << 14)
+#define BMSR_100BTX                (1 << 13)
+#define BMSR_10BT_FD               (1 << 12)
+#define BMSR_10BT                  (1 << 11)
+#define BMSR_UNIDIRECTIONAL_ABLE   (1 << 7)
+#define BMSR_NO_PREAMBLE           (1 << 6)
+#define BMSR_AN_COMPLETE           (1 << 5)
+#define BMSR_REMOTE_FAULT          (1 << 4)
+#define BMSR_AN_ABLE               (1 << 3)
+#define BMSR_LINK_STATUS           (1 << 2)
+#define BMSR_JABBER_DETECT         (1 << 1)
+#define BMSR_EXTENDED_CAP          (1 << 0)
+
+//ANAR register
+#define ANAR_NP                    (1 << 15)
+#define ANAR_RF                    (1 << 13)
+#define ANAR_ASM_DIR               (1 << 11)
+#define ANAR_PAUSE                 (1 << 10)
+#define ANAR_100BT4                (1 << 9)
+#define ANAR_100BTX_FD             (1 << 8)
+#define ANAR_100BTX                (1 << 7)
+#define ANAR_10BT_FD               (1 << 6)
+#define ANAR_10BT                  (1 << 5)
+#define ANAR_SELECTOR4             (1 << 4)
+#define ANAR_SELECTOR3             (1 << 3)
+#define ANAR_SELECTOR2             (1 << 2)
+#define ANAR_SELECTOR1             (1 << 1)
+#define ANAR_SELECTOR0             (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NP                  (1 << 15)
+#define ANLPAR_ACK                 (1 << 14)
+#define ANLPAR_RF                  (1 << 13)
+#define ANLPAR_ASM_DIR             (1 << 11)
+#define ANLPAR_PAUSE               (1 << 10)
+#define ANLPAR_100BT4              (1 << 9)
+#define ANLPAR_100BTX_FD           (1 << 8)
+#define ANLPAR_100BTX              (1 << 7)
+#define ANLPAR_10BT_FD             (1 << 6)
+#define ANLPAR_10BT                (1 << 5)
+#define ANLPAR_SELECTOR4           (1 << 4)
+#define ANLPAR_SELECTOR3           (1 << 3)
+#define ANLPAR_SELECTOR2           (1 << 2)
+#define ANLPAR_SELECTOR1           (1 << 1)
+#define ANLPAR_SELECTOR0           (1 << 0)
+
+//ANER register
+#define ANER_PDF                   (1 << 4)
+#define ANER_LP_NP_ABLE            (1 << 3)
+#define ANER_NP_ABLE               (1 << 2)
+#define ANER_PAGE_RX               (1 << 1)
+#define ANER_LP_AN_ABLE            (1 << 0)
+
+//ANNPTR register
+#define ANNPTR_NP                  (1 << 15)
+#define ANNPTR_MP                  (1 << 13)
+#define ANNPTR_ACK2                (1 << 12)
+#define ANNPTR_TOG_TX              (1 << 11)
+#define ANNPTR_CODE10              (1 << 10)
+#define ANNPTR_CODE9               (1 << 9)
+#define ANNPTR_CODE8               (1 << 8)
+#define ANNPTR_CODE7               (1 << 7)
+#define ANNPTR_CODE6               (1 << 6)
+#define ANNPTR_CODE5               (1 << 5)
+#define ANNPTR_CODE4               (1 << 4)
+#define ANNPTR_CODE3               (1 << 3)
+#define ANNPTR_CODE2               (1 << 2)
+#define ANNPTR_CODE1               (1 << 1)
+#define ANNPTR_CODE0               (1 << 0)
+
+//PHYSTS register
+#define PHYSTS_MDIX_MODE           (1 << 14)
+#define PHYSTS_RX_ERROR_LATCH      (1 << 13)
+#define PHYSTS_POLARITY_STATUS     (1 << 12)
+#define PHYSTS_FALSE_CARRIER_SENSE (1 << 11)
+#define PHYSTS_SIGNAL_DETECT       (1 << 10)
+#define PHYSTS_DESCRAMBLER_LOCK    (1 << 9)
+#define PHYSTS_PAGE_RECEIVED       (1 << 8)
+#define PHYSTS_MII_INTERRUPT       (1 << 7)
+#define PHYSTS_REMOTE_FAULT        (1 << 6)
+#define PHYSTS_JABBER_DETECT       (1 << 5)
+#define PHYSTS_AN_COMPLETE         (1 << 4)
+#define PHYSTS_LOOPBACK_STATUS     (1 << 3)
+#define PHYSTS_DUPLEX_STATUS       (1 << 2)
+#define PHYSTS_SPEED_STATUS        (1 << 1)
+#define PHYSTS_LINK_STATUS         (1 << 0)
+
+//MICR register
+#define MICR_TINT                  (1 << 2)
+#define MICR_INTEN                 (1 << 1)
+#define MICR_INT_OE                (1 << 0)
+
+//MISR register
+#define MISR_ED_INT                (1 << 14)
+#define MISR_LINK_INT              (1 << 13)
+#define MISR_SPD_INT               (1 << 12)
+#define MISR_DUP_INT               (1 << 11)
+#define MISR_ANC_INT               (1 << 10)
+#define MISR_FHF_INT               (1 << 9)
+#define MISR_RHF_INT               (1 << 8)
+#define MISR_LQ_INT_EN             (1 << 7)
+#define MISR_ED_INT_EN             (1 << 6)
+#define MISR_LINK_INT_EN           (1 << 5)
+#define MISR_SPD_INT_EN            (1 << 4)
+#define MISR_DUP_INT_EN            (1 << 3)
+#define MISR_ANC_INT_EN            (1 << 2)
+#define MISR_FHF_INT_EN            (1 << 1)
+#define MISR_RHF_INT_EN            (1 << 0)
+
+//PAGESEL register
+#define PAGESEL_PAGE_SEL2          (1 << 2)
+#define PAGESEL_PAGE_SEL1          (1 << 1)
+#define PAGESEL_PAGE_SEL0          (1 << 0)
+
+//FCSCR register
+#define FCSCR_FCSCNT7              (1 << 7)
+#define FCSCR_FCSCNT6              (1 << 6)
+#define FCSCR_FCSCNT5              (1 << 5)
+#define FCSCR_FCSCNT4              (1 << 4)
+#define FCSCR_FCSCNT3              (1 << 3)
+#define FCSCR_FCSCNT2              (1 << 2)
+#define FCSCR_FCSCNT1              (1 << 1)
+#define FCSCR_FCSCNT0              (1 << 0)
+
+//RECR register
+#define RECR_RXERCNT7              (1 << 7)
+#define RECR_RXERCNT6              (1 << 6)
+#define RECR_RXERCNT5              (1 << 5)
+#define RECR_RXERCNT4              (1 << 4)
+#define RECR_RXERCNT3              (1 << 3)
+#define RECR_RXERCNT2              (1 << 2)
+#define RECR_RXERCNT1              (1 << 1)
+#define RECR_RXERCNT0              (1 << 0)
+
+//PCSR register
+#define PCSR_AUTO_CROSSOVER        (1 << 15)
+#define PCSR_FREE_CLK              (1 << 11)
+#define PCSR_TQ_EN                 (1 << 10)
+#define PCSR_SD_FORCE_PMA          (1 << 9)
+#define PCSR_SD_OPTION             (1 << 8)
+#define PCSR_DESC_TIME             (1 << 7)
+#define PCSR_FX_EN                 (1 << 6)
+#define PCSR_FORCE_100_OK          (1 << 5)
+#define PCSR_FEFI_EN               (1 << 3)
+#define PCSR_NRZI_BYPASS           (1 << 2)
+#define PCSR_SCRAM_BYPASS          (1 << 1)
+#define PCSR_DESCRAM_BYPASS        (1 << 0)
+
+//RBR register
+#define RBR_RMII_MASTER            (1 << 14)
+#define RBR_DIS_TX_OPT             (1 << 13)
+#define RBR_PMD_LOOP               (1 << 8)
+#define RBR_SCMII_RX               (1 << 7)
+#define RBR_SCMII_TX               (1 << 6)
+#define RBR_RMII_MODE              (1 << 5)
+#define RBR_RMII_REV1_0            (1 << 4)
+#define RBR_RX_OVF_STS             (1 << 3)
+#define RBR_RX_UNF_STS             (1 << 2)
+#define RBR_ELAST_BUF1             (1 << 1)
+#define RBR_ELAST_BUF0             (1 << 0)
+
+//LEDCR register
+#define LEDCR_DIS_SPDLED           (1 << 11)
+#define LEDCR_DIS_LNKLED           (1 << 10)
+#define LEDCR_DIS_ACTLED           (1 << 9)
+#define LEDCR_LEDACT_RX            (1 << 8)
+#define LEDCR_BLINK_FREQ1          (1 << 7)
+#define LEDCR_BLINK_FREQ0          (1 << 6)
+#define LEDCR_DRV_SPDLED           (1 << 5)
+#define LEDCR_DRV_LNKLED           (1 << 4)
+#define LEDCR_DRV_ACTLED           (1 << 3)
+#define LEDCR_SPDLED               (1 << 2)
+#define LEDCR_LNKLED               (1 << 1)
+#define LEDCR_ACTLED               (1 << 0)
+
+#define LEDCR_BLINK_FREQ_6HZ       (0 << 6)
+#define LEDCR_BLINK_FREQ_12HZ      (1 << 6)
+#define LEDCR_BLINK_FREQ_24HZ      (2 << 6)
+#define LEDCR_BLINK_FREQ_48HZ      (3 << 6)
+
+//PHYCR register
+#define PHYCR_MDIX_EN              (1 << 15)
+#define PHYCR_FORCE_MDIX           (1 << 14)
+#define PHYCR_PAUSE_RX             (1 << 13)
+#define PHYCR_PAUSE_TX             (1 << 12)
+#define PHYCR_BIST_FE              (1 << 11)
+#define PHYCR_PSR_15               (1 << 10)
+#define PHYCR_BIST_STATUS          (1 << 9)
+#define PHYCR_BIST_START           (1 << 8)
+#define PHYCR_BP_STRETCH           (1 << 7)
+#define PHYCR_LED_CNFG1            (1 << 6)
+#define PHYCR_LED_CNFG0            (1 << 5)
+#define PHYCR_PHYADDR4             (1 << 4)
+#define PHYCR_PHYADDR3             (1 << 3)
+#define PHYCR_PHYADDR2             (1 << 2)
+#define PHYCR_PHYADDR1             (1 << 1)
+#define PHYCR_PHYADDR0             (1 << 0)
+
+//10BTSCR register
+#define _10BTSCR_10BT_SERIAL       (1 << 15)
+#define _10BTSCR_SQUELCH2          (1 << 11)
+#define _10BTSCR_SQUELCH1          (1 << 10)
+#define _10BTSCR_SQUELCH0          (1 << 9)
+#define _10BTSCR_LOOPBACK_10_DIS   (1 << 8)
+#define _10BTSCR_LP_DIS            (1 << 7)
+#define _10BTSCR_FORCE_LINK_10     (1 << 6)
+#define _10BTSCR_POLARITY          (1 << 4)
+#define _10BTSCR_AUTOPOL_DIS       (1 << 3)
+#define _10BTSCR_10BT_SCALE_MSB    (1 << 2)
+#define _10BTSCR_HEARTBEAT_DIS     (1 << 1)
+#define _10BTSCR_JABBER_DIS        (1 << 0)
+
+//CDCTRL1 register
+#define CDCTRL1_BIST_ERROR_COUNT7  (1 << 15)
+#define CDCTRL1_BIST_ERROR_COUNT6  (1 << 14)
+#define CDCTRL1_BIST_ERROR_COUNT5  (1 << 13)
+#define CDCTRL1_BIST_ERROR_COUNT4  (1 << 12)
+#define CDCTRL1_BIST_ERROR_COUNT3  (1 << 11)
+#define CDCTRL1_BIST_ERROR_COUNT2  (1 << 10)
+#define CDCTRL1_BIST_ERROR_COUNT1  (1 << 9)
+#define CDCTRL1_BIST_ERROR_COUNT0  (1 << 8)
+#define CDCTRL1_MII_CLOCK_EN       (1 << 6)
+#define CDCTRL1_BIST_CONT          (1 << 5)
+#define CDCTRL1_CDPATTEN_10        (1 << 4)
+#define CDCTRL1_MDIO_PULL_EN       (1 << 3)
+#define CDCTRL1_PATT_GAP_10M       (1 << 2)
+#define CDCTRL1_CDPATTSEL1         (1 << 1)
+#define CDCTRL1_CDPATTSEL0         (1 << 0)
+
+//PHYCR2 register
+#define PHYCR2_SYNC_ENET_EN        (1 << 13)
+#define PHYCR2_CLK_OUT RXCLK       (1 << 12)
+#define PHYCR2_BC_WRITE            (1 << 11)
+#define PHYCR2_PHYTER_COMP         (1 << 10)
+#define PHYCR2_SOFT_RESET          (1 << 9)
+#define PHYCR2_CLK_OUT_DIS         (1 << 1)
+
+//EDCR register
+#define EDCR_ED_EN                 (1 << 15)
+#define EDCR_ED_AUTO_UP            (1 << 14)
+#define EDCR_ED_AUTO_DOWN          (1 << 13)
+#define EDCR_ED_MAN                (1 << 12)
+#define EDCR_ED_BURST_DIS          (1 << 11)
+#define EDCR_ED_PWR_STATE          (1 << 10)
+#define EDCR_ED_ERR_MET            (1 << 9)
+#define EDCR_ED_DATA_MET           (1 << 8)
+#define EDCR_ED_ERR_COUNT3         (1 << 7)
+#define EDCR_ED_ERR_COUNT2         (1 << 6)
+#define EDCR_ED_ERR_COUNT1         (1 << 5)
+#define EDCR_ED_ERR_COUNT0         (1 << 4)
+#define EDCR_ED_DATA_COUNT3        (1 << 3)
+#define EDCR_ED_DATA_COUNT2        (1 << 2)
+#define EDCR_ED_DATA_COUNT1        (1 << 1)
+#define EDCR_ED_DATA_COUNT0        (1 << 0)
+
+//PCFCR register
+#define PCFCR_PCF_STS_ERR          (1 << 15)
+#define PCFCR_PCF_STS_OK           (1 << 14)
+#define PCFCR_PCF_DA_SEL           (1 << 8)
+#define PCFCR_PCF_INT_CTL1         (1 << 7)
+#define PCFCR_PCF_INT_CTL0         (1 << 6)
+#define PCFCR_PCF_BC_DIS           (1 << 5)
+#define PCFCR_PCF_BUF3             (1 << 4)
+#define PCFCR_PCF_BUF2             (1 << 3)
+#define PCFCR_PCF_BUF1             (1 << 2)
+#define PCFCR_PCF_BUF0             (1 << 1)
+#define PCFCR_PCF_EN               (1 << 0)
+
+//SD_CNFG register
+#define SD_CNFG_SD_TIME            (1 << 8)
+
+//LEN100_DET register
+#define LEN100_DET_CABLE_LEN7      (1 << 7)
+#define LEN100_DET_CABLE_LEN6      (1 << 6)
+#define LEN100_DET_CABLE_LEN5      (1 << 5)
+#define LEN100_DET_CABLE_LEN4      (1 << 4)
+#define LEN100_DET_CABLE_LEN3      (1 << 3)
+#define LEN100_DET_CABLE_LEN2      (1 << 2)
+#define LEN100_DET_CABLE_LEN1      (1 << 1)
+#define LEN100_DET_CABLE_LEN0      (1 << 0)
+
+//FREQ100 register
+#define FREQ100_SAMPLE_FREQ        (1 << 15)
+#define FREQ100_SEL_FC             (1 << 8)
+#define FREQ100_FREQ_OFFSET7       (1 << 7)
+#define FREQ100_FREQ_OFFSET6       (1 << 6)
+#define FREQ100_FREQ_OFFSET5       (1 << 5)
+#define FREQ100_FREQ_OFFSET4       (1 << 4)
+#define FREQ100_FREQ_OFFSET3       (1 << 3)
+#define FREQ100_FREQ_OFFSET2       (1 << 2)
+#define FREQ100_FREQ_OFFSET1       (1 << 1)
+#define FREQ100_FREQ_OFFSET0       (1 << 0)
+
+//TDR_CTRL register
+#define TDR_CTRL_TDR_ENABLE        (1 << 15)
+#define TDR_CTRL_TDR_100MB         (1 << 14)
+#define TDR_CTRL_TX_CHANNEL        (1 << 13)
+#define TDR_CTRL_RX_CHANNEL        (1 << 12)
+#define TDR_CTRL_SEND_TDR          (1 << 11)
+#define TDR_CTRL_TDR_WIDTH2        (1 << 10)
+#define TDR_CTRL_TDR_WIDTH1        (1 << 9)
+#define TDR_CTRL_TDR_WIDTH0        (1 << 8)
+#define TDR_CTRL_TDR_MIN_MODE      (1 << 7)
+#define TDR_CTRL_RX_THRESHOLD5     (1 << 5)
+#define TDR_CTRL_RX_THRESHOLD4     (1 << 4)
+#define TDR_CTRL_RX_THRESHOLD3     (1 << 3)
+#define TDR_CTRL_RX_THRESHOLD2     (1 << 2)
+#define TDR_CTRL_RX_THRESHOLD1     (1 << 1)
+#define TDR_CTRL_RX_THRESHOLD0     (1 << 0)
+
+//TDR_WIN register
+#define TDR_WIN_TDR_START7         (1 << 15)
+#define TDR_WIN_TDR_START6         (1 << 14)
+#define TDR_WIN_TDR_START5         (1 << 13)
+#define TDR_WIN_TDR_START4         (1 << 12)
+#define TDR_WIN_TDR_START3         (1 << 11)
+#define TDR_WIN_TDR_START2         (1 << 10)
+#define TDR_WIN_TDR_START1         (1 << 9)
+#define TDR_WIN_TDR_START0         (1 << 8)
+#define TDR_WIN_TDR_STOP7          (1 << 7)
+#define TDR_WIN_TDR_STOP6          (1 << 6)
+#define TDR_WIN_TDR_STOP5          (1 << 5)
+#define TDR_WIN_TDR_STOP4          (1 << 4)
+#define TDR_WIN_TDR_STOP3          (1 << 3)
+#define TDR_WIN_TDR_STOP2          (1 << 2)
+#define TDR_WIN_TDR_STOP1          (1 << 1)
+#define TDR_WIN_TDR_STOP0          (1 << 0)
+
+//TDR_PEAK register
+#define TDR_PEAK_TDR_PEAK5         (1 << 13)
+#define TDR_PEAK_TDR_PEAK4         (1 << 12)
+#define TDR_PEAK_TDR_PEAK3         (1 << 11)
+#define TDR_PEAK_TDR_PEAK2         (1 << 10)
+#define TDR_PEAK_TDR_PEAK1         (1 << 9)
+#define TDR_PEAK_TDR_PEAK0         (1 << 8)
+#define TDR_PEAK_TDR_PEAK_TIME7    (1 << 7)
+#define TDR_PEAK_TDR_PEAK_TIME6    (1 << 6)
+#define TDR_PEAK_TDR_PEAK_TIME5    (1 << 5)
+#define TDR_PEAK_TDR_PEAK_TIME4    (1 << 4)
+#define TDR_PEAK_TDR_PEAK_TIME3    (1 << 3)
+#define TDR_PEAK_TDR_PEAK_TIME2    (1 << 2)
+#define TDR_PEAK_TDR_PEAK_TIME1    (1 << 1)
+#define TDR_PEAK_TDR_PEAK_TIME0    (1 << 0)
+
+//TDR_THR register
+#define TDR_THR_TDR_THR_MET        (1 << 8)
+#define TDR_THR_TDR_THR_TIME7      (1 << 7)
+#define TDR_THR_TDR_THR_TIME6      (1 << 6)
+#define TDR_THR_TDR_THR_TIME5      (1 << 5)
+#define TDR_THR_TDR_THR_TIME4      (1 << 4)
+#define TDR_THR_TDR_THR_TIME3      (1 << 3)
+#define TDR_THR_TDR_THR_TIME2      (1 << 2)
+#define TDR_THR_TDR_THR_TIME1      (1 << 1)
+#define TDR_THR_TDR_THR_TIME0      (1 << 0)
+
+//VAR_CTRL register
+#define VAR_CTRL_VAR_RDY           (1 << 15)
+#define VAR_CTRL_VAR_FREEZE        (1 << 3)
+#define VAR_CTRL_VAR_TIMER1        (1 << 2)
+#define VAR_CTRL_VAR_TIMER0        (1 << 1)
+#define VAR_CTRL_VAR_ENABLE        (1 << 0)
+
+//LQMR register
+#define LQMR_LQM_ENABLE            (1 << 15)
+#define LQMR_RESTART_ON_FC         (1 << 14)
+#define LQMR_RESTART_ON_FREQ       (1 << 13)
+#define LQMR_RESTART_ON_DBLW       (1 << 12)
+#define LQMR_RESTART_ON_DAGC       (1 << 11)
+#define LQMR_RESTART_ON_C1         (1 << 10)
+#define LQMR_FC_HI_WARN            (1 << 9)
+#define LQMR_FC_LO_WARN            (1 << 8)
+#define LQMR_FREQ_HI_WARN          (1 << 7)
+#define LQMR_FREQ_LO_WARN          (1 << 6)
+#define LQMR_DBLW_HI_WARN          (1 << 5)
+#define LQMR_DBLW_LO_WARN          (1 << 4)
+#define LQMR_DAGC_HI_WARN          (1 << 3)
+#define LQMR_DAGC_LO_WARN          (1 << 2)
+#define LQMR_C1_HI_WARN            (1 << 1)
+#define LQMR_C1_LO_WARN            (1 << 0)
+
+//LQDR register
+#define LQDR_SAMPLE_PARAM          (1 << 13)
+#define LQDR_WRITE_LQ_THR          (1 << 12)
+#define LQDR_LQ_PARAM_SEL2         (1 << 11)
+#define LQDR_LQ_PARAM_SEL1         (1 << 10)
+#define LQDR_LQ_PARAM_SEL0         (1 << 9)
+#define LQDR_LQ_THR_SEL            (1 << 8)
+#define LQDR_LQ_THR_DATA7          (1 << 7)
+#define LQDR_LQ_THR_DATA6          (1 << 6)
+#define LQDR_LQ_THR_DATA5          (1 << 5)
+#define LQDR_LQ_THR_DATA4          (1 << 4)
+#define LQDR_LQ_THR_DATA3          (1 << 3)
+#define LQDR_LQ_THR_DATA2          (1 << 2)
+#define LQDR_LQ_THR_DATA1          (1 << 1)
+#define LQDR_LQ_THR_DATA0          (1 << 0)
+
+//LQMR2 register
+#define LQMR2_RESTART_ON_VAR       (1 << 10)
+#define LQMR2_VAR_HI_WARN          (1 << 1)
+
+//PSF_CFG register
+#define PSF_CFG_MAC_SRC_ADD1       (1 << 12)
+#define PSF_CFG_MAC_SRC_ADD0       (1 << 11)
+#define PSF_CFG_MIN_PRE2           (1 << 10)
+#define PSF_CFG_MIN_PRE1           (1 << 9)
+#define PSF_CFG_MIN_PRE0           (1 << 8)
+#define PSF_CFG_PSF_ENDIAN         (1 << 7)
+#define PSF_CFG_PSF_IPV4           (1 << 6)
+#define PSF_CFG_PSF_PCF_RD         (1 << 5)
+#define PSF_CFG_PSF_ERR_EN         (1 << 4)
+
+//DP83620 Ethernet PHY driver
+extern const PhyDriver dp83620PhyDriver;
+
+//DP83620 related functions
+error_t dp83620Init(NetInterface *interface);
+
+void dp83620Tick(NetInterface *interface);
+
+void dp83620EnableIrq(NetInterface *interface);
+void dp83620DisableIrq(NetInterface *interface);
+
+void dp83620EventHandler(NetInterface *interface);
+
+void dp83620WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t dp83620ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void dp83620DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/dp83848.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,268 @@
+/**
+ * @file dp83848.c
+ * @brief DP83848 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/dp83848.h"
+#include "debug.h"
+
+
+/**
+ * @brief DP83848 Ethernet PHY driver
+ **/
+
+const PhyDriver dp83848PhyDriver =
+{
+   dp83848Init,
+   dp83848Tick,
+   dp83848EnableIrq,
+   dp83848DisableIrq,
+   dp83848EventHandler,
+};
+
+
+/**
+ * @brief DP83848 PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t dp83848Init(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing DP83848...\r\n");
+
+   //Initialize external interrupt line driver
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->init();
+
+   //Reset PHY transceiver
+   dp83848WritePhyReg(interface, DP83848_PHY_REG_BMCR, BMCR_RESET);
+   //Wait for the reset to complete
+   while(dp83848ReadPhyReg(interface, DP83848_PHY_REG_BMCR) & BMCR_RESET);
+
+   //Dump PHY registers for debugging purpose
+   dp83848DumpPhyReg(interface);
+
+   //Configure PWR_DOWN/INT pin as an interrupt output
+   dp83848WritePhyReg(interface, DP83848_PHY_REG_MICR, MICR_INTEN | MICR_INT_OE);
+   //The PHY will generate interrupts when link status changes are detected
+   dp83848WritePhyReg(interface, DP83848_PHY_REG_MISR, MISR_LINK_INT_EN);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief DP83848 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void dp83848Tick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //No external interrupt line driver?
+   if(interface->extIntDriver == NULL)
+   {
+      //Read basic status register
+      value = dp83848ReadPhyReg(interface, DP83848_PHY_REG_BMSR);
+      //Retrieve current link state
+      linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+      //Link up event?
+      if(linkState && !interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+      //Link down event?
+      else if(!linkState && interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void dp83848EnableIrq(NetInterface *interface)
+{
+   //Enable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void dp83848DisableIrq(NetInterface *interface)
+{
+   //Disable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief DP83848 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void dp83848EventHandler(NetInterface *interface)
+{
+   uint16_t status;
+
+   //Read status register to acknowledge the interrupt
+   status = dp83848ReadPhyReg(interface, DP83848_PHY_REG_MISR);
+
+   //Link status change?
+   if(status & MISR_LINK_INT)
+   {
+      //Read PHY status register
+      status = dp83848ReadPhyReg(interface, DP83848_PHY_REG_PHYSTS);
+
+      //Link is up?
+      if(status & PHYSTS_LINK_STATUS)
+      {
+         //Check current speed
+         if(status & PHYSTS_SPEED_STATUS)
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+         else
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+
+         //Check duplex mode
+         if(status & PHYSTS_DUPLEX_STATUS)
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+         else
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+
+         //Update link state
+         interface->linkState = TRUE;
+
+         //Adjust MAC configuration parameters for proper operation
+         interface->nicDriver->updateMacConfig(interface);
+      }
+      else
+      {
+         //Update link state
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void dp83848WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = DP83848_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t dp83848ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = DP83848_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void dp83848DumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, dp83848ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/dp83848.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,310 @@
+/**
+ * @file dp83848.h
+ * @brief DP83848 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DP83848_H
+#define _DP83848_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#ifndef DP83848_PHY_ADDR
+   #define DP83848_PHY_ADDR 1
+#elif (DP83848_PHY_ADDR < 0 || DP83848_PHY_ADDR > 31)
+   #error DP83848_PHY_ADDR parameter is not valid
+#endif
+
+//DP83848 registers
+#define DP83848_PHY_REG_BMCR       0x00
+#define DP83848_PHY_REG_BMSR       0x01
+#define DP83848_PHY_REG_PHYIDR1    0x02
+#define DP83848_PHY_REG_PHYIDR2    0x03
+#define DP83848_PHY_REG_ANAR       0x04
+#define DP83848_PHY_REG_ANLPAR     0x05
+#define DP83848_PHY_REG_ANER       0x06
+#define DP83848_PHY_REG_ANNPTR     0x07
+#define DP83848_PHY_REG_PHYSTS     0x10
+#define DP83848_PHY_REG_MICR       0x11
+#define DP83848_PHY_REG_MISR       0x12
+#define DP83848_PHY_REG_FCSCR      0x14
+#define DP83848_PHY_REG_RECR       0x15
+#define DP83848_PHY_REG_PCSR       0x16
+#define DP83848_PHY_REG_RBR        0x17
+#define DP83848_PHY_REG_LEDCR      0x18
+#define DP83848_PHY_REG_PHYCR      0x19
+#define DP83848_PHY_REG_10BTSCR    0x1A
+#define DP83848_PHY_REG_CDCTRL1    0x1B
+#define DP83848_PHY_REG_EDCR       0x1D
+
+//BMCR register
+#define BMCR_RESET                 (1 << 15)
+#define BMCR_LOOPBACK              (1 << 14)
+#define BMCR_SPEED_SEL             (1 << 13)
+#define BMCR_AN_EN                 (1 << 12)
+#define BMCR_POWER_DOWN            (1 << 11)
+#define BMCR_ISOLATE               (1 << 10)
+#define BMCR_RESTART_AN            (1 << 9)
+#define BMCR_DUPLEX_MODE           (1 << 8)
+#define BMCR_COL_TEST              (1 << 7)
+
+//BMSR register
+#define BMSR_100BT4                (1 << 15)
+#define BMSR_100BTX_FD             (1 << 14)
+#define BMSR_100BTX                (1 << 13)
+#define BMSR_10BT_FD               (1 << 12)
+#define BMSR_10BT                  (1 << 11)
+#define BMSR_NO_PREAMBLE           (1 << 6)
+#define BMSR_AN_COMPLETE           (1 << 5)
+#define BMSR_REMOTE_FAULT          (1 << 4)
+#define BMSR_AN_ABLE               (1 << 3)
+#define BMSR_LINK_STATUS           (1 << 2)
+#define BMSR_JABBER_DETECT         (1 << 1)
+#define BMSR_EXTENDED_CAP          (1 << 0)
+
+//ANAR register
+#define ANAR_NP                    (1 << 15)
+#define ANAR_RF                    (1 << 13)
+#define ANAR_ASM_DIR               (1 << 11)
+#define ANAR_PAUSE                 (1 << 10)
+#define ANAR_100BT4                (1 << 9)
+#define ANAR_100BTX_FD             (1 << 8)
+#define ANAR_100BTX                (1 << 7)
+#define ANAR_10BT_FD               (1 << 6)
+#define ANAR_10BT                  (1 << 5)
+#define ANAR_SELECTOR4             (1 << 4)
+#define ANAR_SELECTOR3             (1 << 3)
+#define ANAR_SELECTOR2             (1 << 2)
+#define ANAR_SELECTOR1             (1 << 1)
+#define ANAR_SELECTOR0             (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NP                  (1 << 15)
+#define ANLPAR_ACK                 (1 << 14)
+#define ANLPAR_RF                  (1 << 13)
+#define ANLPAR_ASM_DIR             (1 << 11)
+#define ANLPAR_PAUSE               (1 << 10)
+#define ANLPAR_100BT4              (1 << 9)
+#define ANLPAR_100BTX_FD           (1 << 8)
+#define ANLPAR_100BTX              (1 << 7)
+#define ANLPAR_10BT_FD             (1 << 6)
+#define ANLPAR_10BT                (1 << 5)
+#define ANLPAR_SELECTOR4           (1 << 4)
+#define ANLPAR_SELECTOR3           (1 << 3)
+#define ANLPAR_SELECTOR2           (1 << 2)
+#define ANLPAR_SELECTOR1           (1 << 1)
+#define ANLPAR_SELECTOR0           (1 << 0)
+
+//ANER register
+#define ANER_PDF                   (1 << 4)
+#define ANER_LP_NP_ABLE            (1 << 3)
+#define ANER_NP_ABLE               (1 << 2)
+#define ANER_PAGE_RX               (1 << 1)
+#define ANER_LP_AN_ABLE            (1 << 0)
+
+//ANNPTR register
+#define ANNPTR_NP                  (1 << 15)
+#define ANNPTR_MP                  (1 << 13)
+#define ANNPTR_ACK2                (1 << 12)
+#define ANNPTR_TOG_TX              (1 << 11)
+#define ANNPTR_CODE10              (1 << 10)
+#define ANNPTR_CODE9               (1 << 9)
+#define ANNPTR_CODE8               (1 << 8)
+#define ANNPTR_CODE7               (1 << 7)
+#define ANNPTR_CODE6               (1 << 6)
+#define ANNPTR_CODE5               (1 << 5)
+#define ANNPTR_CODE4               (1 << 4)
+#define ANNPTR_CODE3               (1 << 3)
+#define ANNPTR_CODE2               (1 << 2)
+#define ANNPTR_CODE1               (1 << 1)
+#define ANNPTR_CODE0               (1 << 0)
+
+//PHYSTS register
+#define PHYSTS_MDIX_MODE           (1 << 14)
+#define PHYSTS_RX_ERROR_LATCH      (1 << 13)
+#define PHYSTS_POLARITY_STATUS     (1 << 12)
+#define PHYSTS_FALSE_CARRIER_SENSE (1 << 11)
+#define PHYSTS_SIGNAL_DETECT       (1 << 10)
+#define PHYSTS_DESCRAMBLER_LOCK    (1 << 9)
+#define PHYSTS_PAGE_RECEIVED       (1 << 8)
+#define PHYSTS_MII_INTERRUPT       (1 << 7)
+#define PHYSTS_REMOTE_FAULT        (1 << 6)
+#define PHYSTS_JABBER_DETECT       (1 << 5)
+#define PHYSTS_AN_COMPLETE         (1 << 4)
+#define PHYSTS_LOOPBACK_STATUS     (1 << 3)
+#define PHYSTS_DUPLEX_STATUS       (1 << 2)
+#define PHYSTS_SPEED_STATUS        (1 << 1)
+#define PHYSTS_LINK_STATUS         (1 << 0)
+
+//MICR register
+#define MICR_TINT                  (1 << 2)
+#define MICR_INTEN                 (1 << 1)
+#define MICR_INT_OE                (1 << 0)
+
+//MISR register
+#define MISR_ED_INT                (1 << 14)
+#define MISR_LINK_INT              (1 << 13)
+#define MISR_SPD_INT               (1 << 12)
+#define MISR_DUP_INT               (1 << 11)
+#define MISR_ANC_INT               (1 << 10)
+#define MISR_FHF_INT               (1 << 9)
+#define MISR_RHF_INT               (1 << 8)
+#define MISR_ED_INT_EN             (1 << 6)
+#define MISR_LINK_INT_EN           (1 << 5)
+#define MISR_SPD_INT_EN            (1 << 4)
+#define MISR_DUP_INT_EN            (1 << 3)
+#define MISR_ANC_INT_EN            (1 << 2)
+#define MISR_FHF_INT_EN            (1 << 1)
+#define MISR_RHF_INT_EN            (1 << 0)
+
+//FCSCR register
+#define FCSCR_FCSCNT7              (1 << 7)
+#define FCSCR_FCSCNT6              (1 << 6)
+#define FCSCR_FCSCNT5              (1 << 5)
+#define FCSCR_FCSCNT4              (1 << 4)
+#define FCSCR_FCSCNT3              (1 << 3)
+#define FCSCR_FCSCNT2              (1 << 2)
+#define FCSCR_FCSCNT1              (1 << 1)
+#define FCSCR_FCSCNT0              (1 << 0)
+
+//RECR register
+#define RECR_RXERCNT7              (1 << 7)
+#define RECR_RXERCNT6              (1 << 6)
+#define RECR_RXERCNT5              (1 << 5)
+#define RECR_RXERCNT4              (1 << 4)
+#define RECR_RXERCNT3              (1 << 3)
+#define RECR_RXERCNT2              (1 << 2)
+#define RECR_RXERCNT1              (1 << 1)
+#define RECR_RXERCNT0              (1 << 0)
+
+//PCSR register
+#define PCSR_TQ_EN                 (1 << 10)
+#define PCSR_SD_FORCE_PMA          (1 << 9)
+#define PCSR_SD_OPTION             (1 << 8)
+#define PCSR_DESC_TIME             (1 << 7)
+#define PCSR_FORCE_100_OK          (1 << 5)
+#define PCSR_NRZI_BYPASS           (1 << 2)
+
+//RBR register
+#define RBR_RMII_MODE              (1 << 5)
+#define RBR_RMII_REV1_0            (1 << 4)
+#define RBR_RX_OVF_STS             (1 << 3)
+#define RBR_RX_UNF_STS             (1 << 2)
+#define RBR_ELAST_BUF1             (1 << 1)
+#define RBR_ELAST_BUF0             (1 << 0)
+
+//LEDCR register
+#define LEDCR_DRV_SPDLED           (1 << 5)
+#define LEDCR_DRV_LNKLED           (1 << 4)
+#define LEDCR_DRV_ACTLED           (1 << 3)
+#define LEDCR_SPDLED               (1 << 2)
+#define LEDCR_LNKLED               (1 << 1)
+#define LEDCR_ACTLED               (1 << 0)
+
+//PHYCR register
+#define PHYCR_MDIX_EN              (1 << 15)
+#define PHYCR_FORCE_MDIX           (1 << 14)
+#define PHYCR_PAUSE_RX             (1 << 13)
+#define PHYCR_PAUSE_TX             (1 << 12)
+#define PHYCR_BIST_FE              (1 << 11)
+#define PHYCR_PSR_15               (1 << 10)
+#define PHYCR_BIST_STATUS          (1 << 9)
+#define PHYCR_BIST_START           (1 << 8)
+#define PHYCR_BP_STRETCH           (1 << 7)
+#define PHYCR_LED_CNFG1            (1 << 6)
+#define PHYCR_LED_CNFG0            (1 << 5)
+#define PHYCR_PHYADDR4             (1 << 4)
+#define PHYCR_PHYADDR3             (1 << 3)
+#define PHYCR_PHYADDR2             (1 << 2)
+#define PHYCR_PHYADDR1             (1 << 1)
+#define PHYCR_PHYADDR0             (1 << 0)
+
+//10BTSCR register
+#define _10BTSCR_10BT_SERIAL       (1 << 15)
+#define _10BTSCR_SQUELCH2          (1 << 11)
+#define _10BTSCR_SQUELCH1          (1 << 10)
+#define _10BTSCR_SQUELCH0          (1 << 9)
+#define _10BTSCR_LOOPBACK_10_DIS   (1 << 8)
+#define _10BTSCR_LP_DIS            (1 << 7)
+#define _10BTSCR_FORCE_LINK_10     (1 << 6)
+#define _10BTSCR_POLARITY          (1 << 4)
+#define _10BTSCR_HEARTBEAT_DIS     (1 << 1)
+#define _10BTSCR_JABBER_DIS        (1 << 0)
+
+//CDCTRL1 register
+#define CDCTRL1_BIST_ERROR_COUNT7  (1 << 15)
+#define CDCTRL1_BIST_ERROR_COUNT6  (1 << 14)
+#define CDCTRL1_BIST_ERROR_COUNT5  (1 << 13)
+#define CDCTRL1_BIST_ERROR_COUNT4  (1 << 12)
+#define CDCTRL1_BIST_ERROR_COUNT3  (1 << 11)
+#define CDCTRL1_BIST_ERROR_COUNT2  (1 << 10)
+#define CDCTRL1_BIST_ERROR_COUNT1  (1 << 9)
+#define CDCTRL1_BIST_ERROR_COUNT0  (1 << 8)
+#define CDCTRL1_BIST_CONT_MODE     (1 << 5)
+#define CDCTRL1_CDPATTEN_10        (1 << 4)
+#define CDCTRL1_10MEG_PATT_GAP     (1 << 2)
+#define CDCTRL1_CDPATTSEL1         (1 << 1)
+#define CDCTRL1_CDPATTSEL0         (1 << 0)
+
+//EDCR register
+#define EDCR_ED_EN                 (1 << 15)
+#define EDCR_ED_AUTO_UP            (1 << 14)
+#define EDCR_ED_AUTO_DOWN          (1 << 13)
+#define EDCR_ED_MAN                (1 << 12)
+#define EDCR_ED_BURST_DIS          (1 << 11)
+#define EDCR_ED_PWR_STATE          (1 << 10)
+#define EDCR_ED_ERR_MET            (1 << 9)
+#define EDCR_ED_DATA_MET           (1 << 8)
+#define EDCR_ED_ERR_COUNT3         (1 << 7)
+#define EDCR_ED_ERR_COUNT2         (1 << 6)
+#define EDCR_ED_ERR_COUNT1         (1 << 5)
+#define EDCR_ED_ERR_COUNT0         (1 << 4)
+#define EDCR_ED_DATA_COUNT3        (1 << 3)
+#define EDCR_ED_DATA_COUNT2        (1 << 2)
+#define EDCR_ED_DATA_COUNT1        (1 << 1)
+#define EDCR_ED_DATA_COUNT0        (1 << 0)
+
+//DP83848 Ethernet PHY driver
+extern const PhyDriver dp83848PhyDriver;
+
+//DP83848 related functions
+error_t dp83848Init(NetInterface *interface);
+
+void dp83848Tick(NetInterface *interface);
+
+void dp83848EnableIrq(NetInterface *interface);
+void dp83848DisableIrq(NetInterface *interface);
+
+void dp83848EventHandler(NetInterface *interface);
+
+void dp83848WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t dp83848ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void dp83848DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/enc28j60.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1019 @@
+/**
+ * @file enc28j60.c
+ * @brief ENC28J60 Ethernet controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include <limits.h>
+#include "core/net.h"
+#include "drivers/enc28j60.h"
+#include "debug.h"
+
+
+/**
+ * @brief ENC28J60 driver
+ **/
+
+const NicDriver enc28j60Driver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   enc28j60Init,
+   enc28j60Tick,
+   enc28j60EnableIrq,
+   enc28j60DisableIrq,
+   enc28j60EventHandler,
+   enc28j60SendPacket,
+   enc28j60SetMulticastFilter,
+   NULL,
+   NULL,
+   NULL,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief ENC28J60 controller initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t enc28j60Init(NetInterface *interface)
+{
+   uint8_t revisionId;
+   Enc28j60Context *context;
+
+   //Debug message
+   TRACE_INFO("Initializing ENC28J60 Ethernet controller...\r\n");
+
+   //Initialize SPI
+   interface->spiDriver->init();
+   //Initialize external interrupt line
+   interface->extIntDriver->init();
+
+   //Issue a system reset
+   enc28j60SoftReset(interface);
+
+   //After issuing the reset command, wait at least 1ms in firmware
+   //for the device to be ready
+   sleep(10);
+
+   //Point to the driver context
+   context = (Enc28j60Context *) interface->nicContext;
+
+   //Initialize driver specific variables
+   context->currentBank = UINT16_MAX;
+   context->nextPacket = ENC28J60_RX_BUFFER_START;
+
+   //Allocate RX buffer
+   context->rxBuffer = memPoolAlloc(ETH_MAX_FRAME_SIZE);
+   //Failed to allocate memory?
+   if(context->rxBuffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Read silicon revision ID
+   revisionId = enc28j60ReadReg(interface, ENC28J60_REG_EREVID);
+
+   //Debug message
+   TRACE_INFO("ENC28J60 revision ID: 0x%02X\r\n", revisionId);
+
+   //Disable CLKOUT output
+   enc28j60WriteReg(interface, ENC28J60_REG_ECOCON, 0x00);
+
+   //Set the MAC address
+   enc28j60WriteReg(interface, ENC28J60_REG_MAADR1, interface->macAddr.b[0]);
+   enc28j60WriteReg(interface, ENC28J60_REG_MAADR2, interface->macAddr.b[1]);
+   enc28j60WriteReg(interface, ENC28J60_REG_MAADR3, interface->macAddr.b[2]);
+   enc28j60WriteReg(interface, ENC28J60_REG_MAADR4, interface->macAddr.b[3]);
+   enc28j60WriteReg(interface, ENC28J60_REG_MAADR5, interface->macAddr.b[4]);
+   enc28j60WriteReg(interface, ENC28J60_REG_MAADR6, interface->macAddr.b[5]);
+
+   //Set receive buffer location
+   enc28j60WriteReg(interface, ENC28J60_REG_ERXSTL, LSB(ENC28J60_RX_BUFFER_START));
+   enc28j60WriteReg(interface, ENC28J60_REG_ERXSTH, MSB(ENC28J60_RX_BUFFER_START));
+   enc28j60WriteReg(interface, ENC28J60_REG_ERXNDL, LSB(ENC28J60_RX_BUFFER_STOP));
+   enc28j60WriteReg(interface, ENC28J60_REG_ERXNDH, MSB(ENC28J60_RX_BUFFER_STOP));
+
+   //The ERXRDPT register defines a location within the FIFO
+   //where the receive hardware is forbidden to write to
+   enc28j60WriteReg(interface, ENC28J60_REG_ERXRDPTL, LSB(ENC28J60_RX_BUFFER_STOP));
+   enc28j60WriteReg(interface, ENC28J60_REG_ERXRDPTH, MSB(ENC28J60_RX_BUFFER_STOP));
+
+   //Configure the receive filters
+   enc28j60WriteReg(interface, ENC28J60_REG_ERXFCON, ERXFCON_UCEN |
+      ERXFCON_CRCEN | ERXFCON_HTEN | ERXFCON_BCEN);
+
+   //Initialize the hash table
+   enc28j60WriteReg(interface, ENC28J60_REG_EHT0, 0x00);
+   enc28j60WriteReg(interface, ENC28J60_REG_EHT1, 0x00);
+   enc28j60WriteReg(interface, ENC28J60_REG_EHT2, 0x00);
+   enc28j60WriteReg(interface, ENC28J60_REG_EHT3, 0x00);
+   enc28j60WriteReg(interface, ENC28J60_REG_EHT4, 0x00);
+   enc28j60WriteReg(interface, ENC28J60_REG_EHT5, 0x00);
+   enc28j60WriteReg(interface, ENC28J60_REG_EHT6, 0x00);
+   enc28j60WriteReg(interface, ENC28J60_REG_EHT7, 0x00);
+
+   //Pull the MAC out of reset
+   enc28j60WriteReg(interface, ENC28J60_REG_MACON2, 0x00);
+
+   //Enable the MAC to receive frames
+   enc28j60WriteReg(interface, ENC28J60_REG_MACON1,
+      MACON1_TXPAUS | MACON1_RXPAUS | MACON1_MARXEN);
+
+   //Enable automatic padding to at least 60 bytes, always append a valid CRC
+   //and check frame length. MAC can operate in half-duplex or full-duplex mode
+#if (ENC28J60_FULL_DUPLEX_SUPPORT == ENABLED)
+   enc28j60WriteReg(interface, ENC28J60_REG_MACON3, MACON3_PADCFG(1) |
+      MACON3_TXCRCEN | MACON3_FRMLNEN | MACON3_FULDPX);
+#else
+   enc28j60WriteReg(interface, ENC28J60_REG_MACON3, MACON3_PADCFG(1) |
+      MACON3_TXCRCEN | MACON3_FRMLNEN);
+#endif
+
+   //When the medium is occupied, the MAC will wait indefinitely for it to
+   //become free when attempting to transmit
+   enc28j60WriteReg(interface, ENC28J60_REG_MACON4, MACON4_DEFER);
+
+   //Maximum frame length that can be received or transmitted (1518 bytes)
+   enc28j60WriteReg(interface, ENC28J60_REG_MAMXFLL, LSB(1518));
+   enc28j60WriteReg(interface, ENC28J60_REG_MAMXFLH, MSB(1518));
+
+   //Configure the back-to-back inter-packet gap register
+#if (ENC28J60_FULL_DUPLEX_SUPPORT == ENABLED)
+   enc28j60WriteReg(interface, ENC28J60_REG_MABBIPG, 0x15);
+#else
+   enc28j60WriteReg(interface, ENC28J60_REG_MABBIPG, 0x12);
+#endif
+
+   //Configure the non-back-to-back inter-packet gap register
+   enc28j60WriteReg(interface, ENC28J60_REG_MAIPGL, 0x12);
+   enc28j60WriteReg(interface, ENC28J60_REG_MAIPGH, 0x0C);
+
+   //Collision window register
+   enc28j60WriteReg(interface, ENC28J60_REG_MACLCON2, 63);
+
+   //Set the PHY to the proper duplex mode
+#if (ENC28J60_FULL_DUPLEX_SUPPORT == ENABLED)
+   enc28j60WritePhyReg(interface, ENC28J60_PHY_REG_PHCON1, PHCON1_PDPXMD);
+#else
+   enc28j60WritePhyReg(interface, ENC28J60_PHY_REG_PHCON1, 0x0000);
+#endif
+
+   //Disable half-duplex loopback in PHY
+   enc28j60WritePhyReg(interface, ENC28J60_PHY_REG_PHCON2, PHCON2_HDLDIS);
+
+   //LEDA displays link status and LEDB displays TX/RX activity
+   enc28j60WritePhyReg(interface, ENC28J60_PHY_REG_PHLCON,
+      PHLCON_LACFG(4) | PHLCON_LBCFG(7) | PHLCON_LFRQ(0) | PHLCON_STRCH);
+
+   //Clear interrupt flags
+   enc28j60WriteReg(interface, ENC28J60_REG_EIR, 0x00);
+
+   //Configure interrupts as desired
+   enc28j60WriteReg(interface, ENC28J60_REG_EIE, EIE_INTIE |
+      EIE_PKTIE | EIE_LINKIE | EIE_TXIE | EIE_TXERIE);
+
+   //Configure PHY interrupts as desired
+   enc28j60WritePhyReg(interface, ENC28J60_PHY_REG_PHIE,
+      PHIE_PLNKIE | PHIE_PGEIE);
+
+   //Set RXEN to enable reception
+   enc28j60SetBit(interface, ENC28J60_REG_ECON1, ECON1_RXEN);
+
+   //Dump registers for debugging purpose
+   enc28j60DumpReg(interface);
+   enc28j60DumpPhyReg(interface);
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->nicEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief ENC28J60 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void enc28j60Tick(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void enc28j60EnableIrq(NetInterface *interface)
+{
+   //Enable interrupts
+   interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void enc28j60DisableIrq(NetInterface *interface)
+{
+   //Disable interrupts
+   interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief ENC28J60 interrupt service routine
+ * @param[in] interface Underlying network interface
+ * @return TRUE if a higher priority task must be woken. Else FALSE is returned
+ **/
+
+bool_t enc28j60IrqHandler(NetInterface *interface)
+{
+   bool_t flag;
+   uint8_t status;
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Clear the INTIE bit, immediately after an interrupt event
+   enc28j60ClearBit(interface, ENC28J60_REG_EIE, EIE_INTIE);
+
+   //Read interrupt status register
+   status = enc28j60ReadReg(interface, ENC28J60_REG_EIR);
+
+   //Link status change?
+   if(status & EIR_LINKIF)
+   {
+      //Disable LINKIE interrupt
+      enc28j60ClearBit(interface, ENC28J60_REG_EIE, EIE_LINKIE);
+
+      //Set event flag
+      interface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Packet received?
+   if(status & EIR_PKTIF)
+   {
+      //Disable PKTIE interrupt
+      enc28j60ClearBit(interface, ENC28J60_REG_EIE, EIE_PKTIE);
+
+      //Set event flag
+      interface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Packet transmission complete?
+   if(status & (EIR_TXIF | EIE_TXERIE))
+   {
+      //Clear interrupt flags
+      enc28j60ClearBit(interface, ENC28J60_REG_EIR, EIR_TXIF | EIE_TXERIE);
+
+      //Notify the TCP/IP stack that the transmitter is ready to send
+      flag |= osSetEventFromIsr(&interface->nicTxEvent);
+   }
+
+   //Once the interrupt has been serviced, the INTIE bit
+   //is set again to re-enable interrupts
+   enc28j60SetBit(interface, ENC28J60_REG_EIE, EIE_INTIE);
+
+   //A higher priority task must be woken?
+   return flag;
+}
+
+
+/**
+ * @brief ENC28J60 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void enc28j60EventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint16_t status;
+   uint16_t value;
+
+   //Read interrupt status register
+   status = enc28j60ReadReg(interface, ENC28J60_REG_EIR);
+
+   //Check whether the link state has changed
+   if(status & EIR_LINKIF)
+   {
+      //Clear PHY interrupts flags
+      enc28j60ReadPhyReg(interface, ENC28J60_PHY_REG_PHIR);
+      //Clear interrupt flag
+      enc28j60ClearBit(interface, ENC28J60_REG_EIR, EIR_LINKIF);
+      //Read PHY status register
+      value = enc28j60ReadPhyReg(interface, ENC28J60_PHY_REG_PHSTAT2);
+
+      //Check link state
+      if(value & PHSTAT2_LSTAT)
+      {
+         //Link speed
+         interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+
+#if (ENC28J60_FULL_DUPLEX_SUPPORT == ENABLED)
+         //Full-duplex mode
+         interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+#else
+         //Half-duplex mode
+         interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+#endif
+         //Link is up
+         interface->linkState = TRUE;
+      }
+      else
+      {
+         //Link is down
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+
+   //Check whether a packet has been received?
+   if(status & EIR_PKTIF)
+   {
+      //Clear interrupt flag
+      enc28j60ClearBit(interface, ENC28J60_REG_EIR, EIR_PKTIF);
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = enc28j60ReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable LINKIE and PKTIE interrupts
+   enc28j60SetBit(interface, ENC28J60_REG_EIE, EIE_LINKIE | EIE_PKTIE);
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t enc28j60SendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > 1536)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the link is up before transmitting the frame
+   if(!interface->linkState)
+   {
+      //The transmitter can accept another packet
+      osSetEventFromIsr(&interface->nicTxEvent);
+      //Drop current packet
+      return NO_ERROR;
+   }
+
+   //It is recommended to reset the transmit logic before
+   //attempting to transmit a packet
+   enc28j60SetBit(interface, ENC28J60_REG_ECON1, ECON1_TXRST);
+   enc28j60ClearBit(interface, ENC28J60_REG_ECON1, ECON1_TXRST);
+
+   //Interrupt flags should be cleared after the reset is completed
+   enc28j60ClearBit(interface, ENC28J60_REG_EIR, EIR_TXIF | EIR_TXERIF);
+
+   //Set transmit buffer location
+   enc28j60WriteReg(interface, ENC28J60_REG_ETXSTL, LSB(ENC28J60_TX_BUFFER_START));
+   enc28j60WriteReg(interface, ENC28J60_REG_ETXSTH, MSB(ENC28J60_TX_BUFFER_START));
+
+   //Point to start of transmit buffer
+   enc28j60WriteReg(interface, ENC28J60_REG_EWRPTL, LSB(ENC28J60_TX_BUFFER_START));
+   enc28j60WriteReg(interface, ENC28J60_REG_EWRPTH, MSB(ENC28J60_TX_BUFFER_START));
+
+   //Copy the data to the transmit buffer
+   enc28j60WriteBuffer(interface, buffer, offset);
+
+   //ETXND should point to the last byte in the data payload
+   enc28j60WriteReg(interface, ENC28J60_REG_ETXNDL, LSB(ENC28J60_TX_BUFFER_START + length));
+   enc28j60WriteReg(interface, ENC28J60_REG_ETXNDH, MSB(ENC28J60_TX_BUFFER_START + length));
+
+   //Start transmission
+   enc28j60SetBit(interface, ENC28J60_REG_ECON1, ECON1_TXRTS);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t enc28j60ReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   uint16_t n;
+   uint16_t status;
+   Enc28j60Context *context;
+
+   //Point to the driver context
+   context = (Enc28j60Context *) interface->nicContext;
+
+   //Any packet pending in the receive buffer?
+   if(enc28j60ReadReg(interface, ENC28J60_REG_EPKTCNT))
+   {
+      //Point to the start of the received packet
+      enc28j60WriteReg(interface, ENC28J60_REG_ERDPTL, LSB(context->nextPacket));
+      enc28j60WriteReg(interface, ENC28J60_REG_ERDPTH, MSB(context->nextPacket));
+
+      //Read the first two bytes, which are the address of the next packet
+      enc28j60ReadBuffer(interface, (uint8_t *) &context->nextPacket, sizeof(uint16_t));
+      //Get the length of the received frame in bytes
+      enc28j60ReadBuffer(interface, (uint8_t *) &n, sizeof(uint16_t));
+      //Read the receive status vector (RSV)
+      enc28j60ReadBuffer(interface, (uint8_t *) &status, sizeof(uint16_t));
+
+      //Make sure no error occurred
+      if(status & RSV_RECEIVED_OK)
+      {
+         //Limit the number of data to read
+         n = MIN(n, ETH_MAX_FRAME_SIZE);
+         //Read the Ethernet frame
+         enc28j60ReadBuffer(interface, context->rxBuffer, n);
+         //Valid packet received
+         error = NO_ERROR;
+      }
+      else
+      {
+         //The received packet contains an error
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Advance the ERXRDPT pointer, taking care to wrap back at the
+      //end of the received memory buffer
+      if(context->nextPacket == ENC28J60_RX_BUFFER_START)
+      {
+         enc28j60WriteReg(interface, ENC28J60_REG_ERXRDPTL, LSB(ENC28J60_RX_BUFFER_STOP));
+         enc28j60WriteReg(interface, ENC28J60_REG_ERXRDPTH, MSB(ENC28J60_RX_BUFFER_STOP));
+      }
+      else
+      {
+         enc28j60WriteReg(interface, ENC28J60_REG_ERXRDPTL, LSB(context->nextPacket - 1));
+         enc28j60WriteReg(interface, ENC28J60_REG_ERXRDPTH, MSB(context->nextPacket - 1));
+      }
+
+      //Decrement the packet counter
+      enc28j60SetBit(interface, ENC28J60_REG_ECON2, ECON2_PKTDEC);
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Check whether a valid packet has been received
+   if(!error)
+   {
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, context->rxBuffer, n);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t enc28j60SetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint8_t hashTable[8];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating ENC28J60 hash table...\r\n");
+
+   //Clear hash table
+   memset(hashTable, 0, sizeof(hashTable));
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = enc28j60CalcCrc(&entry->addr, sizeof(MacAddr));
+         //Calculate the corresponding index in the table
+         k = (crc >> 23) & 0x3F;
+         //Update hash table contents
+         hashTable[k / 8] |= (1 << (k % 8));
+      }
+   }
+
+   //Write the hash table to the ENC28J60 controller
+   enc28j60WriteReg(interface, ENC28J60_REG_EHT0, hashTable[0]);
+   enc28j60WriteReg(interface, ENC28J60_REG_EHT1, hashTable[1]);
+   enc28j60WriteReg(interface, ENC28J60_REG_EHT2, hashTable[2]);
+   enc28j60WriteReg(interface, ENC28J60_REG_EHT3, hashTable[3]);
+   enc28j60WriteReg(interface, ENC28J60_REG_EHT4, hashTable[4]);
+   enc28j60WriteReg(interface, ENC28J60_REG_EHT5, hashTable[5]);
+   enc28j60WriteReg(interface, ENC28J60_REG_EHT6, hashTable[6]);
+   enc28j60WriteReg(interface, ENC28J60_REG_EHT7, hashTable[7]);
+
+   //Debug message
+   TRACE_DEBUG("  EHT0 = %02" PRIX8 "\r\n", enc28j60ReadReg(interface, ENC28J60_REG_EHT0));
+   TRACE_DEBUG("  EHT1 = %02" PRIX8 "\r\n", enc28j60ReadReg(interface, ENC28J60_REG_EHT1));
+   TRACE_DEBUG("  EHT2 = %02" PRIX8 "\r\n", enc28j60ReadReg(interface, ENC28J60_REG_EHT2));
+   TRACE_DEBUG("  EHT3 = %02" PRIX8 "\r\n", enc28j60ReadReg(interface, ENC28J60_REG_EHT3));
+   TRACE_DEBUG("  EHT0 = %02" PRIX8 "\r\n", enc28j60ReadReg(interface, ENC28J60_REG_EHT4));
+   TRACE_DEBUG("  EHT1 = %02" PRIX8 "\r\n", enc28j60ReadReg(interface, ENC28J60_REG_EHT5));
+   TRACE_DEBUG("  EHT2 = %02" PRIX8 "\r\n", enc28j60ReadReg(interface, ENC28J60_REG_EHT6));
+   TRACE_DEBUG("  EHT3 = %02" PRIX8 "\r\n", enc28j60ReadReg(interface, ENC28J60_REG_EHT7));
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief ENC28J60 controller reset
+ * @param[in] interface Underlying network interface
+ **/
+
+void enc28j60SoftReset(NetInterface *interface)
+{
+   //Pull the CS pin low
+   interface->spiDriver->assertCs();
+
+   //Write opcode
+   interface->spiDriver->transfer(ENC28J60_CMD_SRC);
+
+   //Terminate the operation by raising the CS pin
+   interface->spiDriver->deassertCs();
+}
+
+
+/**
+ * @brief Bank selection
+ * @param[in] interface Underlying network interface
+ * @param[in] address Register address
+ **/
+
+void enc28j60SelectBank(NetInterface *interface, uint16_t address)
+{
+   uint16_t bank;
+   Enc28j60Context *context;
+
+   //Point to the driver context
+   context = (Enc28j60Context *) interface->nicContext;
+
+   //Get the bank number from the specified address
+   bank = address & REG_BANK_MASK;
+
+   //Rewrite the bank number only if a change is detected
+   if(bank != context->currentBank)
+   {
+      //Select specified bank
+      switch(bank)
+      {
+      case BANK_0:
+         //Select bank 0
+         enc28j60ClearBit(interface, ENC28J60_REG_ECON1, ECON1_BSEL1 | ECON1_BSEL0);
+         break;
+      case BANK_1:
+         //Select bank 1
+         enc28j60SetBit(interface, ENC28J60_REG_ECON1, ECON1_BSEL0);
+         enc28j60ClearBit(interface, ENC28J60_REG_ECON1, ECON1_BSEL1);
+         break;
+      case BANK_2:
+         //Select bank 2
+         enc28j60ClearBit(interface, ENC28J60_REG_ECON1, ECON1_BSEL0);
+         enc28j60SetBit(interface, ENC28J60_REG_ECON1, ECON1_BSEL1);
+         break;
+      case BANK_3:
+         //Select bank 3
+         enc28j60SetBit(interface, ENC28J60_REG_ECON1, ECON1_BSEL1 | ECON1_BSEL0);
+         break;
+      default:
+         //Invalid bank
+         break;
+      }
+
+      //Save bank number
+      context->currentBank = bank;
+   }
+}
+
+
+/**
+ * @brief Write ENC28J60 register
+ * @param[in] interface Underlying network interface
+ * @param[in] address Register address
+ * @param[in] data Register value
+ **/
+
+void enc28j60WriteReg(NetInterface *interface, uint16_t address, uint8_t data)
+{
+   //Make sure the corresponding bank is selected
+   enc28j60SelectBank(interface, address);
+
+   //Pull the CS pin low
+   interface->spiDriver->assertCs();
+
+   //Write opcode and register address
+   interface->spiDriver->transfer(ENC28J60_CMD_WCR | (address & REG_ADDR_MASK));
+   //Write register value
+   interface->spiDriver->transfer(data);
+
+   //Terminate the operation by raising the CS pin
+   interface->spiDriver->deassertCs();
+}
+
+
+/**
+ * @brief Read ENC28J60 register
+ * @param[in] interface Underlying network interface
+ * @param[in] address Register address
+ * @return Register value
+ **/
+
+uint8_t enc28j60ReadReg(NetInterface *interface, uint16_t address)
+{
+   uint16_t data;
+
+   //Make sure the corresponding bank is selected
+   enc28j60SelectBank(interface, address);
+
+   //Pull the CS pin low
+   interface->spiDriver->assertCs();
+
+   //Write opcode and register address
+   interface->spiDriver->transfer(ENC28J60_CMD_RCR | (address & REG_ADDR_MASK));
+
+   //When reading MAC or MII registers, a dummy byte is first shifted out
+   if((address & REG_TYPE_MASK) != ETH_REG_TYPE)
+      interface->spiDriver->transfer(0x00);
+
+   //Read register contents
+   data = interface->spiDriver->transfer(0x00);
+
+   //Terminate the operation by raising the CS pin
+   interface->spiDriver->deassertCs();
+
+   //Return register contents
+   return data;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void enc28j60WritePhyReg(NetInterface *interface, uint16_t address, uint16_t data)
+{
+   //Write register address
+   enc28j60WriteReg(interface, ENC28J60_REG_MIREGADR, address & REG_ADDR_MASK);
+
+   //Write the lower 8 bits
+   enc28j60WriteReg(interface, ENC28J60_REG_MIWRL, LSB(data));
+   //Write the upper 8 bits
+   enc28j60WriteReg(interface, ENC28J60_REG_MIWRH, MSB(data));
+
+   //Wait until the PHY register has been written
+   while(enc28j60ReadReg(interface, ENC28J60_REG_MISTAT) & MISTAT_BUSY);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t enc28j60ReadPhyReg(NetInterface *interface, uint16_t address)
+{
+   uint16_t data;
+
+   //Write register address
+   enc28j60WriteReg(interface, ENC28J60_REG_MIREGADR, address & REG_ADDR_MASK);
+
+   //Start read operation
+   enc28j60WriteReg(interface, ENC28J60_REG_MICMD, MICMD_MIIRD);
+   //Wait for the read operation to complete
+   while(enc28j60ReadReg(interface, ENC28J60_REG_MISTAT) & MISTAT_BUSY);
+   //Clear command register
+   enc28j60WriteReg(interface, ENC28J60_REG_MICMD, 0);
+
+   //Read the lower 8 bits
+   data = enc28j60ReadReg(interface, ENC28J60_REG_MIRDL);
+   //Read the upper 8 bits
+   data |= enc28j60ReadReg(interface, ENC28J60_REG_MIRDH) << 8;
+
+   //Return register contents
+   return data;
+}
+
+
+/**
+ * @brief Write SRAM buffer
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to be written
+ * @param[in] offset Offset to the first data byte
+ **/
+
+void enc28j60WriteBuffer(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   uint_t i;
+   size_t j;
+   size_t n;
+   uint8_t *p;
+
+   //Pull the CS pin low
+   interface->spiDriver->assertCs();
+
+   //Write opcode
+   interface->spiDriver->transfer(ENC28J60_CMD_WBM);
+   //Write per-packet control byte
+   interface->spiDriver->transfer(0x00);
+
+   //Loop through data chunks
+   for(i = 0; i < buffer->chunkCount; i++)
+   {
+      //Is there any data to copy from the current chunk?
+      if(offset < buffer->chunk[i].length)
+      {
+         //Point to the first byte to be read
+         p = (uint8_t *) buffer->chunk[i].address + offset;
+         //Compute the number of bytes to copy at a time
+         n = buffer->chunk[i].length - offset;
+
+         //Copy data to SRAM buffer
+         for(j = 0; j < n; j++)
+            interface->spiDriver->transfer(p[j]);
+
+         //Process the next block from the start
+         offset = 0;
+      }
+      else
+      {
+         //Skip the current chunk
+         offset -= buffer->chunk[i].length;
+      }
+   }
+
+   //Terminate the operation by raising the CS pin
+   interface->spiDriver->deassertCs();
+}
+
+
+/**
+ * @brief Read SRAM buffer
+ * @param[in] interface Underlying network interface
+ * @param[in] data Buffer where to store the incoming data
+ * @param[in] length Number of data to read
+ **/
+
+void enc28j60ReadBuffer(NetInterface *interface,
+   uint8_t *data, size_t length)
+{
+   size_t i;
+
+   //Pull the CS pin low
+   interface->spiDriver->assertCs();
+
+   //Write opcode
+   interface->spiDriver->transfer(ENC28J60_CMD_RBM);
+
+   //Copy data from SRAM buffer
+   for(i = 0; i < length; i++)
+      data[i] = interface->spiDriver->transfer(0x00);
+
+   //Terminate the operation by raising the CS pin
+   interface->spiDriver->deassertCs();
+}
+
+
+/**
+ * @brief Set bit field
+ * @param[in] interface Underlying network interface
+ * @param[in] address Register address
+ * @param[in] mask Bits to set in the target register
+ **/
+
+void enc28j60SetBit(NetInterface *interface, uint16_t address, uint16_t mask)
+{
+   //Pull the CS pin low
+   interface->spiDriver->assertCs();
+
+   //Write opcode and register address
+   interface->spiDriver->transfer(ENC28J60_CMD_BFS | (address & REG_ADDR_MASK));
+   //Write bit mask
+   interface->spiDriver->transfer(mask);
+
+   //Terminate the operation by raising the CS pin
+   interface->spiDriver->deassertCs();
+}
+
+
+/**
+ * @brief Clear bit field
+ * @param[in] interface Underlying network interface
+ * @param[in] address Register address
+ * @param[in] mask Bits to clear in the target register
+ **/
+
+void enc28j60ClearBit(NetInterface *interface, uint16_t address, uint16_t mask)
+{
+   //Pull the CS pin low
+   interface->spiDriver->assertCs();
+
+   //Write opcode and register address
+   interface->spiDriver->transfer(ENC28J60_CMD_BFC | (address & REG_ADDR_MASK));
+   //Write bit mask
+   interface->spiDriver->transfer(mask);
+
+   //Terminate the operation by raising the CS pin
+   interface->spiDriver->deassertCs();
+}
+
+
+/**
+ * @brief CRC calculation using the polynomial 0x4C11DB7
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t enc28j60CalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return crc;
+}
+
+
+/**
+ * @brief Dump registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void enc28j60DumpReg(NetInterface *interface)
+{
+#if (TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+   uint8_t i;
+   uint8_t bank;
+   uint16_t address;
+
+   //Display header
+   TRACE_DEBUG("    Bank 0  Bank 1  Bank 2  Bank 3\r\n");
+
+   //Loop through register addresses
+   for(i = 0; i < 32; i++)
+   {
+      //Display register address
+      TRACE_DEBUG("%02" PRIX8 ": ", i);
+
+      //Loop through bank numbers
+      for(bank = 0; bank < 4; bank++)
+      {
+         //Format register address
+         address = (bank << 8) | i;
+
+         //MAC and MII registers require a specific read sequence
+         if(address >= 0x200 && address <= 0x219)
+            address |= MAC_REG_TYPE;
+         else if(address >= 0x300 && address <= 0x305)
+            address |= MAC_REG_TYPE;
+         else if(address == 0x30A)
+            address |= MAC_REG_TYPE;
+
+         //Display register contents
+         TRACE_DEBUG("0x%02" PRIX8 "    ", enc28j60ReadReg(interface, address));
+      }
+
+      //Jump to the following line
+      TRACE_DEBUG("\r\n");
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+#endif
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void enc28j60DumpPhyReg(NetInterface *interface)
+{
+#if (TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIX8 ": 0x%04" PRIX16 "\r\n", i, enc28j60ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+#endif
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/enc28j60.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,435 @@
+/**
+ * @file enc28j60.h
+ * @brief ENC28J60 Ethernet controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _ENC28J60_H
+#define _ENC28J60_H
+
+//Full-duplex support
+#ifndef ENC28J60_FULL_DUPLEX_SUPPORT
+   #define ENC28J60_FULL_DUPLEX_SUPPORT ENABLED
+#elif (ENC28J60_FULL_DUPLEX_SUPPORT != ENABLED && ENC28J60_FULL_DUPLEX_SUPPORT != DISABLED)
+   #error ENC28J60_FULL_DUPLEX_SUPPORT parameter is not valid
+#endif
+
+//Silicon revision identifiers
+#define ENC28J60_REV_B1 0x02
+#define ENC28J60_REV_B4 0x04
+#define ENC28J60_REV_B5 0x05
+#define ENC28J60_REV_B7 0x06
+
+//Receive and transmit buffers
+#define ENC28J60_RX_BUFFER_START 0x0000
+#define ENC28J60_RX_BUFFER_STOP  0x17FF
+#define ENC28J60_TX_BUFFER_START 0x1800
+#define ENC28J60_TX_BUFFER_STOP  0x1FFF
+
+//SPI command set
+#define ENC28J60_CMD_RCR 0x00
+#define ENC28J60_CMD_RBM 0x3A
+#define ENC28J60_CMD_WCR 0x40
+#define ENC28J60_CMD_WBM 0x7A
+#define ENC28J60_CMD_BFS 0x80
+#define ENC28J60_CMD_BFC 0xA0
+#define ENC28J60_CMD_SRC 0xFF
+
+//ENC28J60 register types
+#define ETH_REG_TYPE 0x0000
+#define MAC_REG_TYPE 0x1000
+#define MII_REG_TYPE 0x2000
+#define PHY_REG_TYPE 0x3000
+
+//ENC28J60 banks
+#define BANK_0 0x0000
+#define BANK_1 0x0100
+#define BANK_2 0x0200
+#define BANK_3 0x0300
+
+//Related masks
+#define REG_TYPE_MASK 0xF000
+#define REG_BANK_MASK 0x0F00
+#define REG_ADDR_MASK 0x001F
+
+//Bank 0 registers
+#define ENC28J60_REG_ERDPTL      (ETH_REG_TYPE | BANK_0 | 0x00)
+#define ENC28J60_REG_ERDPTH      (ETH_REG_TYPE | BANK_0 | 0x01)
+#define ENC28J60_REG_EWRPTL      (ETH_REG_TYPE | BANK_0 | 0x02)
+#define ENC28J60_REG_EWRPTH      (ETH_REG_TYPE | BANK_0 | 0x03)
+#define ENC28J60_REG_ETXSTL      (ETH_REG_TYPE | BANK_0 | 0x04)
+#define ENC28J60_REG_ETXSTH      (ETH_REG_TYPE | BANK_0 | 0x05)
+#define ENC28J60_REG_ETXNDL      (ETH_REG_TYPE | BANK_0 | 0x06)
+#define ENC28J60_REG_ETXNDH      (ETH_REG_TYPE | BANK_0 | 0x07)
+#define ENC28J60_REG_ERXSTL      (ETH_REG_TYPE | BANK_0 | 0x08)
+#define ENC28J60_REG_ERXSTH      (ETH_REG_TYPE | BANK_0 | 0x09)
+#define ENC28J60_REG_ERXNDL      (ETH_REG_TYPE | BANK_0 | 0x0A)
+#define ENC28J60_REG_ERXNDH      (ETH_REG_TYPE | BANK_0 | 0x0B)
+#define ENC28J60_REG_ERXRDPTL    (ETH_REG_TYPE | BANK_0 | 0x0C)
+#define ENC28J60_REG_ERXRDPTH    (ETH_REG_TYPE | BANK_0 | 0x0D)
+#define ENC28J60_REG_ERXWRPTL    (ETH_REG_TYPE | BANK_0 | 0x0E)
+#define ENC28J60_REG_ERXWRPTH    (ETH_REG_TYPE | BANK_0 | 0x0F)
+#define ENC28J60_REG_EDMASTL     (ETH_REG_TYPE | BANK_0 | 0x10)
+#define ENC28J60_REG_EDMASTH     (ETH_REG_TYPE | BANK_0 | 0x11)
+#define ENC28J60_REG_EDMANDL     (ETH_REG_TYPE | BANK_0 | 0x12)
+#define ENC28J60_REG_EDMANDH     (ETH_REG_TYPE | BANK_0 | 0x13)
+#define ENC28J60_REG_EDMADSTL    (ETH_REG_TYPE | BANK_0 | 0x14)
+#define ENC28J60_REG_EDMADSTH    (ETH_REG_TYPE | BANK_0 | 0x15)
+#define ENC28J60_REG_EDMACSL     (ETH_REG_TYPE | BANK_0 | 0x16)
+#define ENC28J60_REG_EDMACSH     (ETH_REG_TYPE | BANK_0 | 0x17)
+#define ENC28J60_REG_EIE         (ETH_REG_TYPE | BANK_0 | 0x1B)
+#define ENC28J60_REG_EIR         (ETH_REG_TYPE | BANK_0 | 0x1C)
+#define ENC28J60_REG_ESTAT       (ETH_REG_TYPE | BANK_0 | 0x1D)
+#define ENC28J60_REG_ECON2       (ETH_REG_TYPE | BANK_0 | 0x1E)
+#define ENC28J60_REG_ECON1       (ETH_REG_TYPE | BANK_0 | 0x1F)
+
+//Bank 1 registers
+#define ENC28J60_REG_EHT0        (ETH_REG_TYPE | BANK_1 | 0x00)
+#define ENC28J60_REG_EHT1        (ETH_REG_TYPE | BANK_1 | 0x01)
+#define ENC28J60_REG_EHT2        (ETH_REG_TYPE | BANK_1 | 0x02)
+#define ENC28J60_REG_EHT3        (ETH_REG_TYPE | BANK_1 | 0x03)
+#define ENC28J60_REG_EHT4        (ETH_REG_TYPE | BANK_1 | 0x04)
+#define ENC28J60_REG_EHT5        (ETH_REG_TYPE | BANK_1 | 0x05)
+#define ENC28J60_REG_EHT6        (ETH_REG_TYPE | BANK_1 | 0x06)
+#define ENC28J60_REG_EHT7        (ETH_REG_TYPE | BANK_1 | 0x07)
+#define ENC28J60_REG_EPMM0       (ETH_REG_TYPE | BANK_1 | 0x08)
+#define ENC28J60_REG_EPMM1       (ETH_REG_TYPE | BANK_1 | 0x09)
+#define ENC28J60_REG_EPMM2       (ETH_REG_TYPE | BANK_1 | 0x0A)
+#define ENC28J60_REG_EPMM3       (ETH_REG_TYPE | BANK_1 | 0x0B)
+#define ENC28J60_REG_EPMM4       (ETH_REG_TYPE | BANK_1 | 0x0C)
+#define ENC28J60_REG_EPMM5       (ETH_REG_TYPE | BANK_1 | 0x0D)
+#define ENC28J60_REG_EPMM6       (ETH_REG_TYPE | BANK_1 | 0x0E)
+#define ENC28J60_REG_EPMM7       (ETH_REG_TYPE | BANK_1 | 0x0F)
+#define ENC28J60_REG_EPMCSL      (ETH_REG_TYPE | BANK_1 | 0x10)
+#define ENC28J60_REG_EPMCSH      (ETH_REG_TYPE | BANK_1 | 0x11)
+#define ENC28J60_REG_EPMOL       (ETH_REG_TYPE | BANK_1 | 0x14)
+#define ENC28J60_REG_EPMOH       (ETH_REG_TYPE | BANK_1 | 0x15)
+#define ENC28J60_REG_EWOLIE      (ETH_REG_TYPE | BANK_1 | 0x16)
+#define ENC28J60_REG_EWOLIR      (ETH_REG_TYPE | BANK_1 | 0x17)
+#define ENC28J60_REG_ERXFCON     (ETH_REG_TYPE | BANK_1 | 0x18)
+#define ENC28J60_REG_EPKTCNT     (ETH_REG_TYPE | BANK_1 | 0x19)
+
+//Bank 2 registers
+#define ENC28J60_REG_MACON1      (MAC_REG_TYPE | BANK_2 | 0x00)
+#define ENC28J60_REG_MACON2      (MAC_REG_TYPE | BANK_2 | 0x01)
+#define ENC28J60_REG_MACON3      (MAC_REG_TYPE | BANK_2 | 0x02)
+#define ENC28J60_REG_MACON4      (MAC_REG_TYPE | BANK_2 | 0x03)
+#define ENC28J60_REG_MABBIPG     (MAC_REG_TYPE | BANK_2 | 0x04)
+#define ENC28J60_REG_MAIPGL      (MAC_REG_TYPE | BANK_2 | 0x06)
+#define ENC28J60_REG_MAIPGH      (MAC_REG_TYPE | BANK_2 | 0x07)
+#define ENC28J60_REG_MACLCON1    (MAC_REG_TYPE | BANK_2 | 0x08)
+#define ENC28J60_REG_MACLCON2    (MAC_REG_TYPE | BANK_2 | 0x09)
+#define ENC28J60_REG_MAMXFLL     (MAC_REG_TYPE | BANK_2 | 0x0A)
+#define ENC28J60_REG_MAMXFLH     (MAC_REG_TYPE | BANK_2 | 0x0B)
+#define ENC28J60_REG_MAPHSUP     (MAC_REG_TYPE | BANK_2 | 0x0D)
+#define ENC28J60_REG_MICON       (MII_REG_TYPE | BANK_2 | 0x11)
+#define ENC28J60_REG_MICMD       (MII_REG_TYPE | BANK_2 | 0x12)
+#define ENC28J60_REG_MIREGADR    (MII_REG_TYPE | BANK_2 | 0x14)
+#define ENC28J60_REG_MIWRL       (MII_REG_TYPE | BANK_2 | 0x16)
+#define ENC28J60_REG_MIWRH       (MII_REG_TYPE | BANK_2 | 0x17)
+#define ENC28J60_REG_MIRDL       (MII_REG_TYPE | BANK_2 | 0x18)
+#define ENC28J60_REG_MIRDH       (MII_REG_TYPE | BANK_2 | 0x19)
+
+//Bank 3 registers
+#define ENC28J60_REG_MAADR5      (MAC_REG_TYPE | BANK_3 | 0x00)
+#define ENC28J60_REG_MAADR6      (MAC_REG_TYPE | BANK_3 | 0x01)
+#define ENC28J60_REG_MAADR3      (MAC_REG_TYPE | BANK_3 | 0x02)
+#define ENC28J60_REG_MAADR4      (MAC_REG_TYPE | BANK_3 | 0x03)
+#define ENC28J60_REG_MAADR1      (MAC_REG_TYPE | BANK_3 | 0x04)
+#define ENC28J60_REG_MAADR2      (MAC_REG_TYPE | BANK_3 | 0x05)
+#define ENC28J60_REG_EBSTSD      (ETH_REG_TYPE | BANK_3 | 0x06)
+#define ENC28J60_REG_EBSTCON     (ETH_REG_TYPE | BANK_3 | 0x07)
+#define ENC28J60_REG_EBSTCSL     (ETH_REG_TYPE | BANK_3 | 0x08)
+#define ENC28J60_REG_EBSTCSH     (ETH_REG_TYPE | BANK_3 | 0x09)
+#define ENC28J60_REG_MISTAT      (MII_REG_TYPE | BANK_3 | 0x0A)
+#define ENC28J60_REG_EREVID      (ETH_REG_TYPE | BANK_3 | 0x12)
+#define ENC28J60_REG_ECOCON      (ETH_REG_TYPE | BANK_3 | 0x15)
+#define ENC28J60_REG_EFLOCON     (ETH_REG_TYPE | BANK_3 | 0x17)
+#define ENC28J60_REG_EPAUSL      (ETH_REG_TYPE | BANK_3 | 0x18)
+#define ENC28J60_REG_EPAUSH      (ETH_REG_TYPE | BANK_3 | 0x19)
+
+//PHY registers
+#define ENC28J60_PHY_REG_PHCON1  (PHY_REG_TYPE | 0x00)
+#define ENC28J60_PHY_REG_PHSTAT1 (PHY_REG_TYPE | 0x01)
+#define ENC28J60_PHY_REG_PHID1   (PHY_REG_TYPE | 0x02)
+#define ENC28J60_PHY_REG_PHID2   (PHY_REG_TYPE | 0x03)
+#define ENC28J60_PHY_REG_PHCON2  (PHY_REG_TYPE | 0x10)
+#define ENC28J60_PHY_REG_PHSTAT2 (PHY_REG_TYPE | 0x11)
+#define ENC28J60_PHY_REG_PHIE    (PHY_REG_TYPE | 0x12)
+#define ENC28J60_PHY_REG_PHIR    (PHY_REG_TYPE | 0x13)
+#define ENC28J60_PHY_REG_PHLCON  (PHY_REG_TYPE | 0x14)
+
+//EIE register
+#define EIE_INTIE         (1 << 7)
+#define EIE_PKTIE         (1 << 6)
+#define EIE_DMAIE         (1 << 5)
+#define EIE_LINKIE        (1 << 4)
+#define EIE_TXIE          (1 << 3)
+#define EIE_WOLIE         (1 << 2)
+#define EIE_TXERIE        (1 << 1)
+#define EIE_RXERIE        (1 << 0)
+
+//EIR register
+#define EIR_PKTIF         (1 << 6)
+#define EIR_DMAIF         (1 << 5)
+#define EIR_LINKIF        (1 << 4)
+#define EIR_TXIF          (1 << 3)
+#define EIR_WOLIF         (1 << 2)
+#define EIR_TXERIF        (1 << 1)
+#define EIR_RXERIF        (1 << 0)
+
+//ESTAT register
+#define ESTAT_INT         (1 << 7)
+#define ESTAT_LATECOL     (1 << 4)
+#define ESTAT_RXBUSY      (1 << 2)
+#define ESTAT_TXABRT      (1 << 1)
+#define ESTAT_CLKRDY      (1 << 0)
+
+//ECON2 register
+#define ECON2_AUTOINC     (1 << 7)
+#define ECON2_PKTDEC      (1 << 6)
+#define ECON2_PWRSV       (1 << 5)
+#define ECON2_VRPS        (1 << 3)
+
+//ECON1 register
+#define ECON1_TXRST       (1 << 7)
+#define ECON1_RXRST       (1 << 6)
+#define ECON1_DMAST       (1 << 5)
+#define ECON1_CSUMEN      (1 << 4)
+#define ECON1_TXRTS       (1 << 3)
+#define ECON1_RXEN        (1 << 2)
+#define ECON1_BSEL1       (1 << 1)
+#define ECON1_BSEL0       (1 << 0)
+
+//ERXFCON register
+#define ERXFCON_UCEN      (1 << 7)
+#define ERXFCON_ANDOR     (1 << 6)
+#define ERXFCON_CRCEN     (1 << 5)
+#define ERXFCON_PMEN      (1 << 4)
+#define ERXFCON_MPEN      (1 << 3)
+#define ERXFCON_HTEN      (1 << 2)
+#define ERXFCON_MCEN      (1 << 1)
+#define ERXFCON_BCEN      (1 << 0)
+
+//MACON1 register
+#define MACON1_LOOPBK     (1 << 4)
+#define MACON1_TXPAUS     (1 << 3)
+#define MACON1_RXPAUS     (1 << 2)
+#define MACON1_PASSALL    (1 << 1)
+#define MACON1_MARXEN     (1 << 0)
+
+//MACON2 register
+#define MACON2_MARST      (1 << 7)
+#define MACON2_RNDRST     (1 << 6)
+#define MACON2_MARXRST    (1 << 3)
+#define MACON2_RFUNRST    (1 << 2)
+#define MACON2_MATXRST    (1 << 1)
+#define MACON2_TFUNRST    (1 << 0)
+
+//MACON3 register
+#define MACON3_PADCFG2    (1 << 7)
+#define MACON3_PADCFG1    (1 << 6)
+#define MACON3_PADCFG0    (1 << 5)
+#define MACON3_TXCRCEN    (1 << 4)
+#define MACON3_PHDRLEN    (1 << 3)
+#define MACON3_HFRMLEN    (1 << 2)
+#define MACON3_FRMLNEN    (1 << 1)
+#define MACON3_FULDPX     (1 << 0)
+
+#define MACON3_PADCFG(x) ((x) << 5)
+
+//MACON4 register
+#define MACON4_DEFER      (1 << 6)
+#define MACON4_BPEN       (1 << 5)
+#define MACON4_NOBKOFF    (1 << 4)
+#define MACON4_LONGPRE    (1 << 1)
+#define MACON4_PUREPRE    (1 << 0)
+
+//MAPHSUP register
+#define MAPHSUP_RSTINTFC  (1 << 7)
+#define MAPHSUP_RSTRMII   (1 << 3)
+
+//MICON register
+#define MICON_RSTMII      (1 << 7)
+
+//MICMD register
+#define MICMD_MIISCAN     (1 << 1)
+#define MICMD_MIIRD       (1 << 0)
+
+//EBSTCON register
+#define EBSTCON_PSV2      (1 << 7)
+#define EBSTCON_PSV1      (1 << 6)
+#define EBSTCON_PSV0      (1 << 5)
+#define EBSTCON_PSEL      (1 << 4)
+#define EBSTCON_TMSEL1    (1 << 3)
+#define EBSTCON_TMSEL0    (1 << 2)
+#define EBSTCON_TME       (1 << 1)
+#define EBSTCON_BISTST    (1 << 0)
+
+//MISTAT register
+#define MISTAT_NVALID     (1 << 2)
+#define MISTAT_SCAN       (1 << 1)
+#define MISTAT_BUSY       (1 << 0)
+
+//ECOCON register
+#define ECOCON_COCON2     (1 << 2)
+#define ECOCON_COCON1     (1 << 1)
+#define ECOCON_COCON0     (1 << 0)
+
+//EFLOCON register
+#define EFLOCON_FULDPXS   (1 << 2)
+#define EFLOCON_FCEN1     (1 << 1)
+#define EFLOCON_FCEN0     (1 << 0)
+
+//PHCON1 register
+#define PHCON1_PRST       (1 << 15)
+#define PHCON1_PLOOPBK    (1 << 14)
+#define PHCON1_PPWRSV     (1 << 11)
+#define PHCON1_PDPXMD     (1 << 8)
+
+//PHSTAT1 register
+#define PHSTAT1_PFDPX     (1 << 12)
+#define PHSTAT1_PHDPX     (1 << 11)
+#define PHSTAT1_LLSTAT    (1 << 2)
+#define PHSTAT1_JBSTAT    (1 << 1)
+
+//PHCON2 register
+#define PHCON2_FRCLINK    (1 << 14)
+#define PHCON2_TXDIS      (1 << 13)
+#define PHCON2_JABBER     (1 << 10)
+#define PHCON2_HDLDIS     (1 << 8)
+
+//PHSTAT2 register
+#define PHSTAT2_TXSTAT    (1 << 13)
+#define PHSTAT2_RXSTAT    (1 << 12)
+#define PHSTAT2_COLSTAT   (1 << 11)
+#define PHSTAT2_LSTAT     (1 << 10)
+#define PHSTAT2_DPXSTAT   (1 << 9)
+#define PHSTAT2_PLRITY    (1 << 4)
+
+//PHIE register
+#define PHIE_PLNKIE       (1 << 4)
+#define PHIE_PGEIE        (1 << 1)
+
+//PHIR register
+#define PHIR_PLNKIF       (1 << 4)
+#define PHIR_PGIF         (1 << 2)
+
+//PHLCON register
+#define PHLCON_LACFG3     (1 << 11)
+#define PHLCON_LACFG2     (1 << 10)
+#define PHLCON_LACFG1     (1 << 9)
+#define PHLCON_LACFG0     (1 << 8)
+#define PHLCON_LBCFG3     (1 << 7)
+#define PHLCON_LBCFG2     (1 << 6)
+#define PHLCON_LBCFG1     (1 << 5)
+#define PHLCON_LBCFG0     (1 << 4)
+#define PHLCON_LFRQ1      (1 << 3)
+#define PHLCON_LFRQ0      (1 << 2)
+#define PHLCON_STRCH      (1 << 1)
+
+#define PHLCON_LACFG(x)  ((x) << 8)
+#define PHLCON_LBCFG(x)  ((x) << 4)
+#define PHLCON_LFRQ(x)   ((x) << 2)
+
+//Per-packet control byte
+#define TX_CTRL_PHUGEEN   (1 << 3)
+#define TX_CTRL_PPADEN    (1 << 2)
+#define TX_CTRL_PCRCEN    (1 << 1)
+#define TX_CTRL_POVERRIDE (1 << 0)
+
+//Receive status vector
+#define RSV_VLAN_TYPE           0x4000
+#define RSV_UNKNOWN_OPCODE      0x2000
+#define RSV_PAUSE_CONTROL_FRAME 0x1000
+#define RSV_CONTROL_FRAME       0x0800
+#define RSV_DRIBBLE_NIBBLE      0x0400
+#define RSV_BROADCAST_PACKET    0x0200
+#define RSV_MULTICAST_PACKET    0x0100
+#define RSV_RECEIVED_OK         0x0080
+#define RSV_LENGTH_OUT_OF_RANGE 0x0040
+#define RSV_LENGTH_CHECK_ERROR  0x0020
+#define RSV_CRC_ERROR           0x0010
+#define RSV_CARRIER_EVENT       0x0004
+#define RSV_DROP_EVENT          0x0001
+
+
+/**
+ * @brief ENC28J60 driver context
+ **/
+
+typedef struct
+{
+   uint16_t currentBank; ///<Current bank
+   uint16_t nextPacket;  ///<Next packet in the receive buffer
+   uint8_t *rxBuffer;    ///<Receive buffer
+} Enc28j60Context;
+
+
+//ENC28J60 driver
+extern const NicDriver enc28j60Driver;
+
+//ENC28J60 related functions
+error_t enc28j60Init(NetInterface *interface);
+
+void enc28j60Tick(NetInterface *interface);
+
+void enc28j60EnableIrq(NetInterface *interface);
+void enc28j60DisableIrq(NetInterface *interface);
+bool_t enc28j60IrqHandler(NetInterface *interface);
+void enc28j60EventHandler(NetInterface *interface);
+
+error_t enc28j60SendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t enc28j60ReceivePacket(NetInterface *interface);
+
+error_t enc28j60SetMulticastFilter(NetInterface *interface);
+
+void enc28j60SoftReset(NetInterface *interface);
+void enc28j60SelectBank(NetInterface *interface, uint16_t address);
+
+void enc28j60WriteReg(NetInterface *interface, uint16_t address, uint8_t data);
+uint8_t enc28j60ReadReg(NetInterface *interface, uint16_t address);
+
+void enc28j60WritePhyReg(NetInterface *interface, uint16_t address, uint16_t data);
+uint16_t enc28j60ReadPhyReg(NetInterface *interface, uint16_t address);
+
+void enc28j60WriteBuffer(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+void enc28j60ReadBuffer(NetInterface *interface,
+   uint8_t *data, size_t length);
+
+void enc28j60SetBit(NetInterface *interface, uint16_t address, uint16_t mask);
+void enc28j60ClearBit(NetInterface *interface, uint16_t address, uint16_t mask);
+
+uint32_t enc28j60CalcCrc(const void *data, size_t length);
+
+void enc28j60DumpReg(NetInterface *interface);
+void enc28j60DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/enc624j600.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,938 @@
+/**
+ * @file enc624j600.c
+ * @brief ENC624J600/ENC424J600 Ethernet controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/enc624j600.h"
+#include "debug.h"
+
+
+/**
+ * @brief ENC624J600 driver
+ **/
+
+const NicDriver enc624j600Driver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   enc624j600Init,
+   enc624j600Tick,
+   enc624j600EnableIrq,
+   enc624j600DisableIrq,
+   enc624j600EventHandler,
+   enc624j600SendPacket,
+   enc624j600SetMulticastFilter,
+   NULL,
+   NULL,
+   NULL,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief ENC624J600 controller initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t enc624j600Init(NetInterface *interface)
+{
+   Enc624j600Context *context;
+
+   //Debug message
+   TRACE_INFO("Initializing ENC624J600 Ethernet controller...\r\n");
+
+   //Initialize SPI
+   interface->spiDriver->init();
+   //Initialize external interrupt line
+   interface->extIntDriver->init();
+
+   //Point to the driver context
+   context = (Enc624j600Context *) interface->nicContext;
+
+   //Initialize driver specific variables
+   context->nextPacket = ENC624J600_RX_BUFFER_START;
+
+   //Allocate RX buffer
+   context->rxBuffer = memPoolAlloc(ETH_MAX_FRAME_SIZE);
+   //Failed to allocate memory?
+   if(context->rxBuffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Issue a system reset
+   enc624j600SoftReset(interface);
+
+   //Disable CLKOUT output
+   enc624j600WriteReg(interface, ENC624J600_REG_ECON2, ECON2_ETHEN | ECON2_STRCH);
+
+   //Optionally set the station MAC address
+   if(macCompAddr(&interface->macAddr, &MAC_UNSPECIFIED_ADDR))
+   {
+      //Use the factory preprogrammed station address
+      interface->macAddr.w[0] = enc624j600ReadReg(interface, ENC624J600_REG_MAADR1);
+      interface->macAddr.w[1] = enc624j600ReadReg(interface, ENC624J600_REG_MAADR2);
+      interface->macAddr.w[2] = enc624j600ReadReg(interface, ENC624J600_REG_MAADR3);
+
+      //Generate the 64-bit interface identifier
+      macAddrToEui64(&interface->macAddr, &interface->eui64);
+   }
+   else
+   {
+      //Override the factory preprogrammed address
+      enc624j600WriteReg(interface, ENC624J600_REG_MAADR1, interface->macAddr.w[0]);
+      enc624j600WriteReg(interface, ENC624J600_REG_MAADR2, interface->macAddr.w[1]);
+      enc624j600WriteReg(interface, ENC624J600_REG_MAADR3, interface->macAddr.w[2]);
+   }
+
+   //Set receive buffer location
+   enc624j600WriteReg(interface, ENC624J600_REG_ERXST, ENC624J600_RX_BUFFER_START);
+   //Program the tail pointer ERXTAIL to the last even address of the buffer
+   enc624j600WriteReg(interface, ENC624J600_REG_ERXTAIL, ENC624J600_RX_BUFFER_STOP);
+
+   //Configure the receive filters
+   enc624j600WriteReg(interface, ENC624J600_REG_ERXFCON, ERXFCON_HTEN |
+      ERXFCON_CRCEN | ERXFCON_RUNTEN | ERXFCON_UCEN | ERXFCON_BCEN);
+
+   //Initialize the hash table
+   enc624j600WriteReg(interface, ENC624J600_REG_EHT1, 0x0000);
+   enc624j600WriteReg(interface, ENC624J600_REG_EHT2, 0x0000);
+   enc624j600WriteReg(interface, ENC624J600_REG_EHT3, 0x0000);
+   enc624j600WriteReg(interface, ENC624J600_REG_EHT4, 0x0000);
+
+   //All short frames will be zero-padded to 60 bytes and a valid CRC is then appended
+   enc624j600WriteReg(interface, ENC624J600_REG_MACON2,
+      MACON2_DEFER | MACON2_PADCFG0 | MACON2_TXCRCEN | MACON2_R1);
+
+   //Program the MAMXFL register with the maximum frame length to be accepted
+   enc624j600WriteReg(interface, ENC624J600_REG_MAMXFL, 1518);
+
+   //PHY initialization
+   enc624j600WritePhyReg(interface, ENC624J600_PHY_REG_PHANA, PHANA_ADPAUS0 |
+      PHANA_AD100FD | PHANA_AD100 | PHANA_AD10FD | PHANA_AD10 | PHANA_ADIEEE0);
+
+   //Clear interrupt flags
+   enc624j600WriteReg(interface, ENC624J600_REG_EIR, 0x0000);
+
+   //Configure interrupts as desired
+   enc624j600WriteReg(interface, ENC624J600_REG_EIE, EIE_INTIE |
+      EIE_LINKIE | EIE_PKTIE | EIE_TXIE | EIE_TXABTIE);
+
+   //Set RXEN to enable reception
+   enc624j600SetBit(interface, ENC624J600_REG_ECON1, ECON1_RXEN);
+
+   //Dump registers for debugging purpose
+   enc624j600DumpReg(interface);
+   enc624j600DumpPhyReg(interface);
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->nicEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief ENC624J600 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void enc624j600Tick(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void enc624j600EnableIrq(NetInterface *interface)
+{
+   //Enable interrupts
+   interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void enc624j600DisableIrq(NetInterface *interface)
+{
+   //Disable interrupts
+   interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief ENC624J600 interrupt service routine
+ * @param[in] interface Underlying network interface
+ * @return TRUE if a higher priority task must be woken. Else FALSE is returned
+ **/
+
+bool_t enc624j600IrqHandler(NetInterface *interface)
+{
+   bool_t flag;
+   uint16_t status;
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Clear the INTIE bit, immediately after an interrupt event
+   enc624j600ClearBit(interface, ENC624J600_REG_EIE, EIE_INTIE);
+
+   //Read interrupt status register
+   status = enc624j600ReadReg(interface, ENC624J600_REG_EIR);
+
+   //Link status change?
+   if(status & EIR_LINKIF)
+   {
+      //Disable LINKIE interrupt
+      enc624j600ClearBit(interface, ENC624J600_REG_EIE, EIE_LINKIE);
+
+      //Set event flag
+      interface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Packet received?
+   if(status & EIR_PKTIF)
+   {
+      //Disable PKTIE interrupt
+      enc624j600ClearBit(interface, ENC624J600_REG_EIE, EIE_PKTIE);
+
+      //Set event flag
+      interface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Packet transmission complete?
+   if(status & (EIR_TXIF | EIR_TXABTIF))
+   {
+      //Clear interrupt flags
+      enc624j600ClearBit(interface, ENC624J600_REG_EIR, EIR_TXIF | EIR_TXABTIF);
+
+      //Notify the TCP/IP stack that the transmitter is ready to send
+      flag |= osSetEventFromIsr(&interface->nicTxEvent);
+   }
+
+   //Once the interrupt has been serviced, the INTIE bit
+   //is set again to re-enable interrupts
+   enc624j600SetBit(interface, ENC624J600_REG_EIE, EIE_INTIE);
+
+   //A higher priority task must be woken?
+   return flag;
+}
+
+
+/**
+ * @brief ENC624J600 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void enc624j600EventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint16_t status;
+   uint16_t value;
+
+   //Read interrupt status register
+   status = enc624j600ReadReg(interface, ENC624J600_REG_EIR);
+
+   //Check whether the link state has changed
+   if(status & EIR_LINKIF)
+   {
+      //Clear interrupt flag
+      enc624j600ClearBit(interface, ENC624J600_REG_EIR, EIR_LINKIF);
+      //Read Ethernet status register
+      value = enc624j600ReadReg(interface, ENC624J600_REG_ESTAT);
+
+      //Check link state
+      if(value & ESTAT_PHYLNK)
+      {
+         //Read PHY status register 3
+         value = enc624j600ReadPhyReg(interface, ENC624J600_PHY_REG_PHSTAT3);
+
+         //Get current speed
+         if(value & PHSTAT3_SPDDPX1)
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+         else
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+
+         //Determine the new duplex mode
+         if(value & PHSTAT3_SPDDPX2)
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+         else
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+
+         //Link is up
+         interface->linkState = TRUE;
+
+         //Update MAC configuration parameters for proper operation
+         enc624j600UpdateMacConfig(interface);
+      }
+      else
+      {
+         //Link is down
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+
+   //Check whether a packet has been received?
+   if(status & EIR_PKTIF)
+   {
+      //Clear interrupt flag
+      enc624j600ClearBit(interface, ENC624J600_REG_EIR, EIR_PKTIF);
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = enc624j600ReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable LINKIE and PKTIE interrupts
+   enc624j600SetBit(interface, ENC624J600_REG_EIE, EIE_LINKIE | EIE_PKTIE);
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t enc624j600SendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > 1536)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the link is up before transmitting the frame
+   if(!interface->linkState)
+   {
+      //The transmitter can accept another packet
+      osSetEventFromIsr(&interface->nicTxEvent);
+      //Drop current packet
+      return NO_ERROR;
+   }
+
+   //Ensure that the transmitter is ready to send
+   if(enc624j600ReadReg(interface, ENC624J600_REG_ECON1) & ECON1_TXRTS)
+      return ERROR_FAILURE;
+
+   //Point to the SRAM buffer
+   enc624j600WriteReg(interface, ENC624J600_REG_EGPWRPT, ENC624J600_TX_BUFFER_START);
+   //Copy the packet to the SRAM buffer
+   enc624j600WriteBuffer(interface, ENC624J600_CMD_WGPDATA, buffer, offset);
+
+   //Program ETXST to the start address of the packet
+   enc624j600WriteReg(interface, ENC624J600_REG_ETXST, ENC624J600_TX_BUFFER_START);
+   //Program ETXLEN with the length of data copied to the memory
+   enc624j600WriteReg(interface, ENC624J600_REG_ETXLEN, length);
+
+   //Clear TXIF and TXABTIF interrupt flags
+   enc624j600ClearBit(interface, ENC624J600_REG_EIR, EIR_TXIF | EIR_TXABTIF);
+   //Set the TXRTS bit to initiate transmission
+   enc624j600SetBit(interface, ENC624J600_REG_ECON1, ECON1_TXRTS);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t enc624j600ReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   uint16_t n;
+   uint32_t status;
+   Enc624j600Context *context;
+
+   //Point to the driver context
+   context = (Enc624j600Context *) interface->nicContext;
+
+   //Verify that a packet is waiting by ensuring that PKTCNT is non-zero
+   if(enc624j600ReadReg(interface, ENC624J600_REG_ESTAT) & ESTAT_PKTCNT)
+   {
+      //Point to the next packet
+      enc624j600WriteReg(interface, ENC624J600_REG_ERXRDPT, context->nextPacket);
+
+      //Read the first two bytes, which are the address of the next packet
+      enc624j600ReadBuffer(interface, ENC624J600_CMD_RRXDATA,
+         (uint8_t *) &context->nextPacket, sizeof(uint16_t));
+
+      //Get the length of the received frame in bytes
+      enc624j600ReadBuffer(interface, ENC624J600_CMD_RRXDATA,
+         (uint8_t *) &n, sizeof(uint16_t));
+
+      //Read the receive status vector (RSV)
+      enc624j600ReadBuffer(interface, ENC624J600_CMD_RRXDATA,
+         (uint8_t *) &status, sizeof(uint32_t));
+
+      //Make sure no error occurred
+      if(status & RSV_RECEIVED_OK)
+      {
+         //Limit the number of data to read
+         n = MIN(n, ETH_MAX_FRAME_SIZE);
+         //Read the Ethernet frame
+         enc624j600ReadBuffer(interface, ENC624J600_CMD_RRXDATA, context->rxBuffer, n);
+         //Valid packet received
+         error = NO_ERROR;
+      }
+      else
+      {
+         //The received packet contains an error
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Update the ERXTAIL pointer value to the point where the packet
+      //has been processed, taking care to wrap back at the end of the
+      //received memory buffer
+      if(context->nextPacket == ENC624J600_RX_BUFFER_START)
+         enc624j600WriteReg(interface, ENC624J600_REG_ERXTAIL, ENC624J600_RX_BUFFER_STOP);
+      else
+         enc624j600WriteReg(interface, ENC624J600_REG_ERXTAIL, context->nextPacket - 2);
+
+      //Set PKTDEC to decrement the PKTCNT bits
+      enc624j600SetBit(interface, ENC624J600_REG_ECON1, ECON1_PKTDEC);
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Check whether a valid packet has been received
+   if(!error)
+   {
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, context->rxBuffer, n);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t enc624j600SetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint16_t hashTable[4];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating ENC624J600 hash table...\r\n");
+
+   //Clear hash table
+   memset(hashTable, 0, sizeof(hashTable));
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = enc624j600CalcCrc(&entry->addr, sizeof(MacAddr));
+         //Calculate the corresponding index in the table
+         k = (crc >> 23) & 0x3F;
+         //Update hash table contents
+         hashTable[k / 16] |= (1 << (k % 16));
+      }
+   }
+
+   //Write the hash table to the ENC624J600 controller
+   enc624j600WriteReg(interface, ENC624J600_REG_EHT1, hashTable[0]);
+   enc624j600WriteReg(interface, ENC624J600_REG_EHT2, hashTable[1]);
+   enc624j600WriteReg(interface, ENC624J600_REG_EHT3, hashTable[2]);
+   enc624j600WriteReg(interface, ENC624J600_REG_EHT4, hashTable[3]);
+
+   //Debug message
+   TRACE_DEBUG("  EHT1 = %04" PRIX16 "\r\n", enc624j600ReadReg(interface, ENC624J600_REG_EHT1));
+   TRACE_DEBUG("  EHT2 = %04" PRIX16 "\r\n", enc624j600ReadReg(interface, ENC624J600_REG_EHT2));
+   TRACE_DEBUG("  EHT3 = %04" PRIX16 "\r\n", enc624j600ReadReg(interface, ENC624J600_REG_EHT3));
+   TRACE_DEBUG("  EHT4 = %04" PRIX16 "\r\n", enc624j600ReadReg(interface, ENC624J600_REG_EHT4));
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ **/
+
+void enc624j600UpdateMacConfig(NetInterface *interface)
+{
+   uint16_t duplexMode;
+
+   //Determine the new duplex mode by reading the PHYDPX bit
+   duplexMode = enc624j600ReadReg(interface, ENC624J600_REG_ESTAT) & ESTAT_PHYDPX;
+
+   //Full-duplex mode?
+   if(duplexMode)
+   {
+      //Configure the FULDPX bit to match the current duplex mode
+      enc624j600WriteReg(interface, ENC624J600_REG_MACON2, MACON2_DEFER |
+         MACON2_PADCFG2 | MACON2_PADCFG0 | MACON2_TXCRCEN | MACON2_R1 | MACON2_FULDPX);
+      //Configure the Back-to-Back Inter-Packet Gap register
+      enc624j600WriteReg(interface, ENC624J600_REG_MABBIPG, 0x15);
+   }
+   //Half-duplex mode?
+   else
+   {
+      //Configure the FULDPX bit to match the current duplex mode
+      enc624j600WriteReg(interface, ENC624J600_REG_MACON2, MACON2_DEFER |
+         MACON2_PADCFG2 | MACON2_PADCFG0 | MACON2_TXCRCEN | MACON2_R1);
+      //Configure the Back-to-Back Inter-Packet Gap register
+      enc624j600WriteReg(interface, ENC624J600_REG_MABBIPG, 0x12);
+   }
+}
+
+
+/**
+ * @brief Reset ENC624J600 controller
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t enc624j600SoftReset(NetInterface *interface)
+{
+   //Wait for the SPI interface to be ready
+   do
+   {
+      //Write 0x1234 to EUDAST
+      enc624j600WriteReg(interface, ENC624J600_REG_EUDAST, 0x1234);
+      //Read back register and check contents
+   } while(enc624j600ReadReg(interface, ENC624J600_REG_EUDAST) != 0x1234);
+
+   //Poll CLKRDY and wait for it to become set
+   while(!(enc624j600ReadReg(interface, ENC624J600_REG_ESTAT) & ESTAT_CLKRDY));
+
+   //Issue a system reset command by setting ETHRST
+   enc624j600SetBit(interface, ENC624J600_REG_ECON2, ECON2_ETHRST);
+   //Wait at least 25us for the reset to take place
+   sleep(1);
+
+   //Read EUDAST to confirm that the system reset took place.
+   //EUDAST should have reverted back to its reset default
+   if(enc624j600ReadReg(interface, ENC624J600_REG_EUDAST) != 0x0000)
+      return ERROR_FAILURE;
+
+   //Wait at least 256us for the PHY registers and PHY
+   //status bits to become available
+   sleep(1);
+
+   //The controller is now ready to accept further commands
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write ENC624J600 register
+ * @param[in] interface Underlying network interface
+ * @param[in] address Register address
+ * @param[in] data Register value
+ **/
+
+void enc624j600WriteReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   //Pull the CS pin low
+   interface->spiDriver->assertCs();
+
+   //Write opcode
+   interface->spiDriver->transfer(ENC624J600_CMD_WCRU);
+   //Write register address
+   interface->spiDriver->transfer(address);
+   //Write register value
+   interface->spiDriver->transfer(LSB(data));
+   interface->spiDriver->transfer(MSB(data));
+
+   //Terminate the operation by raising the CS pin
+   interface->spiDriver->deassertCs();
+}
+
+
+/**
+ * @brief Read ENC624J600 register
+ * @param[in] interface Underlying network interface
+ * @param[in] address Register address
+ * @return Register value
+ **/
+
+uint16_t enc624j600ReadReg(NetInterface *interface, uint8_t address)
+{
+   uint16_t data;
+
+   //Pull the CS pin low
+   interface->spiDriver->assertCs();
+
+   //Write opcode
+   interface->spiDriver->transfer(ENC624J600_CMD_RCRU);
+   //Write register address
+   interface->spiDriver->transfer(address);
+   //Read the lower 8 bits of data
+   data = interface->spiDriver->transfer(0x00);
+   //Read the upper 8 bits of data
+   data |= interface->spiDriver->transfer(0x00) << 8;
+
+   //Terminate the operation by raising the CS pin
+   interface->spiDriver->deassertCs();
+
+   //Return register contents
+   return data;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void enc624j600WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   //Write the address of the PHY register to write to
+   enc624j600WriteReg(interface, ENC624J600_REG_MIREGADR, MIREGADR_R8 | address);
+   //Write the 16 bits of data into the MIWR register
+   enc624j600WriteReg(interface, ENC624J600_REG_MIWR, data);
+
+   //Wait until the PHY register has been written
+   while(enc624j600ReadReg(interface, ENC624J600_REG_MISTAT) & MISTAT_BUSY);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t enc624j600ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   //Write the address of the PHY register to read from
+   enc624j600WriteReg(interface, ENC624J600_REG_MIREGADR, MIREGADR_R8 | address);
+   //Start read operation
+   enc624j600WriteReg(interface, ENC624J600_REG_MICMD, MICMD_MIIRD);
+
+   //Wait at least 25.6us before polling the BUSY bit
+   usleep(100);
+   //Wait for the read operation to complete
+   while(enc624j600ReadReg(interface, ENC624J600_REG_MISTAT) & MISTAT_BUSY);
+
+   //Clear command register
+   enc624j600WriteReg(interface, ENC624J600_REG_MICMD, 0x00);
+
+   //Return register contents
+   return enc624j600ReadReg(interface, ENC624J600_REG_MIRD);
+}
+
+
+/**
+ * @brief Write SRAM buffer
+ * @param[in] interface Underlying network interface
+ * @param[in] opcode SRAM buffer operation
+ * @param[in] buffer Multi-part buffer containing the data to be written
+ * @param[in] offset Offset to the first data byte
+ **/
+
+void enc624j600WriteBuffer(NetInterface *interface,
+   uint8_t opcode, const NetBuffer *buffer, size_t offset)
+{
+   uint_t i;
+   size_t j;
+   size_t n;
+   uint8_t *p;
+
+   //Pull the CS pin low
+   interface->spiDriver->assertCs();
+
+   //Write opcode
+   interface->spiDriver->transfer(opcode);
+
+   //Loop through data chunks
+   for(i = 0; i < buffer->chunkCount; i++)
+   {
+      //Is there any data to copy from the current chunk?
+      if(offset < buffer->chunk[i].length)
+      {
+         //Point to the first byte to be read
+         p = (uint8_t *) buffer->chunk[i].address + offset;
+         //Compute the number of bytes to copy at a time
+         n = buffer->chunk[i].length - offset;
+
+         //Copy data to SRAM buffer
+         for(j = 0; j < n; j++)
+            interface->spiDriver->transfer(p[j]);
+
+         //Process the next block from the start
+         offset = 0;
+      }
+      else
+      {
+         //Skip the current chunk
+         offset -= buffer->chunk[i].length;
+      }
+   }
+
+   //Terminate the operation by raising the CS pin
+   interface->spiDriver->deassertCs();
+}
+
+
+/**
+ * @brief Read SRAM buffer
+ * @param[in] interface Underlying network interface
+ * @param[in] opcode SRAM buffer operation
+ * @param[in] data Buffer where to store the incoming data
+ * @param[in] length Number of data to read
+ **/
+
+void enc624j600ReadBuffer(NetInterface *interface,
+   uint8_t opcode, uint8_t *data, size_t length)
+{
+   size_t i;
+
+   //Pull the CS pin low
+   interface->spiDriver->assertCs();
+
+   //Write opcode
+   interface->spiDriver->transfer(opcode);
+
+   //Copy data from SRAM buffer
+   for(i = 0; i < length; i++)
+      data[i] = interface->spiDriver->transfer(0x00);
+
+   //Terminate the operation by raising the CS pin
+   interface->spiDriver->deassertCs();
+}
+
+
+/**
+ * @brief Set bit field
+ * @param[in] interface Underlying network interface
+ * @param[in] address Register address
+ * @param[in] mask Bits to set in the target register
+ **/
+
+void enc624j600SetBit(NetInterface *interface, uint8_t address, uint16_t mask)
+{
+   //Pull the CS pin low
+   interface->spiDriver->assertCs();
+
+   //Write opcode
+   interface->spiDriver->transfer(ENC624J600_CMD_BFSU);
+   //Write register address
+   interface->spiDriver->transfer(address);
+   //Write bit mask
+   interface->spiDriver->transfer(LSB(mask));
+   interface->spiDriver->transfer(MSB(mask));
+
+   //Terminate the operation by raising the CS pin
+   interface->spiDriver->deassertCs();
+}
+
+
+/**
+ * @brief Clear bit field
+ * @param[in] interface Underlying network interface
+ * @param[in] address Register address
+ * @param[in] mask Bits to clear in the target register
+ **/
+
+void enc624j600ClearBit(NetInterface *interface, uint8_t address, uint16_t mask)
+{
+   //Pull the CS pin low
+   interface->spiDriver->assertCs();
+
+   //Write opcode
+   interface->spiDriver->transfer(ENC624J600_CMD_BFCU);
+   //Write register address
+   interface->spiDriver->transfer(address);
+   //Write bit mask
+   interface->spiDriver->transfer(LSB(mask));
+   interface->spiDriver->transfer(MSB(mask));
+
+   //Terminate the operation by raising the CS pin
+   interface->spiDriver->deassertCs();
+}
+
+
+/**
+ * @brief CRC calculation using the polynomial 0x4C11DB7
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t enc624j600CalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return crc;
+}
+
+
+/**
+ * @brief Dump registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void enc624j600DumpReg(NetInterface *interface)
+{
+#if (TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+   uint8_t i;
+   uint8_t bank;
+   uint16_t address;
+
+   //Display header
+   TRACE_DEBUG("    Bank 0  Bank 1  Bank 2  Bank 3  Unbanked\r\n");
+
+   //Loop through register addresses
+   for(i = 0; i < 32; i += 2)
+   {
+      //Display register address
+      TRACE_DEBUG("%02" PRIX8 ": ", i);
+
+      //Loop through bank numbers
+      for(bank = 0; bank < 5; bank++)
+      {
+         //Format register address
+         address = 0x7E00 | (bank << 5) | i;
+         //Display register contents
+         TRACE_DEBUG("0x%04" PRIX16 "  ", enc624j600ReadReg(interface, address));
+      }
+
+      //Jump to the following line
+      TRACE_DEBUG("\r\n");
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+#endif
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void enc624j600DumpPhyReg(NetInterface *interface)
+{
+#if (TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIX8 ": 0x%04" PRIX16 "\r\n", i, enc624j600ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+#endif
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/enc624j600.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,498 @@
+/**
+ * @file enc624j600.h
+ * @brief ENC624J600/ENC424J600 Ethernet controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _ENC624J600_H
+#define _ENC624J600_H
+
+//Receive and transmit buffers
+#define ENC624J600_TX_BUFFER_START 0x0000
+#define ENC624J600_TX_BUFFER_STOP  0x17FE
+#define ENC624J600_RX_BUFFER_START 0x1800
+#define ENC624J600_RX_BUFFER_STOP  0x5FFE
+
+//SPI command set
+#define ENC624J600_CMD_B0SEL       0xC0 //Bank 0 Select
+#define ENC624J600_CMD_B1SEL       0xC2 //Bank 1 Select
+#define ENC624J600_CMD_B2SEL       0xC4 //Bank 2 Select
+#define ENC624J600_CMD_B3SEL       0xC6 //Bank 3 Select
+#define ENC624J600_CMD_SETETHRST   0xCA //System Reset
+#define ENC624J600_CMD_FCDISABLE   0xE0 //Flow Control Disable
+#define ENC624J600_CMD_FCSINGLE    0xE2 //Flow Control Single
+#define ENC624J600_CMD_FCMULTIPLE  0xE4 //Flow Control Multiple
+#define ENC624J600_CMD_FCCLEAR     0xE6 //Flow Control Clear
+#define ENC624J600_CMD_SETPKTDEC   0xCC //Decrement Packet Counter
+#define ENC624J600_CMD_DMASTOP     0xD2 //DMA Stop
+#define ENC624J600_CMD_DMACKSUM    0xD8 //DMA Start Checksum
+#define ENC624J600_CMD_DMACKSUMS   0xDA //DMA Start Checksum with Seed
+#define ENC624J600_CMD_DMACOPY     0xDC //DMA Start Copy
+#define ENC624J600_CMD_DMACOPYS    0xDE //DMA Start Copy and Checksum with Seed
+#define ENC624J600_CMD_SETTXRTS    0xD4 //Request Packet Transmission
+#define ENC624J600_CMD_ENABLERX    0xE8 //Enable RX
+#define ENC624J600_CMD_DISABLERX   0xEA //Disable RX
+#define ENC624J600_CMD_SETEIE      0xEC //Enable Interrupts
+#define ENC624J600_CMD_CLREIE      0xEE //Disable Interrupts
+#define ENC624J600_CMD_RBSEL       0xC8 //Read Bank Select
+#define ENC624J600_CMD_WGPRDPT     0x60 //Write EGPRDPT
+#define ENC624J600_CMD_RGPRDPT     0x62 //Read EGPRDPT
+#define ENC624J600_CMD_WRXRDPT     0x64 //Write ERXRDPT
+#define ENC624J600_CMD_RRXRDPT     0x66 //Read ERXRDPT
+#define ENC624J600_CMD_WUDARDPT    0x68 //Write EUDARDPT
+#define ENC624J600_CMD_RUDARDPT    0x6A //Read EUDARDPT
+#define ENC624J600_CMD_WGPWRPT     0x6C //Write EGPWRPT
+#define ENC624J600_CMD_RGPWRPT     0x6E //Read EGPWRPT
+#define ENC624J600_CMD_WRXWRPT     0x70 //Write ERXWRPT
+#define ENC624J600_CMD_RRXWRPT     0x72 //Read ERXWRPT
+#define ENC624J600_CMD_WUDAWRPT    0x74 //Write EUDAWRPT
+#define ENC624J600_CMD_RUDAWRPT    0x76 //Read EUDAWRPT
+#define ENC624J600_CMD_RCR         0x00 //Read Control Register
+#define ENC624J600_CMD_WCR         0x40 //Write Control Register
+#define ENC624J600_CMD_RCRU        0x20 //Read Control Register Unbanked
+#define ENC624J600_CMD_WCRU        0x22 //Write Control Register Unbanked
+#define ENC624J600_CMD_BFS         0x80 //Bit Field Set
+#define ENC624J600_CMD_BFC         0xA0 //Bit Field Clear
+#define ENC624J600_CMD_BFSU        0x24 //Bit Field Set Unbanked
+#define ENC624J600_CMD_BFCU        0x26 //Bit Field Clear Unbanked
+#define ENC624J600_CMD_RGPDATA     0x28 //Read EGPDATA
+#define ENC624J600_CMD_WGPDATA     0x2A //Write EGPDATA
+#define ENC624J600_CMD_RRXDATA     0x2C //Read ERXDATA
+#define ENC624J600_CMD_WRXDATA     0x2E //Write ERXDATA
+#define ENC624J600_CMD_RUDADATA    0x30 //Read EUDADATA
+#define ENC624J600_CMD_WUDADATA    0x32 //Write EUDADATA
+
+//ENC624J600 registers
+#define ENC624J600_REG_ETXST       0x00
+#define ENC624J600_REG_ETXLEN      0x02
+#define ENC624J600_REG_ERXST       0x04
+#define ENC624J600_REG_ERXTAIL     0x06
+#define ENC624J600_REG_ERXHEAD     0x08
+#define ENC624J600_REG_EDMAST      0x0A
+#define ENC624J600_REG_EDMALEN     0x0C
+#define ENC624J600_REG_EDMADST     0x0E
+#define ENC624J600_REG_EDMACS      0x10
+#define ENC624J600_REG_ETXSTAT     0x12
+#define ENC624J600_REG_ETXWIRE     0x14
+#define ENC624J600_REG_EUDAST      0x16
+#define ENC624J600_REG_EUDAND      0x18
+#define ENC624J600_REG_ESTAT       0x1A
+#define ENC624J600_REG_EIR         0x1C
+#define ENC624J600_REG_ECON1       0x1E
+#define ENC624J600_REG_EHT1        0x20
+#define ENC624J600_REG_EHT2        0x22
+#define ENC624J600_REG_EHT3        0x24
+#define ENC624J600_REG_EHT4        0x26
+#define ENC624J600_REG_EPMM1       0x28
+#define ENC624J600_REG_EPMM2       0x2A
+#define ENC624J600_REG_EPMM3       0x2C
+#define ENC624J600_REG_EPMM4       0x2E
+#define ENC624J600_REG_EPMCS       0x30
+#define ENC624J600_REG_EPMO        0x32
+#define ENC624J600_REG_ERXFCON     0x34
+#define ENC624J600_REG_MACON1      0x40
+#define ENC624J600_REG_MACON2      0x42
+#define ENC624J600_REG_MABBIPG     0x44
+#define ENC624J600_REG_MAIPG       0x46
+#define ENC624J600_REG_MACLCON     0x48
+#define ENC624J600_REG_MAMXFL      0x4A
+#define ENC624J600_REG_MICMD       0x52
+#define ENC624J600_REG_MIREGADR    0x54
+#define ENC624J600_REG_MAADR3      0x60
+#define ENC624J600_REG_MAADR2      0x62
+#define ENC624J600_REG_MAADR1      0x64
+#define ENC624J600_REG_MIWR        0x66
+#define ENC624J600_REG_MIRD        0x68
+#define ENC624J600_REG_MISTAT      0x6A
+#define ENC624J600_REG_EPAUS       0x6C
+#define ENC624J600_REG_ECON2       0x6E
+#define ENC624J600_REG_ERXWM       0x70
+#define ENC624J600_REG_EIE         0x72
+#define ENC624J600_REG_EIDLED      0x74
+#define ENC624J600_REG_EGPDATA     0x80
+#define ENC624J600_REG_ERXDATA     0x82
+#define ENC624J600_REG_EUDADATA    0x84
+#define ENC624J600_REG_EGPRDPT     0x86
+#define ENC624J600_REG_EGPWRPT     0x88
+#define ENC624J600_REG_ERXRDPT     0x8A
+#define ENC624J600_REG_ERXWRPT     0x8C
+#define ENC624J600_REG_EUDARDPT    0x8E
+#define ENC624J600_REG_EUDAWRPT    0x90
+
+//ENC624J600 PHY registers
+#define ENC624J600_PHY_REG_PHCON1  0x00
+#define ENC624J600_PHY_REG_PHSTAT1 0x01
+#define ENC624J600_PHY_REG_PHANA   0x04
+#define ENC624J600_PHY_REG_PHANLPA 0x05
+#define ENC624J600_PHY_REG_PHANE   0x06
+#define ENC624J600_PHY_REG_PHCON2  0x11
+#define ENC624J600_PHY_REG_PHSTAT2 0x1B
+#define ENC624J600_PHY_REG_PHSTAT3 0x1F
+
+//ESTAT register
+#define ESTAT_INT                  0x8000
+#define ESTAT_FCIDLE               0x4000
+#define ESTAT_RXBUSY               0x2000
+#define ESTAT_CLKRDY               0x1000
+#define ESTAT_R11                  0x0800
+#define ESTAT_PHYDPX               0x0400
+#define ESTAT_R9                   0x0200
+#define ESTAT_PHYLNK               0x0100
+#define ESTAT_PKTCNT               0x00FF
+
+//EIR register
+#define EIR_CRYPTEN                0x8000
+#define EIR_MODEXIF                0x4000
+#define EIR_HASHIF                 0x2000
+#define EIR_AESIF                  0x1000
+#define EIR_LINKIF                 0x0800
+#define EIR_R10                    0x0400
+#define EIR_R9                     0x0200
+#define EIR_R8                     0x0100
+#define EIR_R7                     0x0080
+#define EIR_PKTIF                  0x0040
+#define EIR_DMAIF                  0x0020
+#define EIR_R4                     0x0010
+#define EIR_TXIF                   0x0008
+#define EIR_TXABTIF                0x0004
+#define EIR_RXABTIF                0x0002
+#define EIR_PCFULIF                0x0001
+
+//ECON1 register
+#define ECON1_MODEXST              0x8000
+#define ECON1_HASHEN               0x4000
+#define ECON1_HASHOP               0x2000
+#define ECON1_HASHLST              0x1000
+#define ECON1_AESST                0x0800
+#define ECON1_AESOP1               0x0400
+#define ECON1_AESOP0               0x0200
+#define ECON1_PKTDEC               0x0100
+#define ECON1_FCOP1                0x0080
+#define ECON1_FCOP0                0x0040
+#define ECON1_DMAST                0x0020
+#define ECON1_DMACPY               0x0010
+#define ECON1_DMACSSD              0x0008
+#define ECON1_DMANOCS              0x0004
+#define ECON1_TXRTS                0x0002
+#define ECON1_RXEN                 0x0001
+
+//ETXSTAT register
+#define ETXSTAT_R12                0x1000
+#define ETXSTAT_R11                0x0800
+#define ETXSTAT_LATECOL            0x0400
+#define ETXSTAT_MAXCOL             0x0200
+#define ETXSTAT_EXDEFER            0x0100
+#define ETXSTAT_DEFER              0x0080
+#define ETXSTAT_R6                 0x0040
+#define ETXSTAT_R5                 0x0020
+#define ETXSTAT_CRCBAD             0x0010
+#define ETXSTAT_COLCNT             0x000F
+
+//ERXFCON register
+#define ERXFCON_HTEN               0x8000
+#define ERXFCON_MPEN               0x4000
+#define ERXFCON_NOTPM              0x1000
+#define ERXFCON_PMEN3              0x0800
+#define ERXFCON_PMEN2              0x0400
+#define ERXFCON_PMEN1              0x0200
+#define ERXFCON_PMEN0              0x0100
+#define ERXFCON_CRCEEN             0x0080
+#define ERXFCON_CRCEN              0x0040
+#define ERXFCON_RUNTEEN            0x0020
+#define ERXFCON_RUNTEN             0x0010
+#define ERXFCON_UCEN               0x0008
+#define ERXFCON_NOTMEEN            0x0004
+#define ERXFCON_MCEN               0x0002
+#define ERXFCON_BCEN               0x0001
+
+//MACON1 register
+#define MACON1_R15                 0x8000
+#define MACON1_R14                 0x4000
+#define MACON1_R11                 0x0800
+#define MACON1_R10                 0x0400
+#define MACON1_R9                  0x0200
+#define MACON1_R8                  0x0100
+#define MACON1_LOOPBK              0x0010
+#define MACON1_R3                  0x0008
+#define MACON1_RXPAUS              0x0004
+#define MACON1_PASSALL             0x0002
+#define MACON1_R0                  0x0001
+
+//MACON2 register
+#define MACON2_DEFER               0x4000
+#define MACON2_BPEN                0x2000
+#define MACON2_NOBKOFF             0x1000
+#define MACON2_R9                  0x0200
+#define MACON2_R8                  0x0100
+#define MACON2_PADCFG2             0x0080
+#define MACON2_PADCFG1             0x0040
+#define MACON2_PADCFG0             0x0020
+#define MACON2_TXCRCEN             0x0010
+#define MACON2_PHDREN              0x0008
+#define MACON2_HFRMEN              0x0004
+#define MACON2_R1                  0x0002
+#define MACON2_FULDPX              0x0001
+
+//MABBIPG register
+#define MABBIPG_BBIPG              0x007F
+
+//MAIPG register
+#define MAIPG_R14                  0x4000
+#define MAIPG_R13                  0x2000
+#define MAIPG_R12                  0x1000
+#define MAIPG_R11                  0x0800
+#define MAIPG_R10                  0x0400
+#define MAIPG_R9                   0x0200
+#define MAIPG_R8                   0x0100
+#define MAIPG_IPG                  0x007F
+
+//MACLCON register
+#define MACLCON_R13                0x2000
+#define MACLCON_R12                0x1000
+#define MACLCON_R11                0x0800
+#define MACLCON_R10                0x0400
+#define MACLCON_R9                 0x0200
+#define MACLCON_R8                 0x0100
+#define MACLCON_MAXRET             0x000F
+
+//MICMD register
+#define MICMD_MIISCAN              0x0002
+#define MICMD_MIIRD                0x0001
+
+//MIREGADR register
+#define MIREGADR_R12               0x1000
+#define MIREGADR_R11               0x0800
+#define MIREGADR_R10               0x0400
+#define MIREGADR_R9                0x0200
+#define MIREGADR_R8                0x0100
+#define MIREGADR_PHREG             0x001F
+
+//MISTAT register
+#define MISTAT_R3                  0x0008
+#define MISTAT_NVALID              0x0004
+#define MISTAT_SCAN                0x0002
+#define MISTAT_BUSY                0x0001
+
+//ECON2 register
+#define ECON2_ETHEN                0x8000
+#define ECON2_STRCH                0x4000
+#define ECON2_TXMAC                0x2000
+#define ECON2_SHA1MD5              0x1000
+#define ECON2_COCON3               0x0800
+#define ECON2_COCON2               0x0400
+#define ECON2_COCON1               0x0200
+#define ECON2_COCON0               0x0100
+#define ECON2_AUTOFC               0x0080
+#define ECON2_TXRST                0x0040
+#define ECON2_RXRST                0x0020
+#define ECON2_ETHRST               0x0010
+#define ECON2_MODLEN1              0x0008
+#define ECON2_MODLEN0              0x0004
+#define ECON2_AESLEN1              0x0002
+#define ECON2_AESLEN0              0x0001
+
+//ERXWM register
+#define ERXWM_RXFWM                0xFF00
+#define ERXWM_RXEWM                0x00FF
+
+//EIE register
+#define EIE_INTIE                  0x8000
+#define EIE_MODEXIE                0x4000
+#define EIE_HASHIE                 0x2000
+#define EIE_AESIE                  0x1000
+#define EIE_LINKIE                 0x0800
+#define EIE_R10                    0x0400
+#define EIE_R9                     0x0200
+#define EIE_R8                     0x0100
+#define EIE_R7                     0x0080
+#define EIE_PKTIE                  0x0040
+#define EIE_DMAIE                  0x0020
+#define EIE_R4                     0x0010
+#define EIE_TXIE                   0x0008
+#define EIE_TXABTIE                0x0004
+#define EIE_RXABTIE                0x0002
+#define EIE_PCFULIE                0x0001
+
+//EIDLED register
+#define EIDLED_LACFG3              0x8000
+#define EIDLED_LACFG2              0x4000
+#define EIDLED_LACFG1              0x2000
+#define EIDLED_LACFG0              0x1000
+#define EIDLED_LBCFG3              0x0800
+#define EIDLED_LBCFG2              0x0400
+#define EIDLED_LBCFG1              0x0200
+#define EIDLED_LBCFG0              0x0100
+#define EIDLED_DEVID               0x00FF
+
+//PHCON1 register
+#define PHCON1_PRST                0x8000
+#define PHCON1_PLOOPBK             0x4000
+#define PHCON1_SPD100              0x2000
+#define PHCON1_ANEN                0x1000
+#define PHCON1_PSLEEP              0x0800
+#define PHCON1_RENEG               0x0200
+#define PHCON1_PFULDPX             0x0100
+
+//PHSTAT1 register
+#define PHSTAT1_FULL100            0x4000
+#define PHSTAT1_HALF100            0x2000
+#define PHSTAT1_FULL10             0x1000
+#define PHSTAT1_HALF10             0x0800
+#define PHSTAT1_ANDONE             0x0020
+#define PHSTAT1_LRFAULT            0x0010
+#define PHSTAT1_ANABLE             0x0008
+#define PHSTAT1_LLSTAT             0x0004
+#define PHSTAT1_EXTREGS            0x0001
+
+//PHANA register
+#define PHANA_ADNP                 0x8000
+#define PHANA_ADFAULT              0x2000
+#define PHANA_ADPAUS1              0x0800
+#define PHANA_ADPAUS0              0x0400
+#define PHANA_AD100FD              0x0100
+#define PHANA_AD100                0x0080
+#define PHANA_AD10FD               0x0040
+#define PHANA_AD10                 0x0020
+#define PHANA_ADIEEE4              0x0010
+#define PHANA_ADIEEE3              0x0008
+#define PHANA_ADIEEE2              0x0004
+#define PHANA_ADIEEE1              0x0002
+#define PHANA_ADIEEE0              0x0001
+
+//PHANLPA register
+#define PHANLPA_LPNP               0x8000
+#define PHANLPA_LPACK              0x4000
+#define PHANLPA_LPFAULT            0x2000
+#define PHANLPA_LPPAUS1            0x0800
+#define PHANLPA_LPPAUS0            0x0400
+#define PHANLPA_LP100T4            0x0200
+#define PHANLPA_LP100FD            0x0100
+#define PHANLPA_LP100              0x0080
+#define PHANLPA_LP10FD             0x0040
+#define PHANLPA_LP10               0x0020
+#define PHANLPA_LPIEEE             0x001F
+#define PHANLPA_LPIEEE4            0x0010
+#define PHANLPA_LPIEEE3            0x0008
+#define PHANLPA_LPIEEE2            0x0004
+#define PHANLPA_LPIEEE1            0x0002
+#define PHANLPA_LPIEEE0            0x0001
+
+//PHANE register
+#define PHANE_PDFLT                0x0010
+#define PHANE_LPARCD               0x0002
+#define PHANE_LPANABL              0x0001
+
+//PHCON2 register
+#define PHCON2_EDPWRDN             0x2000
+#define PHCON2_EDTHRES             0x0800
+#define PHCON2_FRCLNK              0x0004
+#define PHCON2_EDSTAT              0x0002
+
+//PHSTAT2 register
+#define PHSTAT2_PLRITY             0x0010
+
+//PHSTAT3 register
+#define PHSTAT3_SPDDPX2            0x0010
+#define PHSTAT3_SPDDPX1            0x0008
+#define PHSTAT3_SPDDPX0            0x0004
+
+//Receive status vector
+#define RSV_UNICAST_FILTER         0x00100000
+#define RSV_PATTERN_MATCH_FILTER   0x00080000
+#define RSV_MAGIC_PACKET_FILTER    0x00040000
+#define RSV_HASH_FILTER            0x00020000
+#define RSV_NOT_ME_FILTER          0x00010000
+#define RSV_RUNT_FILTER            0x00008000
+#define RSV_VLAN_TYPE              0x00004000
+#define RSV_UNKNOWN_OPCODE         0x00002000
+#define RSV_PAUSE_CONTROL_FRAME    0x00001000
+#define RSV_CONTROL_FRAME          0x00000800
+#define RSV_DRIBBLE_NIBBLE         0x00000400
+#define RSV_BROADCAST_PACKET       0x00000200
+#define RSV_MULTICAST_PACKET       0x00000100
+#define RSV_RECEIVED_OK            0x00000080
+#define RSV_LENGTH_OUT_OF_RANGE    0x00000040
+#define RSV_LENGTH_CHECK_ERROR     0x00000020
+#define RSV_CRC_ERROR              0x00000010
+#define RSV_CARRIER_EVENT          0x00000004
+#define RSV_PACKET_IGNORED         0x00000001
+
+
+/**
+ * @brief ENC624J600 driver context
+ **/
+
+typedef struct
+{
+   uint16_t nextPacket; ///<Next packet in the receive buffer
+   uint8_t *rxBuffer;   ///<Receive buffer
+} Enc624j600Context;
+
+
+//ENC624J600 driver
+extern const NicDriver enc624j600Driver;
+
+//ENC624J600 related functions
+error_t enc624j600Init(NetInterface *interface);
+
+void enc624j600Tick(NetInterface *interface);
+
+void enc624j600EnableIrq(NetInterface *interface);
+void enc624j600DisableIrq(NetInterface *interface);
+bool_t enc624j600IrqHandler(NetInterface *interface);
+void enc624j600EventHandler(NetInterface *interface);
+
+error_t enc624j600SendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t enc624j600ReceivePacket(NetInterface *interface);
+
+error_t enc624j600SetMulticastFilter(NetInterface *interface);
+void enc624j600UpdateMacConfig(NetInterface *interface);
+
+error_t enc624j600SoftReset(NetInterface *interface);
+
+void enc624j600WriteReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t enc624j600ReadReg(NetInterface *interface, uint8_t address);
+
+void enc624j600WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t enc624j600ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void enc624j600WriteBuffer(NetInterface *interface,
+   uint8_t opcode, const NetBuffer *buffer, size_t offset);
+
+void enc624j600ReadBuffer(NetInterface *interface,
+   uint8_t opcode, uint8_t *data, size_t length);
+
+void enc624j600SetBit(NetInterface *interface, uint8_t address, uint16_t mask);
+void enc624j600ClearBit(NetInterface *interface, uint8_t address, uint16_t mask);
+
+uint32_t enc624j600CalcCrc(const void *data, size_t length);
+
+void enc624j600DumpReg(NetInterface *interface);
+void enc624j600DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/f28m35x_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,673 @@
+/**
+ * @file f28m35x_eth.c
+ * @brief F28M35x Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "inc/hw_ethernet.h"
+#include "inc/hw_ints.h"
+#include "inc/hw_memmap.h"
+#include "inc/hw_types.h"
+#include "driverlib/gpio.h"
+#include "driverlib/interrupt.h"
+#include "driverlib/sysctl.h"
+#include "core/net.h"
+#include "drivers/f28m35x_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+static uint8_t txBuffer[ETH_MAX_FRAME_SIZE + 2];
+//Receive buffer
+#pragma data_alignment = 4
+static uint8_t rxBuffer[ETH_MAX_FRAME_SIZE];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[ETH_MAX_FRAME_SIZE + 2] __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[ETH_MAX_FRAME_SIZE] __attribute__((aligned(4)));
+
+#endif
+
+
+/**
+ * @brief F28M35x Ethernet MAC driver
+ **/
+
+const NicDriver f28m35xEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   f28m35xEthInit,
+   f28m35xEthTick,
+   f28m35xEthEnableIrq,
+   f28m35xEthDisableIrq,
+   f28m35xEthEventHandler,
+   f28m35xEthSendPacket,
+   f28m35xEthSetMulticastFilter,
+   f28m35xEthUpdateMacConfig,
+   f28m35xEthWritePhyReg,
+   f28m35xEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief F28M35x Ethernet MAC controller initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t f28m35xEthInit(NetInterface *interface)
+{
+   error_t error;
+   uint_t div;
+#ifdef ti_sysbios_BIOS___VERS
+   Hwi_Params hwiParams;
+#endif
+
+   //Debug message
+   TRACE_INFO("Initializing F28M35x Ethernet MAC controller...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Enable Ethernet controller clock
+   SysCtlPeripheralEnable(SYSCTL_PERIPH_ETH);
+   //Reset Ethernet controller
+   SysCtlPeripheralReset(SYSCTL_PERIPH_ETH);
+
+   //GPIO configuration
+   f28m35xEthInitGpio(interface);
+
+   //The MDC clock frequency cannot exceed 2.5MHz
+   div = SysCtlClockGet(20000000) / (2 * 2500000) - 1;
+   //Adjust MDC clock frequency
+   MAC_MDV_R = div & MAC_MDV_DIV_M;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Set the MAC address
+   MAC_IA0_R = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   MAC_IA1_R = interface->macAddr.w[2];
+
+   //Enable automatic CRC generation and packet padding
+   MAC_TCTL_R = MAC_TCTL_DUPLEX | MAC_TCTL_CRC | MAC_TCTL_PADEN;
+   //Flush the receive FIFO and enable CRC verification
+   MAC_RCTL_R = MAC_RCTL_RSTFIFO | MAC_RCTL_BADCRC;
+
+   //Configure Ethernet interrupts
+   MAC_IM_R = MAC_IM_TXEMPM | MAC_IM_RXINTM;
+
+#ifdef ti_sysbios_BIOS___VERS
+   //Configure Ethernet interrupt
+   Hwi_Params_init(&hwiParams);
+   hwiParams.enableInt = FALSE;
+   hwiParams.priority = F28M35X_ETH_IRQ_PRIORITY;
+
+   //Register interrupt handler
+   Hwi_create(INT_ETH, (Hwi_FuncPtr) f28m35xEthIrqHandler, &hwiParams, NULL);
+#else
+   //Register interrupt handler
+   IntRegister(INT_ETH, f28m35xEthIrqHandler);
+
+   //Set priority grouping (3 bits for pre-emption priority, no bits for subpriority)
+   IntPriorityGroupingSet(F28M35X_ETH_IRQ_PRIORITY_GROUPING);
+   //Configure Ethernet interrupt priority
+   IntPrioritySet(INT_ETH, F28M35X_ETH_IRQ_PRIORITY);
+#endif
+
+   //Enable transmitter
+   MAC_TCTL_R |= MAC_TCTL_TXEN;
+   //Enable receiver
+   MAC_RCTL_R |= MAC_RCTL_RXEN;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//TMDXCNCDH52C1 evaluation board?
+#if defined(USE_TMDXCNCDH52C1)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void f28m35xEthInitGpio(NetInterface *interface)
+{
+   //Enable GPIO clocks
+   SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
+   SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
+   SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
+   SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);
+   SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOH);
+   SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOJ);
+
+   //Configure MII_TXD3 (PC4)
+   GPIODirModeSet(GPIO_PORTC_BASE, GPIO_PIN_4, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTC_BASE, GPIO_PIN_4, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PC4_MIITXD3);
+
+   //Configure MII_MDIO (PE6)
+   GPIODirModeSet(GPIO_PORTE_BASE, GPIO_PIN_6, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTE_BASE, GPIO_PIN_6, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PE6_MIIMDIO);
+
+   //Configure MII_RXD3 (PF5)
+   GPIODirModeSet(GPIO_PORTF_BASE, GPIO_PIN_5, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_5, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PF5_MIIRXD3);
+
+   //Configure MII_RXD2 (PG0)
+   GPIODirModeSet(GPIO_PORTG_BASE, GPIO_PIN_0, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTG_BASE, GPIO_PIN_0, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PG0_MIIRXD2);
+
+   //Configure MII_RXD1 (PG1)
+   GPIODirModeSet(GPIO_PORTG_BASE, GPIO_PIN_1, GPIO_DIR_MODE_HW);
+      GPIOPadConfigSet(GPIO_PORTG_BASE, GPIO_PIN_1, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PG1_MIIRXD1);
+
+   //Configure MII_RXDV (PG3)
+   GPIODirModeSet(GPIO_PORTG_BASE, GPIO_PIN_3, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTG_BASE, GPIO_PIN_3, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PG3_MIIRXDV);
+
+   //Configure MII_TXER (PG7)
+   GPIODirModeSet(GPIO_PORTG_BASE, GPIO_PIN_7, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTG_BASE, GPIO_PIN_7, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PG7_MIITXER);
+
+   //Configure MII_RXD0 (PH1)
+   GPIODirModeSet(GPIO_PORTH_BASE, GPIO_PIN_1, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTH_BASE, GPIO_PIN_1, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PH1_MIIRXD0);
+
+   //Configure MII_TXD2 (PH3)
+   GPIODirModeSet(GPIO_PORTH_BASE, GPIO_PIN_3, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTH_BASE, GPIO_PIN_3, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PH3_MIITXD2);
+
+   //Configure MII_TXD1 (PH4)
+   GPIODirModeSet(GPIO_PORTH_BASE, GPIO_PIN_4, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTH_BASE, GPIO_PIN_4, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PH4_MIITXD1);
+
+   //Configure MII_TXD0 (PH5)
+   GPIODirModeSet(GPIO_PORTH_BASE, GPIO_PIN_5, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTH_BASE, GPIO_PIN_5, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PH5_MIITXD0);
+
+   //Configure MII_TXEN (PH6)
+   GPIODirModeSet(GPIO_PORTH_BASE, GPIO_PIN_6, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTH_BASE, GPIO_PIN_6, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PH6_MIITXEN);
+
+   //Configure MII_TXCK (PH7)
+   GPIODirModeSet(GPIO_PORTH_BASE, GPIO_PIN_7, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTH_BASE, GPIO_PIN_7, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PH7_MIITXCK);
+
+   //Configure MII_RXER (PJ0)
+   GPIODirModeSet(GPIO_PORTJ_BASE, GPIO_PIN_0, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTJ_BASE, GPIO_PIN_0, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PJ0_MIIRXER);
+
+   //Configure MII_RXCK (PJ2)
+   GPIODirModeSet(GPIO_PORTJ_BASE, GPIO_PIN_2, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTJ_BASE, GPIO_PIN_2, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PJ2_MIIRXCK);
+
+   //Configure MII_MDC (PJ3)
+   GPIODirModeSet(GPIO_PORTJ_BASE, GPIO_PIN_3, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTJ_BASE, GPIO_PIN_3, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PJ3_MIIMDC);
+
+   //Configure MII_COL (PJ4)
+   GPIODirModeSet(GPIO_PORTJ_BASE, GPIO_PIN_4, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTJ_BASE, GPIO_PIN_4, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PJ4_MIICOL);
+
+   //Configure MII_CRS (PJ5)
+   GPIODirModeSet(GPIO_PORTJ_BASE, GPIO_PIN_5, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTJ_BASE, GPIO_PIN_5, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PJ5_MIICRS);
+
+   //Configure MII_PHYINTR (PJ6)
+   GPIODirModeSet(GPIO_PORTJ_BASE, GPIO_PIN_6, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTJ_BASE, GPIO_PIN_6, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PJ6_MIIPHYINTRn);
+
+   //Configure MII_PHYRSTn (PJ7)
+   GPIODirModeSet(GPIO_PORTJ_BASE, GPIO_PIN_7, GPIO_DIR_MODE_HW);
+   GPIOPadConfigSet(GPIO_PORTJ_BASE, GPIO_PIN_7, GPIO_PIN_TYPE_STD);
+   GPIOPinConfigure(GPIO_PJ7_MIIPHYRSTn);
+}
+
+#endif
+
+
+/**
+ * @brief F28M35x Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void f28m35xEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void f28m35xEthEnableIrq(NetInterface *interface)
+{
+#ifdef ti_sysbios_BIOS___VERS
+   //Enable Ethernet MAC interrupts
+   Hwi_enableInterrupt(INT_ETH);
+#else
+   //Enable Ethernet MAC interrupts
+   IntEnable(INT_ETH);
+#endif
+
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void f28m35xEthDisableIrq(NetInterface *interface)
+{
+#ifdef ti_sysbios_BIOS___VERS
+   //Disable Ethernet MAC interrupts
+   Hwi_disableInterrupt(INT_ETH);
+#else
+   //Disable Ethernet MAC interrupts
+   IntDisable(INT_ETH);
+#endif
+
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief F28M35x Ethernet MAC interrupt service routine
+ **/
+
+void f28m35xEthIrqHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read interrupt status register
+   status = MAC_RIS_R;
+
+   //Transmit FIFO empty?
+   if(status & MAC_RIS_TXEMP)
+   {
+      //Acknowledge TXEMP interrupt
+      MAC_IACK_R = MAC_IACK_TXEMP;
+
+      //Check whether the transmit FIFO is available for writing
+      if(!(MAC_TR_R & MAC_TR_NEWTX))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //Packet received?
+   if(status & MAC_RIS_RXINT)
+   {
+      //Disable RXINT interrupt
+      MAC_IM_R &= ~MAC_IM_RXINTM;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief F28M35x Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void f28m35xEthEventHandler(NetInterface *interface)
+{
+   //Packet received?
+   if(MAC_RIS_R & MAC_RIS_RXINT)
+   {
+      //Acknowledge RXINT interrupt
+      MAC_IACK_R = MAC_IACK_RXINT;
+
+      //Process all the pending packets
+      while(MAC_NP_R & MAC_NP_NPR_M)
+      {
+         //Read incoming packet
+         f28m35xEthReceivePacket(interface);
+      }
+   }
+
+   //Re-enable Ethernet interrupts
+   MAC_IM_R = MAC_IM_TXEMPM | MAC_IM_RXINTM;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t f28m35xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t i;
+   size_t length;
+   uint32_t *p;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length < sizeof(EthHeader) || length > ETH_MAX_FRAME_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the transmit FIFO is available for writing
+   if(MAC_TR_R & MAC_TR_NEWTX)
+      return ERROR_FAILURE;
+
+   //Copy user data
+   netBufferRead(txBuffer + 2, buffer, offset, length);
+
+   //The packet is preceded by a 16-bit length field
+   txBuffer[0] = LSB(length - sizeof(EthHeader));
+   txBuffer[1] = MSB(length - sizeof(EthHeader));
+
+   //Point to the beginning of the packet
+   p = (uint32_t *) txBuffer;
+   //Compute the length of the packet in 32-bit words
+   length = (length + 5) / 4;
+
+   //Copy packet to transmit FIFO
+   for(i = 0; i < length; i++)
+      MAC_DATA_R = p[i];
+
+   //Start transmitting
+   MAC_TR_R = MAC_TR_NEWTX;
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t f28m35xEthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t i;
+   size_t n;
+   size_t length;
+   uint32_t data;
+   uint16_t *p;
+
+   //Make sure the FIFO is not empty
+   if(MAC_NP_R & MAC_NP_NPR_M)
+   {
+      //Read the first word
+      data = MAC_DATA_R;
+      //Retrieve the total length of the packet
+      length = data & 0xFFFF;
+
+      //Make sure the length field is valid
+      if(length > 2)
+      {
+         //Point to the beginning of the buffer
+         p = (uint16_t *) rxBuffer;
+
+         //Retrieve the length of the frame
+         length -= 2;
+         //Limit the number of data to be read
+         n = MIN(length, ETH_MAX_FRAME_SIZE);
+
+         //Copy the first half word
+         if(n > 0)
+            *(p++) = (uint16_t) (data >> 16);
+
+         //Copy data from receive FIFO
+         for(i = 2; i < n; i += 4)
+         {
+            //Read a 32-bit word from the FIFO
+            data = MAC_DATA_R;
+            //Write the 32-bit to the receive buffer
+            *(p++) = (uint16_t) data;
+            *(p++) = (uint16_t) (data >> 16);
+         }
+
+         //Skip the remaining bytes
+         while(i < length)
+         {
+            //Read a 32-bit word from the FIFO
+            data = MAC_DATA_R;
+            //Increment byte counter
+            i += 4;
+         }
+
+         //Valid packet received
+         error = NO_ERROR;
+      }
+      else
+      {
+         //Disable receiver
+         MAC_RCTL_R &= ~MAC_RCTL_RXEN;
+         //Flush the receive FIFO
+         MAC_RCTL_R |= MAC_RCTL_RSTFIFO;
+         //Re-enable receiver
+         MAC_RCTL_R |= MAC_RCTL_RXEN;
+
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Check whether a valid packet has been received
+   if(!error)
+   {
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, rxBuffer, n);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t f28m35xEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   bool_t acceptMulticast;
+
+   //This flag will be set if multicast addresses should be accepted
+   acceptMulticast = FALSE;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Valid entry?
+      if(interface->macMulticastFilter[i].refCount > 0)
+      {
+         //Accept multicast addresses
+         acceptMulticast = TRUE;
+         //We are done
+         break;
+      }
+   }
+
+   //Enable the reception of multicast frames if necessary
+   if(acceptMulticast)
+      MAC_RCTL_R |= MAC_RCTL_AMUL;
+   else
+      MAC_RCTL_R &= ~MAC_RCTL_AMUL;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t f28m35xEthUpdateMacConfig(NetInterface *interface)
+{
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      MAC_TCTL_R |= MAC_TCTL_DUPLEX;
+   else
+      MAC_TCTL_R &= ~MAC_TCTL_DUPLEX;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void f28m35xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   //Set PHY address
+   MAC_MAR_R = phyAddr;
+   //Data to be written in the PHY register
+   MAC_MTXD_R = data & MAC_MTXD_MDTX_M;
+   //Start a write operation
+   MAC_MCTL_R = (regAddr << 3) | MAC_MCTL_WRITE | MAC_MCTL_START;
+
+   //Wait for the write to complete
+   while(MAC_MCTL_R & MAC_MCTL_START);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t f28m35xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   //Set PHY address
+   MAC_MAR_R = phyAddr;
+   //Start a read operation
+   MAC_MCTL_R = (regAddr << 3) | MAC_MCTL_START;
+
+   //Wait for the read to complete
+   while(MAC_MCTL_R & MAC_MCTL_START);
+
+   //Return PHY register contents
+   return MAC_MRXD_R & MAC_MRXD_MDRX_M;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/f28m35x_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,94 @@
+/**
+ * @file f28m35x_eth.h
+ * @brief F28M35x Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _F28M35X_ETH_H
+#define _F28M35X_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Interrupt priority grouping
+#ifndef F28M35X_ETH_IRQ_PRIORITY_GROUPING
+   #define F28M35X_ETH_IRQ_PRIORITY_GROUPING 3
+#elif (F28M35X_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error F28M35X_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt priority
+#ifndef F28M35X_ETH_IRQ_PRIORITY
+   #define F28M35X_ETH_IRQ_PRIORITY 192
+#elif (F28M35X_ETH_IRQ_PRIORITY < 0)
+   #error F28M35X_ETH_IRQ_PRIORITY parameter is not valid
+#endif
+
+//F28M35x Ethernet MAC registers
+#define MAC_RIS_R    HWREG(ETH_BASE + MAC_O_RIS)
+#define MAC_IACK_R   HWREG(ETH_BASE + MAC_O_IACK)
+#define MAC_IM_R     HWREG(ETH_BASE + MAC_O_IM)
+#define MAC_RCTL_R   HWREG(ETH_BASE + MAC_O_RCTL)
+#define MAC_TCTL_R   HWREG(ETH_BASE + MAC_O_TCTL)
+#define MAC_DATA_R   HWREG(ETH_BASE + MAC_O_DATA)
+#define MAC_IA0_R    HWREG(ETH_BASE + MAC_O_IA0)
+#define MAC_IA1_R    HWREG(ETH_BASE + MAC_O_IA1)
+#define MAC_THR_R    HWREG(ETH_BASE + MAC_O_THR)
+#define MAC_MCTL_R   HWREG(ETH_BASE + MAC_O_MCTL)
+#define MAC_MDV_R    HWREG(ETH_BASE + MAC_O_MDV)
+#define MAC_MAR_R    HWREG(ETH_BASE + 0x28)
+#define MAC_MTXD_R   HWREG(ETH_BASE + MAC_O_MTXD)
+#define MAC_MRXD_R   HWREG(ETH_BASE + MAC_O_MRXD)
+#define MAC_NP_R     HWREG(ETH_BASE + MAC_O_NP)
+#define MAC_TR_R     HWREG(ETH_BASE + MAC_O_TR)
+#define MAC_TS_R     HWREG(ETH_BASE + MAC_O_TS)
+
+//F28M35x Ethernet MAC driver
+extern const NicDriver f28m35xEthDriver;
+
+//F28M35x Ethernet MAC related functions
+error_t f28m35xEthInit(NetInterface *interface);
+void f28m35xEthInitGpio(NetInterface *interface);
+
+void f28m35xEthTick(NetInterface *interface);
+
+void f28m35xEthEnableIrq(NetInterface *interface);
+void f28m35xEthDisableIrq(NetInterface *interface);
+void f28m35xEthIrqHandler(void);
+void f28m35xEthEventHandler(NetInterface *interface);
+
+error_t f28m35xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t f28m35xEthReceivePacket(NetInterface *interface);
+
+error_t f28m35xEthSetMulticastFilter(NetInterface *interface);
+error_t f28m35xEthUpdateMacConfig(NetInterface *interface);
+
+void f28m35xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t f28m35xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/fm4_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,789 @@
+/**
+ * @file fm4_eth.c
+ * @brief Spansion FM4 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "s6e2cc.h"
+#include "core/net.h"
+#include "drivers/fm4_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+static uint8_t txBuffer[FM4_ETH_TX_BUFFER_COUNT][FM4_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+static uint8_t rxBuffer[FM4_ETH_RX_BUFFER_COUNT][FM4_ETH_RX_BUFFER_SIZE];
+//Transmit DMA descriptors
+#pragma data_alignment = 4
+static Fm4TxDmaDesc txDmaDesc[FM4_ETH_TX_BUFFER_COUNT];
+//Receive DMA descriptors
+#pragma data_alignment = 4
+static Fm4RxDmaDesc rxDmaDesc[FM4_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[FM4_ETH_TX_BUFFER_COUNT][FM4_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[FM4_ETH_RX_BUFFER_COUNT][FM4_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Transmit DMA descriptors
+static Fm4TxDmaDesc txDmaDesc[FM4_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive DMA descriptors
+static Fm4RxDmaDesc rxDmaDesc[FM4_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+
+#endif
+
+//Pointer to the current TX DMA descriptor
+static Fm4TxDmaDesc *txCurDmaDesc;
+//Pointer to the current RX DMA descriptor
+static Fm4RxDmaDesc *rxCurDmaDesc;
+
+
+/**
+ * @brief Spansion FM4 Ethernet MAC driver
+ **/
+
+const NicDriver fm4EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   fm4EthInit,
+   fm4EthTick,
+   fm4EthEnableIrq,
+   fm4EthDisableIrq,
+   fm4EthEventHandler,
+   fm4EthSendPacket,
+   fm4EthSetMulticastFilter,
+   fm4EthUpdateMacConfig,
+   fm4EthWritePhyReg,
+   fm4EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief Spansion FM4 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t fm4EthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing Spansion FM4 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //GPIO configuration
+   fm4EthInitGpio(interface);
+
+   //Enable Ethernet MAC clock
+   FM4_ETHERNET_CONTROL->ETH_CLKG_f.MACEN = 1;
+
+   //Reset Ethernet MAC peripheral
+   FM4_ETHERNET_CONTROL->ETH_MODE_f.RST0 = 1;
+   FM4_ETHERNET_CONTROL->ETH_MODE_f.RST0 = 0;
+
+   //Perform a software reset
+   FM4_ETHERNET_MAC0->BMR_f.SWR = 1;
+   //Wait for the reset to complete
+   while(FM4_ETHERNET_MAC0->BMR_f.SWR);
+
+   //Ensure that ongoing AHB transactions are complete
+   while(FM4_ETHERNET_MAC0->AHBSR_f.AHBS);
+
+   //Adjust MDC clock range depending on HCLK frequency
+   FM4_ETHERNET_MAC0->GAR_f.CR = 5;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Use default MAC configuration
+   FM4_ETHERNET_MAC0->MCR = 0;
+   FM4_ETHERNET_MAC0->MCR_f.PS = 1;
+   FM4_ETHERNET_MAC0->MCR_f.DO = 1;
+
+   //Set the MAC address
+   FM4_ETHERNET_MAC0->MAR0L = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   FM4_ETHERNET_MAC0->MAR0H = interface->macAddr.w[2];
+
+   //Initialize hash table
+   FM4_ETHERNET_MAC0->MHTRL = 0;
+   FM4_ETHERNET_MAC0->MHTRH = 0;
+
+   //Configure the receive filter
+   FM4_ETHERNET_MAC0->MFFR = 0;
+   FM4_ETHERNET_MAC0->MFFR_f.HPF = 1;
+   FM4_ETHERNET_MAC0->MFFR_f.HMC = 1;
+
+   //Disable flow control
+   FM4_ETHERNET_MAC0->FCR = 0;
+
+   //Enable store and forward mode
+   FM4_ETHERNET_MAC0->OMR = 0;
+   FM4_ETHERNET_MAC0->OMR_f.RSF = 1;
+   FM4_ETHERNET_MAC0->OMR_f.TSF = 1;
+
+   //Configure DMA bus mode
+   FM4_ETHERNET_MAC0->BMR = 0;
+   FM4_ETHERNET_MAC0->BMR_f.TXPR = 0;
+   FM4_ETHERNET_MAC0->BMR_f.MB = 1;
+   FM4_ETHERNET_MAC0->BMR_f.AAL = 0;
+   FM4_ETHERNET_MAC0->BMR_f._8XPBL = 0;
+   FM4_ETHERNET_MAC0->BMR_f.USP = 1;
+   FM4_ETHERNET_MAC0->BMR_f.RPBL = 1;
+   FM4_ETHERNET_MAC0->BMR_f.FB = 0;
+   FM4_ETHERNET_MAC0->BMR_f.PR = 0;
+   FM4_ETHERNET_MAC0->BMR_f.PBL = 1;
+   FM4_ETHERNET_MAC0->BMR_f.ATDS = 1;
+   FM4_ETHERNET_MAC0->BMR_f.DSL = 0;
+   FM4_ETHERNET_MAC0->BMR_f.DA = 0;
+
+   //Initialize DMA descriptor lists
+   fm4EthInitDmaDesc(interface);
+
+   //Prevent interrupts from being generated when statistic counters reach
+   //half their maximum value
+   FM4_ETHERNET_MAC0->mmc_intr_mask_tx = 0xFFFFFFFF;
+   FM4_ETHERNET_MAC0->mmc_intr_mask_rx = 0xFFFFFFFF;
+   FM4_ETHERNET_MAC0->mmc_ipc_intr_mask_rx = 0xFFFFFFFF;
+
+   //Disable MAC interrupts
+   bFM4_ETHERNET_MAC0_IMR_LPIIM = 1;
+   bFM4_ETHERNET_MAC0_IMR_TSIM = 1;
+   bFM4_ETHERNET_MAC0_IMR_PIM = 1;
+   bFM4_ETHERNET_MAC0_IMR_RGIM = 1;
+
+   //Enable the desired DMA interrupts
+   bFM4_ETHERNET_MAC0_IER_NIE = 1;
+   bFM4_ETHERNET_MAC0_IER_RIE = 1;
+   bFM4_ETHERNET_MAC0_IER_TIE = 1;
+
+   //Set priority grouping (4 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(FM4_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure Ethernet interrupt priority
+   NVIC_SetPriority(ETHER0_IRQn, NVIC_EncodePriority(FM4_ETH_IRQ_PRIORITY_GROUPING,
+      FM4_ETH_IRQ_GROUP_PRIORITY, FM4_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable MAC transmission and reception
+   bFM4_ETHERNET_MAC0_MCR_TE = 1;
+   bFM4_ETHERNET_MAC0_MCR_RE = 1;
+
+   //Enable DMA transmission and reception
+   bFM4_ETHERNET_MAC0_OMR_ST = 1;
+   bFM4_ETHERNET_MAC0_OMR_SR = 1;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//SK-FM4-176L-S6E2CC-ETH evaluation board?
+#if defined(USE_SK_FM4_176L_S6E2CC_ETH)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void fm4EthInitGpio(NetInterface *interface)
+{
+   //Select MII interface mode
+   FM4_ETHERNET_CONTROL->ETH_MODE_f.IFMODE = 0;
+
+   //Configure E_RXER (PC0)
+   FM4_GPIO->PFRC_f.P0 = 1;
+   //Configure E_RX03 (PC1)
+   FM4_GPIO->PFRC_f.P1 = 1;
+   //Configure E_RX02 (PC2)
+   FM4_GPIO->PFRC_f.P2 = 1;
+   //Configure E_RX01 (PC3)
+   FM4_GPIO->PFRC_f.P3 = 1;
+   //Configure E_RX00 (PC4)
+   FM4_GPIO->PFRC_f.P4 = 1;
+   //Configure E_RXDV (PC5)
+   FM4_GPIO->PFRC_f.P5 = 1;
+   //Configure E_MDIO (PC6)
+   FM4_GPIO->PFRC_f.P6 = 1;
+   //Configure E_MDC (PC7)
+   FM4_GPIO->PFRC_f.P7 = 1;
+   //Configure E_RXCK_REFCK (PC8)
+   FM4_GPIO->PFRC_f.P8 = 1;
+   //Configure E_COL (PC9)
+   FM4_GPIO->PFRC_f.P9 = 1;
+   //Configure E_CRS (PCA)
+   FM4_GPIO->PFRC_f.PA = 1;
+   //Configure E_TCK (PCC)
+   FM4_GPIO->PFRC_f.PC = 1;
+   //Configure E_TXER (PCD)
+   FM4_GPIO->PFRC_f.PD = 1;
+   //Configure E_TX03 (PCE)
+   FM4_GPIO->PFRC_f.PE = 1;
+   //Configure E_TX02 (PCF)
+   FM4_GPIO->PFRC_f.PF = 1;
+   //Configure E_TX01 (PD0)
+   FM4_GPIO->PFRD_f.P0 = 1;
+   //Configure E_TX00 (PD1)
+   FM4_GPIO->PFRD_f.P1 = 1;
+   //Configure E_TXEN (PD2)
+   FM4_GPIO->PFRD_f.P2 = 1;
+
+   //Peripheral assignment
+   FM4_GPIO->EPFR14_f.E_TD0E = 1;
+   FM4_GPIO->EPFR14_f.E_TD1E = 1;
+   FM4_GPIO->EPFR14_f.E_TE0E = 1;
+   FM4_GPIO->EPFR14_f.E_TE1E = 1;
+   FM4_GPIO->EPFR14_f.E_MC0E = 1;
+   FM4_GPIO->EPFR14_f.E_MC1B = 1;
+   FM4_GPIO->EPFR14_f.E_MD0B = 1;
+   FM4_GPIO->EPFR14_f.E_MD1B = 1;
+   FM4_GPIO->EPFR14_f.E_SPLC = 1;
+
+   //Configure PHY_RST as an output
+   FM4_GPIO->PFR6_f.PA = 0;
+   FM4_GPIO->DDR6_f.PA = 1;
+
+   //Reset PHY transceiver
+   FM4_GPIO->PDOR6_f.PA = 0;
+   sleep(10);
+
+   //Take the PHY transceiver out of reset
+   FM4_GPIO->PDOR6_f.PA = 1;
+   sleep(10);
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void fm4EthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX DMA descriptor list
+   for(i = 0; i < FM4_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Use chain structure rather than ring structure
+      txDmaDesc[i].tdes0 = ETH_TDES0_IC | ETH_TDES0_TCH;
+      //Initialize transmit buffer size
+      txDmaDesc[i].tdes1 = 0;
+      //Transmit buffer address
+      txDmaDesc[i].tdes2 = (uint32_t) txBuffer[i];
+      //Next descriptor address
+      txDmaDesc[i].tdes3 = (uint32_t) &txDmaDesc[i + 1];
+      //Reserved fields
+      txDmaDesc[i].tdes4 = 0;
+      txDmaDesc[i].tdes5 = 0;
+      //Transmit frame time stamp
+      txDmaDesc[i].tdes6 = 0;
+      txDmaDesc[i].tdes7 = 0;
+   }
+
+   //The last descriptor is chained to the first entry
+   txDmaDesc[i - 1].tdes3 = (uint32_t) &txDmaDesc[0];
+   //Point to the very first descriptor
+   txCurDmaDesc = &txDmaDesc[0];
+
+   //Initialize RX DMA descriptor list
+   for(i = 0; i < FM4_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the DMA
+      rxDmaDesc[i].rdes0 = ETH_RDES0_OWN;
+      //Use chain structure rather than ring structure
+      rxDmaDesc[i].rdes1 = ETH_RDES1_RCH | (FM4_ETH_RX_BUFFER_SIZE & ETH_RDES1_RBS1);
+      //Receive buffer address
+      rxDmaDesc[i].rdes2 = (uint32_t) rxBuffer[i];
+      //Next descriptor address
+      rxDmaDesc[i].rdes3 = (uint32_t) &rxDmaDesc[i + 1];
+      //Extended status
+      rxDmaDesc[i].rdes4 = 0;
+      //Reserved field
+      rxDmaDesc[i].rdes5 = 0;
+      //Receive frame time stamp
+      rxDmaDesc[i].rdes6 = 0;
+      rxDmaDesc[i].rdes7 = 0;
+   }
+
+   //The last descriptor is chained to the first entry
+   rxDmaDesc[i - 1].rdes3 = (uint32_t) &rxDmaDesc[0];
+   //Point to the very first descriptor
+   rxCurDmaDesc = &rxDmaDesc[0];
+
+   //Start location of the TX descriptor list
+   FM4_ETHERNET_MAC0->TDLAR = (uint32_t) txDmaDesc;
+   //Start location of the RX descriptor list
+   FM4_ETHERNET_MAC0->RDLAR = (uint32_t) rxDmaDesc;
+}
+
+
+/**
+ * @brief Spansion FM4 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void fm4EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void fm4EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(ETHER0_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void fm4EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(ETHER0_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief Spansion FM4 Ethernet MAC interrupt service routine
+ **/
+
+void ETHER0_IRQHandler(void)
+{
+   bool_t flag;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //A packet has been transmitted?
+   if(bFM4_ETHERNET_MAC0_SR_TI)
+   {
+      //Clear TI interrupt flag
+      bFM4_ETHERNET_MAC0_SR_TI = 1;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(bFM4_ETHERNET_MAC0_SR_RI)
+   {
+      //Disable RIE interrupt
+      bFM4_ETHERNET_MAC0_IER_RIE = 0;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Clear NIS interrupt flag
+   bFM4_ETHERNET_MAC0_SR_NIS = 1;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief Spansion FM4 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void fm4EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(bFM4_ETHERNET_MAC0_SR_RI)
+   {
+      //Clear interrupt flag
+      bFM4_ETHERNET_MAC0_SR_RI = 1;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = fm4EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable DMA interrupts
+   bFM4_ETHERNET_MAC0_IER_NIE = 1;
+   bFM4_ETHERNET_MAC0_IER_RIE = 1;
+   bFM4_ETHERNET_MAC0_IER_TIE = 1;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t fm4EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > FM4_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurDmaDesc->tdes0 & ETH_TDES0_OWN)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txCurDmaDesc->tdes2, buffer, offset, length);
+
+   //Write the number of bytes to send
+   txCurDmaDesc->tdes1 = length & ETH_TDES1_TBS1;
+   //Set LS and FS flags as the data fits in a single buffer
+   txCurDmaDesc->tdes0 |= ETH_TDES0_LS | ETH_TDES0_FS;
+   //Give the ownership of the descriptor to the DMA
+   txCurDmaDesc->tdes0 |= ETH_TDES0_OWN;
+
+   //Clear TU flag to resume processing
+   bFM4_ETHERNET_MAC0_SR_TU = 1;
+   //Instruct the DMA to poll the transmit descriptor list
+   FM4_ETHERNET_MAC0->TPDR = 0;
+
+   //Point to the next descriptor in the list
+   txCurDmaDesc = (Fm4TxDmaDesc *) txCurDmaDesc->tdes3;
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t fm4EthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_OWN))
+   {
+      //FS and LS flags should be set
+      if((rxCurDmaDesc->rdes0 & ETH_RDES0_FS) && (rxCurDmaDesc->rdes0 & ETH_RDES0_LS))
+      {
+         //Make sure no error occurred
+         if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_ES))
+         {
+            //Retrieve the length of the frame
+            n = (rxCurDmaDesc->rdes0 & ETH_RDES0_FL) >> 16;
+            //Limit the number of data to read
+            n = MIN(n, FM4_ETH_RX_BUFFER_SIZE);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, (uint8_t *) rxCurDmaDesc->rdes2, n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Give the ownership of the descriptor back to the DMA
+      rxCurDmaDesc->rdes0 = ETH_RDES0_OWN;
+      //Point to the next descriptor in the list
+      rxCurDmaDesc = (Fm4RxDmaDesc *) rxCurDmaDesc->rdes3;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Clear RU flag to resume processing
+   bFM4_ETHERNET_MAC0_SR_RU = 1;
+   //Instruct the DMA to poll the receive descriptor list
+   FM4_ETHERNET_MAC0->RPDR = 0;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t fm4EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating Spansion FM4 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = fm4EthCalcCrc(&entry->addr, sizeof(MacAddr));
+
+         //The upper 6 bits in the CRC register are used to index the
+         //contents of the hash table
+         k = (crc >> 26) & 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   FM4_ETHERNET_MAC0->MHTRL = hashTable[0];
+   FM4_ETHERNET_MAC0->MHTRH = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  MACHTLR = %08" PRIX32 "\r\n", FM4_ETHERNET_MAC0->MHTRL);
+   TRACE_DEBUG("  MACHTHR = %08" PRIX32 "\r\n", FM4_ETHERNET_MAC0->MHTRH);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t fm4EthUpdateMacConfig(NetInterface *interface)
+{
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      bFM4_ETHERNET_MAC0_MCR_FES = 1;
+   else
+      bFM4_ETHERNET_MAC0_MCR_FES = 0;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      bFM4_ETHERNET_MAC0_MCR_DM = 1;
+   else
+      bFM4_ETHERNET_MAC0_MCR_DM = 0;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void fm4EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   //Set up a write operation
+   FM4_ETHERNET_MAC0->GAR_f.GW = 1;
+   //PHY address
+   FM4_ETHERNET_MAC0->GAR_f.PA = phyAddr;
+   //Register address
+   FM4_ETHERNET_MAC0->GAR_f.GR = regAddr;
+
+   //Data to be written in the PHY register
+   FM4_ETHERNET_MAC0->GDR_f.GD = data;
+
+   //Start a write operation
+   FM4_ETHERNET_MAC0->GAR_f.GB = 1;
+   //Wait for the write to complete
+   while(FM4_ETHERNET_MAC0->GAR_f.GB);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t fm4EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   //Set up a read operation
+   FM4_ETHERNET_MAC0->GAR_f.GW = 0;
+   //PHY address
+   FM4_ETHERNET_MAC0->GAR_f.PA = phyAddr;
+   //Register address
+   FM4_ETHERNET_MAC0->GAR_f.GR = regAddr;
+
+   //Start a read operation
+   FM4_ETHERNET_MAC0->GAR_f.GB = 1;
+   //Wait for the read to complete
+   while(FM4_ETHERNET_MAC0->GAR_f.GB);
+
+   //Return PHY register contents
+   return FM4_ETHERNET_MAC0->GDR_f.GD;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t fm4EthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return ~crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/fm4_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,220 @@
+/**
+ * @file fm4_eth.h
+ * @brief Spansion FM4 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _FM4_ETH_H
+#define _FM4_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef FM4_ETH_TX_BUFFER_COUNT
+   #define FM4_ETH_TX_BUFFER_COUNT 3
+#elif (FM4_ETH_TX_BUFFER_COUNT < 1)
+   #error FM4_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef FM4_ETH_TX_BUFFER_SIZE
+   #define FM4_ETH_TX_BUFFER_SIZE 1536
+#elif (FM4_ETH_TX_BUFFER_SIZE != 1536)
+   #error FM4_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef FM4_ETH_RX_BUFFER_COUNT
+   #define FM4_ETH_RX_BUFFER_COUNT 6
+#elif (FM4_ETH_RX_BUFFER_COUNT < 1)
+   #error FM4_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef FM4_ETH_RX_BUFFER_SIZE
+   #define FM4_ETH_RX_BUFFER_SIZE 1536
+#elif (FM4_ETH_RX_BUFFER_SIZE != 1536)
+   #error FM4_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef FM4_ETH_IRQ_PRIORITY_GROUPING
+   #define FM4_ETH_IRQ_PRIORITY_GROUPING 3
+#elif (FM4_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error FM4_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef FM4_ETH_IRQ_GROUP_PRIORITY
+   #define FM4_ETH_IRQ_GROUP_PRIORITY 12
+#elif (FM4_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error FM4_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef FM4_ETH_IRQ_SUB_PRIORITY
+   #define FM4_ETH_IRQ_SUB_PRIORITY 0
+#elif (FM4_ETH_IRQ_SUB_PRIORITY < 0)
+   #error FM4_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//Transmit DMA descriptor flags
+#define ETH_TDES0_OWN    0x80000000
+#define ETH_TDES0_IC     0x40000000
+#define ETH_TDES0_LS     0x20000000
+#define ETH_TDES0_FS     0x10000000
+#define ETH_TDES0_DC     0x08000000
+#define ETH_TDES0_DP     0x04000000
+#define ETH_TDES0_TTSE   0x02000000
+#define ETH_TDES0_CIC    0x00C00000
+#define ETH_TDES0_TER    0x00200000
+#define ETH_TDES0_TCH    0x00100000
+#define ETH_TDES0_TTSS   0x00020000
+#define ETH_TDES0_IHE    0x00010000
+#define ETH_TDES0_ES     0x00008000
+#define ETH_TDES0_JT     0x00004000
+#define ETH_TDES0_FF     0x00002000
+#define ETH_TDES0_IPE    0x00001000
+#define ETH_TDES0_LCA    0x00000800
+#define ETH_TDES0_NC     0x00000400
+#define ETH_TDES0_LCO    0x00000200
+#define ETH_TDES0_EC     0x00000100
+#define ETH_TDES0_VF     0x00000080
+#define ETH_TDES0_CC     0x00000078
+#define ETH_TDES0_ED     0x00000004
+#define ETH_TDES0_UF     0x00000002
+#define ETH_TDES0_DB     0x00000001
+#define ETH_TDES1_TBS2   0x1FFF0000
+#define ETH_TDES1_TBS1   0x00001FFF
+#define ETH_TDES2_B1AP   0xFFFFFFFF
+#define ETH_TDES3_B2AP   0xFFFFFFFF
+#define ETH_TDES6_TTSL   0xFFFFFFFF
+#define ETH_TDES7_TTSH   0xFFFFFFFF
+
+//Receive DMA descriptor flags
+#define ETH_RDES0_OWN    0x80000000
+#define ETH_RDES0_AFM    0x40000000
+#define ETH_RDES0_FL     0x3FFF0000
+#define ETH_RDES0_ES     0x00008000
+#define ETH_RDES0_DE     0x00004000
+#define ETH_RDES0_SAF    0x00002000
+#define ETH_RDES0_LE     0x00001000
+#define ETH_RDES0_OE     0x00000800
+#define ETH_RDES0_VLAN   0x00000400
+#define ETH_RDES0_FS     0x00000200
+#define ETH_RDES0_LS     0x00000100
+#define ETH_RDES0_TS     0x00000080
+#define ETH_RDES0_LCO    0x00000040
+#define ETH_RDES0_FT     0x00000020
+#define ETH_RDES0_RWT    0x00000010
+#define ETH_RDES0_RE     0x00000008
+#define ETH_RDES0_DBE    0x00000004
+#define ETH_RDES0_CE     0x00000002
+#define ETH_RDES0_ESA    0x00000001
+#define ETH_RDES1_DIC    0x80000000
+#define ETH_RDES1_RBS2   0x1FFF0000
+#define ETH_RDES1_RER    0x00008000
+#define ETH_RDES1_RCH    0x00004000
+#define ETH_RDES1_RBS1   0x00001FFF
+#define ETH_RDES2_B1AP  0xFFFFFFFF
+#define ETH_RDES3_B2AP  0xFFFFFFFF
+#define ETH_RDES4_TD     0x00004000
+#define ETH_RDES4_PV     0x00002000
+#define ETH_RDES4_PFT    0x00001000
+#define ETH_RDES4_MT     0x00000F00
+#define ETH_RDES4_IP6R   0x00000080
+#define ETH_RDES4_IP4R   0x00000040
+#define ETH_RDES4_IPCB   0x00000020
+#define ETH_RDES4_IPE    0x00000010
+#define ETH_RDES4_IPHE   0x00000008
+#define ETH_RDES4_IPT    0x00000007
+#define ETH_RDES6_RTSL   0xFFFFFFFF
+#define ETH_RDES7_RTSH   0xFFFFFFFF
+
+
+/**
+ * @brief Enhanced TX DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t tdes0;
+   uint32_t tdes1;
+   uint32_t tdes2;
+   uint32_t tdes3;
+   uint32_t tdes4;
+   uint32_t tdes5;
+   uint32_t tdes6;
+   uint32_t tdes7;
+} Fm4TxDmaDesc;
+
+
+/**
+ * @brief Enhanced RX DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t rdes0;
+   uint32_t rdes1;
+   uint32_t rdes2;
+   uint32_t rdes3;
+   uint32_t rdes4;
+   uint32_t rdes5;
+   uint32_t rdes6;
+   uint32_t rdes7;
+} Fm4RxDmaDesc;
+
+
+//Spansion FM4 Ethernet MAC driver
+extern const NicDriver fm4EthDriver;
+
+//Spansion FM4 Ethernet MAC related functions
+error_t fm4EthInit(NetInterface *interface);
+void fm4EthInitGpio(NetInterface *interface);
+void fm4EthInitDmaDesc(NetInterface *interface);
+
+void fm4EthTick(NetInterface *interface);
+
+void fm4EthEnableIrq(NetInterface *interface);
+void fm4EthDisableIrq(NetInterface *interface);
+void fm4EthEventHandler(NetInterface *interface);
+
+error_t fm4EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t fm4EthReceivePacket(NetInterface *interface);
+
+error_t fm4EthSetMulticastFilter(NetInterface *interface);
+error_t fm4EthUpdateMacConfig(NetInterface *interface);
+
+void fm4EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t fm4EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t fm4EthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8031.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,289 @@
+/**
+ * @file ksz8031.c
+ * @brief KSZ8031 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/ksz8031.h"
+#include "debug.h"
+
+
+/**
+ * @brief KSZ8031 Ethernet PHY driver
+ **/
+
+const PhyDriver ksz8031PhyDriver =
+{
+   ksz8031Init,
+   ksz8031Tick,
+   ksz8031EnableIrq,
+   ksz8031DisableIrq,
+   ksz8031EventHandler,
+};
+
+
+/**
+ * @brief KSZ8031 PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ksz8031Init(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing KSZ8031...\r\n");
+
+   //Initialize external interrupt line driver
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->init();
+
+   //Reset PHY transceiver
+   ksz8031WritePhyReg(interface, KSZ8031_PHY_REG_BMCR, BMCR_RESET);
+   //Wait for the reset to complete
+   while(ksz8031ReadPhyReg(interface, KSZ8031_PHY_REG_BMCR) & BMCR_RESET);
+
+   //Dump PHY registers for debugging purpose
+   ksz8031DumpPhyReg(interface);
+
+   //The PHY will generate interrupts when link status changes are detected
+   ksz8031WritePhyReg(interface, KSZ8031_PHY_REG_ICSR, ICSR_LINK_DOWN_IE | ICSR_LINK_UP_IE);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief KSZ8031 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8031Tick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //No external interrupt line driver?
+   if(interface->extIntDriver == NULL)
+   {
+      //Read basic status register
+      value = ksz8031ReadPhyReg(interface, KSZ8031_PHY_REG_BMSR);
+      //Retrieve current link state
+      linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+      //Link up event?
+      if(linkState && !interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+      //Link down event?
+      else if(!linkState && interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8031EnableIrq(NetInterface *interface)
+{
+   //Enable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8031DisableIrq(NetInterface *interface)
+{
+   //Disable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief KSZ8031 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8031EventHandler(NetInterface *interface)
+{
+   uint16_t value;
+
+   //Read status register to acknowledge the interrupt
+   value = ksz8031ReadPhyReg(interface, KSZ8031_PHY_REG_ICSR);
+
+   //Link status change?
+   if(value & (ICSR_LINK_DOWN_IF | ICSR_LINK_UP_IF))
+   {
+      //Any link failure condition is latched in the BMSR register... Reading
+      //the register twice will always return the actual link status
+      value = ksz8031ReadPhyReg(interface, KSZ8031_PHY_REG_BMSR);
+      value = ksz8031ReadPhyReg(interface, KSZ8031_PHY_REG_BMSR);
+
+      //Link is up?
+      if(value & BMSR_LINK_STATUS)
+      {
+         //Read PHY control register
+         value = ksz8031ReadPhyReg(interface, KSZ8031_PHY_REG_PHYCON1);
+
+         //Check current operation mode
+         switch(value & PHYCON1_OP_MODE_MASK)
+         {
+         //10BASE-T
+         case PHYCON1_OP_MODE_10BT:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //10BASE-T full-duplex
+         case PHYCON1_OP_MODE_10BT_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //100BASE-TX
+         case PHYCON1_OP_MODE_100BTX:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //100BASE-TX full-duplex
+         case PHYCON1_OP_MODE_100BTX_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //Unknown operation mode
+         default:
+            //Debug message
+            TRACE_WARNING("Invalid Duplex mode\r\n");
+            break;
+         }
+
+         //Update link state
+         interface->linkState = TRUE;
+
+         //Adjust MAC configuration parameters for proper operation
+         interface->nicDriver->updateMacConfig(interface);
+      }
+      else
+      {
+         //Update link state
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void ksz8031WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = KSZ8031_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t ksz8031ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = KSZ8031_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8031DumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, ksz8031ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8031.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,269 @@
+/**
+ * @file ksz8031.h
+ * @brief KSZ8031 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _KSZ8031_H
+#define _KSZ8031_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#ifndef KSZ8031_PHY_ADDR
+   #define KSZ8031_PHY_ADDR 0
+#elif (KSZ8031_PHY_ADDR < 0 || KSZ8031_PHY_ADDR > 31)
+   #error KSZ8031_PHY_ADDR parameter is not valid
+#endif
+
+//KSZ8031 registers
+#define KSZ8031_PHY_REG_BMCR        0x00
+#define KSZ8031_PHY_REG_BMSR        0x01
+#define KSZ8031_PHY_REG_PHYIDR1     0x02
+#define KSZ8031_PHY_REG_PHYIDR2     0x03
+#define KSZ8031_PHY_REG_ANAR        0x04
+#define KSZ8031_PHY_REG_ANLPAR      0x05
+#define KSZ8031_PHY_REG_ANER        0x06
+#define KSZ8031_PHY_REG_ANNPTR      0x07
+#define KSZ8031_PHY_REG_LPNPAR      0x08
+#define KSZ8031_PHY_REG_DRCON       0x10
+#define KSZ8031_PHY_REG_AFECON1     0x11
+#define KSZ8031_PHY_REG_RXERCTR     0x15
+#define KSZ8031_PHY_REG_OMSO        0x16
+#define KSZ8031_PHY_REG_OMSS        0x17
+#define KSZ8031_PHY_REG_EXCON       0x18
+#define KSZ8031_PHY_REG_ICSR        0x1B
+#define KSZ8031_PHY_REG_LINKMDCS    0x1D
+#define KSZ8031_PHY_REG_PHYCON1     0x1E
+#define KSZ8031_PHY_REG_PHYCON2     0x1F
+
+//BMCR register
+#define BMCR_RESET                  (1 << 15)
+#define BMCR_LOOPBACK               (1 << 14)
+#define BMCR_SPEED_SEL              (1 << 13)
+#define BMCR_AN_EN                  (1 << 12)
+#define BMCR_POWER_DOWN             (1 << 11)
+#define BMCR_ISOLATE                (1 << 10)
+#define BMCR_RESTART_AN             (1 << 9)
+#define BMCR_DUPLEX_MODE            (1 << 8)
+#define BMCR_COL_TEST               (1 << 7)
+
+//BMSR register
+#define BMSR_100BT4                 (1 << 15)
+#define BMSR_100BTX_FD              (1 << 14)
+#define BMSR_100BTX                 (1 << 13)
+#define BMSR_10BT_FD                (1 << 12)
+#define BMSR_10BT                   (1 << 11)
+#define BMSR_NO_PREAMBLE            (1 << 6)
+#define BMSR_AN_COMPLETE            (1 << 5)
+#define BMSR_REMOTE_FAULT           (1 << 4)
+#define BMSR_AN_ABLE                (1 << 3)
+#define BMSR_LINK_STATUS            (1 << 2)
+#define BMSR_JABBER_DETECT          (1 << 1)
+#define BMSR_EXTENDED_CAP           (1 << 0)
+
+//ANAR register
+#define ANAR_NEXT_PAGE              (1 << 15)
+#define ANAR_REMOTE_FAULT           (1 << 13)
+#define ANAR_PAUSE1                 (1 << 11)
+#define ANAR_PAUSE0                 (1 << 10)
+#define ANAR_100BT4                 (1 << 9)
+#define ANAR_100BTX_FD              (1 << 8)
+#define ANAR_100BTX                 (1 << 7)
+#define ANAR_10BT_FD                (1 << 6)
+#define ANAR_10BT                   (1 << 5)
+#define ANAR_SELECTOR4              (1 << 4)
+#define ANAR_SELECTOR3              (1 << 3)
+#define ANAR_SELECTOR2              (1 << 2)
+#define ANAR_SELECTOR1              (1 << 1)
+#define ANAR_SELECTOR0              (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NEXT_PAGE            (1 << 15)
+#define ANLPAR_LP_ACK               (1 << 14)
+#define ANLPAR_REMOTE_FAULT         (1 << 13)
+#define ANLPAR_PAUSE1               (1 << 11)
+#define ANLPAR_PAUSE0               (1 << 10)
+#define ANLPAR_100BT4               (1 << 9)
+#define ANLPAR_100BTX_FD            (1 << 8)
+#define ANLPAR_100BTX               (1 << 7)
+#define ANLPAR_10BT_FD              (1 << 6)
+#define ANLPAR_10BT                 (1 << 5)
+#define ANLPAR_SELECTOR4            (1 << 4)
+#define ANLPAR_SELECTOR3            (1 << 3)
+#define ANLPAR_SELECTOR2            (1 << 2)
+#define ANLPAR_SELECTOR1            (1 << 1)
+#define ANLPAR_SELECTOR0            (1 << 0)
+
+//ANER register
+#define ANER_PAR_DET_FAULT          (1 << 4)
+#define ANER_LP_NEXT_PAGE_ABLE      (1 << 3)
+#define ANER_NEXT_PAGE_ABLE         (1 << 2)
+#define ANER_PAGE_RECEIVED          (1 << 1)
+#define ANER_LP_AN_ABLE             (1 << 0)
+
+//ANNPTR register
+#define ANNPTR_NEXT_PAGE            (1 << 15)
+#define ANNPTR_MSG_PAGE             (1 << 13)
+#define ANNPTR_ACK2                 (1 << 12)
+#define ANNPTR_TOGGLE               (1 << 11)
+#define ANNPTR_MESSAGE10            (1 << 10)
+#define ANNPTR_MESSAGE9             (1 << 9)
+#define ANNPTR_MESSAGE8             (1 << 8)
+#define ANNPTR_MESSAGE7             (1 << 7)
+#define ANNPTR_MESSAGE6             (1 << 6)
+#define ANNPTR_MESSAGE5             (1 << 5)
+#define ANNPTR_MESSAGE4             (1 << 4)
+#define ANNPTR_MESSAGE3             (1 << 3)
+#define ANNPTR_MESSAGE2             (1 << 2)
+#define ANNPTR_MESSAGE1             (1 << 1)
+#define ANNPTR_MESSAGE0             (1 << 0)
+
+//LPNPAR register
+#define LPNPAR_NEXT_PAGE            (1 << 15)
+#define LPNPAR_ACK                  (1 << 14)
+#define LPNPAR_MSG_PAGE             (1 << 13)
+#define LPNPAR_ACK2                 (1 << 12)
+#define LPNPAR_TOGGLE               (1 << 11)
+#define LPNPAR_MESSAGE10            (1 << 10)
+#define LPNPAR_MESSAGE9             (1 << 9)
+#define LPNPAR_MESSAGE8             (1 << 8)
+#define LPNPAR_MESSAGE7             (1 << 7)
+#define LPNPAR_MESSAGE6             (1 << 6)
+#define LPNPAR_MESSAGE5             (1 << 5)
+#define LPNPAR_MESSAGE4             (1 << 4)
+#define LPNPAR_MESSAGE3             (1 << 3)
+#define LPNPAR_MESSAGE2             (1 << 2)
+#define LPNPAR_MESSAGE1             (1 << 1)
+#define LPNPAR_MESSAGE0             (1 << 0)
+
+//DRCON register
+#define DRCON_PLL_OFF               (1 << 4)
+
+//AFECON1 register
+#define AFECON1_SLOW_OSC_MODE_EN    (1 << 5)
+
+//OMSO register
+#define OMSO_RMII_BTB_OVERRIDE      (1 << 6)
+#define OMSO_NAND_TREE_OVERRIDE     (1 << 5)
+#define OMSO_RMII_OVERRIDE          (1 << 1)
+
+//OMSS register
+#define OMSS_PHYAD2                 (1 << 15)
+#define OMSS_PHYAD1                 (1 << 14)
+#define OMSS_PHYAD0                 (1 << 13)
+#define OMSS_RMII_STATUS            (1 << 1)
+
+//EXCON register
+#define EXCON_EDPD_DIS              (1 << 11)
+
+//ICSR register
+#define ICSR_JABBER_IE              (1 << 15)
+#define ICSR_RECEIVE_ERROR_IE       (1 << 14)
+#define ICSR_PAGE_RECEIVED_IE       (1 << 13)
+#define ICSR_PAR_DET_FAULT_IE       (1 << 12)
+#define ICSR_LP_ACK_IE              (1 << 11)
+#define ICSR_LINK_DOWN_IE           (1 << 10)
+#define ICSR_REMOTE_FAULT_IE        (1 << 9)
+#define ICSR_LINK_UP_IE             (1 << 8)
+#define ICSR_JABBER_IF              (1 << 7)
+#define ICSR_RECEIVE_ERROR_IF       (1 << 6)
+#define ICSR_PAGE_RECEIVED_IF       (1 << 5)
+#define ICSR_PAR_DET_FAULT_IF       (1 << 4)
+#define ICSR_LP_ACK_IF              (1 << 3)
+#define ICSR_LINK_DOWN_IF           (1 << 2)
+#define ICSR_REMOTE_FAULT_IF        (1 << 1)
+#define ICSR_LINK_UP_IF             (1 << 0)
+
+//LINKMDCS register
+#define LINKMDCS_CABLE_DIAG_EN      (1 << 15)
+#define LINKMDCS_CABLE_DIAG_RES1    (1 << 14)
+#define LINKMDCS_CABLE_DIAG_RES0    (1 << 13)
+#define LINKMDCS_SHORT_CABLE        (1 << 12)
+#define LINKMDCS_CABLE_FAULT_CNT8   (1 << 8)
+#define LINKMDCS_CABLE_FAULT_CNT7   (1 << 7)
+#define LINKMDCS_CABLE_FAULT_CNT6   (1 << 6)
+#define LINKMDCS_CABLE_FAULT_CNT5   (1 << 5)
+#define LINKMDCS_CABLE_FAULT_CNT4   (1 << 4)
+#define LINKMDCS_CABLE_FAULT_CNT3   (1 << 3)
+#define LINKMDCS_CABLE_FAULT_CNT2   (1 << 2)
+#define LINKMDCS_CABLE_FAULT_CNT1   (1 << 1)
+#define LINKMDCS_CABLE_FAULT_CNT0   (1 << 0)
+
+//PHYCON1 register
+#define PHYCON1_PAUSE_EN            (1 << 9)
+#define PHYCON1_LINK_STATUS         (1 << 8)
+#define PHYCON1_POL_STATUS          (1 << 7)
+#define PHYCON1_MDIX_STATE          (1 << 5)
+#define PHYCON1_ENERGY_DETECT       (1 << 4)
+#define PHYCON1_ISOLATE             (1 << 3)
+#define PHYCON1_OP_MODE2            (1 << 2)
+#define PHYCON1_OP_MODE1            (1 << 1)
+#define PHYCON1_OP_MODE0            (1 << 0)
+
+//Operation mode indication
+#define PHYCON1_OP_MODE_MASK        (7 << 0)
+#define PHYCON1_OP_MODE_AN          (0 << 0)
+#define PHYCON1_OP_MODE_10BT        (1 << 0)
+#define PHYCON1_OP_MODE_100BTX      (2 << 0)
+#define PHYCON1_OP_MODE_10BT_FD     (5 << 0)
+#define PHYCON1_OP_MODE_100BTX_FD   (6 << 0)
+
+//PHYCON2 register
+#define PHYCON2_HP_MDIX             (1 << 15)
+#define PHYCON2_MDIX_SEL            (1 << 14)
+#define PHYCON2_PAIR_SWAP_DIS       (1 << 13)
+#define PHYCON2_FORCE_LINK          (1 << 11)
+#define PHYCON2_POWER_SAVING        (1 << 10)
+#define PHYCON2_INT_LEVEL           (1 << 9)
+#define PHYCON2_JABBER_EN           (1 << 8)
+#define PHYCON2_RMII_REF_CLK_SEL    (1 << 7)
+#define PHYCON2_LED_MODE1           (1 << 5)
+#define PHYCON2_LED_MODE0           (1 << 4)
+#define PHYCON2_TX_DIS              (1 << 3)
+#define PHYCON2_REMOTE_LOOPBACK     (1 << 2)
+#define PHYCON2_SCRAMBLER_DIS       (1 << 0)
+
+//KSZ8031 Ethernet PHY driver
+extern const PhyDriver ksz8031PhyDriver;
+
+//KSZ8031 related functions
+error_t ksz8031Init(NetInterface *interface);
+
+void ksz8031Tick(NetInterface *interface);
+
+void ksz8031EnableIrq(NetInterface *interface);
+void ksz8031DisableIrq(NetInterface *interface);
+
+void ksz8031EventHandler(NetInterface *interface);
+
+void ksz8031WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t ksz8031ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void ksz8031DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8041.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,289 @@
+/**
+ * @file ksz8041.c
+ * @brief KSZ8041 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/ksz8041.h"
+#include "debug.h"
+
+
+/**
+ * @brief KSZ8041 Ethernet PHY driver
+ **/
+
+const PhyDriver ksz8041PhyDriver =
+{
+   ksz8041Init,
+   ksz8041Tick,
+   ksz8041EnableIrq,
+   ksz8041DisableIrq,
+   ksz8041EventHandler,
+};
+
+
+/**
+ * @brief KSZ8041 PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ksz8041Init(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing KSZ8041...\r\n");
+
+   //Initialize external interrupt line driver
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->init();
+
+   //Reset PHY transceiver
+   ksz8041WritePhyReg(interface, KSZ8041_PHY_REG_BMCR, BMCR_RESET);
+   //Wait for the reset to complete
+   while(ksz8041ReadPhyReg(interface, KSZ8041_PHY_REG_BMCR) & BMCR_RESET);
+
+   //Dump PHY registers for debugging purpose
+   ksz8041DumpPhyReg(interface);
+
+   //The PHY will generate interrupts when link status changes are detected
+   ksz8041WritePhyReg(interface, KSZ8041_PHY_REG_ICSR, ICSR_LINK_DOWN_IE | ICSR_LINK_UP_IE);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief KSZ8041 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8041Tick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //No external interrupt line driver?
+   if(interface->extIntDriver == NULL)
+   {
+      //Read basic status register
+      value = ksz8041ReadPhyReg(interface, KSZ8041_PHY_REG_BMSR);
+      //Retrieve current link state
+      linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+      //Link up event?
+      if(linkState && !interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+      //Link down event?
+      else if(!linkState && interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8041EnableIrq(NetInterface *interface)
+{
+   //Enable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8041DisableIrq(NetInterface *interface)
+{
+   //Disable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief KSZ8041 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8041EventHandler(NetInterface *interface)
+{
+   uint16_t value;
+
+   //Read status register to acknowledge the interrupt
+   value = ksz8041ReadPhyReg(interface, KSZ8041_PHY_REG_ICSR);
+
+   //Link status change?
+   if(value & (ICSR_LINK_DOWN_IF | ICSR_LINK_UP_IF))
+   {
+      //Any link failure condition is latched in the BMSR register... Reading
+      //the register twice will always return the actual link status
+      value = ksz8041ReadPhyReg(interface, KSZ8041_PHY_REG_BMSR);
+      value = ksz8041ReadPhyReg(interface, KSZ8041_PHY_REG_BMSR);
+
+      //Link is up?
+      if(value & BMSR_LINK_STATUS)
+      {
+         //Read PHY control register
+         value = ksz8041ReadPhyReg(interface, KSZ8041_PHY_REG_PHYCON2);
+
+         //Check current operation mode
+         switch(value & PHYCON2_OP_MODE_MASK)
+         {
+         //10BASE-T
+         case PHYCON2_OP_MODE_10BT:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //10BASE-T full-duplex
+         case PHYCON2_OP_MODE_10BT_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //100BASE-TX
+         case PHYCON2_OP_MODE_100BTX:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //100BASE-TX full-duplex
+         case PHYCON2_OP_MODE_100BTX_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //Unknown operation mode
+         default:
+            //Debug message
+            TRACE_WARNING("Invalid Duplex mode\r\n");
+            break;
+         }
+
+         //Update link state
+         interface->linkState = TRUE;
+
+         //Adjust MAC configuration parameters for proper operation
+         interface->nicDriver->updateMacConfig(interface);
+      }
+      else
+      {
+         //Update link state
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void ksz8041WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = KSZ8041_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t ksz8041ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = KSZ8041_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8041DumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, ksz8041ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8041.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,232 @@
+/**
+ * @file ksz8041.h
+ * @brief KSZ8041 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _KSZ8041_H
+#define _KSZ8041_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#ifndef KSZ8041_PHY_ADDR
+   #define KSZ8041_PHY_ADDR 1
+#elif (KSZ8041_PHY_ADDR < 0 || KSZ8041_PHY_ADDR > 31)
+   #error KSZ8041_PHY_ADDR parameter is not valid
+#endif
+
+//KSZ8041 registers
+#define KSZ8041_PHY_REG_BMCR           0x00
+#define KSZ8041_PHY_REG_BMSR           0x01
+#define KSZ8041_PHY_REG_PHYIDR1        0x02
+#define KSZ8041_PHY_REG_PHYIDR2        0x03
+#define KSZ8041_PHY_REG_ANAR           0x04
+#define KSZ8041_PHY_REG_ANLPAR         0x05
+#define KSZ8041_PHY_REG_ANER           0x06
+#define KSZ8041_PHY_REG_ANNPTR         0x07
+#define KSZ8041_PHY_REG_LPNPAR         0x08
+#define KSZ8041_PHY_REG_MIICON         0x14
+#define KSZ8041_PHY_REG_RXERCTR        0x15
+#define KSZ8041_PHY_REG_ICSR           0x1B
+#define KSZ8041_PHY_REG_PHYCON1        0x1E
+#define KSZ8041_PHY_REG_PHYCON2        0x1F
+
+//BMCR register
+#define BMCR_RESET                     (1 << 15)
+#define BMCR_LOOPBACK                  (1 << 14)
+#define BMCR_SPEED_SEL                 (1 << 13)
+#define BMCR_AN_EN                     (1 << 12)
+#define BMCR_POWER_DOWN                (1 << 11)
+#define BMCR_ISOLATE                   (1 << 10)
+#define BMCR_RESTART_AN                (1 << 9)
+#define BMCR_DUPLEX_MODE               (1 << 8)
+#define BMCR_COL_TEST                  (1 << 7)
+#define BMCR_TX_DIS                    (1 << 0)
+
+//BMSR register
+#define BMSR_100BT4                    (1 << 15)
+#define BMSR_100BTX_FD                 (1 << 14)
+#define BMSR_100BTX                    (1 << 13)
+#define BMSR_10BT_FD                   (1 << 12)
+#define BMSR_10BT                      (1 << 11)
+#define BMSR_NO_PREAMBLE               (1 << 6)
+#define BMSR_AN_COMPLETE               (1 << 5)
+#define BMSR_REMOTE_FAULT              (1 << 4)
+#define BMSR_AN_ABLE                   (1 << 3)
+#define BMSR_LINK_STATUS               (1 << 2)
+#define BMSR_JABBER_DETECT             (1 << 1)
+#define BMSR_EXTENDED_CAP              (1 << 0)
+
+//ANAR register
+#define ANAR_NEXT_PAGE                 (1 << 15)
+#define ANAR_REMOTE_FAULT              (1 << 13)
+#define ANAR_PAUSE1                    (1 << 11)
+#define ANAR_PAUSE0                    (1 << 10)
+#define ANAR_100BT4                    (1 << 9)
+#define ANAR_100BTX_FD                 (1 << 8)
+#define ANAR_100BTX                    (1 << 7)
+#define ANAR_10BT_FD                   (1 << 6)
+#define ANAR_10BT                      (1 << 5)
+#define ANAR_SELECTOR4                 (1 << 4)
+#define ANAR_SELECTOR3                 (1 << 3)
+#define ANAR_SELECTOR2                 (1 << 2)
+#define ANAR_SELECTOR1                 (1 << 1)
+#define ANAR_SELECTOR0                 (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NEXT_PAGE               (1 << 15)
+#define ANLPAR_LP_ACK                  (1 << 14)
+#define ANLPAR_REMOTE_FAULT            (1 << 13)
+#define ANLPAR_PAUSE1                  (1 << 11)
+#define ANLPAR_PAUSE0                  (1 << 10)
+#define ANLPAR_100BT4                  (1 << 9)
+#define ANLPAR_100BTX_FD               (1 << 8)
+#define ANLPAR_100BTX                  (1 << 7)
+#define ANLPAR_10BT_FD                 (1 << 6)
+#define ANLPAR_10BT                    (1 << 5)
+#define ANLPAR_SELECTOR4               (1 << 4)
+#define ANLPAR_SELECTOR3               (1 << 3)
+#define ANLPAR_SELECTOR2               (1 << 2)
+#define ANLPAR_SELECTOR1               (1 << 1)
+#define ANLPAR_SELECTOR0               (1 << 0)
+
+//ANER register
+#define ANER_PAR_DET_FAULT             (1 << 4)
+#define ANER_LP_NEXT_PAGE_ABLE         (1 << 3)
+#define ANER_NEXT_PAGE_ABLE            (1 << 2)
+#define ANER_PAGE_RECEIVED             (1 << 1)
+#define ANER_LP_AN_ABLE                (1 << 0)
+
+//ANNPTR register
+#define ANNPTR_NEXT_PAGE               (1 << 15)
+#define ANNPTR_MSG_PAGE                (1 << 13)
+#define ANNPTR_ACK2                    (1 << 12)
+#define ANNPTR_TOGGLE                  (1 << 11)
+#define ANNPTR_MESSAGE10               (1 << 10)
+#define ANNPTR_MESSAGE9                (1 << 9)
+#define ANNPTR_MESSAGE8                (1 << 8)
+#define ANNPTR_MESSAGE7                (1 << 7)
+#define ANNPTR_MESSAGE6                (1 << 6)
+#define ANNPTR_MESSAGE5                (1 << 5)
+#define ANNPTR_MESSAGE4                (1 << 4)
+#define ANNPTR_MESSAGE3                (1 << 3)
+#define ANNPTR_MESSAGE2                (1 << 2)
+#define ANNPTR_MESSAGE1                (1 << 1)
+#define ANNPTR_MESSAGE0                (1 << 0)
+
+//LPNPAR register
+#define LPNPAR_NEXT_PAGE               (1 << 15)
+#define LPNPAR_ACK                     (1 << 14)
+#define LPNPAR_MSG_PAGE                (1 << 13)
+#define LPNPAR_ACK2                    (1 << 12)
+#define LPNPAR_TOGGLE                  (1 << 11)
+#define LPNPAR_MESSAGE10               (1 << 10)
+#define LPNPAR_MESSAGE9                (1 << 9)
+#define LPNPAR_MESSAGE8                (1 << 8)
+#define LPNPAR_MESSAGE7                (1 << 7)
+#define LPNPAR_MESSAGE6                (1 << 6)
+#define LPNPAR_MESSAGE5                (1 << 5)
+#define LPNPAR_MESSAGE4                (1 << 4)
+#define LPNPAR_MESSAGE3                (1 << 3)
+#define LPNPAR_MESSAGE2                (1 << 2)
+#define LPNPAR_MESSAGE1                (1 << 1)
+#define LPNPAR_MESSAGE0                (1 << 0)
+
+//MIICON register
+#define MIICON_100BTX_PREAMBLE_RESTORE (1 << 7)
+#define MIICON_10BT_PREAMBLE_RESTORE   (1 << 6)
+
+//ICSR register
+#define ICSR_JABBER_IE                 (1 << 15)
+#define ICSR_RECEIVE_ERROR_IE          (1 << 14)
+#define ICSR_PAGE_RECEIVED_IE          (1 << 13)
+#define ICSR_PAR_DET_FAULT_IE          (1 << 12)
+#define ICSR_LP_ACK_IE                 (1 << 11)
+#define ICSR_LINK_DOWN_IE              (1 << 10)
+#define ICSR_REMOTE_FAULT_IE           (1 << 9)
+#define ICSR_LINK_UP_IE                (1 << 8)
+#define ICSR_JABBER_IF                 (1 << 7)
+#define ICSR_RECEIVE_ERROR_IF          (1 << 6)
+#define ICSR_PAGE_RECEIVED_IF          (1 << 5)
+#define ICSR_PAR_DET_FAULT_IF          (1 << 4)
+#define ICSR_LP_ACK_IF                 (1 << 3)
+#define ICSR_LINK_DOWN_IF              (1 << 2)
+#define ICSR_REMOTE_FAULT_IF           (1 << 1)
+#define ICSR_LINK_UP_IF                (1 << 0)
+
+//PHYCON1 register
+#define PHYCON1_LED_MODE1              (1 << 15)
+#define PHYCON1_LED_MODE0              (1 << 14)
+#define PHYCON1_POLARITY               (1 << 13)
+#define PHYCON1_MDIX_STATE             (1 << 11)
+#define PHYCON1_REMOTE_LOOPBACK        (1 << 7)
+
+//PHYCON2 register
+#define PHYCON2_HP_MDIX             (1 << 15)
+#define PHYCON2_MDIX_SEL            (1 << 14)
+#define PHYCON2_PAIR_SWAP_DIS       (1 << 13)
+#define PHYCON2_ENERGY_DETECT       (1 << 12)
+#define PHYCON2_FORCE_LINK          (1 << 11)
+#define PHYCON2_POWER_SAVING        (1 << 10)
+#define PHYCON2_INT_LEVEL           (1 << 9)
+#define PHYCON2_JABBER_EN           (1 << 8)
+#define PHYCON2_AN_COMPLETE         (1 << 7)
+#define PHYCON2_PHY_ISOLATE         (1 << 5)
+#define PHYCON2_OP_MODE2            (1 << 4)
+#define PHYCON2_OP_MODE1            (1 << 3)
+#define PHYCON2_OP_MODE0            (1 << 2)
+#define PHYCON2_SQE_TEST_EN         (1 << 1)
+#define PHYCON2_SCRAMBLER_DIS       (1 << 0)
+
+//Operation mode indication
+#define PHYCON2_OP_MODE_MASK        (7 << 2)
+#define PHYCON2_OP_MODE_AN          (0 << 2)
+#define PHYCON2_OP_MODE_10BT        (1 << 2)
+#define PHYCON2_OP_MODE_100BTX      (2 << 2)
+#define PHYCON2_OP_MODE_10BT_FD     (5 << 2)
+#define PHYCON2_OP_MODE_100BTX_FD   (6 << 2)
+
+//KSZ8041 Ethernet PHY driver
+extern const PhyDriver ksz8041PhyDriver;
+
+//KSZ8041 related functions
+error_t ksz8041Init(NetInterface *interface);
+
+void ksz8041Tick(NetInterface *interface);
+
+void ksz8041EnableIrq(NetInterface *interface);
+void ksz8041DisableIrq(NetInterface *interface);
+
+void ksz8041EventHandler(NetInterface *interface);
+
+void ksz8041WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t ksz8041ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void ksz8041DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8051.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,289 @@
+/**
+ * @file ksz8051.c
+ * @brief KSZ8051 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/ksz8051.h"
+#include "debug.h"
+
+
+/**
+ * @brief KSZ8051 Ethernet PHY driver
+ **/
+
+const PhyDriver ksz8051PhyDriver =
+{
+   ksz8051Init,
+   ksz8051Tick,
+   ksz8051EnableIrq,
+   ksz8051DisableIrq,
+   ksz8051EventHandler,
+};
+
+
+/**
+ * @brief KSZ8051 PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ksz8051Init(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing KSZ8051...\r\n");
+
+   //Initialize external interrupt line driver
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->init();
+
+   //Reset PHY transceiver
+   ksz8051WritePhyReg(interface, KSZ8051_PHY_REG_BMCR, BMCR_RESET);
+   //Wait for the reset to complete
+   while(ksz8051ReadPhyReg(interface, KSZ8051_PHY_REG_BMCR) & BMCR_RESET);
+
+   //Dump PHY registers for debugging purpose
+   ksz8051DumpPhyReg(interface);
+
+   //The PHY will generate interrupts when link status changes are detected
+   ksz8051WritePhyReg(interface, KSZ8051_PHY_REG_ICSR, ICSR_LINK_DOWN_IE | ICSR_LINK_UP_IE);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief KSZ8051 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8051Tick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //No external interrupt line driver?
+   if(interface->extIntDriver == NULL)
+   {
+      //Read basic status register
+      value = ksz8051ReadPhyReg(interface, KSZ8051_PHY_REG_BMSR);
+      //Retrieve current link state
+      linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+      //Link up event?
+      if(linkState && !interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+      //Link down event?
+      else if(!linkState && interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8051EnableIrq(NetInterface *interface)
+{
+   //Enable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8051DisableIrq(NetInterface *interface)
+{
+   //Disable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief KSZ8051 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8051EventHandler(NetInterface *interface)
+{
+   uint16_t value;
+
+   //Read status register to acknowledge the interrupt
+   value = ksz8051ReadPhyReg(interface, KSZ8051_PHY_REG_ICSR);
+
+   //Link status change?
+   if(value & (ICSR_LINK_DOWN_IF | ICSR_LINK_UP_IF))
+   {
+      //Any link failure condition is latched in the BMSR register... Reading
+      //the register twice will always return the actual link status
+      value = ksz8051ReadPhyReg(interface, KSZ8051_PHY_REG_BMSR);
+      value = ksz8051ReadPhyReg(interface, KSZ8051_PHY_REG_BMSR);
+
+      //Link is up?
+      if(value & BMSR_LINK_STATUS)
+      {
+         //Read PHY control register
+         value = ksz8051ReadPhyReg(interface, KSZ8051_PHY_REG_PHYCON1);
+
+         //Check current operation mode
+         switch(value & PHYCON1_OP_MODE_MASK)
+         {
+         //10BASE-T
+         case PHYCON1_OP_MODE_10BT:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //10BASE-T full-duplex
+         case PHYCON1_OP_MODE_10BT_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //100BASE-TX
+         case PHYCON1_OP_MODE_100BTX:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //100BASE-TX full-duplex
+         case PHYCON1_OP_MODE_100BTX_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //Unknown operation mode
+         default:
+            //Debug message
+            TRACE_WARNING("Invalid Duplex mode\r\n");
+            break;
+         }
+
+         //Update link state
+         interface->linkState = TRUE;
+
+         //Adjust MAC configuration parameters for proper operation
+         interface->nicDriver->updateMacConfig(interface);
+      }
+      else
+      {
+         //Update link state
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void ksz8051WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = KSZ8051_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t ksz8051ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = KSZ8051_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8051DumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, ksz8051ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8051.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,276 @@
+/**
+ * @file ksz8051.h
+ * @brief KSZ8051 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _KSZ8051_H
+#define _KSZ8051_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#ifndef KSZ8051_PHY_ADDR
+   #define KSZ8051_PHY_ADDR 1
+#elif (KSZ8051_PHY_ADDR < 0 || KSZ8051_PHY_ADDR > 31)
+   #error KSZ8051_PHY_ADDR parameter is not valid
+#endif
+
+//KSZ8051 registers
+#define KSZ8051_PHY_REG_BMCR        0x00
+#define KSZ8051_PHY_REG_BMSR        0x01
+#define KSZ8051_PHY_REG_PHYIDR1     0x02
+#define KSZ8051_PHY_REG_PHYIDR2     0x03
+#define KSZ8051_PHY_REG_ANAR        0x04
+#define KSZ8051_PHY_REG_ANLPAR      0x05
+#define KSZ8051_PHY_REG_ANER        0x06
+#define KSZ8051_PHY_REG_ANNPTR      0x07
+#define KSZ8051_PHY_REG_LPNPAR      0x08
+#define KSZ8051_PHY_REG_AFECON1     0x11
+#define KSZ8051_PHY_REG_RXERCTR     0x15
+#define KSZ8051_PHY_REG_OMSO        0x16
+#define KSZ8051_PHY_REG_OMSS        0x17
+#define KSZ8051_PHY_REG_EXCON       0x18
+#define KSZ8051_PHY_REG_ICSR        0x1B
+#define KSZ8051_PHY_REG_LINKMDCS    0x1D
+#define KSZ8051_PHY_REG_PHYCON1     0x1E
+#define KSZ8051_PHY_REG_PHYCON2     0x1F
+
+//BMCR register
+#define BMCR_RESET                  (1 << 15)
+#define BMCR_LOOPBACK               (1 << 14)
+#define BMCR_SPEED_SEL              (1 << 13)
+#define BMCR_AN_EN                  (1 << 12)
+#define BMCR_POWER_DOWN             (1 << 11)
+#define BMCR_ISOLATE                (1 << 10)
+#define BMCR_RESTART_AN             (1 << 9)
+#define BMCR_DUPLEX_MODE            (1 << 8)
+#define BMCR_COL_TEST               (1 << 7)
+
+//BMSR register
+#define BMSR_100BT4                 (1 << 15)
+#define BMSR_100BTX_FD              (1 << 14)
+#define BMSR_100BTX                 (1 << 13)
+#define BMSR_10BT_FD                (1 << 12)
+#define BMSR_10BT                   (1 << 11)
+#define BMSR_NO_PREAMBLE            (1 << 6)
+#define BMSR_AN_COMPLETE            (1 << 5)
+#define BMSR_REMOTE_FAULT           (1 << 4)
+#define BMSR_AN_ABLE                (1 << 3)
+#define BMSR_LINK_STATUS            (1 << 2)
+#define BMSR_JABBER_DETECT          (1 << 1)
+#define BMSR_EXTENDED_CAP           (1 << 0)
+
+//ANAR register
+#define ANAR_NEXT_PAGE              (1 << 15)
+#define ANAR_REMOTE_FAULT           (1 << 13)
+#define ANAR_PAUSE1                 (1 << 11)
+#define ANAR_PAUSE0                 (1 << 10)
+#define ANAR_100BT4                 (1 << 9)
+#define ANAR_100BTX_FD              (1 << 8)
+#define ANAR_100BTX                 (1 << 7)
+#define ANAR_10BT_FD                (1 << 6)
+#define ANAR_10BT                   (1 << 5)
+#define ANAR_SELECTOR4              (1 << 4)
+#define ANAR_SELECTOR3              (1 << 3)
+#define ANAR_SELECTOR2              (1 << 2)
+#define ANAR_SELECTOR1              (1 << 1)
+#define ANAR_SELECTOR0              (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NEXT_PAGE            (1 << 15)
+#define ANLPAR_LP_ACK               (1 << 14)
+#define ANLPAR_REMOTE_FAULT         (1 << 13)
+#define ANLPAR_PAUSE1               (1 << 11)
+#define ANLPAR_PAUSE0               (1 << 10)
+#define ANLPAR_100BT4               (1 << 9)
+#define ANLPAR_100BTX_FD            (1 << 8)
+#define ANLPAR_100BTX               (1 << 7)
+#define ANLPAR_10BT_FD              (1 << 6)
+#define ANLPAR_10BT                 (1 << 5)
+#define ANLPAR_SELECTOR4            (1 << 4)
+#define ANLPAR_SELECTOR3            (1 << 3)
+#define ANLPAR_SELECTOR2            (1 << 2)
+#define ANLPAR_SELECTOR1            (1 << 1)
+#define ANLPAR_SELECTOR0            (1 << 0)
+
+//ANER register
+#define ANER_PAR_DET_FAULT          (1 << 4)
+#define ANER_LP_NEXT_PAGE_ABLE      (1 << 3)
+#define ANER_NEXT_PAGE_ABLE         (1 << 2)
+#define ANER_PAGE_RECEIVED          (1 << 1)
+#define ANER_LP_AN_ABLE             (1 << 0)
+
+//ANNPTR register
+#define ANNPTR_NEXT_PAGE            (1 << 15)
+#define ANNPTR_MSG_PAGE             (1 << 13)
+#define ANNPTR_ACK2                 (1 << 12)
+#define ANNPTR_TOGGLE               (1 << 11)
+#define ANNPTR_MESSAGE10            (1 << 10)
+#define ANNPTR_MESSAGE9             (1 << 9)
+#define ANNPTR_MESSAGE8             (1 << 8)
+#define ANNPTR_MESSAGE7             (1 << 7)
+#define ANNPTR_MESSAGE6             (1 << 6)
+#define ANNPTR_MESSAGE5             (1 << 5)
+#define ANNPTR_MESSAGE4             (1 << 4)
+#define ANNPTR_MESSAGE3             (1 << 3)
+#define ANNPTR_MESSAGE2             (1 << 2)
+#define ANNPTR_MESSAGE1             (1 << 1)
+#define ANNPTR_MESSAGE0             (1 << 0)
+
+//LPNPAR register
+#define LPNPAR_NEXT_PAGE            (1 << 15)
+#define LPNPAR_ACK                  (1 << 14)
+#define LPNPAR_MSG_PAGE             (1 << 13)
+#define LPNPAR_ACK2                 (1 << 12)
+#define LPNPAR_TOGGLE               (1 << 11)
+#define LPNPAR_MESSAGE10            (1 << 10)
+#define LPNPAR_MESSAGE9             (1 << 9)
+#define LPNPAR_MESSAGE8             (1 << 8)
+#define LPNPAR_MESSAGE7             (1 << 7)
+#define LPNPAR_MESSAGE6             (1 << 6)
+#define LPNPAR_MESSAGE5             (1 << 5)
+#define LPNPAR_MESSAGE4             (1 << 4)
+#define LPNPAR_MESSAGE3             (1 << 3)
+#define LPNPAR_MESSAGE2             (1 << 2)
+#define LPNPAR_MESSAGE1             (1 << 1)
+#define LPNPAR_MESSAGE0             (1 << 0)
+
+//AFECON1 register
+#define AFECON1_SLOW_OSC_MODE_EN    (1 << 5)
+
+//OMSO register
+#define OMSO_BCAST_OFF_OVERRIDE     (1 << 9)
+#define OMSO_MII_BTB_OVERRIDE       (1 << 7)
+#define OMSO_RMII_BTB_OVERRIDE      (1 << 6)
+#define OMSO_NAND_TREE_OVERRIDE     (1 << 5)
+#define OMSO_RMII_OVERRIDE          (1 << 1)
+#define OMSO_MII_OVERRIDE           (1 << 0)
+
+//OMSS register
+#define OMSS_PHYAD2                 (1 << 15)
+#define OMSS_PHYAD1                 (1 << 14)
+#define OMSS_PHYAD0                 (1 << 13)
+#define OMSS_BCAST_OFF_STATUS       (1 << 9)
+#define OMSS_MII_BTB_STATUS         (1 << 7)
+#define OMSS_RMII_BTB_STATUS        (1 << 6)
+#define OMSS_NAND_TREE_STATUS       (1 << 5)
+#define OMSS_RMII_STATUS            (1 << 1)
+#define OMSS_MII_STATUS             (1 << 0)
+
+//EXCON register
+#define EXCON_EDPD_DIS              (1 << 11)
+#define EXCON_100BTX_PREAMBLE_RES   (1 << 10)
+#define EXCON_10BT_PREAMBLE_RES     (1 << 6)
+
+//ICSR register
+#define ICSR_JABBER_IE              (1 << 15)
+#define ICSR_RECEIVE_ERROR_IE       (1 << 14)
+#define ICSR_PAGE_RECEIVED_IE       (1 << 13)
+#define ICSR_PAR_DET_FAULT_IE       (1 << 12)
+#define ICSR_LP_ACK_IE              (1 << 11)
+#define ICSR_LINK_DOWN_IE           (1 << 10)
+#define ICSR_REMOTE_FAULT_IE        (1 << 9)
+#define ICSR_LINK_UP_IE             (1 << 8)
+#define ICSR_JABBER_IF              (1 << 7)
+#define ICSR_RECEIVE_ERROR_IF       (1 << 6)
+#define ICSR_PAGE_RECEIVED_IF       (1 << 5)
+#define ICSR_PAR_DET_FAULT_IF       (1 << 4)
+#define ICSR_LP_ACK_IF              (1 << 3)
+#define ICSR_LINK_DOWN_IF           (1 << 2)
+#define ICSR_REMOTE_FAULT_IF        (1 << 1)
+#define ICSR_LINK_UP_IF             (1 << 0)
+
+//LINKMDCS register
+#define LINKMDCS_CABLE_DIAG_EN      (1 << 15)
+#define LINKMDCS_CABLE_DIAG_RES1    (1 << 14)
+#define LINKMDCS_CABLE_DIAG_RES0    (1 << 13)
+#define LINKMDCS_SHORT_CABLE        (1 << 12)
+#define LINKMDCS_CABLE_FAULT_CNT8   (1 << 8)
+#define LINKMDCS_CABLE_FAULT_CNT7   (1 << 7)
+#define LINKMDCS_CABLE_FAULT_CNT6   (1 << 6)
+#define LINKMDCS_CABLE_FAULT_CNT5   (1 << 5)
+#define LINKMDCS_CABLE_FAULT_CNT4   (1 << 4)
+#define LINKMDCS_CABLE_FAULT_CNT3   (1 << 3)
+#define LINKMDCS_CABLE_FAULT_CNT2   (1 << 2)
+#define LINKMDCS_CABLE_FAULT_CNT1   (1 << 1)
+#define LINKMDCS_CABLE_FAULT_CNT0   (1 << 0)
+
+//PHYCON1 register
+#define PHYCON1_PAUSE_EN            (1 << 9)
+#define PHYCON1_LINK_STATUS         (1 << 8)
+#define PHYCON1_POL_STATUS          (1 << 7)
+#define PHYCON1_MDIX_STATE          (1 << 5)
+#define PHYCON1_ENERGY_DETECT       (1 << 4)
+#define PHYCON1_ISOLATE             (1 << 3)
+#define PHYCON1_OP_MODE2            (1 << 2)
+#define PHYCON1_OP_MODE1            (1 << 1)
+#define PHYCON1_OP_MODE0            (1 << 0)
+
+//Operation mode indication
+#define PHYCON1_OP_MODE_MASK        (7 << 0)
+#define PHYCON1_OP_MODE_AN          (0 << 0)
+#define PHYCON1_OP_MODE_10BT        (1 << 0)
+#define PHYCON1_OP_MODE_100BTX      (2 << 0)
+#define PHYCON1_OP_MODE_10BT_FD     (5 << 0)
+#define PHYCON1_OP_MODE_100BTX_FD   (6 << 0)
+
+//PHYCON2 register
+#define PHYCON2_HP_MDIX             (1 << 15)
+#define PHYCON2_MDIX_SEL            (1 << 14)
+#define PHYCON2_PAIR_SWAP_DIS       (1 << 13)
+#define PHYCON2_FORCE_LINK          (1 << 11)
+#define PHYCON2_POWER_SAVING        (1 << 10)
+#define PHYCON2_INT_LEVEL           (1 << 9)
+#define PHYCON2_JABBER_EN           (1 << 8)
+#define PHYCON2_RMII_REF_CLK_SEL    (1 << 7)
+#define PHYCON2_LED_MODE1           (1 << 5)
+#define PHYCON2_LED_MODE0           (1 << 4)
+#define PHYCON2_TX_DIS              (1 << 3)
+#define PHYCON2_REMOTE_LOOPBACK     (1 << 2)
+#define PHYCON2_SQE_TEST_EN         (1 << 1)
+#define PHYCON2_SCRAMBLER_DIS       (1 << 0)
+
+//KSZ8051 Ethernet PHY driver
+extern const PhyDriver ksz8051PhyDriver;
+
+//KSZ8051 related functions
+error_t ksz8051Init(NetInterface *interface);
+
+void ksz8051Tick(NetInterface *interface);
+
+void ksz8051EnableIrq(NetInterface *interface);
+void ksz8051DisableIrq(NetInterface *interface);
+
+void ksz8051EventHandler(NetInterface *interface);
+
+void ksz8051WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t ksz8051ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void ksz8051DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8061.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,289 @@
+/**
+ * @file ksz8061.c
+ * @brief KSZ8061 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/ksz8061.h"
+#include "debug.h"
+
+
+/**
+ * @brief KSZ8061 Ethernet PHY driver
+ **/
+
+const PhyDriver ksz8061PhyDriver =
+{
+   ksz8061Init,
+   ksz8061Tick,
+   ksz8061EnableIrq,
+   ksz8061DisableIrq,
+   ksz8061EventHandler,
+};
+
+
+/**
+ * @brief KSZ8061 PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ksz8061Init(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing KSZ8061...\r\n");
+
+   //Initialize external interrupt line driver
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->init();
+
+   //Reset PHY transceiver
+   ksz8061WritePhyReg(interface, KSZ8061_PHY_REG_BMCR, BMCR_RESET);
+   //Wait for the reset to complete
+   while(ksz8061ReadPhyReg(interface, KSZ8061_PHY_REG_BMCR) & BMCR_RESET);
+
+   //Dump PHY registers for debugging purpose
+   ksz8061DumpPhyReg(interface);
+
+   //The PHY will generate interrupts when link status changes are detected
+   ksz8061WritePhyReg(interface, KSZ8061_PHY_REG_ICSR, ICSR_LINK_DOWN_IE | ICSR_LINK_UP_IE);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief KSZ8061 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8061Tick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //No external interrupt line driver?
+   if(interface->extIntDriver == NULL)
+   {
+      //Read basic status register
+      value = ksz8061ReadPhyReg(interface, KSZ8061_PHY_REG_BMSR);
+      //Retrieve current link state
+      linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+      //Link up event?
+      if(linkState && !interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+      //Link down event?
+      else if(!linkState && interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8061EnableIrq(NetInterface *interface)
+{
+   //Enable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8061DisableIrq(NetInterface *interface)
+{
+   //Disable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief KSZ8061 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8061EventHandler(NetInterface *interface)
+{
+   uint16_t value;
+
+   //Read status register to acknowledge the interrupt
+   value = ksz8061ReadPhyReg(interface, KSZ8061_PHY_REG_ICSR);
+
+   //Link status change?
+   if(value & (ICSR_LINK_DOWN_IF | ICSR_LINK_UP_IF))
+   {
+      //Any link failure condition is latched in the BMSR register... Reading
+      //the register twice will always return the actual link status
+      value = ksz8061ReadPhyReg(interface, KSZ8061_PHY_REG_BMSR);
+      value = ksz8061ReadPhyReg(interface, KSZ8061_PHY_REG_BMSR);
+
+      //Link is up?
+      if(value & BMSR_LINK_STATUS)
+      {
+         //Read PHY control register
+         value = ksz8061ReadPhyReg(interface, KSZ8061_PHY_REG_PHYCON1);
+
+         //Check current operation mode
+         switch(value & PHYCON1_OP_MODE_MASK)
+         {
+         //10BASE-T
+         case PHYCON1_OP_MODE_10BT:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //10BASE-T full-duplex
+         case PHYCON1_OP_MODE_10BT_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //100BASE-TX
+         case PHYCON1_OP_MODE_100BTX:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //100BASE-TX full-duplex
+         case PHYCON1_OP_MODE_100BTX_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //Unknown operation mode
+         default:
+            //Debug message
+            TRACE_WARNING("Invalid Duplex mode\r\n");
+            break;
+         }
+
+         //Update link state
+         interface->linkState = TRUE;
+
+         //Adjust MAC configuration parameters for proper operation
+         interface->nicDriver->updateMacConfig(interface);
+      }
+      else
+      {
+         //Update link state
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void ksz8061WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = KSZ8061_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t ksz8061ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = KSZ8061_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8061DumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, ksz8061ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8061.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,276 @@
+/**
+ * @file ksz8061.h
+ * @brief KSZ8061 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _KSZ8061_H
+#define _KSZ8061_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#ifndef KSZ8061_PHY_ADDR
+   #define KSZ8061_PHY_ADDR 1
+#elif (KSZ8061_PHY_ADDR < 0 || KSZ8061_PHY_ADDR > 31)
+   #error KSZ8061_PHY_ADDR parameter is not valid
+#endif
+
+//KSZ8061 registers
+#define KSZ8061_PHY_REG_BMCR        0x00
+#define KSZ8061_PHY_REG_BMSR        0x01
+#define KSZ8061_PHY_REG_PHYIDR1     0x02
+#define KSZ8061_PHY_REG_PHYIDR2     0x03
+#define KSZ8061_PHY_REG_ANAR        0x04
+#define KSZ8061_PHY_REG_ANLPAR      0x05
+#define KSZ8061_PHY_REG_ANER        0x06
+#define KSZ8061_PHY_REG_ANNPTR      0x07
+#define KSZ8061_PHY_REG_LPNPAR      0x08
+#define KSZ8061_PHY_REG_AFECON1     0x11
+#define KSZ8061_PHY_REG_RXERCTR     0x15
+#define KSZ8061_PHY_REG_OMSO        0x16
+#define KSZ8061_PHY_REG_OMSS        0x17
+#define KSZ8061_PHY_REG_EXCON       0x18
+#define KSZ8061_PHY_REG_ICSR        0x1B
+#define KSZ8061_PHY_REG_LINKMDCS    0x1D
+#define KSZ8061_PHY_REG_PHYCON1     0x1E
+#define KSZ8061_PHY_REG_PHYCON2     0x1F
+
+//BMCR register
+#define BMCR_RESET                  (1 << 15)
+#define BMCR_LOOPBACK               (1 << 14)
+#define BMCR_SPEED_SEL              (1 << 13)
+#define BMCR_AN_EN                  (1 << 12)
+#define BMCR_POWER_DOWN             (1 << 11)
+#define BMCR_ISOLATE                (1 << 10)
+#define BMCR_RESTART_AN             (1 << 9)
+#define BMCR_DUPLEX_MODE            (1 << 8)
+#define BMCR_COL_TEST               (1 << 7)
+
+//BMSR register
+#define BMSR_100BT4                 (1 << 15)
+#define BMSR_100BTX_FD              (1 << 14)
+#define BMSR_100BTX                 (1 << 13)
+#define BMSR_10BT_FD                (1 << 12)
+#define BMSR_10BT                   (1 << 11)
+#define BMSR_NO_PREAMBLE            (1 << 6)
+#define BMSR_AN_COMPLETE            (1 << 5)
+#define BMSR_REMOTE_FAULT           (1 << 4)
+#define BMSR_AN_ABLE                (1 << 3)
+#define BMSR_LINK_STATUS            (1 << 2)
+#define BMSR_JABBER_DETECT          (1 << 1)
+#define BMSR_EXTENDED_CAP           (1 << 0)
+
+//ANAR register
+#define ANAR_NEXT_PAGE              (1 << 15)
+#define ANAR_REMOTE_FAULT           (1 << 13)
+#define ANAR_PAUSE1                 (1 << 11)
+#define ANAR_PAUSE0                 (1 << 10)
+#define ANAR_100BT4                 (1 << 9)
+#define ANAR_100BTX_FD              (1 << 8)
+#define ANAR_100BTX                 (1 << 7)
+#define ANAR_10BT_FD                (1 << 6)
+#define ANAR_10BT                   (1 << 5)
+#define ANAR_SELECTOR4              (1 << 4)
+#define ANAR_SELECTOR3              (1 << 3)
+#define ANAR_SELECTOR2              (1 << 2)
+#define ANAR_SELECTOR1              (1 << 1)
+#define ANAR_SELECTOR0              (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NEXT_PAGE            (1 << 15)
+#define ANLPAR_LP_ACK               (1 << 14)
+#define ANLPAR_REMOTE_FAULT         (1 << 13)
+#define ANLPAR_PAUSE1               (1 << 11)
+#define ANLPAR_PAUSE0               (1 << 10)
+#define ANLPAR_100BT4               (1 << 9)
+#define ANLPAR_100BTX_FD            (1 << 8)
+#define ANLPAR_100BTX               (1 << 7)
+#define ANLPAR_10BT_FD              (1 << 6)
+#define ANLPAR_10BT                 (1 << 5)
+#define ANLPAR_SELECTOR4            (1 << 4)
+#define ANLPAR_SELECTOR3            (1 << 3)
+#define ANLPAR_SELECTOR2            (1 << 2)
+#define ANLPAR_SELECTOR1            (1 << 1)
+#define ANLPAR_SELECTOR0            (1 << 0)
+
+//ANER register
+#define ANER_PAR_DET_FAULT          (1 << 4)
+#define ANER_LP_NEXT_PAGE_ABLE      (1 << 3)
+#define ANER_NEXT_PAGE_ABLE         (1 << 2)
+#define ANER_PAGE_RECEIVED          (1 << 1)
+#define ANER_LP_AN_ABLE             (1 << 0)
+
+//ANNPTR register
+#define ANNPTR_NEXT_PAGE            (1 << 15)
+#define ANNPTR_MSG_PAGE             (1 << 13)
+#define ANNPTR_ACK2                 (1 << 12)
+#define ANNPTR_TOGGLE               (1 << 11)
+#define ANNPTR_MESSAGE10            (1 << 10)
+#define ANNPTR_MESSAGE9             (1 << 9)
+#define ANNPTR_MESSAGE8             (1 << 8)
+#define ANNPTR_MESSAGE7             (1 << 7)
+#define ANNPTR_MESSAGE6             (1 << 6)
+#define ANNPTR_MESSAGE5             (1 << 5)
+#define ANNPTR_MESSAGE4             (1 << 4)
+#define ANNPTR_MESSAGE3             (1 << 3)
+#define ANNPTR_MESSAGE2             (1 << 2)
+#define ANNPTR_MESSAGE1             (1 << 1)
+#define ANNPTR_MESSAGE0             (1 << 0)
+
+//LPNPAR register
+#define LPNPAR_NEXT_PAGE            (1 << 15)
+#define LPNPAR_ACK                  (1 << 14)
+#define LPNPAR_MSG_PAGE             (1 << 13)
+#define LPNPAR_ACK2                 (1 << 12)
+#define LPNPAR_TOGGLE               (1 << 11)
+#define LPNPAR_MESSAGE10            (1 << 10)
+#define LPNPAR_MESSAGE9             (1 << 9)
+#define LPNPAR_MESSAGE8             (1 << 8)
+#define LPNPAR_MESSAGE7             (1 << 7)
+#define LPNPAR_MESSAGE6             (1 << 6)
+#define LPNPAR_MESSAGE5             (1 << 5)
+#define LPNPAR_MESSAGE4             (1 << 4)
+#define LPNPAR_MESSAGE3             (1 << 3)
+#define LPNPAR_MESSAGE2             (1 << 2)
+#define LPNPAR_MESSAGE1             (1 << 1)
+#define LPNPAR_MESSAGE0             (1 << 0)
+
+//AFECON1 register
+#define AFECON1_SLOW_OSC_MODE_EN    (1 << 5)
+
+//OMSO register
+#define OMSO_BCAST_OFF_OVERRIDE     (1 << 9)
+#define OMSO_MII_BTB_OVERRIDE       (1 << 7)
+#define OMSO_RMII_BTB_OVERRIDE      (1 << 6)
+#define OMSO_NAND_TREE_OVERRIDE     (1 << 5)
+#define OMSO_RMII_OVERRIDE          (1 << 1)
+#define OMSO_MII_OVERRIDE           (1 << 0)
+
+//OMSS register
+#define OMSS_PHYAD2                 (1 << 15)
+#define OMSS_PHYAD1                 (1 << 14)
+#define OMSS_PHYAD0                 (1 << 13)
+#define OMSS_BCAST_OFF_STATUS       (1 << 9)
+#define OMSS_MII_BTB_STATUS         (1 << 7)
+#define OMSS_RMII_BTB_STATUS        (1 << 6)
+#define OMSS_NAND_TREE_STATUS       (1 << 5)
+#define OMSS_RMII_STATUS            (1 << 1)
+#define OMSS_MII_STATUS             (1 << 0)
+
+//EXCON register
+#define EXCON_EDPD_DIS              (1 << 11)
+#define EXCON_100BTX_PREAMBLE_RES   (1 << 10)
+#define EXCON_10BT_PREAMBLE_RES     (1 << 6)
+
+//ICSR register
+#define ICSR_JABBER_IE              (1 << 15)
+#define ICSR_RECEIVE_ERROR_IE       (1 << 14)
+#define ICSR_PAGE_RECEIVED_IE       (1 << 13)
+#define ICSR_PAR_DET_FAULT_IE       (1 << 12)
+#define ICSR_LP_ACK_IE              (1 << 11)
+#define ICSR_LINK_DOWN_IE           (1 << 10)
+#define ICSR_REMOTE_FAULT_IE        (1 << 9)
+#define ICSR_LINK_UP_IE             (1 << 8)
+#define ICSR_JABBER_IF              (1 << 7)
+#define ICSR_RECEIVE_ERROR_IF       (1 << 6)
+#define ICSR_PAGE_RECEIVED_IF       (1 << 5)
+#define ICSR_PAR_DET_FAULT_IF       (1 << 4)
+#define ICSR_LP_ACK_IF              (1 << 3)
+#define ICSR_LINK_DOWN_IF           (1 << 2)
+#define ICSR_REMOTE_FAULT_IF        (1 << 1)
+#define ICSR_LINK_UP_IF             (1 << 0)
+
+//LINKMDCS register
+#define LINKMDCS_CABLE_DIAG_EN      (1 << 15)
+#define LINKMDCS_CABLE_DIAG_RES1    (1 << 14)
+#define LINKMDCS_CABLE_DIAG_RES0    (1 << 13)
+#define LINKMDCS_SHORT_CABLE        (1 << 12)
+#define LINKMDCS_CABLE_FAULT_CNT8   (1 << 8)
+#define LINKMDCS_CABLE_FAULT_CNT7   (1 << 7)
+#define LINKMDCS_CABLE_FAULT_CNT6   (1 << 6)
+#define LINKMDCS_CABLE_FAULT_CNT5   (1 << 5)
+#define LINKMDCS_CABLE_FAULT_CNT4   (1 << 4)
+#define LINKMDCS_CABLE_FAULT_CNT3   (1 << 3)
+#define LINKMDCS_CABLE_FAULT_CNT2   (1 << 2)
+#define LINKMDCS_CABLE_FAULT_CNT1   (1 << 1)
+#define LINKMDCS_CABLE_FAULT_CNT0   (1 << 0)
+
+//PHYCON1 register
+#define PHYCON1_PAUSE_EN            (1 << 9)
+#define PHYCON1_LINK_STATUS         (1 << 8)
+#define PHYCON1_POL_STATUS          (1 << 7)
+#define PHYCON1_MDIX_STATE          (1 << 5)
+#define PHYCON1_ENERGY_DETECT       (1 << 4)
+#define PHYCON1_ISOLATE             (1 << 3)
+#define PHYCON1_OP_MODE2            (1 << 2)
+#define PHYCON1_OP_MODE1            (1 << 1)
+#define PHYCON1_OP_MODE0            (1 << 0)
+
+//Operation mode indication
+#define PHYCON1_OP_MODE_MASK        (7 << 0)
+#define PHYCON1_OP_MODE_AN          (0 << 0)
+#define PHYCON1_OP_MODE_10BT        (1 << 0)
+#define PHYCON1_OP_MODE_100BTX      (2 << 0)
+#define PHYCON1_OP_MODE_10BT_FD     (5 << 0)
+#define PHYCON1_OP_MODE_100BTX_FD   (6 << 0)
+
+//PHYCON2 register
+#define PHYCON2_HP_MDIX             (1 << 15)
+#define PHYCON2_MDIX_SEL            (1 << 14)
+#define PHYCON2_PAIR_SWAP_DIS       (1 << 13)
+#define PHYCON2_FORCE_LINK          (1 << 11)
+#define PHYCON2_POWER_SAVING        (1 << 10)
+#define PHYCON2_INT_LEVEL           (1 << 9)
+#define PHYCON2_JABBER_EN           (1 << 8)
+#define PHYCON2_RMII_REF_CLK_SEL    (1 << 7)
+#define PHYCON2_LED_MODE1           (1 << 5)
+#define PHYCON2_LED_MODE0           (1 << 4)
+#define PHYCON2_TX_DIS              (1 << 3)
+#define PHYCON2_REMOTE_LOOPBACK     (1 << 2)
+#define PHYCON2_SQE_TEST_EN         (1 << 1)
+#define PHYCON2_SCRAMBLER_DIS       (1 << 0)
+
+//KSZ8061 Ethernet PHY driver
+extern const PhyDriver ksz8061PhyDriver;
+
+//KSZ8061 related functions
+error_t ksz8061Init(NetInterface *interface);
+
+void ksz8061Tick(NetInterface *interface);
+
+void ksz8061EnableIrq(NetInterface *interface);
+void ksz8061DisableIrq(NetInterface *interface);
+
+void ksz8061EventHandler(NetInterface *interface);
+
+void ksz8061WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t ksz8061ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void ksz8061DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8081.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,289 @@
+/**
+ * @file ksz8081.c
+ * @brief KSZ8081 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/ksz8081.h"
+#include "debug.h"
+
+
+/**
+ * @brief KSZ8081 Ethernet PHY driver
+ **/
+
+const PhyDriver ksz8081PhyDriver =
+{
+   ksz8081Init,
+   ksz8081Tick,
+   ksz8081EnableIrq,
+   ksz8081DisableIrq,
+   ksz8081EventHandler,
+};
+
+
+/**
+ * @brief KSZ8081 PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ksz8081Init(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing KSZ8081...\r\n");
+
+   //Initialize external interrupt line driver
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->init();
+
+   //Reset PHY transceiver
+   ksz8081WritePhyReg(interface, KSZ8081_PHY_REG_BMCR, BMCR_RESET);
+   //Wait for the reset to complete
+   while(ksz8081ReadPhyReg(interface, KSZ8081_PHY_REG_BMCR) & BMCR_RESET);
+
+   //Dump PHY registers for debugging purpose
+   ksz8081DumpPhyReg(interface);
+
+   //The PHY will generate interrupts when link status changes are detected
+   ksz8081WritePhyReg(interface, KSZ8081_PHY_REG_ICSR, ICSR_LINK_DOWN_IE | ICSR_LINK_UP_IE);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief KSZ8081 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8081Tick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //No external interrupt line driver?
+   if(interface->extIntDriver == NULL)
+   {
+      //Read basic status register
+      value = ksz8081ReadPhyReg(interface, KSZ8081_PHY_REG_BMSR);
+      //Retrieve current link state
+      linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+      //Link up event?
+      if(linkState && !interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+      //Link down event?
+      else if(!linkState && interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8081EnableIrq(NetInterface *interface)
+{
+   //Enable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8081DisableIrq(NetInterface *interface)
+{
+   //Disable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief KSZ8081 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8081EventHandler(NetInterface *interface)
+{
+   uint16_t value;
+
+   //Read status register to acknowledge the interrupt
+   value = ksz8081ReadPhyReg(interface, KSZ8081_PHY_REG_ICSR);
+
+   //Link status change?
+   if(value & (ICSR_LINK_DOWN_IF | ICSR_LINK_UP_IF))
+   {
+      //Any link failure condition is latched in the BMSR register... Reading
+      //the register twice will always return the actual link status
+      value = ksz8081ReadPhyReg(interface, KSZ8081_PHY_REG_BMSR);
+      value = ksz8081ReadPhyReg(interface, KSZ8081_PHY_REG_BMSR);
+
+      //Link is up?
+      if(value & BMSR_LINK_STATUS)
+      {
+         //Read PHY control register
+         value = ksz8081ReadPhyReg(interface, KSZ8081_PHY_REG_PHYCON1);
+
+         //Check current operation mode
+         switch(value & PHYCON1_OP_MODE_MASK)
+         {
+         //10BASE-T
+         case PHYCON1_OP_MODE_10BT:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //10BASE-T full-duplex
+         case PHYCON1_OP_MODE_10BT_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //100BASE-TX
+         case PHYCON1_OP_MODE_100BTX:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //100BASE-TX full-duplex
+         case PHYCON1_OP_MODE_100BTX_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //Unknown operation mode
+         default:
+            //Debug message
+            TRACE_WARNING("Invalid Duplex mode\r\n");
+            break;
+         }
+
+         //Update link state
+         interface->linkState = TRUE;
+
+         //Adjust MAC configuration parameters for proper operation
+         interface->nicDriver->updateMacConfig(interface);
+      }
+      else
+      {
+         //Update link state
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void ksz8081WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = KSZ8081_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t ksz8081ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = KSZ8081_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8081DumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, ksz8081ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8081.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,272 @@
+/**
+ * @file ksz8081.h
+ * @brief KSZ8081 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _KSZ8081_H
+#define _KSZ8081_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#ifndef KSZ8081_PHY_ADDR
+   #define KSZ8081_PHY_ADDR 0
+#elif (KSZ8081_PHY_ADDR < 0 || KSZ8081_PHY_ADDR > 31)
+   #error KSZ8081_PHY_ADDR parameter is not valid
+#endif
+
+//KSZ8081 registers
+#define KSZ8081_PHY_REG_BMCR        0x00
+#define KSZ8081_PHY_REG_BMSR        0x01
+#define KSZ8081_PHY_REG_PHYIDR1     0x02
+#define KSZ8081_PHY_REG_PHYIDR2     0x03
+#define KSZ8081_PHY_REG_ANAR        0x04
+#define KSZ8081_PHY_REG_ANLPAR      0x05
+#define KSZ8081_PHY_REG_ANER        0x06
+#define KSZ8081_PHY_REG_ANNPTR      0x07
+#define KSZ8081_PHY_REG_LPNPAR      0x08
+#define KSZ8081_PHY_REG_DRC         0x10
+#define KSZ8081_PHY_REG_AFECON1     0x11
+#define KSZ8081_PHY_REG_RXERCTR     0x15
+#define KSZ8081_PHY_REG_OMSO        0x16
+#define KSZ8081_PHY_REG_OMSS        0x17
+#define KSZ8081_PHY_REG_EXCON       0x18
+#define KSZ8081_PHY_REG_ICSR        0x1B
+#define KSZ8081_PHY_REG_LINKMDCS    0x1D
+#define KSZ8081_PHY_REG_PHYCON1     0x1E
+#define KSZ8081_PHY_REG_PHYCON2     0x1F
+
+//BMCR register
+#define BMCR_RESET                  (1 << 15)
+#define BMCR_LOOPBACK               (1 << 14)
+#define BMCR_SPEED_SEL              (1 << 13)
+#define BMCR_AN_EN                  (1 << 12)
+#define BMCR_POWER_DOWN             (1 << 11)
+#define BMCR_ISOLATE                (1 << 10)
+#define BMCR_RESTART_AN             (1 << 9)
+#define BMCR_DUPLEX_MODE            (1 << 8)
+#define BMCR_COL_TEST               (1 << 7)
+
+//BMSR register
+#define BMSR_100BT4                 (1 << 15)
+#define BMSR_100BTX_FD              (1 << 14)
+#define BMSR_100BTX                 (1 << 13)
+#define BMSR_10BT_FD                (1 << 12)
+#define BMSR_10BT                   (1 << 11)
+#define BMSR_NO_PREAMBLE            (1 << 6)
+#define BMSR_AN_COMPLETE            (1 << 5)
+#define BMSR_REMOTE_FAULT           (1 << 4)
+#define BMSR_AN_ABLE                (1 << 3)
+#define BMSR_LINK_STATUS            (1 << 2)
+#define BMSR_JABBER_DETECT          (1 << 1)
+#define BMSR_EXTENDED_CAP           (1 << 0)
+
+//ANAR register
+#define ANAR_NEXT_PAGE              (1 << 15)
+#define ANAR_REMOTE_FAULT           (1 << 13)
+#define ANAR_PAUSE1                 (1 << 11)
+#define ANAR_PAUSE0                 (1 << 10)
+#define ANAR_100BT4                 (1 << 9)
+#define ANAR_100BTX_FD              (1 << 8)
+#define ANAR_100BTX                 (1 << 7)
+#define ANAR_10BT_FD                (1 << 6)
+#define ANAR_10BT                   (1 << 5)
+#define ANAR_SELECTOR4              (1 << 4)
+#define ANAR_SELECTOR3              (1 << 3)
+#define ANAR_SELECTOR2              (1 << 2)
+#define ANAR_SELECTOR1              (1 << 1)
+#define ANAR_SELECTOR0              (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NEXT_PAGE            (1 << 15)
+#define ANLPAR_LP_ACK               (1 << 14)
+#define ANLPAR_REMOTE_FAULT         (1 << 13)
+#define ANLPAR_PAUSE1               (1 << 11)
+#define ANLPAR_PAUSE0               (1 << 10)
+#define ANLPAR_100BT4               (1 << 9)
+#define ANLPAR_100BTX_FD            (1 << 8)
+#define ANLPAR_100BTX               (1 << 7)
+#define ANLPAR_10BT_FD              (1 << 6)
+#define ANLPAR_10BT                 (1 << 5)
+#define ANLPAR_SELECTOR4            (1 << 4)
+#define ANLPAR_SELECTOR3            (1 << 3)
+#define ANLPAR_SELECTOR2            (1 << 2)
+#define ANLPAR_SELECTOR1            (1 << 1)
+#define ANLPAR_SELECTOR0            (1 << 0)
+
+//ANER register
+#define ANER_PAR_DET_FAULT          (1 << 4)
+#define ANER_LP_NEXT_PAGE_ABLE      (1 << 3)
+#define ANER_NEXT_PAGE_ABLE         (1 << 2)
+#define ANER_PAGE_RECEIVED          (1 << 1)
+#define ANER_LP_AN_ABLE             (1 << 0)
+
+//ANNPTR register
+#define ANNPTR_NEXT_PAGE            (1 << 15)
+#define ANNPTR_MSG_PAGE             (1 << 13)
+#define ANNPTR_ACK2                 (1 << 12)
+#define ANNPTR_TOGGLE               (1 << 11)
+#define ANNPTR_MESSAGE10            (1 << 10)
+#define ANNPTR_MESSAGE9             (1 << 9)
+#define ANNPTR_MESSAGE8             (1 << 8)
+#define ANNPTR_MESSAGE7             (1 << 7)
+#define ANNPTR_MESSAGE6             (1 << 6)
+#define ANNPTR_MESSAGE5             (1 << 5)
+#define ANNPTR_MESSAGE4             (1 << 4)
+#define ANNPTR_MESSAGE3             (1 << 3)
+#define ANNPTR_MESSAGE2             (1 << 2)
+#define ANNPTR_MESSAGE1             (1 << 1)
+#define ANNPTR_MESSAGE0             (1 << 0)
+
+//LPNPAR register
+#define LPNPAR_NEXT_PAGE            (1 << 15)
+#define LPNPAR_ACK                  (1 << 14)
+#define LPNPAR_MSG_PAGE             (1 << 13)
+#define LPNPAR_ACK2                 (1 << 12)
+#define LPNPAR_TOGGLE               (1 << 11)
+#define LPNPAR_MESSAGE10            (1 << 10)
+#define LPNPAR_MESSAGE9             (1 << 9)
+#define LPNPAR_MESSAGE8             (1 << 8)
+#define LPNPAR_MESSAGE7             (1 << 7)
+#define LPNPAR_MESSAGE6             (1 << 6)
+#define LPNPAR_MESSAGE5             (1 << 5)
+#define LPNPAR_MESSAGE4             (1 << 4)
+#define LPNPAR_MESSAGE3             (1 << 3)
+#define LPNPAR_MESSAGE2             (1 << 2)
+#define LPNPAR_MESSAGE1             (1 << 1)
+#define LPNPAR_MESSAGE0             (1 << 0)
+
+//DRC register
+#define DRC_PLL_OFF                 (1 << 4)
+
+//AFECON1 register
+#define AFECON1_SLOW_OSC_MODE_EN    (1 << 5)
+
+//OMSO register
+#define OMSO_BCAST_OFF_OVERRIDE     (1 << 9)
+#define OMSO_MII_BTB_OVERRIDE       (1 << 7)
+#define OMSO_RMII_BTB_OVERRIDE      (1 << 6)
+#define OMSO_NAND_TREE_OVERRIDE     (1 << 5)
+#define OMSO_RMII_OVERRIDE          (1 << 1)
+#define OMSO_MII_OVERRIDE           (1 << 0)
+
+//OMSS register
+#define OMSS_PHYAD2                 (1 << 15)
+#define OMSS_PHYAD1                 (1 << 14)
+#define OMSS_PHYAD0                 (1 << 13)
+#define OMSS_RMII_STATUS            (1 << 1)
+
+//EXCON register
+#define EXCON_EDPD_DIS              (1 << 11)
+
+//ICSR register
+#define ICSR_JABBER_IE              (1 << 15)
+#define ICSR_RECEIVE_ERROR_IE       (1 << 14)
+#define ICSR_PAGE_RECEIVED_IE       (1 << 13)
+#define ICSR_PAR_DET_FAULT_IE       (1 << 12)
+#define ICSR_LP_ACK_IE              (1 << 11)
+#define ICSR_LINK_DOWN_IE           (1 << 10)
+#define ICSR_REMOTE_FAULT_IE        (1 << 9)
+#define ICSR_LINK_UP_IE             (1 << 8)
+#define ICSR_JABBER_IF              (1 << 7)
+#define ICSR_RECEIVE_ERROR_IF       (1 << 6)
+#define ICSR_PAGE_RECEIVED_IF       (1 << 5)
+#define ICSR_PAR_DET_FAULT_IF       (1 << 4)
+#define ICSR_LP_ACK_IF              (1 << 3)
+#define ICSR_LINK_DOWN_IF           (1 << 2)
+#define ICSR_REMOTE_FAULT_IF        (1 << 1)
+#define ICSR_LINK_UP_IF             (1 << 0)
+
+//LINKMDCS register
+#define LINKMDCS_CABLE_DIAG_EN      (1 << 15)
+#define LINKMDCS_CABLE_DIAG_RES1    (1 << 14)
+#define LINKMDCS_CABLE_DIAG_RES0    (1 << 13)
+#define LINKMDCS_SHORT_CABLE        (1 << 12)
+#define LINKMDCS_CABLE_FAULT_CNT8   (1 << 8)
+#define LINKMDCS_CABLE_FAULT_CNT7   (1 << 7)
+#define LINKMDCS_CABLE_FAULT_CNT6   (1 << 6)
+#define LINKMDCS_CABLE_FAULT_CNT5   (1 << 5)
+#define LINKMDCS_CABLE_FAULT_CNT4   (1 << 4)
+#define LINKMDCS_CABLE_FAULT_CNT3   (1 << 3)
+#define LINKMDCS_CABLE_FAULT_CNT2   (1 << 2)
+#define LINKMDCS_CABLE_FAULT_CNT1   (1 << 1)
+#define LINKMDCS_CABLE_FAULT_CNT0   (1 << 0)
+
+//PHYCON1 register
+#define PHYCON1_PAUSE_EN            (1 << 9)
+#define PHYCON1_LINK_STATUS         (1 << 8)
+#define PHYCON1_POL_STATUS          (1 << 7)
+#define PHYCON1_MDIX_STATE          (1 << 5)
+#define PHYCON1_ENERGY_DETECT       (1 << 4)
+#define PHYCON1_ISOLATE             (1 << 3)
+#define PHYCON1_OP_MODE2            (1 << 2)
+#define PHYCON1_OP_MODE1            (1 << 1)
+#define PHYCON1_OP_MODE0            (1 << 0)
+
+//Operation mode indication
+#define PHYCON1_OP_MODE_MASK        (7 << 0)
+#define PHYCON1_OP_MODE_AN          (0 << 0)
+#define PHYCON1_OP_MODE_10BT        (1 << 0)
+#define PHYCON1_OP_MODE_100BTX      (2 << 0)
+#define PHYCON1_OP_MODE_10BT_FD     (5 << 0)
+#define PHYCON1_OP_MODE_100BTX_FD   (6 << 0)
+
+//PHYCON2 register
+#define PHYCON2_HP_MDIX             (1 << 15)
+#define PHYCON2_MDIX_SEL            (1 << 14)
+#define PHYCON2_PAIR_SWAP_DIS       (1 << 13)
+#define PHYCON2_FORCE_LINK          (1 << 11)
+#define PHYCON2_POWER_SAVING        (1 << 10)
+#define PHYCON2_INT_LEVEL           (1 << 9)
+#define PHYCON2_JABBER_EN           (1 << 8)
+#define PHYCON2_RMII_REF_CLK_SEL    (1 << 7)
+#define PHYCON2_LED_MODE1           (1 << 5)
+#define PHYCON2_LED_MODE0           (1 << 4)
+#define PHYCON2_TX_DIS              (1 << 3)
+#define PHYCON2_REMOTE_LOOPBACK     (1 << 2)
+#define PHYCON2_SCRAMBLER_DIS       (1 << 0)
+
+//KSZ8081 Ethernet PHY driver
+extern const PhyDriver ksz8081PhyDriver;
+
+//KSZ8081 related functions
+error_t ksz8081Init(NetInterface *interface);
+
+void ksz8081Tick(NetInterface *interface);
+
+void ksz8081EnableIrq(NetInterface *interface);
+void ksz8081DisableIrq(NetInterface *interface);
+
+void ksz8081EventHandler(NetInterface *interface);
+
+void ksz8081WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t ksz8081ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void ksz8081DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8091.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,289 @@
+/**
+ * @file ksz8091.c
+ * @brief KSZ8091 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/ksz8091.h"
+#include "debug.h"
+
+
+/**
+ * @brief KSZ8091 Ethernet PHY driver
+ **/
+
+const PhyDriver ksz8091PhyDriver =
+{
+   ksz8091Init,
+   ksz8091Tick,
+   ksz8091EnableIrq,
+   ksz8091DisableIrq,
+   ksz8091EventHandler,
+};
+
+
+/**
+ * @brief KSZ8091 PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ksz8091Init(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing KSZ8091...\r\n");
+
+   //Initialize external interrupt line driver
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->init();
+
+   //Reset PHY transceiver
+   ksz8091WritePhyReg(interface, KSZ8091_PHY_REG_BMCR, BMCR_RESET);
+   //Wait for the reset to complete
+   while(ksz8091ReadPhyReg(interface, KSZ8091_PHY_REG_BMCR) & BMCR_RESET);
+
+   //Dump PHY registers for debugging purpose
+   ksz8091DumpPhyReg(interface);
+
+   //The PHY will generate interrupts when link status changes are detected
+   ksz8091WritePhyReg(interface, KSZ8091_PHY_REG_ICSR, ICSR_LINK_DOWN_IE | ICSR_LINK_UP_IE);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief KSZ8091 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8091Tick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //No external interrupt line driver?
+   if(interface->extIntDriver == NULL)
+   {
+      //Read basic status register
+      value = ksz8091ReadPhyReg(interface, KSZ8091_PHY_REG_BMSR);
+      //Retrieve current link state
+      linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+      //Link up event?
+      if(linkState && !interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+      //Link down event?
+      else if(!linkState && interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8091EnableIrq(NetInterface *interface)
+{
+   //Enable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8091DisableIrq(NetInterface *interface)
+{
+   //Disable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief KSZ8091 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8091EventHandler(NetInterface *interface)
+{
+   uint16_t value;
+
+   //Read status register to acknowledge the interrupt
+   value = ksz8091ReadPhyReg(interface, KSZ8091_PHY_REG_ICSR);
+
+   //Link status change?
+   if(value & (ICSR_LINK_DOWN_IF | ICSR_LINK_UP_IF))
+   {
+      //Any link failure condition is latched in the BMSR register... Reading
+      //the register twice will always return the actual link status
+      value = ksz8091ReadPhyReg(interface, KSZ8091_PHY_REG_BMSR);
+      value = ksz8091ReadPhyReg(interface, KSZ8091_PHY_REG_BMSR);
+
+      //Link is up?
+      if(value & BMSR_LINK_STATUS)
+      {
+         //Read PHY control register
+         value = ksz8091ReadPhyReg(interface, KSZ8091_PHY_REG_PHYCON1);
+
+         //Check current operation mode
+         switch(value & PHYCON1_OP_MODE_MASK)
+         {
+         //10BASE-T
+         case PHYCON1_OP_MODE_10BT:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //10BASE-T full-duplex
+         case PHYCON1_OP_MODE_10BT_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //100BASE-TX
+         case PHYCON1_OP_MODE_100BTX:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //100BASE-TX full-duplex
+         case PHYCON1_OP_MODE_100BTX_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //Unknown operation mode
+         default:
+            //Debug message
+            TRACE_WARNING("Invalid Duplex mode\r\n");
+            break;
+         }
+
+         //Update link state
+         interface->linkState = TRUE;
+
+         //Adjust MAC configuration parameters for proper operation
+         interface->nicDriver->updateMacConfig(interface);
+      }
+      else
+      {
+         //Update link state
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void ksz8091WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = KSZ8091_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t ksz8091ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = KSZ8091_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8091DumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, ksz8091ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8091.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,286 @@
+/**
+ * @file ksz8091.h
+ * @brief KSZ8091 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _KSZ8091_H
+#define _KSZ8091_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#ifndef KSZ8091_PHY_ADDR
+   #define KSZ8091_PHY_ADDR 7
+#elif (KSZ8091_PHY_ADDR < 0 || KSZ8091_PHY_ADDR > 31)
+   #error KSZ8091_PHY_ADDR parameter is not valid
+#endif
+
+//KSZ8091 registers
+#define KSZ8091_PHY_REG_BMCR        0x00
+#define KSZ8091_PHY_REG_BMSR        0x01
+#define KSZ8091_PHY_REG_PHYIDR1     0x02
+#define KSZ8091_PHY_REG_PHYIDR2     0x03
+#define KSZ8091_PHY_REG_ANAR        0x04
+#define KSZ8091_PHY_REG_ANLPAR      0x05
+#define KSZ8091_PHY_REG_ANER        0x06
+#define KSZ8091_PHY_REG_ANNPTR      0x07
+#define KSZ8091_PHY_REG_LPNPAR      0x08
+#define KSZ8091_PHY_REG_MMDCON      0x0D
+#define KSZ8091_PHY_REG_MMDDATA     0x0E
+#define KSZ8091_PHY_REG_DRC         0x10
+#define KSZ8091_PHY_REG_AFECON1     0x11
+#define KSZ8091_PHY_REG_AFECON4     0x13
+#define KSZ8091_PHY_REG_RXERCTR     0x15
+#define KSZ8091_PHY_REG_OMSO        0x16
+#define KSZ8091_PHY_REG_OMSS        0x17
+#define KSZ8091_PHY_REG_EXCON       0x18
+#define KSZ8091_PHY_REG_ICSR        0x1B
+#define KSZ8091_PHY_REG_LINKMDCS    0x1D
+#define KSZ8091_PHY_REG_PHYCON1     0x1E
+#define KSZ8091_PHY_REG_PHYCON2     0x1F
+
+//BMCR register
+#define BMCR_RESET                  (1 << 15)
+#define BMCR_LOOPBACK               (1 << 14)
+#define BMCR_SPEED_SEL              (1 << 13)
+#define BMCR_AN_EN                  (1 << 12)
+#define BMCR_POWER_DOWN             (1 << 11)
+#define BMCR_ISOLATE                (1 << 10)
+#define BMCR_RESTART_AN             (1 << 9)
+#define BMCR_DUPLEX_MODE            (1 << 8)
+#define BMCR_COL_TEST               (1 << 7)
+
+//BMSR register
+#define BMSR_100BT4                 (1 << 15)
+#define BMSR_100BTX_FD              (1 << 14)
+#define BMSR_100BTX                 (1 << 13)
+#define BMSR_10BT_FD                (1 << 12)
+#define BMSR_10BT                   (1 << 11)
+#define BMSR_NO_PREAMBLE            (1 << 6)
+#define BMSR_AN_COMPLETE            (1 << 5)
+#define BMSR_REMOTE_FAULT           (1 << 4)
+#define BMSR_AN_ABLE                (1 << 3)
+#define BMSR_LINK_STATUS            (1 << 2)
+#define BMSR_JABBER_DETECT          (1 << 1)
+#define BMSR_EXTENDED_CAP           (1 << 0)
+
+//ANAR register
+#define ANAR_NEXT_PAGE              (1 << 15)
+#define ANAR_REMOTE_FAULT           (1 << 13)
+#define ANAR_PAUSE1                 (1 << 11)
+#define ANAR_PAUSE0                 (1 << 10)
+#define ANAR_100BT4                 (1 << 9)
+#define ANAR_100BTX_FD              (1 << 8)
+#define ANAR_100BTX                 (1 << 7)
+#define ANAR_10BT_FD                (1 << 6)
+#define ANAR_10BT                   (1 << 5)
+#define ANAR_SELECTOR4              (1 << 4)
+#define ANAR_SELECTOR3              (1 << 3)
+#define ANAR_SELECTOR2              (1 << 2)
+#define ANAR_SELECTOR1              (1 << 1)
+#define ANAR_SELECTOR0              (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NEXT_PAGE            (1 << 15)
+#define ANLPAR_LP_ACK               (1 << 14)
+#define ANLPAR_REMOTE_FAULT         (1 << 13)
+#define ANLPAR_PAUSE1               (1 << 11)
+#define ANLPAR_PAUSE0               (1 << 10)
+#define ANLPAR_100BT4               (1 << 9)
+#define ANLPAR_100BTX_FD            (1 << 8)
+#define ANLPAR_100BTX               (1 << 7)
+#define ANLPAR_10BT_FD              (1 << 6)
+#define ANLPAR_10BT                 (1 << 5)
+#define ANLPAR_SELECTOR4            (1 << 4)
+#define ANLPAR_SELECTOR3            (1 << 3)
+#define ANLPAR_SELECTOR2            (1 << 2)
+#define ANLPAR_SELECTOR1            (1 << 1)
+#define ANLPAR_SELECTOR0            (1 << 0)
+
+//ANER register
+#define ANER_PAR_DET_FAULT          (1 << 4)
+#define ANER_LP_NEXT_PAGE_ABLE      (1 << 3)
+#define ANER_NEXT_PAGE_ABLE         (1 << 2)
+#define ANER_PAGE_RECEIVED          (1 << 1)
+#define ANER_LP_AN_ABLE             (1 << 0)
+
+//ANNPTR register
+#define ANNPTR_NEXT_PAGE            (1 << 15)
+#define ANNPTR_MSG_PAGE             (1 << 13)
+#define ANNPTR_ACK2                 (1 << 12)
+#define ANNPTR_TOGGLE               (1 << 11)
+#define ANNPTR_MESSAGE10            (1 << 10)
+#define ANNPTR_MESSAGE9             (1 << 9)
+#define ANNPTR_MESSAGE8             (1 << 8)
+#define ANNPTR_MESSAGE7             (1 << 7)
+#define ANNPTR_MESSAGE6             (1 << 6)
+#define ANNPTR_MESSAGE5             (1 << 5)
+#define ANNPTR_MESSAGE4             (1 << 4)
+#define ANNPTR_MESSAGE3             (1 << 3)
+#define ANNPTR_MESSAGE2             (1 << 2)
+#define ANNPTR_MESSAGE1             (1 << 1)
+#define ANNPTR_MESSAGE0             (1 << 0)
+
+//LPNPAR register
+#define LPNPAR_NEXT_PAGE            (1 << 15)
+#define LPNPAR_ACK                  (1 << 14)
+#define LPNPAR_MSG_PAGE             (1 << 13)
+#define LPNPAR_ACK2                 (1 << 12)
+#define LPNPAR_TOGGLE               (1 << 11)
+#define LPNPAR_MESSAGE10            (1 << 10)
+#define LPNPAR_MESSAGE9             (1 << 9)
+#define LPNPAR_MESSAGE8             (1 << 8)
+#define LPNPAR_MESSAGE7             (1 << 7)
+#define LPNPAR_MESSAGE6             (1 << 6)
+#define LPNPAR_MESSAGE5             (1 << 5)
+#define LPNPAR_MESSAGE4             (1 << 4)
+#define LPNPAR_MESSAGE3             (1 << 3)
+#define LPNPAR_MESSAGE2             (1 << 2)
+#define LPNPAR_MESSAGE1             (1 << 1)
+#define LPNPAR_MESSAGE0             (1 << 0)
+
+//MMDCON register
+#define MMDCON_OP_MODE1             (1 << 15)
+#define MMDCON_OP_MODE0             (1 << 14)
+#define MMDCON_DEVICE_ADDR4         (1 << 4)
+#define MMDCON_DEVICE_ADDR3         (1 << 3)
+#define MMDCON_DEVICE_ADDR2         (1 << 2)
+#define MMDCON_DEVICE_ADDR1         (1 << 1)
+#define MMDCON_DEVICE_ADDR0         (1 << 0)
+
+//DRC register
+#define DRC_PLL_OFF                 (1 << 4)
+
+//AFECON1 register
+#define AFECON1_SLOW_OSC_MODE_EN    (1 << 5)
+
+//AFECON4 register
+#define AFECON4_10BT_MODE           (1 << 4)
+
+//OMSO register
+#define OMSO_PME_ENABLE             (1 << 15)
+#define OMSO_BCAST_OFF_OVERRIDE     (1 << 9)
+#define OMSO_RMII_BTB_OVERRIDE      (1 << 6)
+#define OMSO_NAND_TREE_OVERRIDE     (1 << 5)
+#define OMSO_RMII_OVERRIDE          (1 << 1)
+
+//OMSS register
+#define OMSS_PHYAD2                 (1 << 15)
+#define OMSS_PHYAD1                 (1 << 14)
+#define OMSS_PHYAD0                 (1 << 13)
+#define OMSS_RMII_STATUS            (1 << 1)
+
+//EXCON register
+#define EXCON_EDPD_DIS              (1 << 11)
+
+//ICSR register
+#define ICSR_JABBER_IE              (1 << 15)
+#define ICSR_RECEIVE_ERROR_IE       (1 << 14)
+#define ICSR_PAGE_RECEIVED_IE       (1 << 13)
+#define ICSR_PAR_DET_FAULT_IE       (1 << 12)
+#define ICSR_LP_ACK_IE              (1 << 11)
+#define ICSR_LINK_DOWN_IE           (1 << 10)
+#define ICSR_REMOTE_FAULT_IE        (1 << 9)
+#define ICSR_LINK_UP_IE             (1 << 8)
+#define ICSR_JABBER_IF              (1 << 7)
+#define ICSR_RECEIVE_ERROR_IF       (1 << 6)
+#define ICSR_PAGE_RECEIVED_IF       (1 << 5)
+#define ICSR_PAR_DET_FAULT_IF       (1 << 4)
+#define ICSR_LP_ACK_IF              (1 << 3)
+#define ICSR_LINK_DOWN_IF           (1 << 2)
+#define ICSR_REMOTE_FAULT_IF        (1 << 1)
+#define ICSR_LINK_UP_IF             (1 << 0)
+
+//LINKMDCS register
+#define LINKMDCS_CABLE_DIAG_EN      (1 << 15)
+#define LINKMDCS_CABLE_DIAG_RES1    (1 << 14)
+#define LINKMDCS_CABLE_DIAG_RES0    (1 << 13)
+#define LINKMDCS_SHORT_CABLE        (1 << 12)
+#define LINKMDCS_CABLE_FAULT_CNT8   (1 << 8)
+#define LINKMDCS_CABLE_FAULT_CNT7   (1 << 7)
+#define LINKMDCS_CABLE_FAULT_CNT6   (1 << 6)
+#define LINKMDCS_CABLE_FAULT_CNT5   (1 << 5)
+#define LINKMDCS_CABLE_FAULT_CNT4   (1 << 4)
+#define LINKMDCS_CABLE_FAULT_CNT3   (1 << 3)
+#define LINKMDCS_CABLE_FAULT_CNT2   (1 << 2)
+#define LINKMDCS_CABLE_FAULT_CNT1   (1 << 1)
+#define LINKMDCS_CABLE_FAULT_CNT0   (1 << 0)
+
+//PHYCON1 register
+#define PHYCON1_PAUSE_EN            (1 << 9)
+#define PHYCON1_LINK_STATUS         (1 << 8)
+#define PHYCON1_POL_STATUS          (1 << 7)
+#define PHYCON1_MDIX_STATE          (1 << 5)
+#define PHYCON1_ENERGY_DETECT       (1 << 4)
+#define PHYCON1_ISOLATE             (1 << 3)
+#define PHYCON1_OP_MODE2            (1 << 2)
+#define PHYCON1_OP_MODE1            (1 << 1)
+#define PHYCON1_OP_MODE0            (1 << 0)
+
+//Operation mode indication
+#define PHYCON1_OP_MODE_MASK        (7 << 0)
+#define PHYCON1_OP_MODE_AN          (0 << 0)
+#define PHYCON1_OP_MODE_10BT        (1 << 0)
+#define PHYCON1_OP_MODE_100BTX      (2 << 0)
+#define PHYCON1_OP_MODE_10BT_FD     (5 << 0)
+#define PHYCON1_OP_MODE_100BTX_FD   (6 << 0)
+
+//PHYCON2 register
+#define PHYCON2_HP_MDIX             (1 << 15)
+#define PHYCON2_MDIX_SEL            (1 << 14)
+#define PHYCON2_PAIR_SWAP_DIS       (1 << 13)
+#define PHYCON2_FORCE_LINK          (1 << 11)
+#define PHYCON2_POWER_SAVING        (1 << 10)
+#define PHYCON2_INT_LEVEL           (1 << 9)
+#define PHYCON2_JABBER_EN           (1 << 8)
+#define PHYCON2_RMII_REF_CLK_SEL    (1 << 7)
+#define PHYCON2_LED_MODE1           (1 << 5)
+#define PHYCON2_LED_MODE0           (1 << 4)
+#define PHYCON2_TX_DIS              (1 << 3)
+#define PHYCON2_REMOTE_LOOPBACK     (1 << 2)
+#define PHYCON2_SCRAMBLER_DIS       (1 << 0)
+
+//KSZ8091 Ethernet PHY driver
+extern const PhyDriver ksz8091PhyDriver;
+
+//KSZ8091 related functions
+error_t ksz8091Init(NetInterface *interface);
+
+void ksz8091Tick(NetInterface *interface);
+
+void ksz8091EnableIrq(NetInterface *interface);
+void ksz8091DisableIrq(NetInterface *interface);
+
+void ksz8091EventHandler(NetInterface *interface);
+
+void ksz8091WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t ksz8091ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void ksz8091DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8721.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,289 @@
+/**
+ * @file ksz8721.c
+ * @brief KSZ8721 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/ksz8721.h"
+#include "debug.h"
+
+
+/**
+ * @brief KSZ8721 Ethernet PHY driver
+ **/
+
+const PhyDriver ksz8721PhyDriver =
+{
+   ksz8721Init,
+   ksz8721Tick,
+   ksz8721EnableIrq,
+   ksz8721DisableIrq,
+   ksz8721EventHandler,
+};
+
+
+/**
+ * @brief KSZ8721 PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ksz8721Init(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing KSZ8721...\r\n");
+
+   //Initialize external interrupt line driver
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->init();
+
+   //Reset PHY transceiver
+   ksz8721WritePhyReg(interface, KSZ8721_PHY_REG_BMCR, BMCR_RESET);
+   //Wait for the reset to complete
+   while(ksz8721ReadPhyReg(interface, KSZ8721_PHY_REG_BMCR) & BMCR_RESET);
+
+   //Dump PHY registers for debugging purpose
+   ksz8721DumpPhyReg(interface);
+
+   //The PHY will generate interrupts when link status changes are detected
+   ksz8721WritePhyReg(interface, KSZ8721_PHY_REG_ICSR, ICSR_LINK_DOWN_IE | ICSR_LINK_UP_IE);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief KSZ8721 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8721Tick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //No external interrupt line driver?
+   if(interface->extIntDriver == NULL)
+   {
+      //Read basic status register
+      value = ksz8721ReadPhyReg(interface, KSZ8721_PHY_REG_BMSR);
+      //Retrieve current link state
+      linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+      //Link up event?
+      if(linkState && !interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+      //Link down event?
+      else if(!linkState && interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8721EnableIrq(NetInterface *interface)
+{
+   //Enable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8721DisableIrq(NetInterface *interface)
+{
+   //Disable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief KSZ8721 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8721EventHandler(NetInterface *interface)
+{
+   uint16_t value;
+
+   //Read status register to acknowledge the interrupt
+   value = ksz8721ReadPhyReg(interface, KSZ8721_PHY_REG_ICSR);
+
+   //Link status change?
+   if(value & (ICSR_LINK_DOWN_IF | ICSR_LINK_UP_IF))
+   {
+      //Any link failure condition is latched in the BMSR register... Reading
+      //the register twice will always return the actual link status
+      value = ksz8721ReadPhyReg(interface, KSZ8721_PHY_REG_BMSR);
+      value = ksz8721ReadPhyReg(interface, KSZ8721_PHY_REG_BMSR);
+
+      //Link is up?
+      if(value & BMSR_LINK_STATUS)
+      {
+         //Read PHY control register
+         value = ksz8721ReadPhyReg(interface, KSZ8721_PHY_REG_PHYCON);
+
+         //Check current operation mode
+         switch(value & PHYCON_OP_MODE_MASK)
+         {
+         //10BASE-T
+         case PHYCON_OP_MODE_10BT:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //10BASE-T full-duplex
+         case PHYCON_OP_MODE_10BT_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //100BASE-TX
+         case PHYCON_OP_MODE_100BTX:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //100BASE-TX full-duplex
+         case PHYCON_OP_MODE_100BTX_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //Unknown operation mode
+         default:
+            //Debug message
+            TRACE_WARNING("Invalid Duplex mode\r\n");
+            break;
+         }
+
+         //Update link state
+         interface->linkState = TRUE;
+
+         //Adjust MAC configuration parameters for proper operation
+         interface->nicDriver->updateMacConfig(interface);
+      }
+      else
+      {
+         //Update link state
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void ksz8721WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = KSZ8721_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t ksz8721ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = KSZ8721_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8721DumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, ksz8721ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8721.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,200 @@
+/**
+ * @file ksz8721.h
+ * @brief KSZ8721 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _KSZ8721_H
+#define _KSZ8721_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#ifndef KSZ8721_PHY_ADDR
+   #define KSZ8721_PHY_ADDR 1
+#elif (KSZ8721_PHY_ADDR < 0 || KSZ8721_PHY_ADDR > 31)
+   #error KSZ8721_PHY_ADDR parameter is not valid
+#endif
+
+//KSZ8721 registers
+#define KSZ8721_PHY_REG_BMCR     0x00
+#define KSZ8721_PHY_REG_BMSR     0x01
+#define KSZ8721_PHY_REG_PHYIDR1  0x02
+#define KSZ8721_PHY_REG_PHYIDR2  0x03
+#define KSZ8721_PHY_REG_ANAR     0x04
+#define KSZ8721_PHY_REG_ANLPAR   0x05
+#define KSZ8721_PHY_REG_ANER     0x06
+#define KSZ8721_PHY_REG_ANNPTR   0x07
+#define KSZ8721_PHY_REG_LPNPAR   0x08
+#define KSZ8721_PHY_REG_RECR     0x15
+#define KSZ8721_PHY_REG_ICSR     0x1B
+#define KSZ8721_PHY_REG_PHYCON   0x1F
+
+//BMCR register
+#define BMCR_RESET               (1 << 15)
+#define BMCR_LOOPBACK            (1 << 14)
+#define BMCR_SPEED_SEL           (1 << 13)
+#define BMCR_AN_EN               (1 << 12)
+#define BMCR_POWER_DOWN          (1 << 11)
+#define BMCR_ISOLATE             (1 << 10)
+#define BMCR_RESTART_AN          (1 << 9)
+#define BMCR_DUPLEX_MODE         (1 << 8)
+#define BMCR_COL_TEST            (1 << 7)
+#define BMCR_TX_DIS              (1 << 0)
+
+//BMSR register
+#define BMSR_100BT4              (1 << 15)
+#define BMSR_100BTX_FD           (1 << 14)
+#define BMSR_100BTX              (1 << 13)
+#define BMSR_10BT_FD             (1 << 12)
+#define BMSR_10BT                (1 << 11)
+#define BMSR_NO_PREAMBLE         (1 << 6)
+#define BMSR_AN_COMPLETE         (1 << 5)
+#define BMSR_REMOTE_FAULT        (1 << 4)
+#define BMSR_AN_ABLE             (1 << 3)
+#define BMSR_LINK_STATUS         (1 << 2)
+#define BMSR_JABBER_DETECT       (1 << 1)
+#define BMSR_EXTENDED_CAP        (1 << 0)
+
+//ANAR register
+#define ANAR_NEXT_PAGE           (1 << 15)
+#define ANAR_REMOTE_FAULT        (1 << 13)
+#define ANAR_PAUSE               (1 << 10)
+#define ANAR_100BT4              (1 << 9)
+#define ANAR_100BTX_FD           (1 << 8)
+#define ANAR_100BTX              (1 << 7)
+#define ANAR_10BT_FD             (1 << 6)
+#define ANAR_10BT                (1 << 5)
+#define ANAR_SELECTOR4           (1 << 4)
+#define ANAR_SELECTOR3           (1 << 3)
+#define ANAR_SELECTOR2           (1 << 2)
+#define ANAR_SELECTOR1           (1 << 1)
+#define ANAR_SELECTOR0           (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NEXT_PAGE         (1 << 15)
+#define ANLPAR_LP_ACK            (1 << 14)
+#define ANLPAR_REMOTE_FAULT      (1 << 13)
+#define ANLPAR_PAUSE1            (1 << 11)
+#define ANLPAR_PAUSE0            (1 << 10)
+#define ANLPAR_100BT4            (1 << 9)
+#define ANLPAR_100BTX_FD         (1 << 8)
+#define ANLPAR_100BTX            (1 << 7)
+#define ANLPAR_10BT_FD           (1 << 6)
+#define ANLPAR_10BT              (1 << 5)
+#define ANLPAR_SELECTOR4         (1 << 4)
+#define ANLPAR_SELECTOR3         (1 << 3)
+#define ANLPAR_SELECTOR2         (1 << 2)
+#define ANLPAR_SELECTOR1         (1 << 1)
+#define ANLPAR_SELECTOR0         (1 << 0)
+
+//ANER register
+#define ANER_PAR_DET_FAULT       (1 << 4)
+#define ANER_LP_NEXT_PAGE_ABLE   (1 << 3)
+#define ANER_NEXT_PAGE_ABLE      (1 << 2)
+#define ANER_PAGE_RECEIVED       (1 << 1)
+#define ANER_LP_AN_ABLE          (1 << 0)
+
+//ANNPTR register
+#define ANNPTR_NEXT_PAGE         (1 << 15)
+#define ANNPTR_MSG_PAGE          (1 << 13)
+#define ANNPTR_ACK2              (1 << 12)
+#define ANNPTR_TOGGLE            (1 << 11)
+#define ANNPTR_MESSAGE10         (1 << 10)
+#define ANNPTR_MESSAGE9          (1 << 9)
+#define ANNPTR_MESSAGE8          (1 << 8)
+#define ANNPTR_MESSAGE7          (1 << 7)
+#define ANNPTR_MESSAGE6          (1 << 6)
+#define ANNPTR_MESSAGE5          (1 << 5)
+#define ANNPTR_MESSAGE4          (1 << 4)
+#define ANNPTR_MESSAGE3          (1 << 3)
+#define ANNPTR_MESSAGE2          (1 << 2)
+#define ANNPTR_MESSAGE1          (1 << 1)
+#define ANNPTR_MESSAGE0          (1 << 0)
+
+//ICSR register
+#define ICSR_JABBER_IE           (1 << 15)
+#define ICSR_RECEIVE_ERROR_IE    (1 << 14)
+#define ICSR_PAGE_RECEIVED_IE    (1 << 13)
+#define ICSR_PAR_DET_FAULT_IE    (1 << 12)
+#define ICSR_LP_ACK_IE           (1 << 11)
+#define ICSR_LINK_DOWN_IE        (1 << 10)
+#define ICSR_REMOTE_FAULT_IE     (1 << 9)
+#define ICSR_LINK_UP_IE          (1 << 8)
+#define ICSR_JABBER_IF           (1 << 7)
+#define ICSR_RECEIVE_ERROR_IF    (1 << 6)
+#define ICSR_PAGE_RECEIVED_IF    (1 << 5)
+#define ICSR_PAR_DET_FAULT_IF    (1 << 4)
+#define ICSR_LP_ACK_IF           (1 << 3)
+#define ICSR_LINK_DOWN_IF        (1 << 2)
+#define ICSR_REMOTE_FAULT_IF     (1 << 1)
+#define ICSR_LINK_UP_IF          (1 << 0)
+
+//PHYCON register
+#define PHYCON_PAIR_SWAP_DIS     (1 << 13)
+#define PHYCON_ENERGY_DETECT     (1 << 12)
+#define PHYCON_FORCE_LINK        (1 << 11)
+#define PHYCON_POWER_SAVING      (1 << 10)
+#define PHYCON_INT_LEVEL         (1 << 9)
+#define PHYCON_JABBER_EN         (1 << 8)
+#define PHYCON_AN_COMPLETE       (1 << 7)
+#define PHYCON_PAUSE_EN          (1 << 6)
+#define PHYCON_ISOLATE           (1 << 5)
+#define PHYCON_OP_MODE2          (1 << 4)
+#define PHYCON_OP_MODE1          (1 << 3)
+#define PHYCON_OP_MODE0          (1 << 2)
+#define PHYCON_SQE_TEST_EN       (1 << 1)
+#define PHYCON_SCRAMBLER_DIS     (1 << 0)
+
+//Operation mode indication
+#define PHYCON_OP_MODE_MASK      (7 << 2)
+#define PHYCON_OP_MODE_AN        (0 << 2)
+#define PHYCON_OP_MODE_10BT      (1 << 2)
+#define PHYCON_OP_MODE_100BTX    (2 << 2)
+#define PHYCON_OP_MODE_10BT_FD   (5 << 2)
+#define PHYCON_OP_MODE_100BTX_FD (6 << 2)
+#define PHYCON_OP_MODE_ISOLATE   (7 << 2)
+
+//KSZ8721 Ethernet PHY driver
+extern const PhyDriver ksz8721PhyDriver;
+
+//KSZ8721 related functions
+error_t ksz8721Init(NetInterface *interface);
+
+void ksz8721Tick(NetInterface *interface);
+
+void ksz8721EnableIrq(NetInterface *interface);
+void ksz8721DisableIrq(NetInterface *interface);
+
+void ksz8721EventHandler(NetInterface *interface);
+
+void ksz8721WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t ksz8721ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void ksz8721DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8851.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,819 @@
+/**
+ * @file ksz8851.c
+ * @brief KSZ8851 Ethernet controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/ksz8851.h"
+#include "debug.h"
+
+
+/**
+ * @brief KSZ8851 driver
+ **/
+
+const NicDriver ksz8851Driver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   ksz8851Init,
+   ksz8851Tick,
+   ksz8851EnableIrq,
+   ksz8851DisableIrq,
+   ksz8851EventHandler,
+   ksz8851SendPacket,
+   ksz8851SetMulticastFilter,
+   NULL,
+   NULL,
+   NULL,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief KSZ8851 controller initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ksz8851Init(NetInterface *interface)
+{
+   //Point to the driver context
+   Ksz8851Context *context = (Ksz8851Context *) interface->nicContext;
+
+   //Debug message
+   TRACE_INFO("Initializing KSZ8851 Ethernet controller...\r\n");
+
+#if (KSZ8851_SPI_SUPPORT == ENABLED)
+   //Initialize SPI
+   interface->spiDriver->init();
+#endif
+
+   //Initialize external interrupt line
+   interface->extIntDriver->init();
+
+   //Debug message
+   TRACE_DEBUG("CIDER=0x%04" PRIX16 "\r\n", ksz8851ReadReg(interface, KSZ8851_REG_CIDER));
+   TRACE_DEBUG("PHY1ILR=0x%04" PRIX16 "\r\n", ksz8851ReadReg(interface, KSZ8851_REG_PHY1ILR));
+   TRACE_DEBUG("PHY1IHR=0x%04" PRIX16 "\r\n", ksz8851ReadReg(interface, KSZ8851_REG_PHY1IHR));
+
+   //Check device ID and revision ID
+   if(ksz8851ReadReg(interface, KSZ8851_REG_CIDER) != KSZ8851_REV_A3_ID)
+      return ERROR_WRONG_IDENTIFIER;
+
+   //Dump registers for debugging purpose
+   ksz8851DumpReg(interface);
+
+   //Initialize driver specific variables
+   context->frameId = 0;
+
+   //Allocate TX and RX buffers
+   context->txBuffer = memPoolAlloc(ETH_MAX_FRAME_SIZE);
+   context->rxBuffer = memPoolAlloc(ETH_MAX_FRAME_SIZE);
+
+   //Failed to allocate memory?
+   if(context->txBuffer == NULL || context->rxBuffer == NULL)
+   {
+      //Clean up side effects
+      memPoolFree(context->txBuffer);
+      memPoolFree(context->rxBuffer);
+
+      //Report an error
+      return ERROR_OUT_OF_MEMORY;
+   }
+
+   //Initialize MAC address
+   ksz8851WriteReg(interface, KSZ8851_REG_MARH, htons(interface->macAddr.w[0]));
+   ksz8851WriteReg(interface, KSZ8851_REG_MARM, htons(interface->macAddr.w[1]));
+   ksz8851WriteReg(interface, KSZ8851_REG_MARL, htons(interface->macAddr.w[2]));
+
+   //Packets shorter than 64 bytes are padded and the CRC is automatically generated
+   ksz8851WriteReg(interface, KSZ8851_REG_TXCR, TXCR_TXFCE | TXCR_TXPE | TXCR_TXCE);
+   //Automatically increment TX data pointer
+   ksz8851WriteReg(interface, KSZ8851_REG_TXFDPR, TXFDPR_TXFPAI);
+
+   //Configure address filtering
+   ksz8851WriteReg(interface, KSZ8851_REG_RXCR1,
+      RXCR1_RXPAFMA | RXCR1_RXFCE | RXCR1_RXBE | RXCR1_RXME | RXCR1_RXUE);
+
+   //No checksum verification
+   ksz8851WriteReg(interface, KSZ8851_REG_RXCR2,
+      RXCR2_SRDBL2 | RXCR2_IUFFP | RXCR2_RXIUFCEZ);
+
+   //Enable automatic RXQ frame buffer dequeue
+   ksz8851WriteReg(interface, KSZ8851_REG_RXQCR, RXQCR_RXFCTE | RXQCR_ADRFE);
+   //Automatically increment RX data pointer
+   ksz8851WriteReg(interface, KSZ8851_REG_RXFDPR, RXFDPR_RXFPAI);
+   //Configure receive frame count threshold
+   ksz8851WriteReg(interface, KSZ8851_REG_RXFCTR, 1);
+
+   //Force link in half-duplex if auto-negotiation failed
+   ksz8851ClearBit(interface, KSZ8851_REG_P1CR, P1CR_FORCE_DUPLEX);
+   //Restart auto-negotiation
+   ksz8851SetBit(interface, KSZ8851_REG_P1CR, P1CR_RESTART_AN);
+
+   //Clear interrupt flags
+   ksz8851SetBit(interface, KSZ8851_REG_ISR, ISR_LCIS | ISR_TXIS |
+      ISR_RXIS | ISR_RXOIS | ISR_TXPSIS | ISR_RXPSIS | ISR_TXSAIS |
+      ISR_RXWFDIS | ISR_RXMPDIS | ISR_LDIS | ISR_EDIS | ISR_SPIBEIS);
+
+   //Configure interrupts as desired
+   ksz8851SetBit(interface, KSZ8851_REG_IER, IER_LCIE | IER_TXIE | IER_RXIE);
+
+   //Enable TX operation
+   ksz8851SetBit(interface, KSZ8851_REG_TXCR, TXCR_TXE);
+   //Enable RX operation
+   ksz8851SetBit(interface, KSZ8851_REG_RXCR1, RXCR1_RXE);
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->nicEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief KSZ8851 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8851Tick(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8851EnableIrq(NetInterface *interface)
+{
+   //Enable interrupts
+   interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8851DisableIrq(NetInterface *interface)
+{
+   //Disable interrupts
+   interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief KSZ8851 interrupt service routine
+ * @param[in] interface Underlying network interface
+ * @return TRUE if a higher priority task must be woken. Else FALSE is returned
+ **/
+
+bool_t ksz8851IrqHandler(NetInterface *interface)
+{
+   bool_t flag;
+   uint16_t ier;
+   uint16_t isr;
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Save IER register value
+   ier = ksz8851ReadReg(interface, KSZ8851_REG_IER);
+   //Disable interrupts to release the interrupt line
+   ksz8851WriteReg(interface, KSZ8851_REG_IER, 0);
+
+   //Read interrupt status register
+   isr = ksz8851ReadReg(interface, KSZ8851_REG_ISR);
+
+   //Link status change?
+   if(isr & ISR_LCIS)
+   {
+      //Disable LCIE interrupt
+      ier &= ~IER_LCIE;
+
+      //Set event flag
+      interface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Packet transmission complete?
+   if(isr & ISR_TXIS)
+   {
+      //Clear interrupt flag
+      ksz8851WriteReg(interface, KSZ8851_REG_ISR, ISR_TXIS);
+
+      //Notify the TCP/IP stack that the transmitter is ready to send
+      flag |= osSetEventFromIsr(&interface->nicTxEvent);
+   }
+
+   //Packet received?
+   if(isr & ISR_RXIS)
+   {
+      //Disable RXIE interrupt
+      ier &= ~IER_RXIE;
+
+      //Set event flag
+      interface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Re-enable interrupts once the interrupt has been serviced
+   ksz8851WriteReg(interface, KSZ8851_REG_IER, ier);
+
+   //A higher priority task must be woken?
+   return flag;
+}
+
+
+/**
+ * @brief KSZ8851 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8851EventHandler(NetInterface *interface)
+{
+   uint16_t status;
+   uint_t frameCount;
+
+   //Read interrupt status register
+   status = ksz8851ReadReg(interface, KSZ8851_REG_ISR);
+
+   //Check whether the link status has changed?
+   if(status & ISR_LCIS)
+   {
+      //Clear interrupt flag
+      ksz8851WriteReg(interface, KSZ8851_REG_ISR, ISR_LCIS);
+      //Read PHY status register
+      status = ksz8851ReadReg(interface, KSZ8851_REG_P1SR);
+
+      //Check link state
+      if(status & P1SR_LINK_GOOD)
+      {
+         //Get current speed
+         if(status & P1SR_OPERATION_SPEED)
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+         else
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+
+         //Determine the new duplex mode
+         if(status & P1SR_OPERATION_DUPLEX)
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+         else
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+
+         //Link is up
+         interface->linkState = TRUE;
+      }
+      else
+      {
+         //Link is down
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+
+   //Check whether a packet has been received?
+   if(status & ISR_RXIS)
+   {
+      //Clear interrupt flag
+      ksz8851WriteReg(interface, KSZ8851_REG_ISR, ISR_RXIS);
+      //Get the total number of frames that are pending in the buffer
+      frameCount = MSB(ksz8851ReadReg(interface, KSZ8851_REG_RXFCTR));
+
+      //Process all pending packets
+      while(frameCount > 0)
+      {
+         //Read incoming packet
+         ksz8851ReceivePacket(interface);
+         //Decrement frame counter
+         frameCount--;
+      }
+   }
+
+   //Re-enable LCIE and RXIE interrupts
+   ksz8851SetBit(interface, KSZ8851_REG_IER, IER_LCIE | IER_RXIE);
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t ksz8851SendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t n;
+   size_t length;
+   Ksz8851TxHeader header;
+   Ksz8851Context *context;
+
+   //Point to the driver context
+   context = (Ksz8851Context *) interface->nicContext;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > ETH_MAX_FRAME_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Get the amount of free memory available in the TX FIFO
+   n = ksz8851ReadReg(interface, KSZ8851_REG_TXMIR) & TXMIR_TXMA_MASK;
+
+   //Make sure enough memory is available
+   if((length + 8) > n)
+      return ERROR_FAILURE;
+
+   //Copy user data
+   netBufferRead(context->txBuffer, buffer, offset, length);
+
+   //Format control word
+   header.controlWord = TX_CTRL_TXIC | (context->frameId++ & TX_CTRL_TXFID);
+   //Total number of bytes to be transmitted
+   header.byteCount = length;
+
+   //Enable TXQ write access
+   ksz8851SetBit(interface, KSZ8851_REG_RXQCR, RXQCR_SDA);
+   //Write TX packet header
+   ksz8851WriteFifo(interface, (uint8_t *) &header, sizeof(Ksz8851TxHeader));
+   //Write data
+   ksz8851WriteFifo(interface, context->txBuffer, length);
+   //End TXQ write access
+   ksz8851ClearBit(interface, KSZ8851_REG_RXQCR, RXQCR_SDA);
+
+   //Start transmission
+   ksz8851SetBit(interface, KSZ8851_REG_TXQCR, TXQCR_METFE);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ksz8851ReceivePacket(NetInterface *interface)
+{
+   size_t n;
+   uint16_t status;
+   Ksz8851Context *context;
+
+   //Point to the driver context
+   context = (Ksz8851Context *) interface->nicContext;
+
+   //Read received frame status from RXFHSR
+   status = ksz8851ReadReg(interface, KSZ8851_REG_RXFHSR);
+
+   //Make sure the frame is valid
+   if(status & RXFHSR_RXFV)
+   {
+      //Check error flags
+      if(!(status & (RXFHSR_RXMR | RXFHSR_RXFTL | RXFHSR_RXRF | RXFHSR_RXCE)))
+      {
+         //Read received frame byte size from RXFHBCR
+         n = ksz8851ReadReg(interface, KSZ8851_REG_RXFHBCR) & RXFHBCR_RXBC_MASK;
+
+         //Ensure the frame size is acceptable
+         if(n > 0 && n <= ETH_MAX_FRAME_SIZE)
+         {
+            //Reset QMU RXQ frame pointer to zero
+            ksz8851WriteReg(interface, KSZ8851_REG_RXFDPR, RXFDPR_RXFPAI);
+            //Enable RXQ read access
+            ksz8851SetBit(interface, KSZ8851_REG_RXQCR, RXQCR_SDA);
+            //Read data
+            ksz8851ReadFifo(interface, context->rxBuffer, n);
+            //End RXQ read access
+            ksz8851ClearBit(interface, KSZ8851_REG_RXQCR, RXQCR_SDA);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, context->rxBuffer, n);
+            //Valid packet received
+            return NO_ERROR;
+         }
+      }
+   }
+
+   //Release the current error frame from RXQ
+   ksz8851SetBit(interface, KSZ8851_REG_RXQCR, RXQCR_RRXEF);
+   //Report an error
+   return ERROR_INVALID_PACKET;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ksz8851SetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint16_t hashTable[4];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating KSZ8851 hash table...\r\n");
+
+   //Clear hash table
+   memset(hashTable, 0, sizeof(hashTable));
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = ksz8851CalcCrc(&entry->addr, sizeof(MacAddr));
+         //Calculate the corresponding index in the table
+         k = (crc >> 26) & 0x3F;
+         //Update hash table contents
+         hashTable[k / 16] |= (1 << (k % 16));
+      }
+   }
+
+   //Write the hash table to the KSZ8851 controller
+   ksz8851WriteReg(interface, KSZ8851_REG_MAHTR0, hashTable[0]);
+   ksz8851WriteReg(interface, KSZ8851_REG_MAHTR1, hashTable[1]);
+   ksz8851WriteReg(interface, KSZ8851_REG_MAHTR2, hashTable[2]);
+   ksz8851WriteReg(interface, KSZ8851_REG_MAHTR3, hashTable[3]);
+
+   //Debug message
+   TRACE_DEBUG("  MAHTR0 = %04" PRIX16 "\r\n", ksz8851ReadReg(interface, KSZ8851_REG_MAHTR0));
+   TRACE_DEBUG("  MAHTR1 = %04" PRIX16 "\r\n", ksz8851ReadReg(interface, KSZ8851_REG_MAHTR1));
+   TRACE_DEBUG("  MAHTR2 = %04" PRIX16 "\r\n", ksz8851ReadReg(interface, KSZ8851_REG_MAHTR2));
+   TRACE_DEBUG("  MAHTR3 = %04" PRIX16 "\r\n", ksz8851ReadReg(interface, KSZ8851_REG_MAHTR3));
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write KSZ8851 register
+ * @param[in] interface Underlying network interface
+ * @param[in] address Register address
+ * @param[in] data Register value
+ **/
+
+void ksz8851WriteReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+#if (KSZ8851_SPI_SUPPORT == ENABLED)
+   uint8_t command;
+
+   //Form the write command
+   if(address & 0x02)
+      command = KSZ8851_CMD_WR_REG | KSZ8851_CMD_B3 | KSZ8851_CMD_B2;
+   else
+      command = KSZ8851_CMD_WR_REG | KSZ8851_CMD_B1 | KSZ8851_CMD_B0;
+
+   //Pull the CS pin low
+   interface->spiDriver->assertCs();
+
+   //Command phase
+   interface->spiDriver->transfer(command | (address >> 6));
+   interface->spiDriver->transfer(address << 2);
+
+   //Data phase
+   interface->spiDriver->transfer(LSB(data));
+   interface->spiDriver->transfer(MSB(data));
+
+   //Terminate the operation by raising the CS pin
+   interface->spiDriver->deassertCs();
+#else
+   //Set register address
+   if(address & 0x02)
+      KSZ8851_CMD_REG = KSZ8851_CMD_B3 | KSZ8851_CMD_B2 | address;
+   else
+      KSZ8851_CMD_REG = KSZ8851_CMD_B1 | KSZ8851_CMD_B0 | address;
+
+   //Write register value
+   KSZ8851_DATA_REG = data;
+#endif
+}
+
+
+/**
+ * @brief Read KSZ8851 register
+ * @param[in] interface Underlying network interface
+ * @param[in] address Register address
+ * @return Register value
+ **/
+
+uint16_t ksz8851ReadReg(NetInterface *interface, uint8_t address)
+{
+#if (KSZ8851_SPI_SUPPORT == ENABLED)
+   uint8_t command;
+   uint16_t data;
+
+   //Form the read command
+   if(address & 0x02)
+      command = KSZ8851_CMD_RD_REG | KSZ8851_CMD_B3 | KSZ8851_CMD_B2;
+   else
+      command = KSZ8851_CMD_RD_REG | KSZ8851_CMD_B1 | KSZ8851_CMD_B0;
+
+   //Pull the CS pin low
+   interface->spiDriver->assertCs();
+
+   //Command phase
+   interface->spiDriver->transfer(command | (address >> 6));
+   interface->spiDriver->transfer(address << 2);
+
+   //Data phase (lower 8 bits)
+   data = interface->spiDriver->transfer(0x00);
+   //Data phase (upper 8 bits)
+   data |= interface->spiDriver->transfer(0x00) << 8;
+
+   //Terminate the operation by raising the CS pin
+   interface->spiDriver->deassertCs();
+
+   //Return register value
+   return data;
+#else
+   //Set register address
+   if(address & 0x02)
+      KSZ8851_CMD_REG = KSZ8851_CMD_B3 | KSZ8851_CMD_B2 | address;
+   else
+      KSZ8851_CMD_REG = KSZ8851_CMD_B1 | KSZ8851_CMD_B0 | address;
+
+   //Return register value
+   return KSZ8851_DATA_REG;
+#endif
+}
+
+
+/**
+ * @brief Write TX FIFO
+ * @param[in] interface Underlying network interface
+ * @param[in] data Pointer to the data being written
+ * @param[in] length Number of data to write
+ **/
+
+void ksz8851WriteFifo(NetInterface *interface, const uint8_t *data, size_t length)
+{
+#if (KSZ8851_SPI_SUPPORT == ENABLED)
+   uint_t i;
+
+   //Pull the CS pin low
+   interface->spiDriver->assertCs();
+
+   //Command phase
+   interface->spiDriver->transfer(KSZ8851_CMD_WR_FIFO);
+
+   //Data phase
+   for(i = 0; i < length; i++)
+      interface->spiDriver->transfer(data[i]);
+
+   //Maintain alignment to 4-byte boundaries
+   for(; i % 4; i++)
+      interface->spiDriver->transfer(0x00);
+
+   //Terminate the operation by raising the CS pin
+   interface->spiDriver->deassertCs();
+#else
+   uint_t i;
+
+   //Data phase
+   for(i = 0; i < length; i+=2)
+      KSZ8851_DATA_REG = data[i] | data[i+1]<<8;
+
+   //Maintain alignment to 4-byte boundaries
+   for(; i % 4; i+=2)
+      KSZ8851_DATA_REG = 0x0000;
+#endif
+}
+
+
+/**
+ * @brief Read RX FIFO
+ * @param[in] interface Underlying network interface
+ * @param[in] data Buffer where to store the incoming data
+ * @param[in] length Number of data to read
+ **/
+
+void ksz8851ReadFifo(NetInterface *interface, uint8_t *data, size_t length)
+{
+#if (KSZ8851_SPI_SUPPORT == ENABLED)
+   uint_t i;
+
+   //Pull the CS pin low
+   interface->spiDriver->assertCs();
+
+   //Command phase
+   interface->spiDriver->transfer(KSZ8851_CMD_RD_FIFO);
+
+   //The first 4 bytes are dummy data and must be discarded
+   for(i = 0; i < 4; i++)
+      interface->spiDriver->transfer(0x00);
+
+   //Ignore RX packet header
+   for(i = 0; i < 4; i++)
+      interface->spiDriver->transfer(0x00);
+
+   //Data phase
+   for(i = 0; i < length; i++)
+      data[i] = interface->spiDriver->transfer(0x00);
+
+   //Maintain alignment to 4-byte boundaries
+   for(; i % 4; i++)
+      interface->spiDriver->transfer(0x00);
+
+   //Terminate the operation by raising the CS pin
+   interface->spiDriver->deassertCs();
+#else
+   uint_t i;
+   uint16_t temp;
+
+   //The first 2 bytes are dummy data and must be discarded
+   temp = KSZ8851_DATA_REG;
+
+   //Ignore RX packet header
+   temp = KSZ8851_DATA_REG;
+   temp = KSZ8851_DATA_REG;
+
+   //Data phase
+   for(i = 0; i < length; i+=2)
+   {
+      temp = KSZ8851_DATA_REG;
+      data [i] = temp & 0xFF;
+      data [i+1] = (temp>>8) & 0xFF;
+   }
+
+   //Maintain alignment to 4-byte boundaries
+   for(; i % 4; i+=2)
+      temp = KSZ8851_DATA_REG;
+#endif
+}
+
+
+/**
+ * @brief Set bit field
+ * @param[in] interface Underlying network interface
+ * @param[in] address Register address
+ * @param[in] mask Bits to set in the target register
+ **/
+
+void ksz8851SetBit(NetInterface *interface, uint8_t address, uint16_t mask)
+{
+   uint16_t value;
+
+   //Read current register value
+   value = ksz8851ReadReg(interface, address);
+   //Set specified bits
+   ksz8851WriteReg(interface, address, value | mask);
+}
+
+
+/**
+ * @brief Clear bit field
+ * @param[in] interface Underlying network interface
+ * @param[in] address Register address
+ * @param[in] mask Bits to clear in the target register
+ **/
+
+void ksz8851ClearBit(NetInterface *interface, uint8_t address, uint16_t mask)
+{
+   uint16_t value;
+
+   //Read current register value
+   value = ksz8851ReadReg(interface, address);
+   //Clear specified bits
+   ksz8851WriteReg(interface, address, value & ~mask);
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t ksz8851CalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return crc;
+}
+
+
+/**
+ * @brief Dump registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8851DumpReg(NetInterface *interface)
+{
+#if (TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+   uint_t i;
+   uint_t j;
+   uint_t address;
+
+   //Loop through register addresses
+   for(i = 0; i < 256; i += 16)
+   {
+      //Display register address
+      TRACE_DEBUG("%02" PRIu8 ": ", i);
+
+      //Display 8 registers at a time
+      for(j = 0; j < 16; j += 2)
+      {
+         //Format register address
+         address = i + j;
+         //Display register contents
+         TRACE_DEBUG("0x%04" PRIX16 "  ", ksz8851ReadReg(interface, address));
+      }
+
+      //Jump to the following line
+      TRACE_DEBUG("\r\n");
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+#endif
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8851.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,504 @@
+/**
+ * @file ksz8851.h
+ * @brief KSZ8851 Ethernet controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _KSZ8851_H
+#define _KSZ8851_H
+
+//SPI interface support
+#ifndef KSZ8851_SPI_SUPPORT
+   #define KSZ8851_SPI_SUPPORT ENABLED
+#elif (KSZ8851_SPI_SUPPORT != ENABLED && KSZ8851_SPI_SUPPORT != DISABLED)
+   #error KSZ8851_SPI_SUPPORT parameter is not valid
+#endif
+
+//KSZ8851 data register
+#ifndef KSZ8851_DATA_REG
+   #define KSZ8851_DATA_REG *((volatile uint16_t *) 0x60000000)
+#endif
+
+//KSZ8851 command register
+#ifndef KSZ8851_CMD_REG
+   #define KSZ8851_CMD_REG *((volatile uint16_t *) 0x60000004)
+#endif
+
+//Device ID
+#define KSZ8851_REV_A2_ID        0x8870
+#define KSZ8851_REV_A3_ID        0x8872
+
+//SPI command set
+#define KSZ8851_CMD_RD_REG       0x00
+#define KSZ8851_CMD_WR_REG       0x40
+#define KSZ8851_CMD_RD_FIFO      0x80
+#define KSZ8851_CMD_WR_FIFO      0xC0
+
+//Byte enable bits
+#if (KSZ8851_SPI_SUPPORT == ENABLED)
+   #define KSZ8851_CMD_B0        0x04
+   #define KSZ8851_CMD_B1        0x08
+   #define KSZ8851_CMD_B2        0x10
+   #define KSZ8851_CMD_B3        0x20
+#else
+   #define KSZ8851_CMD_B0        0x1000
+   #define KSZ8851_CMD_B1        0x2000
+   #define KSZ8851_CMD_B2        0x4000
+   #define KSZ8851_CMD_B3        0x8000
+#endif
+
+//KSZ8851 registers
+#define KSZ8851_REG_CCR          0x08
+#define KSZ8851_REG_MARL         0x10
+#define KSZ8851_REG_MARM         0x12
+#define KSZ8851_REG_MARH         0x14
+#define KSZ8851_REG_OBCR         0x20
+#define KSZ8851_REG_EEPCR        0x22
+#define KSZ8851_REG_MBIR         0x24
+#define KSZ8851_REG_GRR          0x26
+#define KSZ8851_REG_WFCR         0x2A
+#define KSZ8851_REG_WF0CRC0      0x30
+#define KSZ8851_REG_WF0CRC1      0x32
+#define KSZ8851_REG_WF0BM0       0x34
+#define KSZ8851_REG_WF0BM1       0x36
+#define KSZ8851_REG_WF0BM2       0x38
+#define KSZ8851_REG_WF0BM3       0x3A
+#define KSZ8851_REG_WF1CRC0      0x40
+#define KSZ8851_REG_WF1CRC1      0x42
+#define KSZ8851_REG_WF1BM0       0x44
+#define KSZ8851_REG_WF1BM1       0x46
+#define KSZ8851_REG_WF1BM2       0x48
+#define KSZ8851_REG_WF1BM3       0x4A
+#define KSZ8851_REG_WF2CRC0      0x50
+#define KSZ8851_REG_WF2CRC1      0x52
+#define KSZ8851_REG_WF2BM0       0x54
+#define KSZ8851_REG_WF2BM1       0x56
+#define KSZ8851_REG_WF2BM2       0x58
+#define KSZ8851_REG_WF2BM3       0x5A
+#define KSZ8851_REG_WF3CRC0      0x60
+#define KSZ8851_REG_WF3CRC1      0x62
+#define KSZ8851_REG_WF3BM0       0x64
+#define KSZ8851_REG_WF3BM1       0x66
+#define KSZ8851_REG_WF3BM2       0x68
+#define KSZ8851_REG_WF3BM3       0x6A
+#define KSZ8851_REG_TXCR         0x70
+#define KSZ8851_REG_TXSR         0x72
+#define KSZ8851_REG_RXCR1        0x74
+#define KSZ8851_REG_RXCR2        0x76
+#define KSZ8851_REG_TXMIR        0x78
+#define KSZ8851_REG_RXFHSR       0x7C
+#define KSZ8851_REG_RXFHBCR      0x7E
+#define KSZ8851_REG_TXQCR        0x80
+#define KSZ8851_REG_RXQCR        0x82
+#define KSZ8851_REG_TXFDPR       0x84
+#define KSZ8851_REG_RXFDPR       0x86
+#define KSZ8851_REG_RXDTTR       0x8C
+#define KSZ8851_REG_RXDBCTR      0x8E
+#define KSZ8851_REG_IER          0x90
+#define KSZ8851_REG_ISR          0x92
+#define KSZ8851_REG_RXFCTR       0x9C
+#define KSZ8851_REG_TXNTFSR      0x9E
+#define KSZ8851_REG_MAHTR0       0xA0
+#define KSZ8851_REG_MAHTR1       0xA2
+#define KSZ8851_REG_MAHTR2       0xA4
+#define KSZ8851_REG_MAHTR3       0xA6
+#define KSZ8851_REG_FCLWR        0xB0
+#define KSZ8851_REG_FCHWR        0xB2
+#define KSZ8851_REG_FCOWR        0xB4
+#define KSZ8851_REG_CIDER        0xC0
+#define KSZ8851_REG_CGCR         0xC6
+#define KSZ8851_REG_IACR         0xC8
+#define KSZ8851_REG_IADLR        0xD0
+#define KSZ8851_REG_IADHR        0xD2
+#define KSZ8851_REG_PMECR        0xD4
+#define KSZ8851_REG_GSWUTR       0xD6
+#define KSZ8851_REG_PHYRR        0xD8
+#define KSZ8851_REG_P1MBCR       0xE4
+#define KSZ8851_REG_P1MBSR       0xE6
+#define KSZ8851_REG_PHY1ILR      0xE8
+#define KSZ8851_REG_PHY1IHR      0xEA
+#define KSZ8851_REG_P1ANAR       0xEC
+#define KSZ8851_REG_P1ANLPR      0xEE
+#define KSZ8851_REG_P1SCLMD      0xF4
+#define KSZ8851_REG_P1CR         0xF6
+#define KSZ8851_REG_P1SR         0xF8
+
+//CCR register
+#define CCR_BUS_ENDIAN_MODE      0x0400
+#define CCR_EEPROM_PRESENCE      0x0200
+#define CCR_SPI_MODE             0x0100
+#define CCR_8_BIT_DATA_BUS       0x0080
+#define CCR_16_BIT_DATA_BUS      0x0040
+#define CCR_32_BIT_DATA_BUS      0x0020
+#define CCR_BUS_SHARED_MODE      0x0010
+#define CCR_128_PIN_PACKAGE      0x0008
+#define CCR_48_PIN_PACKAGE       0x0002
+#define CCR_32_PIN_PACKAGE       0x0001
+
+//OBCR register
+#define OBCR_OUT_DRIVE_STRENGTH  0x0040
+#define OBCR_SPI_SO_DELAY2       0x0020
+#define OBCR_SPI_SO_DELAY1       0x0010
+#define OBCR_SPI_SO_DELAY0       0x0008
+#define OBCR_BUS_CLOCK_SEL       0x0004
+#define OBCR_BUS_CLOCK_DIV1      0x0002
+#define OBCR_BUS_CLOCK_DIV0      0x0001
+
+//EEPCR register
+#define EEPCR_EESA               0x0010
+#define EEPCR_EESB               0x0008
+#define EEPCR_EECB2              0x0004
+#define EEPCR_EECB1              0x0002
+#define EEPCR_EECB0              0x0001
+
+//MBIR register
+#define MBIR_TXMBF               0x1000
+#define MBIR_TXMBFA              0x0800
+#define MBIR_TXMBFC2             0x0400
+#define MBIR_TXMBFC1             0x0200
+#define MBIR_TXMBFC0             0x0100
+#define MBIR_RXMBF               0x0010
+#define MBIR_RXMBFA              0x0008
+#define MBIR_RXMBFC2             0x0004
+#define MBIR_RXMBFC1             0x0002
+#define MBIR_RXMBFC0             0x0001
+
+//GRR register
+#define GRR_QMU_MODULE_SOFT_RST  0x0002
+#define GRR_GLOBAL_SOFT_RST      0x0001
+
+//WFCR register
+#define WFCR_MPRXE               0x0080
+#define WFCR_WF3E                0x0008
+#define WFCR_WF2E                0x0004
+#define WFCR_WF1E                0x0002
+#define WFCR_WF0E                0x0001
+
+//TXCR register
+#define TXCR_TCGICMP             0x0100
+#define TXCR_TCGUDP              0x0080
+#define TXCR_TCGTCP              0x0040
+#define TXCR_TCGIP               0x0020
+#define TXCR_FTXQ                0x0010
+#define TXCR_TXFCE               0x0008
+#define TXCR_TXPE                0x0004
+#define TXCR_TXCE                0x0002
+#define TXCR_TXE                 0x0001
+
+//TXSR register
+#define TXSR_TXLC                0x2000
+#define TXSR_TXMC                0x1000
+#define TXSR_TXFID5              0x0020
+#define TXSR_TXFID4              0x0010
+#define TXSR_TXFID3              0x0008
+#define TXSR_TXFID2              0x0004
+#define TXSR_TXFID1              0x0002
+#define TXSR_TXFID0              0x0001
+
+//RXCR1 register
+#define RXCR1_FRXQ               0x8000
+#define RXCR1_RXUDPFCC           0x4000
+#define RXCR1_RXTCPFCC           0x2000
+#define RXCR1_RXIPFCC            0x1000
+#define RXCR1_RXPAFMA            0x0800
+#define RXCR1_RXFCE              0x0400
+#define RXCR1_RXEFE              0x0200
+#define RXCR1_RXMAFMA            0x0100
+#define RXCR1_RXBE               0x0080
+#define RXCR1_RXME               0x0040
+#define RXCR1_RXUE               0x0020
+#define RXCR1_RXAE               0x0010
+#define RXCR1_RXINVF             0x0002
+#define RXCR1_RXE                0x0001
+
+//RXCR2 register
+#define RXCR2_SRDBL2             0x0080
+#define RXCR2_SRDBL1             0x0040
+#define RXCR2_SRDBL0             0x0020
+#define RXCR2_IUFFP              0x0010
+#define RXCR2_RXIUFCEZ           0x0008
+#define RXCR2_UDPLFE             0x0004
+#define RXCR2_RXICMPFCC          0x0002
+#define RXCR2_RXSAF              0x0001
+
+//TXMIR register
+#define TXMIR_TXMA_MASK          0x1FFF
+
+//RXFHSR register
+#define RXFHSR_RXFV              0x8000
+#define RXFHSR_RXICMPFCS         0x2000
+#define RXFHSR_RXIPFCS           0x1000
+#define RXFHSR_RXTCPFCS          0x0800
+#define RXFHSR_RXUDPFCS          0x0400
+#define RXFHSR_RXBF              0x0080
+#define RXFHSR_RXMF              0x0040
+#define RXFHSR_RXUF              0x0020
+#define RXFHSR_RXMR              0x0010
+#define RXFHSR_RXFT              0x0008
+#define RXFHSR_RXFTL             0x0004
+#define RXFHSR_RXRF              0x0002
+#define RXFHSR_RXCE              0x0001
+
+//RXFHBCR register
+#define RXFHBCR_RXBC_MASK        0x0FFF
+
+//TXQCR register
+#define TXQCR_AETFE              0x0004
+#define TXQCR_TXQMAM             0x0002
+#define TXQCR_METFE              0x0001
+
+//RXQCR register
+#define RXQCR_RXDTTS             0x1000
+#define RXQCR_RXDBCTS            0x0800
+#define RXQCR_RXFCTS             0x0400
+#define RXQCR_RXIPHTOE           0x0200
+#define RXQCR_RXDTTE             0x0080
+#define RXQCR_RXDBCTE            0x0040
+#define RXQCR_RXFCTE             0x0020
+#define RXQCR_ADRFE              0x0010
+#define RXQCR_SDA                0x0008
+#define RXQCR_RRXEF              0x0001
+
+//TXFDPR register
+#define TXFDPR_TXFPAI            0x4000
+
+//RXFDPR register
+#define RXFDPR_RXFPAI            0x4000
+
+//IER register
+#define IER_LCIE                 0x8000
+#define IER_TXIE                 0x4000
+#define IER_RXIE                 0x2000
+#define IER_RXOIE                0x0800
+#define IER_TXPSIE               0x0200
+#define IER_RXPSIE               0x0100
+#define IER_TXSAIE               0x0040
+#define IER_RXWFDIE              0x0020
+#define IER_RXMPDIE              0x0010
+#define IER_LDIE                 0x0008
+#define IER_EDIE                 0x0004
+#define IER_SPIBEIE              0x0002
+#define IER_DEDIE                0x0001
+
+//ISR register
+#define ISR_LCIS                 0x8000
+#define ISR_TXIS                 0x4000
+#define ISR_RXIS                 0x2000
+#define ISR_RXOIS                0x0800
+#define ISR_TXPSIS               0x0200
+#define ISR_RXPSIS               0x0100
+#define ISR_TXSAIS               0x0040
+#define ISR_RXWFDIS              0x0020
+#define ISR_RXMPDIS              0x0010
+#define ISR_LDIS                 0x0008
+#define ISR_EDIS                 0x0004
+#define ISR_SPIBEIS              0x0002
+
+//CGCR register
+#define CGCR_LEDSEL0             0x0200
+
+//IACR register
+#define IACR_READ_ENABLE         0x1000
+#define IACR_TABLE_SELECT1       0x0800
+#define IACR_TABLE_SELECT0       0x0400
+
+//PMECR register
+#define PMECR_PME_DELAY_EN       0x4000
+#define PMECR_PME_POLARITY       0x1000
+#define PMECR_PME_WUP_FRAME_EN   0x0800
+#define PMECR_PME_MAGIC_EN       0x0400
+#define PMECR_PME_LINK_UP_EN     0x0200
+#define PMECR_PME_ENERGY_EN      0x0100
+#define PMECR_AUTO_WUP_EN        0x0080
+#define PMECR_WUP_NORMAL_OP_MODE 0x0040
+#define PMECR_WUP_FROM_WUP_FRAME 0x0020
+#define PMECR_WUP_FROM_MAGIC     0x0010
+#define PMECR_WUP_FROM_LINK_UP   0x0008
+#define PMECR_WUP_FROM_ENERGY    0x0004
+#define PMECR_PWR_MODE1          0x0002
+#define PMECR_PWR_MODE0          0x0001
+
+//PHYRR register
+#define PHYRR_PHY_RESET          0x0001
+
+//P1MBCR register
+#define P1MBCR_LOCAL_LOOPBACK    0x4000
+#define P1MBCR_FORCE_100         0x2000
+#define P1MBCR_AN_ENABLE         0x1000
+#define P1MBCR_RESTART_AN        0x0200
+#define P1MBCR_FORCE_FULL_DUPLEX 0x0100
+#define P1MBCR_HP_MDIX           0x0020
+#define P1MBCR_FORCE_MDIX        0x0010
+#define P1MBCR_DISABLE_MDIX      0x0008
+#define P1MBCR_DISABLE_TRANSMIT  0x0002
+#define P1MBCR_DISABLE_LED       0x0001
+
+//P1MBSR register
+#define P1MBSR_T4_CAPABLE        0x8000
+#define P1MBSR_100_FD_CAPABLE    0x4000
+#define P1MBSR_100_CAPABLE       0x2000
+#define P1MBSR_10_FD_CAPABLE     0x1000
+#define P1MBSR_10_CAPABLE        0x0800
+#define P1MBSR_PREAMBLE_SUPPR    0x0040
+#define P1MBSR_AN_COMPLETE       0x0020
+#define P1MBSR_AN_CAPABLE        0x0008
+#define P1MBSR_LINK_STATUS       0x0004
+#define P1MBSR_JABBER_TEST       0x0002
+#define P1MBSR_EXTENDED_CAPABLE  0x0001
+
+//P1ANAR register
+#define P1ANAR_NEXT_PAGE         0x8000
+#define P1ANAR_REMOTE_FAULT      0x2000
+#define P1ANAR_ADV_PAUSE         0x0400
+#define P1ANAR_ADV_100_FD        0x0100
+#define P1ANAR_ADV_100           0x0080
+#define P1ANAR_ADV_10_FD         0x0040
+#define P1ANAR_ADV_10            0x0020
+#define P1ANAR_SELECTOR_FIELD4   0x0010
+#define P1ANAR_SELECTOR_FIELD3   0x0008
+#define P1ANAR_SELECTOR_FIELD2   0x0004
+#define P1ANAR_SELECTOR_FIELD1   0x0002
+#define P1ANAR_SELECTOR_FIELD0   0x0001
+
+//P1ANLPR register
+#define P1ANLPR_NEXT_PAGE        0x8000
+#define P1ANLPR_LP_ACK           0x4000
+#define P1ANLPR_REMOTE_FAULT     0x2000
+#define P1ANLPR_ADV_PAUSE        0x0400
+#define P1ANLPR_ADV_100_FD       0x0100
+#define P1ANLPR_ADV_100          0x0080
+#define P1ANLPR_ADV_10_FD        0x0040
+#define P1ANLPR_ADV_10           0x0020
+
+//P1SCLMD register
+#define P1SCLMD_VCT_RESULT1      0x4000
+#define P1SCLMD_VCT_RESULT0      0x2000
+#define P1SCLMD_VCT_EN           0x1000
+#define P1SCLMD_FORCE_LNK        0x0800
+#define P1SCLMD_REMOTE_LOOPBACK  0x0200
+
+//P1CR register
+#define P1CR_LED_OFF             0x8000
+#define P1CR_TX_DISABLE          0x4000
+#define P1CR_RESTART_AN          0x2000
+#define P1CR_DISABLE_AUTO_MDIX   0x0400
+#define P1CR_FORCE_MDIX          0x0200
+#define P1CR_AN_ENABLE           0x0080
+#define P1CR_FORCE_SPEED         0x0040
+#define P1CR_FORCE_DUPLEX        0x0020
+#define P1CR_ADV_PAUSE           0x0010
+#define P1CR_ADV_100_FD          0x0008
+#define P1CR_ADV_100             0x0004
+#define P1CR_ADV_10_FD           0x0002
+#define P1CR_ADV_10              0x0001
+
+//P1SR register
+#define P1SR_HP_MDIX             0x8000
+#define P1SR_REVERSED_POLARITY   0x2000
+#define P1SR_OPERATION_SPEED     0x0400
+#define P1SR_OPERATION_DUPLEX    0x0200
+#define P1SR_MDIX_STATUS         0x0080
+#define P1SR_AN_DONE             0x0040
+#define P1SR_LINK_GOOD           0x0020
+#define P1SR_PARTNER_ADV_PAUSE   0x0010
+#define P1SR_PARTNER_ADV_100_FD  0x0008
+#define P1SR_PARTNER_ADV_100     0x0004
+#define P1SR_PARTNER_ADV_10_FD   0x0002
+#define P1SR_PARTNER_ADV_10      0x0001
+
+//Transmit control word
+#define TX_CTRL_TXIC             0x8000
+#define TX_CTRL_TXFID            0x003F
+
+
+/**
+ * @brief TX packet header
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t controlWord;
+   uint16_t byteCount;
+} __end_packed Ksz8851TxHeader;
+
+
+/**
+ * @brief RX packet header
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t statusWord;
+   uint16_t byteCount;
+} __end_packed Ksz8851RxHeader;
+
+
+/**
+ * @brief KSZ8851 driver context
+ **/
+
+typedef struct
+{
+   uint_t frameId;    ///<Identify a frame and its associated status
+   uint8_t *txBuffer; ///<Transmit buffer
+   uint8_t *rxBuffer; ///<Receive buffer
+} Ksz8851Context;
+
+
+//KSZ8851 driver
+extern const NicDriver ksz8851Driver;
+
+//KSZ8851 related functions
+error_t ksz8851Init(NetInterface *interface);
+
+void ksz8851Tick(NetInterface *interface);
+
+void ksz8851EnableIrq(NetInterface *interface);
+void ksz8851DisableIrq(NetInterface *interface);
+bool_t ksz8851IrqHandler(NetInterface *interface);
+void ksz8851EventHandler(NetInterface *interface);
+
+error_t ksz8851SendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t ksz8851ReceivePacket(NetInterface *interface);
+
+error_t ksz8851SetMulticastFilter(NetInterface *interface);
+
+void ksz8851WriteReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t ksz8851ReadReg(NetInterface *interface, uint8_t address);
+
+void ksz8851WriteFifo(NetInterface *interface, const uint8_t *data, size_t length);
+void ksz8851ReadFifo(NetInterface *interface, uint8_t *data, size_t length);
+
+void ksz8851SetBit(NetInterface *interface, uint8_t address, uint16_t mask);
+void ksz8851ClearBit(NetInterface *interface, uint8_t address, uint16_t mask);
+
+uint32_t ksz8851CalcCrc(const void *data, size_t length);
+
+void ksz8851DumpReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8873.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,292 @@
+/**
+ * @file ksz8873.c
+ * @brief KSZ8873 Ethernet switch
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/ksz8873.h"
+#include "debug.h"
+
+
+/**
+ * @brief KSZ8873 Ethernet switch driver
+ **/
+
+const PhyDriver ksz8873PhyDriver =
+{
+   ksz8873Init,
+   ksz8873Tick,
+   ksz8873EnableIrq,
+   ksz8873DisableIrq,
+   ksz8873EventHandler,
+};
+
+
+/**
+ * @brief KSZ8873 Ethernet switch initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ksz8873Init(NetInterface *interface)
+{
+   uint_t port;
+
+   //Debug message
+   TRACE_INFO("Initializing KSZ8873...\r\n");
+
+   //Loop through ports
+   for(port = KSZ8873_PORT1; port <= KSZ8873_PORT2; port++)
+   {
+      //Debug message
+      TRACE_INFO("Port %u:\r\n", port);
+      //Dump PHY registers for debugging purpose
+      ksz8873DumpPhyReg(interface, port);
+   }
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Get link state
+ * @param[in] interface Underlying network interface
+ * @param[in] port Port number
+ * @return Link state
+ **/
+
+bool_t ksz8873GetLinkState(NetInterface *interface, uint8_t port)
+{
+   uint16_t status;
+   bool_t linkState;
+
+   //Check port number
+   if(port >= KSZ8873_PORT1 && port <= KSZ8873_PORT2)
+   {
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+      //Read status register
+      status = ksz8873ReadPhyReg(interface, port, KSZ8873_PHY_REG_BMSR);
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+
+      //Retrieve current link state
+      linkState = (status & BMSR_LINK_STATUS) ? TRUE : FALSE;
+   }
+   else
+   {
+      //The specified port number is not valid
+      linkState = FALSE;
+   }
+
+   //Return link status
+   return linkState;
+}
+
+
+/**
+ * @brief KSZ8873 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8873Tick(NetInterface *interface)
+{
+   uint_t port;
+   uint16_t status;
+   bool_t linkState;
+
+   //Initialize link state
+   linkState = FALSE;
+
+   //Loop through ports
+   for(port = KSZ8873_PORT1; port <= KSZ8873_PORT2; port++)
+   {
+      //Read status register
+      status = ksz8873ReadPhyReg(interface, port, KSZ8873_PHY_REG_BMSR);
+
+      //Retrieve current link state
+      if(status & BMSR_LINK_STATUS)
+         linkState = TRUE;
+   }
+
+   //Link up event?
+   if(linkState)
+   {
+      if(!interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+   //Link down event?
+   else
+   {
+      if(interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8873EnableIrq(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8873DisableIrq(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief KSZ8873 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8873EventHandler(NetInterface *interface)
+{
+   uint_t port;
+   uint16_t status;
+   bool_t linkState;
+
+   //Initialize link state
+   linkState = FALSE;
+
+   //Loop through ports
+   for(port = KSZ8873_PORT1; port <= KSZ8873_PORT2; port++)
+   {
+      //Read status register
+      status = ksz8873ReadPhyReg(interface, port, KSZ8873_PHY_REG_BMSR);
+
+      //Retrieve current link state
+      if(status & BMSR_LINK_STATUS)
+         linkState = TRUE;
+   }
+
+   //Link up event?
+   if(linkState)
+   {
+      //Set current speed
+      interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+      //Set duplex mode
+      interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+
+      //Update link state
+      interface->linkState = TRUE;
+
+      //Adjust MAC configuration parameters for proper operation
+      interface->nicDriver->updateMacConfig(interface);
+   }
+   else
+   {
+      //Update link state
+      interface->linkState = FALSE;
+   }
+
+   //Process link state change event
+   nicNotifyLinkChange(interface);
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] port Port number
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void ksz8873WritePhyReg(NetInterface *interface,
+   uint8_t port, uint8_t address, uint16_t data)
+{
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(port, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] port Port number
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t ksz8873ReadPhyReg(NetInterface *interface,
+   uint8_t port, uint8_t address)
+{
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(port, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ * @param[in] port Port number
+ **/
+
+void ksz8873DumpPhyReg(NetInterface *interface, uint8_t port)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, ksz8873ReadPhyReg(interface, port, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8873.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,150 @@
+/**
+ * @file ksz8873.h
+ * @brief KSZ8873 Ethernet switch
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _KSZ8873_H
+#define _KSZ8873_H
+
+//Dependencies
+#include "core/nic.h"
+
+//KSZ8873 ports
+#define KSZ8873_PORT1               1
+#define KSZ8873_PORT2               2
+
+//KSZ8873 registers
+#define KSZ8873_PHY_REG_BMCR        0x00
+#define KSZ8873_PHY_REG_BMSR        0x01
+#define KSZ8873_PHY_REG_PHYIDR1     0x02
+#define KSZ8873_PHY_REG_PHYIDR2     0x03
+#define KSZ8873_PHY_REG_ANAR        0x04
+#define KSZ8873_PHY_REG_ANLPAR      0x05
+#define KSZ8873_PHY_REG_LINKMDCS    0x1D
+#define KSZ8873_PHY_REG_PHYSCS      0x1F
+
+//BMCR register
+#define BMCR_SOFT_RESET             (1 << 15)
+#define BMCR_LOOPBACK               (1 << 14)
+#define BMCR_FORCE_100              (1 << 13)
+#define BMCR_AN_EN                  (1 << 12)
+#define BMCR_POWER_DOWN             (1 << 11)
+#define BMCR_ISOLATE                (1 << 10)
+#define BMCR_RESTART_AN             (1 << 9)
+#define BMCR_FORCE_FULL_DUPLEX      (1 << 8)
+#define BMCR_COL_TEST               (1 << 7)
+#define BMCR_HP_MDIX                (1 << 5)
+#define BMCR_FORCE_MDI              (1 << 4)
+#define BMCR_DIS_AUTO_MDIX          (1 << 3)
+#define BMCR_DIS_FAR_END_FAULT      (1 << 2)
+#define BMCR_DIS_TRANSMIT           (1 << 1)
+#define BMCR_DIS_LED                (1 << 0)
+
+//BMSR register
+#define BMSR_100BT4                 (1 << 15)
+#define BMSR_100BTX_FD              (1 << 14)
+#define BMSR_100BTX                 (1 << 13)
+#define BMSR_10BT_FD                (1 << 12)
+#define BMSR_10BT                   (1 << 11)
+#define BMSR_NO_PREAMBLE            (1 << 6)
+#define BMSR_AN_COMPLETE            (1 << 5)
+#define BMSR_FAR_END_FAULT          (1 << 4)
+#define BMSR_AN_ABLE                (1 << 3)
+#define BMSR_LINK_STATUS            (1 << 2)
+#define BMSR_JABBER_TEST            (1 << 1)
+#define BMSR_EXTENDED_CAP           (1 << 0)
+
+//ANAR register
+#define ANAR_NEXT_PAGE              (1 << 15)
+#define ANAR_REMOTE_FAULT           (1 << 13)
+#define ANAR_PAUSE                  (1 << 10)
+#define ANAR_100BTX_FD              (1 << 8)
+#define ANAR_100BTX                 (1 << 7)
+#define ANAR_10BT_FD                (1 << 6)
+#define ANAR_10BT                   (1 << 5)
+#define ANAR_SELECTOR4              (1 << 4)
+#define ANAR_SELECTOR3              (1 << 3)
+#define ANAR_SELECTOR2              (1 << 2)
+#define ANAR_SELECTOR1              (1 << 1)
+#define ANAR_SELECTOR0              (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NEXT_PAGE            (1 << 15)
+#define ANLPAR_LP_ACK               (1 << 14)
+#define ANLPAR_REMOTE_FAULT         (1 << 13)
+#define ANLPAR_PAUSE                (1 << 10)
+#define ANLPAR_100BTX_FD            (1 << 8)
+#define ANLPAR_100BTX               (1 << 7)
+#define ANLPAR_10BT_FD              (1 << 6)
+#define ANLPAR_10BT                 (1 << 5)
+
+//LINKMDCS register
+#define LINKMDCS_VCT_EN             (1 << 15)
+#define LINKMDCS_VCT_RESULT1        (1 << 14)
+#define LINKMDCS_VCT_RESULT0        (1 << 13)
+#define LINKMDCS_VCT_10M_SHORT      (1 << 12)
+#define LINKMDCS_VCT_FAULT_COUNT8   (1 << 8)
+#define LINKMDCS_VCT_FAULT_COUNT7   (1 << 7)
+#define LINKMDCS_VCT_FAULT_COUNT6   (1 << 6)
+#define LINKMDCS_VCT_FAULT_COUNT5   (1 << 5)
+#define LINKMDCS_VCT_FAULT_COUNT4   (1 << 4)
+#define LINKMDCS_VCT_FAULT_COUNT3   (1 << 3)
+#define LINKMDCS_VCT_FAULT_COUNT2   (1 << 2)
+#define LINKMDCS_VCT_FAULT_COUNT1   (1 << 1)
+#define LINKMDCS_VCT_FAULT_COUNT0   (1 << 0)
+
+//PHYSCS register
+#define PHYSCS_POLRVS               (1 << 5)
+#define PHYSCS_MDIX_STATUS          (1 << 4)
+#define PHYSCS_FORCE_LINK           (1 << 3)
+#define PHYSCS_PWRSAVE              (1 << 2)
+#define PHYSCS_REMOTE_LOOPBACK      (1 << 1)
+
+//KSZ8873 Ethernet switch driver
+extern const PhyDriver ksz8873PhyDriver;
+
+//KSZ8873 related functions
+error_t ksz8873Init(NetInterface *interface);
+
+bool_t ksz8873GetLinkState(NetInterface *interface, uint8_t port);
+
+void ksz8873Tick(NetInterface *interface);
+
+void ksz8873EnableIrq(NetInterface *interface);
+void ksz8873DisableIrq(NetInterface *interface);
+
+void ksz8873EventHandler(NetInterface *interface);
+
+void ksz8873WritePhyReg(NetInterface *interface,
+   uint8_t port, uint8_t address, uint16_t data);
+
+uint16_t ksz8873ReadPhyReg(NetInterface *interface,
+   uint8_t port, uint8_t address);
+
+void ksz8873DumpPhyReg(NetInterface *interface, uint8_t port);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8895.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,292 @@
+/**
+ * @file ksz8895.c
+ * @brief KSZ8895 Ethernet switch
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/ksz8895.h"
+#include "debug.h"
+
+
+/**
+ * @brief KSZ8895 Ethernet switch driver
+ **/
+
+const PhyDriver ksz8895PhyDriver =
+{
+   ksz8895Init,
+   ksz8895Tick,
+   ksz8895EnableIrq,
+   ksz8895DisableIrq,
+   ksz8895EventHandler,
+};
+
+
+/**
+ * @brief KSZ8895 Ethernet switch initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ksz8895Init(NetInterface *interface)
+{
+   uint_t port;
+
+   //Debug message
+   TRACE_INFO("Initializing KSZ8895...\r\n");
+
+   //Loop through ports
+   for(port = KSZ8895_PORT1; port <= KSZ8895_PORT4; port++)
+   {
+      //Debug message
+      TRACE_DEBUG("Port %u:\r\n", port);
+      //Dump PHY registers for debugging purpose
+      ksz8895DumpPhyReg(interface, port);
+   }
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Get link state
+ * @param[in] interface Underlying network interface
+ * @param[in] port Port number
+ * @return Link state
+ **/
+
+bool_t ksz8895GetLinkState(NetInterface *interface, uint8_t port)
+{
+   uint16_t status;
+   bool_t linkState;
+
+   //Check port number
+   if(port >= KSZ8895_PORT1 && port <= KSZ8895_PORT4)
+   {
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+      //Read status register
+      status = ksz8895ReadPhyReg(interface, port, KSZ8895_PHY_REG_BMSR);
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+
+      //Retrieve current link state
+      linkState = (status & BMSR_LINK_STATUS) ? TRUE : FALSE;
+   }
+   else
+   {
+      //The specified port number is not valid
+      linkState = FALSE;
+   }
+
+   //Return link status
+   return linkState;
+}
+
+
+/**
+ * @brief KSZ8895 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8895Tick(NetInterface *interface)
+{
+   uint_t port;
+   uint16_t status;
+   bool_t linkState;
+
+   //Initialize link state
+   linkState = FALSE;
+
+   //Loop through ports
+   for(port = KSZ8895_PORT1; port <= KSZ8895_PORT4; port++)
+   {
+      //Read status register
+      status = ksz8895ReadPhyReg(interface, port, KSZ8895_PHY_REG_BMSR);
+
+      //Retrieve current link state
+      if(status & BMSR_LINK_STATUS)
+         linkState = TRUE;
+   }
+
+   //Link up event?
+   if(linkState)
+   {
+      if(!interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+   //Link down event?
+   else
+   {
+      if(interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8895EnableIrq(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8895DisableIrq(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief KSZ8895 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz8895EventHandler(NetInterface *interface)
+{
+   uint_t port;
+   uint16_t status;
+   bool_t linkState;
+
+   //Initialize link state
+   linkState = FALSE;
+
+   //Loop through ports
+   for(port = KSZ8895_PORT1; port <= KSZ8895_PORT4; port++)
+   {
+      //Read status register
+      status = ksz8895ReadPhyReg(interface, port, KSZ8895_PHY_REG_BMSR);
+
+      //Retrieve current link state
+      if(status & BMSR_LINK_STATUS)
+         linkState = TRUE;
+   }
+
+   //Link up event?
+   if(linkState)
+   {
+      //Set current speed
+      interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+      //Set duplex mode
+      interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+
+      //Update link state
+      interface->linkState = TRUE;
+
+      //Adjust MAC configuration parameters for proper operation
+      interface->nicDriver->updateMacConfig(interface);
+   }
+   else
+   {
+      //Update link state
+      interface->linkState = FALSE;
+   }
+
+   //Process link state change event
+   nicNotifyLinkChange(interface);
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] port Port number
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void ksz8895WritePhyReg(NetInterface *interface,
+   uint8_t port, uint8_t address, uint16_t data)
+{
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(port, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] port Port number
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t ksz8895ReadPhyReg(NetInterface *interface,
+   uint8_t port, uint8_t address)
+{
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(port, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ * @param[in] port Port number
+ **/
+
+void ksz8895DumpPhyReg(NetInterface *interface, uint8_t port)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, ksz8895ReadPhyReg(interface, port, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz8895.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,163 @@
+/**
+ * @file ksz8895.h
+ * @brief KSZ8895 Ethernet switch
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _KSZ8895_H
+#define _KSZ8895_H
+
+//Dependencies
+#include "core/nic.h"
+
+//KSZ8895 ports
+#define KSZ8895_PORT1               1
+#define KSZ8895_PORT2               2
+#define KSZ8895_PORT3               3
+#define KSZ8895_PORT4               4
+
+//KSZ8895 registers
+#define KSZ8895_PHY_REG_BMCR        0x00
+#define KSZ8895_PHY_REG_BMSR        0x01
+#define KSZ8895_PHY_REG_PHYIDR1     0x02
+#define KSZ8895_PHY_REG_PHYIDR2     0x03
+#define KSZ8895_PHY_REG_ANAR        0x04
+#define KSZ8895_PHY_REG_ANLPAR      0x05
+#define KSZ8895_PHY_REG_LINKMDCS    0x1D
+#define KSZ8895_PHY_REG_PHYSCS      0x1F
+
+//BMCR register
+#define BMCR_SOFT_RESET             (1 << 15)
+#define BMCR_LOOPBACK               (1 << 14)
+#define BMCR_FORCE_100              (1 << 13)
+#define BMCR_AN_EN                  (1 << 12)
+#define BMCR_POWER_DOWN             (1 << 11)
+#define BMCR_ISOLATE                (1 << 10)
+#define BMCR_RESTART_AN             (1 << 9)
+#define BMCR_FORCE_FULL_DUPLEX      (1 << 8)
+#define BMCR_COL_TEST               (1 << 7)
+#define BMCR_HP_MDIX                (1 << 5)
+#define BMCR_FORCE_MDI              (1 << 4)
+#define BMCR_DIS_AUTO_MDIX          (1 << 3)
+#define BMCR_DIS_FAR_END_FAULT      (1 << 2)
+#define BMCR_DIS_TRANSMIT           (1 << 1)
+#define BMCR_DIS_LED                (1 << 0)
+
+//BMSR register
+#define BMSR_100BT4                 (1 << 15)
+#define BMSR_100BTX_FD              (1 << 14)
+#define BMSR_100BTX                 (1 << 13)
+#define BMSR_10BT_FD                (1 << 12)
+#define BMSR_10BT                   (1 << 11)
+#define BMSR_NO_PREAMBLE            (1 << 6)
+#define BMSR_AN_COMPLETE            (1 << 5)
+#define BMSR_FAR_END_FAULT          (1 << 4)
+#define BMSR_AN_ABLE                (1 << 3)
+#define BMSR_LINK_STATUS            (1 << 2)
+#define BMSR_JABBER_TEST            (1 << 1)
+#define BMSR_EXTENDED_CAP           (1 << 0)
+
+//ANAR register
+#define ANAR_NEXT_PAGE              (1 << 15)
+#define ANAR_REMOTE_FAULT           (1 << 13)
+#define ANAR_PAUSE                  (1 << 10)
+#define ANAR_100BTX_FD              (1 << 8)
+#define ANAR_100BTX                 (1 << 7)
+#define ANAR_10BT_FD                (1 << 6)
+#define ANAR_10BT                   (1 << 5)
+#define ANAR_SELECTOR4              (1 << 4)
+#define ANAR_SELECTOR3              (1 << 3)
+#define ANAR_SELECTOR2              (1 << 2)
+#define ANAR_SELECTOR1              (1 << 1)
+#define ANAR_SELECTOR0              (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NEXT_PAGE            (1 << 15)
+#define ANLPAR_LP_ACK               (1 << 14)
+#define ANLPAR_REMOTE_FAULT         (1 << 13)
+#define ANLPAR_PAUSE                (1 << 10)
+#define ANLPAR_100BTX_FD            (1 << 8)
+#define ANLPAR_100BTX               (1 << 7)
+#define ANLPAR_10BT_FD              (1 << 6)
+#define ANLPAR_10BT                 (1 << 5)
+
+//LINKMDCS register
+#define LINKMDCS_VCT_EN             (1 << 15)
+#define LINKMDCS_VCT_RESULT1        (1 << 14)
+#define LINKMDCS_VCT_RESULT0        (1 << 13)
+#define LINKMDCS_VCT_10M_SHORT      (1 << 12)
+#define LINKMDCS_VCT_FAULT_COUNT8   (1 << 8)
+#define LINKMDCS_VCT_FAULT_COUNT7   (1 << 7)
+#define LINKMDCS_VCT_FAULT_COUNT6   (1 << 6)
+#define LINKMDCS_VCT_FAULT_COUNT5   (1 << 5)
+#define LINKMDCS_VCT_FAULT_COUNT4   (1 << 4)
+#define LINKMDCS_VCT_FAULT_COUNT3   (1 << 3)
+#define LINKMDCS_VCT_FAULT_COUNT2   (1 << 2)
+#define LINKMDCS_VCT_FAULT_COUNT1   (1 << 1)
+#define LINKMDCS_VCT_FAULT_COUNT0   (1 << 0)
+
+//PHYSCS register
+#define PHYSCS_OP_MODE2             (1 << 10)
+#define PHYSCS_OP_MODE1             (1 << 9)
+#define PHYSCS_OP_MODE0             (1 << 8)
+#define PHYSCS_POLRVS               (1 << 5)
+#define PHYSCS_MDIX_STATUS          (1 << 4)
+#define PHYSCS_FORCE_LINK           (1 << 3)
+#define PHYSCS_PWRSAVE              (1 << 2)
+#define PHYSCS_REMOTE_LOOPBACK      (1 << 1)
+
+//Operation mode indication
+#define PHYCON1_OP_MODE_MASK        (7 << 8)
+#define PHYCON1_OP_MODE_AN          (0 << 8)
+#define PHYCON1_OP_MODE_10BT        (1 << 8)
+#define PHYCON1_OP_MODE_100BTX      (2 << 8)
+#define PHYCON1_OP_MODE_10BT_FD     (5 << 8)
+#define PHYCON1_OP_MODE_100BTX_FD   (6 << 8)
+
+//KSZ8895 Ethernet switch driver
+extern const PhyDriver ksz8895PhyDriver;
+
+//KSZ8895 related functions
+error_t ksz8895Init(NetInterface *interface);
+
+bool_t ksz8895GetLinkState(NetInterface *interface, uint8_t port);
+
+void ksz8895Tick(NetInterface *interface);
+
+void ksz8895EnableIrq(NetInterface *interface);
+void ksz8895DisableIrq(NetInterface *interface);
+
+void ksz8895EventHandler(NetInterface *interface);
+
+void ksz8895WritePhyReg(NetInterface *interface,
+   uint8_t port, uint8_t address, uint16_t data);
+
+uint16_t ksz8895ReadPhyReg(NetInterface *interface,
+   uint8_t port, uint8_t address);
+
+void ksz8895DumpPhyReg(NetInterface *interface, uint8_t port);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz9031.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,287 @@
+/**
+ * @file ksz9031.c
+ * @brief KSZ9031 Gigabit Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/ksz9031.h"
+#include "debug.h"
+
+
+/**
+ * @brief KSZ9031 Ethernet PHY driver
+ **/
+
+const PhyDriver ksz9031PhyDriver =
+{
+   ksz9031Init,
+   ksz9031Tick,
+   ksz9031EnableIrq,
+   ksz9031DisableIrq,
+   ksz9031EventHandler,
+};
+
+
+/**
+ * @brief KSZ9031 PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ksz9031Init(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing KSZ9031...\r\n");
+
+   //Initialize external interrupt line driver
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->init();
+
+   //Reset PHY transceiver
+   ksz9031WritePhyReg(interface, KSZ9031_PHY_REG_BMCR, BMCR_RESET);
+   //Wait for the reset to complete
+   while(ksz9031ReadPhyReg(interface, KSZ9031_PHY_REG_BMCR) & BMCR_RESET);
+
+   //Dump PHY registers for debugging purpose
+   ksz9031DumpPhyReg(interface);
+
+   //The PHY will generate interrupts when link status changes are detected
+   ksz9031WritePhyReg(interface, KSZ9031_PHY_REG_ICSR, ICSR_LINK_DOWN_IE | ICSR_LINK_UP_IE);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief KSZ9031 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz9031Tick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //No external interrupt line driver?
+   if(interface->extIntDriver == NULL)
+   {
+      //Read basic status register
+      value = ksz9031ReadPhyReg(interface, KSZ9031_PHY_REG_BMSR);
+      //Retrieve current link state
+      linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+      //Link up event?
+      if(linkState && !interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+      //Link down event?
+      else if(!linkState && interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz9031EnableIrq(NetInterface *interface)
+{
+   //Enable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz9031DisableIrq(NetInterface *interface)
+{
+   //Disable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief KSZ9031 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz9031EventHandler(NetInterface *interface)
+{
+   uint16_t value;
+
+   //Read status register to acknowledge the interrupt
+   value = ksz9031ReadPhyReg(interface, KSZ9031_PHY_REG_ICSR);
+
+   //Link status change?
+   if(value & (ICSR_LINK_DOWN_IF | ICSR_LINK_UP_IF))
+   {
+      //Any link failure condition is latched in the BMSR register... Reading
+      //the register twice will always return the actual link status
+      value = ksz9031ReadPhyReg(interface, KSZ9031_PHY_REG_BMSR);
+      value = ksz9031ReadPhyReg(interface, KSZ9031_PHY_REG_BMSR);
+
+      //Link is up?
+      if(value & BMSR_LINK_STATUS)
+      {
+         //Read PHY control register
+         value = ksz9031ReadPhyReg(interface, KSZ9031_PHY_REG_PHYCON);
+
+         //Check current speed
+         if(value & PHYCON_SPEED_1000BT)
+         {
+            //1000BASE-T
+            interface->linkSpeed = NIC_LINK_SPEED_1GBPS;
+         }
+         else if(value & PHYCON_SPEED_100BTX)
+         {
+            //100BASE-TX
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+         }
+         else if(value & PHYCON_SPEED_10BT)
+         {
+            //10BASE-T
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+         }
+         else
+         {
+            //Debug message
+            TRACE_WARNING("Invalid speed!\r\n");
+         }
+
+         //Check current duplex mode
+         if(value & PHYCON_DUPLEX_STATUS)
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+         else
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+
+         //Update link state
+         interface->linkState = TRUE;
+
+         //Adjust MAC configuration parameters for proper operation
+         interface->nicDriver->updateMacConfig(interface);
+      }
+      else
+      {
+         //Update link state
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void ksz9031WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = KSZ9031_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t ksz9031ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = KSZ9031_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void ksz9031DumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, ksz9031ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/ksz9031.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,279 @@
+/**
+ * @file ksz9031.h
+ * @brief KSZ9031 Gigabit Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _KSZ9031_H
+#define _KSZ9031_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#ifndef KSZ9031_PHY_ADDR
+   #define KSZ9031_PHY_ADDR 7
+#elif (KSZ9031_PHY_ADDR < 0 || KSZ9031_PHY_ADDR > 31)
+   #error KSZ9031_PHY_ADDR parameter is not valid
+#endif
+
+//KSZ9031 registers
+#define KSZ9031_PHY_REG_BMCR          0x00
+#define KSZ9031_PHY_REG_BMSR          0x01
+#define KSZ9031_PHY_REG_PHYIDR1       0x02
+#define KSZ9031_PHY_REG_PHYIDR2       0x03
+#define KSZ9031_PHY_REG_ANAR          0x04
+#define KSZ9031_PHY_REG_ANLPAR        0x05
+#define KSZ9031_PHY_REG_ANER          0x06
+#define KSZ9031_PHY_REG_ANNPTR        0x07
+#define KSZ9031_PHY_REG_LPNPAR        0x08
+#define KSZ9031_PHY_REG_1000BT_CTRL   0x09
+#define KSZ9031_PHY_REG_1000BT_STATUS 0x0A
+#define KSZ9031_PHY_REG_MMD_CTRL      0x0D
+#define KSZ9031_PHY_REG_MMD_DATA      0x0E
+#define KSZ9031_PHY_REG_EXT_STATUS    0x0F
+#define KSZ9031_PHY_REG_RLB           0x11
+#define KSZ9031_PHY_REG_LINKMDCD      0x12
+#define KSZ9031_PHY_REG_DPMAPCSS      0x13
+#define KSZ9031_PHY_REG_RXERCTR       0x15
+#define KSZ9031_PHY_REG_ICSR          0x1B
+#define KSZ9031_PHY_REG_AUTOMDI       0x1C
+#define KSZ9031_PHY_REG_PHYCON        0x1F
+
+//BMCR register
+#define BMCR_RESET                    (1 << 15)
+#define BMCR_LOOPBACK                 (1 << 14)
+#define BMCR_SPEED_SEL                (1 << 13)
+#define BMCR_AN_EN                    (1 << 12)
+#define BMCR_POWER_DOWN               (1 << 11)
+#define BMCR_ISOLATE                  (1 << 10)
+#define BMCR_RESTART_AN               (1 << 9)
+#define BMCR_DUPLEX_MODE              (1 << 8)
+
+//BMSR register
+#define BMSR_100BT4                   (1 << 15)
+#define BMSR_100BTX_FD                (1 << 14)
+#define BMSR_100BTX_HD                (1 << 13)
+#define BMSR_10BT_FD                  (1 << 12)
+#define BMSR_10BT_HD                  (1 << 11)
+#define BMSR_EXTENDED_STATUS          (1 << 8)
+#define BMSR_NO_PREAMBLE              (1 << 6)
+#define BMSR_AN_COMPLETE              (1 << 5)
+#define BMSR_REMOTE_FAULT             (1 << 4)
+#define BMSR_AN_ABLE                  (1 << 3)
+#define BMSR_LINK_STATUS              (1 << 2)
+#define BMSR_JABBER_DETECT            (1 << 1)
+#define BMSR_EXTENDED_CAP             (1 << 0)
+
+//ANAR register
+#define ANAR_NEXT_PAGE                (1 << 15)
+#define ANAR_REMOTE_FAULT             (1 << 13)
+#define ANAR_PAUSE1                   (1 << 11)
+#define ANAR_PAUSE0                   (1 << 10)
+#define ANAR_100BT4                   (1 << 9)
+#define ANAR_100BTX_FD                (1 << 8)
+#define ANAR_100BTX_HD                (1 << 7)
+#define ANAR_10BT_FD                  (1 << 6)
+#define ANAR_10BT_HD                  (1 << 5)
+#define ANAR_SELECTOR4                (1 << 4)
+#define ANAR_SELECTOR3                (1 << 3)
+#define ANAR_SELECTOR2                (1 << 2)
+#define ANAR_SELECTOR1                (1 << 1)
+#define ANAR_SELECTOR0                (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NEXT_PAGE              (1 << 15)
+#define ANLPAR_LP_ACK                 (1 << 14)
+#define ANLPAR_REMOTE_FAULT           (1 << 13)
+#define ANLPAR_PAUSE1                 (1 << 11)
+#define ANLPAR_PAUSE0                 (1 << 10)
+#define ANLPAR_100BT4                 (1 << 9)
+#define ANLPAR_100BTX_FD              (1 << 8)
+#define ANLPAR_100BTX_HD              (1 << 7)
+#define ANLPAR_10BT_FD                (1 << 6)
+#define ANLPAR_10BT_HD                (1 << 5)
+#define ANLPAR_SELECTOR4              (1 << 4)
+#define ANLPAR_SELECTOR3              (1 << 3)
+#define ANLPAR_SELECTOR2              (1 << 2)
+#define ANLPAR_SELECTOR1              (1 << 1)
+#define ANLPAR_SELECTOR0              (1 << 0)
+
+//ANER register
+#define ANER_PAR_DET_FAULT            (1 << 4)
+#define ANER_LP_NEXT_PAGE_ABLE        (1 << 3)
+#define ANER_NEXT_PAGE_ABLE           (1 << 2)
+#define ANER_PAGE_RECEIVED            (1 << 1)
+#define ANER_LP_AN_ABLE               (1 << 0)
+
+//ANNPTR register
+#define ANNPTR_NEXT_PAGE              (1 << 15)
+#define ANNPTR_MSG_PAGE               (1 << 13)
+#define ANNPTR_ACK2                   (1 << 12)
+#define ANNPTR_TOGGLE                 (1 << 11)
+#define ANNPTR_MESSAGE10              (1 << 10)
+#define ANNPTR_MESSAGE9               (1 << 9)
+#define ANNPTR_MESSAGE8               (1 << 8)
+#define ANNPTR_MESSAGE7               (1 << 7)
+#define ANNPTR_MESSAGE6               (1 << 6)
+#define ANNPTR_MESSAGE5               (1 << 5)
+#define ANNPTR_MESSAGE4               (1 << 4)
+#define ANNPTR_MESSAGE3               (1 << 3)
+#define ANNPTR_MESSAGE2               (1 << 2)
+#define ANNPTR_MESSAGE1               (1 << 1)
+#define ANNPTR_MESSAGE0               (1 << 0)
+
+//LPNPAR register
+#define LPNPAR_NEXT_PAGE              (1 << 15)
+#define LPNPAR_ACK                    (1 << 14)
+#define LPNPAR_MSG_PAGE               (1 << 13)
+#define LPNPAR_ACK2                   (1 << 12)
+#define LPNPAR_TOGGLE                 (1 << 11)
+#define LPNPAR_MESSAGE10              (1 << 10)
+#define LPNPAR_MESSAGE9               (1 << 9)
+#define LPNPAR_MESSAGE8               (1 << 8)
+#define LPNPAR_MESSAGE7               (1 << 7)
+#define LPNPAR_MESSAGE6               (1 << 6)
+#define LPNPAR_MESSAGE5               (1 << 5)
+#define LPNPAR_MESSAGE4               (1 << 4)
+#define LPNPAR_MESSAGE3               (1 << 3)
+#define LPNPAR_MESSAGE2               (1 << 2)
+#define LPNPAR_MESSAGE1               (1 << 1)
+#define LPNPAR_MESSAGE0               (1 << 0)
+
+//1000BT_CTRL register
+#define _1000BT_CTRL_TEST_MODE2       (1 << 15)
+#define _1000BT_CTRL_TEST_MODE1       (1 << 14)
+#define _1000BT_CTRL_TEST_MODE0       (1 << 13)
+#define _1000BT_CTRL_MS_MAN_CONF_EN   (1 << 12)
+#define _1000BT_CTRL_MS_MAN_CONF_VAL  (1 << 11)
+#define _1000BT_CTRL_PORT_TYPE        (1 << 10)
+#define _1000BT_CTRL_1000BT_FD        (1 << 9)
+#define _1000BT_CTRL_1000BT_HD        (1 << 8)
+
+//1000BT_STATUS register
+#define _1000BT_STATUS_MS_CONF_FAULT  (1 << 15)
+#define _1000BT_STATUS_MS_CONF_RES    (1 << 14)
+#define _1000BT_STATUS_LOC_REC_STATUS (1 << 13)
+#define _1000BT_STATUS_REM_REC_STATUS (1 << 12)
+#define _1000BT_STATUS_LP_1000BT_FD   (1 << 11)
+#define _1000BT_STATUS_LP_1000BT_HD   (1 << 10)
+#define _1000BT_STATUS_IDLE_ERR_CTR7  (1 << 7)
+#define _1000BT_STATUS_IDLE_ERR_CTR6  (1 << 6)
+#define _1000BT_STATUS_IDLE_ERR_CTR5  (1 << 5)
+#define _1000BT_STATUS_IDLE_ERR_CTR4  (1 << 4)
+#define _1000BT_STATUS_IDLE_ERR_CTR3  (1 << 3)
+#define _1000BT_STATUS_IDLE_ERR_CTR2  (1 << 2)
+#define _1000BT_STATUS_IDLE_ERR_CTR1  (1 << 1)
+#define _1000BT_STATUS_IDLE_ERR_CTR0  (1 << 0)
+
+//MMD_CTRL register
+#define MMD_CTRL_DEVICE_OP_MODE1      (1 << 15)
+#define MMD_CTRL_DEVICE_OP_MODE0      (1 << 14)
+#define MMD_CTRL_DEVICE_ADDR4         (1 << 4)
+#define MMD_CTRL_DEVICE_ADDR3         (1 << 3)
+#define MMD_CTRL_DEVICE_ADDR2         (1 << 2)
+#define MMD_CTRL_DEVICE_ADDR1         (1 << 1)
+#define MMD_CTRL_DEVICE_ADDR0         (1 << 0)
+
+//EXT_STATUS register
+#define EXT_STATUS_1000BX_FD          (1 << 15)
+#define EXT_STATUS_1000BX_HD          (1 << 14)
+#define EXT_STATUS_1000BT_FD          (1 << 13)
+#define EXT_STATUS_1000BT_HD          (1 << 12)
+
+//RLB register
+#define RLB_REMOTE_LOOPBACK           (1 << 8)
+
+//LINKMDCD register
+#define LINKMDCD_DIAG_EN              (1 << 15)
+#define LINKMDCD_DIAG_TEST_PAIR1      (1 << 13)
+#define LINKMDCD_DIAG_TEST_PAIR0      (1 << 12)
+#define LINKMDCD_FAULT_STATUS1        (1 << 9)
+#define LINKMDCD_FAULT_STATUS0        (1 << 8)
+#define LINKMDCD_FAULT_DATA7          (1 << 7)
+#define LINKMDCD_FAULT_DATA6          (1 << 6)
+#define LINKMDCD_FAULT_DATA5          (1 << 5)
+#define LINKMDCD_FAULT_DATA4          (1 << 4)
+#define LINKMDCD_FAULT_DATA3          (1 << 3)
+#define LINKMDCD_FAULT_DATA2          (1 << 2)
+#define LINKMDCD_FAULT_DATA1          (1 << 1)
+#define LINKMDCD_FAULT_DATA0          (1 << 0)
+
+//DPMAPCSS register
+#define DPMAPCSS_1000BT_LINK_STATUS   (1 << 2)
+#define DPMAPCSS_100BTX_LINK_STATUS   (1 << 1)
+
+//ICSR register
+#define ICSR_JABBER_IE                (1 << 15)
+#define ICSR_RECEIVE_ERROR_IE         (1 << 14)
+#define ICSR_PAGE_RECEIVED_IE         (1 << 13)
+#define ICSR_PAR_DET_FAULT_IE         (1 << 12)
+#define ICSR_LP_ACK_IE                (1 << 11)
+#define ICSR_LINK_DOWN_IE             (1 << 10)
+#define ICSR_REMOTE_FAULT_IE          (1 << 9)
+#define ICSR_LINK_UP_IE               (1 << 8)
+#define ICSR_JABBER_IF                (1 << 7)
+#define ICSR_RECEIVE_ERROR_IF         (1 << 6)
+#define ICSR_PAGE_RECEIVED_IF         (1 << 5)
+#define ICSR_PAR_DET_FAULT_IF         (1 << 4)
+#define ICSR_LP_ACK_IF                (1 << 3)
+#define ICSR_LINK_DOWN_IF             (1 << 2)
+#define ICSR_REMOTE_FAULT_IF          (1 << 1)
+#define ICSR_LINK_UP_IF               (1 << 0)
+
+//AUTOMDI register
+#define AUTOMDI_MDI_SEL               (1 << 7)
+#define AUTOMDI_SWAP_OFF              (1 << 6)
+
+//PHYCON register
+#define PHYCON_INT_LEVEL              (1 << 14)
+#define PHYCON_JABBER_EN              (1 << 9)
+#define PHYCON_SPEED_1000BT           (1 << 6)
+#define PHYCON_SPEED_100BTX           (1 << 5)
+#define PHYCON_SPEED_10BT             (1 << 4)
+#define PHYCON_DUPLEX_STATUS          (1 << 3)
+#define PHYCON_1000BT_MS_STATUS       (1 << 2)
+#define PHYCON_LINK_STATUS_CHECK_FAIL (1 << 0)
+
+//KSZ9031 Ethernet PHY driver
+extern const PhyDriver ksz9031PhyDriver;
+
+//KSZ9031 related functions
+error_t ksz9031Init(NetInterface *interface);
+
+void ksz9031Tick(NetInterface *interface);
+
+void ksz9031EnableIrq(NetInterface *interface);
+void ksz9031DisableIrq(NetInterface *interface);
+
+void ksz9031EventHandler(NetInterface *interface);
+
+void ksz9031WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t ksz9031ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void ksz9031DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lan8710.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,289 @@
+/**
+ * @file lan8710.c
+ * @brief LAN8710 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/lan8710.h"
+#include "debug.h"
+
+
+/**
+ * @brief LAN8710 Ethernet PHY driver
+ **/
+
+const PhyDriver lan8710PhyDriver =
+{
+   lan8710Init,
+   lan8710Tick,
+   lan8710EnableIrq,
+   lan8710DisableIrq,
+   lan8710EventHandler,
+};
+
+
+/**
+ * @brief LAN8710 PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lan8710Init(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing LAN8710...\r\n");
+
+   //Initialize external interrupt line driver
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->init();
+
+   //Reset PHY transceiver (soft reset)
+   lan8710WritePhyReg(interface, LAN8710_PHY_REG_BMCR, BMCR_RESET);
+   //Wait for the reset to complete
+   while(lan8710ReadPhyReg(interface, LAN8710_PHY_REG_BMCR) & BMCR_RESET);
+
+   //Dump PHY registers for debugging purpose
+   lan8710DumpPhyReg(interface);
+
+   //The PHY will generate interrupts when link status changes are detected
+   lan8710WritePhyReg(interface, LAN8710_PHY_REG_IMR, IMR_AN_COMPLETE | IMR_LINK_DOWN);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief LAN8710 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8710Tick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //No external interrupt line driver?
+   if(interface->extIntDriver == NULL)
+   {
+      //Read basic status register
+      value = lan8710ReadPhyReg(interface, LAN8710_PHY_REG_BMSR);
+      //Retrieve current link state
+      linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+      //Link up event?
+      if(linkState && !interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+      //Link down event?
+      else if(!linkState && interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8710EnableIrq(NetInterface *interface)
+{
+   //Enable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8710DisableIrq(NetInterface *interface)
+{
+   //Disable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief LAN8710 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8710EventHandler(NetInterface *interface)
+{
+   uint16_t value;
+
+   //Read status register to acknowledge the interrupt
+   value = lan8710ReadPhyReg(interface, LAN8710_PHY_REG_ISR);
+
+   //Link status change?
+   if(value & (IMR_AN_COMPLETE | IMR_LINK_DOWN))
+   {
+      //Any link failure condition is latched in the BMSR register... Reading
+      //the register twice will always return the actual link status
+      value = lan8710ReadPhyReg(interface, LAN8710_PHY_REG_BMSR);
+      value = lan8710ReadPhyReg(interface, LAN8710_PHY_REG_BMSR);
+
+      //Link is up?
+      if(value & BMSR_LINK_STATUS)
+      {
+         //Read PHY special control/status register
+         value = lan8710ReadPhyReg(interface, LAN8710_PHY_REG_PSCSR);
+
+         //Check current operation mode
+         switch(value & PSCSR_HCDSPEED_MASK)
+         {
+         //10BASE-T
+         case PSCSR_HCDSPEED_10BT:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //10BASE-T full-duplex
+         case PSCSR_HCDSPEED_10BT_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //100BASE-TX
+         case PSCSR_HCDSPEED_100BTX:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //100BASE-TX full-duplex
+         case PSCSR_HCDSPEED_100BTX_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //Unknown operation mode
+         default:
+            //Debug message
+            TRACE_WARNING("Invalid Duplex mode\r\n");
+            break;
+         }
+
+         //Update link state
+         interface->linkState = TRUE;
+
+         //Adjust MAC configuration parameters for proper operation
+         interface->nicDriver->updateMacConfig(interface);
+      }
+      else
+      {
+         //Update link state
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void lan8710WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = LAN8710_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t lan8710ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = LAN8710_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8710DumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, lan8710ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lan8710.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,211 @@
+/**
+ * @file lan8710.h
+ * @brief LAN8710 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _LAN8710_H
+#define _LAN8710_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#ifndef LAN8710_PHY_ADDR
+   #define LAN8710_PHY_ADDR 0
+#elif (LAN8710_PHY_ADDR < 0 || LAN8710_PHY_ADDR > 31)
+   #error LAN8710_PHY_ADDR parameter is not valid
+#endif
+
+//LAN8710 registers
+#define LAN8710_PHY_REG_BMCR        0x00
+#define LAN8710_PHY_REG_BMSR        0x01
+#define LAN8710_PHY_REG_PHYIDR1     0x02
+#define LAN8710_PHY_REG_PHYIDR2     0x03
+#define LAN8710_PHY_REG_ANAR        0x04
+#define LAN8710_PHY_REG_ANLPAR      0x05
+#define LAN8710_PHY_REG_ANER        0x06
+#define LAN8710_PHY_REG_SRR         0x10
+#define LAN8710_PHY_REG_MCSR        0x11
+#define LAN8710_PHY_REG_SMR         0x12
+#define LAN8710_PHY_REG_SECR        0x1A
+#define LAN8710_PHY_REG_SCSIR       0x1B
+#define LAN8710_PHY_REG_SITCR       0x1C
+#define LAN8710_PHY_REG_ISR         0x1D
+#define LAN8710_PHY_REG_IMR         0x1E
+#define LAN8710_PHY_REG_PSCSR       0x1F
+
+//BMCR register
+#define BMCR_RESET                  (1 << 15)
+#define BMCR_LOOPBACK               (1 << 14)
+#define BMCR_SPEED_SEL              (1 << 13)
+#define BMCR_AN_EN                  (1 << 12)
+#define BMCR_POWER_DOWN             (1 << 11)
+#define BMCR_ISOLATE                (1 << 10)
+#define BMCR_RESTART_AN             (1 << 9)
+#define BMCR_DUPLEX_MODE            (1 << 8)
+#define BMCR_COL_TEST               (1 << 7)
+
+//BMSR register
+#define BMSR_100BT4                 (1 << 15)
+#define BMSR_100BTX_FD              (1 << 14)
+#define BMSR_100BTX                 (1 << 13)
+#define BMSR_10BT_FD                (1 << 12)
+#define BMSR_10BT                   (1 << 11)
+#define BMSR_AN_COMPLETE            (1 << 5)
+#define BMSR_REMOTE_FAULT           (1 << 4)
+#define BMSR_AN_ABLE                (1 << 3)
+#define BMSR_LINK_STATUS            (1 << 2)
+#define BMSR_JABBER_DETECT          (1 << 1)
+#define BMSR_EXTENDED_CAP           (1 << 0)
+
+//ANAR register
+#define ANAR_NP                     (1 << 15)
+#define ANAR_RF                     (1 << 13)
+#define ANAR_PAUSE1                 (1 << 11)
+#define ANAR_PAUSE0                 (1 << 10)
+#define ANAR_100BT4                 (1 << 9)
+#define ANAR_100BTX_FD              (1 << 8)
+#define ANAR_100BTX                 (1 << 7)
+#define ANAR_10BT_FD                (1 << 6)
+#define ANAR_10BT                   (1 << 5)
+#define ANAR_SELECTOR4              (1 << 4)
+#define ANAR_SELECTOR3              (1 << 3)
+#define ANAR_SELECTOR2              (1 << 2)
+#define ANAR_SELECTOR1              (1 << 1)
+#define ANAR_SELECTOR0              (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NP                   (1 << 15)
+#define ANLPAR_ACK                  (1 << 14)
+#define ANLPAR_RF                   (1 << 13)
+#define ANLPAR_PAUSE                (1 << 10)
+#define ANLPAR_100BT4               (1 << 9)
+#define ANLPAR_100BTX_FD            (1 << 8)
+#define ANLPAR_100BTX               (1 << 7)
+#define ANLPAR_10BT_FD              (1 << 6)
+#define ANLPAR_10BT                 (1 << 5)
+#define ANLPAR_SELECTOR4            (1 << 4)
+#define ANLPAR_SELECTOR3            (1 << 3)
+#define ANLPAR_SELECTOR2            (1 << 2)
+#define ANLPAR_SELECTOR1            (1 << 1)
+#define ANLPAR_SELECTOR0            (1 << 0)
+
+//ANER register
+#define ANER_PDF                    (1 << 4)
+#define ANER_LP_NP_ABLE             (1 << 3)
+#define ANER_NP_ABLE                (1 << 2)
+#define ANER_PAGE_RX                (1 << 1)
+#define ANER_LP_AN_ABLE             (1 << 0)
+
+//SRR register
+#define SRR_SILICON_REVISON3        (1 << 9)
+#define SRR_SILICON_REVISON2        (1 << 8)
+#define SRR_SILICON_REVISON1        (1 << 7)
+#define SRR_SILICON_REVISON0        (1 << 6)
+
+//MCSR register
+#define MCSR_EDPWRDOWN              (1 << 13)
+#define MCSR_LOWSQEN                (1 << 11)
+#define MCSR_MDPREBP                (1 << 10)
+#define MCSR_FARLOOPBACK            (1 << 9)
+#define MCSR_ALTINT                 (1 << 6)
+#define MCSR_PHYADBP                (1 << 3)
+#define MCSR_FORCE_GOOD_LINK_STATUS (1 << 2)
+#define MCSR_ENERGYON               (1 << 1)
+
+//SMR register
+#define SMR_MIIMODE                 (1 << 14)
+#define SMR_MODE2                   (1 << 7)
+#define SMR_MODE1                   (1 << 6)
+#define SMR_MODE0                   (1 << 5)
+#define SMR_PHYAD4                  (1 << 4)
+#define SMR_PHYAD3                  (1 << 3)
+#define SMR_PHYAD2                  (1 << 2)
+#define SMR_PHYAD1                  (1 << 1)
+#define SMR_PHYAD0                  (1 << 0)
+
+//SCSIR register
+#define SCSIR_AMDIXCTRL            (1 << 15)
+#define SCSIR_CH_SELECT            (1 << 13)
+#define SCSIR_SQEOFF               (1 << 11)
+#define SCSIR_XPOL                 (1 << 4)
+
+//ISR register
+#define ISR_ENERGYON                (1 << 7)
+#define ISR_AN_COMPLETE             (1 << 6)
+#define ISR_REMOTE_FAULT            (1 << 5)
+#define ISR_LINK_DOWN               (1 << 4)
+#define ISR_AN_LP_ACK               (1 << 3)
+#define ISR_PD_FAULT                (1 << 2)
+#define ISR_AN_PAGE_RECEIVED        (1 << 1)
+
+//IMR register
+#define IMR_ENERGYON                (1 << 7)
+#define IMR_AN_COMPLETE             (1 << 6)
+#define IMR_REMOTE_FAULT            (1 << 5)
+#define IMR_LINK_DOWN               (1 << 4)
+#define IMR_AN_LP_ACK               (1 << 3)
+#define IMR_PD_FAULT                (1 << 2)
+#define IMR_AN_PAGE_RECEIVED        (1 << 1)
+
+//PSCSR register
+#define PSCSR_AUTODONE              (1 << 12)
+#define PSCSR_GPO2                  (1 << 9)
+#define PSCSR_GPO1                  (1 << 8)
+#define PSCSR_GPO0                  (1 << 7)
+#define PSCSR_ENABLE_4B5B           (1 << 6)
+#define PSCSR_HCDSPEED2             (1 << 4)
+#define PSCSR_HCDSPEED1             (1 << 3)
+#define PSCSR_HCDSPEED0             (1 << 2)
+#define PSCSR_SCRAMBLE_DISABLE      (1 << 0)
+
+//Speed indication
+#define PSCSR_HCDSPEED_MASK         (7 << 2)
+#define PSCSR_HCDSPEED_10BT         (1 << 2)
+#define PSCSR_HCDSPEED_100BTX       (2 << 2)
+#define PSCSR_HCDSPEED_10BT_FD      (5 << 2)
+#define PSCSR_HCDSPEED_100BTX_FD    (6 << 2)
+
+//LAN8710 Ethernet PHY driver
+extern const PhyDriver lan8710PhyDriver;
+
+//LAN8710 related functions
+error_t lan8710Init(NetInterface *interface);
+
+void lan8710Tick(NetInterface *interface);
+
+void lan8710EnableIrq(NetInterface *interface);
+void lan8710DisableIrq(NetInterface *interface);
+
+void lan8710EventHandler(NetInterface *interface);
+
+void lan8710WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t lan8710ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void lan8710DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lan8720.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,289 @@
+/**
+ * @file lan8720.c
+ * @brief LAN8720 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/lan8720.h"
+#include "debug.h"
+
+
+/**
+ * @brief LAN8720 Ethernet PHY driver
+ **/
+
+const PhyDriver lan8720PhyDriver =
+{
+   lan8720Init,
+   lan8720Tick,
+   lan8720EnableIrq,
+   lan8720DisableIrq,
+   lan8720EventHandler,
+};
+
+
+/**
+ * @brief LAN8720 PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lan8720Init(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing LAN8720...\r\n");
+
+   //Initialize external interrupt line driver
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->init();
+
+   //Reset PHY transceiver (soft reset)
+   lan8720WritePhyReg(interface, LAN8720_PHY_REG_BMCR, BMCR_RESET);
+   //Wait for the reset to complete
+   while(lan8720ReadPhyReg(interface, LAN8720_PHY_REG_BMCR) & BMCR_RESET);
+
+   //Dump PHY registers for debugging purpose
+   lan8720DumpPhyReg(interface);
+
+   //The PHY will generate interrupts when link status changes are detected
+   lan8720WritePhyReg(interface, LAN8720_PHY_REG_IMR, IMR_AN_COMPLETE | IMR_LINK_DOWN);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief LAN8720 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8720Tick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //No external interrupt line driver?
+   if(interface->extIntDriver == NULL)
+   {
+      //Read basic status register
+      value = lan8720ReadPhyReg(interface, LAN8720_PHY_REG_BMSR);
+      //Retrieve current link state
+      linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+      //Link up event?
+      if(linkState && !interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+      //Link down event?
+      else if(!linkState && interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8720EnableIrq(NetInterface *interface)
+{
+   //Enable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8720DisableIrq(NetInterface *interface)
+{
+   //Disable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief LAN8720 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8720EventHandler(NetInterface *interface)
+{
+   uint16_t value;
+
+   //Read status register to acknowledge the interrupt
+   value = lan8720ReadPhyReg(interface, LAN8720_PHY_REG_ISR);
+
+   //Link status change?
+   if(value & (IMR_AN_COMPLETE | IMR_LINK_DOWN))
+   {
+      //Any link failure condition is latched in the BMSR register... Reading
+      //the register twice will always return the actual link status
+      value = lan8720ReadPhyReg(interface, LAN8720_PHY_REG_BMSR);
+      value = lan8720ReadPhyReg(interface, LAN8720_PHY_REG_BMSR);
+
+      //Link is up?
+      if(value & BMSR_LINK_STATUS)
+      {
+         //Read PHY special control/status register
+         value = lan8720ReadPhyReg(interface, LAN8720_PHY_REG_PSCSR);
+
+         //Check current operation mode
+         switch(value & PSCSR_HCDSPEED_MASK)
+         {
+         //10BASE-T
+         case PSCSR_HCDSPEED_10BT:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //10BASE-T full-duplex
+         case PSCSR_HCDSPEED_10BT_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //100BASE-TX
+         case PSCSR_HCDSPEED_100BTX:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //100BASE-TX full-duplex
+         case PSCSR_HCDSPEED_100BTX_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //Unknown operation mode
+         default:
+            //Debug message
+            TRACE_WARNING("Invalid Duplex mode\r\n");
+            break;
+         }
+
+         //Update link state
+         interface->linkState = TRUE;
+
+         //Adjust MAC configuration parameters for proper operation
+         interface->nicDriver->updateMacConfig(interface);
+      }
+      else
+      {
+         //Update link state
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void lan8720WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = LAN8720_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t lan8720ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = LAN8720_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8720DumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, lan8720ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lan8720.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,210 @@
+/**
+ * @file lan8720.h
+ * @brief LAN8720 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _LAN8720_H
+#define _LAN8720_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#ifndef LAN8720_PHY_ADDR
+   #define LAN8720_PHY_ADDR 0
+#elif (LAN8720_PHY_ADDR < 0 || LAN8720_PHY_ADDR > 31)
+   #error LAN8720_PHY_ADDR parameter is not valid
+#endif
+
+//LAN8720 registers
+#define LAN8720_PHY_REG_BMCR        0x00
+#define LAN8720_PHY_REG_BMSR        0x01
+#define LAN8720_PHY_REG_PHYIDR1     0x02
+#define LAN8720_PHY_REG_PHYIDR2     0x03
+#define LAN8720_PHY_REG_ANAR        0x04
+#define LAN8720_PHY_REG_ANLPAR      0x05
+#define LAN8720_PHY_REG_ANER        0x06
+#define LAN8720_PHY_REG_SRR         0x10
+#define LAN8720_PHY_REG_MCSR        0x11
+#define LAN8720_PHY_REG_SMR         0x12
+#define LAN8720_PHY_REG_SECR        0x1A
+#define LAN8720_PHY_REG_SCSIR       0x1B
+#define LAN8720_PHY_REG_SITCR       0x1C
+#define LAN8720_PHY_REG_ISR         0x1D
+#define LAN8720_PHY_REG_IMR         0x1E
+#define LAN8720_PHY_REG_PSCSR       0x1F
+
+//BMCR register
+#define BMCR_RESET                  (1 << 15)
+#define BMCR_LOOPBACK               (1 << 14)
+#define BMCR_SPEED_SEL              (1 << 13)
+#define BMCR_AN_EN                  (1 << 12)
+#define BMCR_POWER_DOWN             (1 << 11)
+#define BMCR_ISOLATE                (1 << 10)
+#define BMCR_RESTART_AN             (1 << 9)
+#define BMCR_DUPLEX_MODE            (1 << 8)
+#define BMCR_COL_TEST               (1 << 7)
+
+//BMSR register
+#define BMSR_100BT4                 (1 << 15)
+#define BMSR_100BTX_FD              (1 << 14)
+#define BMSR_100BTX                 (1 << 13)
+#define BMSR_10BT_FD                (1 << 12)
+#define BMSR_10BT                   (1 << 11)
+#define BMSR_AN_COMPLETE            (1 << 5)
+#define BMSR_REMOTE_FAULT           (1 << 4)
+#define BMSR_AN_ABLE                (1 << 3)
+#define BMSR_LINK_STATUS            (1 << 2)
+#define BMSR_JABBER_DETECT          (1 << 1)
+#define BMSR_EXTENDED_CAP           (1 << 0)
+
+//ANAR register
+#define ANAR_NP                     (1 << 15)
+#define ANAR_RF                     (1 << 13)
+#define ANAR_PAUSE1                 (1 << 11)
+#define ANAR_PAUSE0                 (1 << 10)
+#define ANAR_100BT4                 (1 << 9)
+#define ANAR_100BTX_FD              (1 << 8)
+#define ANAR_100BTX                 (1 << 7)
+#define ANAR_10BT_FD                (1 << 6)
+#define ANAR_10BT                   (1 << 5)
+#define ANAR_SELECTOR4              (1 << 4)
+#define ANAR_SELECTOR3              (1 << 3)
+#define ANAR_SELECTOR2              (1 << 2)
+#define ANAR_SELECTOR1              (1 << 1)
+#define ANAR_SELECTOR0              (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NP                   (1 << 15)
+#define ANLPAR_ACK                  (1 << 14)
+#define ANLPAR_RF                   (1 << 13)
+#define ANLPAR_PAUSE                (1 << 10)
+#define ANLPAR_100BT4               (1 << 9)
+#define ANLPAR_100BTX_FD            (1 << 8)
+#define ANLPAR_100BTX               (1 << 7)
+#define ANLPAR_10BT_FD              (1 << 6)
+#define ANLPAR_10BT                 (1 << 5)
+#define ANLPAR_SELECTOR4            (1 << 4)
+#define ANLPAR_SELECTOR3            (1 << 3)
+#define ANLPAR_SELECTOR2            (1 << 2)
+#define ANLPAR_SELECTOR1            (1 << 1)
+#define ANLPAR_SELECTOR0            (1 << 0)
+
+//ANER register
+#define ANER_PDF                    (1 << 4)
+#define ANER_LP_NP_ABLE             (1 << 3)
+#define ANER_NP_ABLE                (1 << 2)
+#define ANER_PAGE_RX                (1 << 1)
+#define ANER_LP_AN_ABLE             (1 << 0)
+
+//SRR register
+#define SRR_SILICON_REVISON3        (1 << 9)
+#define SRR_SILICON_REVISON2        (1 << 8)
+#define SRR_SILICON_REVISON1        (1 << 7)
+#define SRR_SILICON_REVISON0        (1 << 6)
+
+//MCSR register
+#define MCSR_EDPWRDOWN              (1 << 13)
+#define MCSR_LOWSQEN                (1 << 11)
+#define MCSR_MDPREBP                (1 << 10)
+#define MCSR_FARLOOPBACK            (1 << 9)
+#define MCSR_ALTINT                 (1 << 6)
+#define MCSR_PHYADBP                (1 << 3)
+#define MCSR_FORCE_GOOD_LINK_STATUS (1 << 2)
+#define MCSR_ENERGYON               (1 << 1)
+
+//SMR register
+#define SMR_MODE2                   (1 << 7)
+#define SMR_MODE1                   (1 << 6)
+#define SMR_MODE0                   (1 << 5)
+#define SMR_PHYAD4                  (1 << 4)
+#define SMR_PHYAD3                  (1 << 3)
+#define SMR_PHYAD2                  (1 << 2)
+#define SMR_PHYAD1                  (1 << 1)
+#define SMR_PHYAD0                  (1 << 0)
+
+//SCSIR register
+#define SCSIR_AMDIXCTRL            (1 << 15)
+#define SCSIR_CH_SELECT            (1 << 13)
+#define SCSIR_SQEOFF               (1 << 11)
+#define SCSIR_XPOL                 (1 << 4)
+
+//ISR register
+#define ISR_ENERGYON                (1 << 7)
+#define ISR_AN_COMPLETE             (1 << 6)
+#define ISR_REMOTE_FAULT            (1 << 5)
+#define ISR_LINK_DOWN               (1 << 4)
+#define ISR_AN_LP_ACK               (1 << 3)
+#define ISR_PD_FAULT                (1 << 2)
+#define ISR_AN_PAGE_RECEIVED        (1 << 1)
+
+//IMR register
+#define IMR_ENERGYON                (1 << 7)
+#define IMR_AN_COMPLETE             (1 << 6)
+#define IMR_REMOTE_FAULT            (1 << 5)
+#define IMR_LINK_DOWN               (1 << 4)
+#define IMR_AN_LP_ACK               (1 << 3)
+#define IMR_PD_FAULT                (1 << 2)
+#define IMR_AN_PAGE_RECEIVED        (1 << 1)
+
+//PSCSR register
+#define PSCSR_AUTODONE              (1 << 12)
+#define PSCSR_GPO2                  (1 << 9)
+#define PSCSR_GPO1                  (1 << 8)
+#define PSCSR_GPO0                  (1 << 7)
+#define PSCSR_ENABLE_4B5B           (1 << 6)
+#define PSCSR_HCDSPEED2             (1 << 4)
+#define PSCSR_HCDSPEED1             (1 << 3)
+#define PSCSR_HCDSPEED0             (1 << 2)
+#define PSCSR_SCRAMBLE_DISABLE      (1 << 0)
+
+//Speed indication
+#define PSCSR_HCDSPEED_MASK         (7 << 2)
+#define PSCSR_HCDSPEED_10BT         (1 << 2)
+#define PSCSR_HCDSPEED_100BTX       (2 << 2)
+#define PSCSR_HCDSPEED_10BT_FD      (5 << 2)
+#define PSCSR_HCDSPEED_100BTX_FD    (6 << 2)
+
+//LAN8720 Ethernet PHY driver
+extern const PhyDriver lan8720PhyDriver;
+
+//LAN8720 related functions
+error_t lan8720Init(NetInterface *interface);
+
+void lan8720Tick(NetInterface *interface);
+
+void lan8720EnableIrq(NetInterface *interface);
+void lan8720DisableIrq(NetInterface *interface);
+
+void lan8720EventHandler(NetInterface *interface);
+
+void lan8720WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t lan8720ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void lan8720DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lan8740.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,289 @@
+/**
+ * @file lan8740.c
+ * @brief LAN8740 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/lan8740.h"
+#include "debug.h"
+
+
+/**
+ * @brief LAN8740 Ethernet PHY driver
+ **/
+
+const PhyDriver lan8740PhyDriver =
+{
+   lan8740Init,
+   lan8740Tick,
+   lan8740EnableIrq,
+   lan8740DisableIrq,
+   lan8740EventHandler,
+};
+
+
+/**
+ * @brief LAN8740 PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lan8740Init(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing LAN8740...\r\n");
+
+   //Initialize external interrupt line driver
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->init();
+
+   //Reset PHY transceiver (soft reset)
+   lan8740WritePhyReg(interface, LAN8740_PHY_REG_BMCR, BMCR_RESET);
+   //Wait for the reset to complete
+   while(lan8740ReadPhyReg(interface, LAN8740_PHY_REG_BMCR) & BMCR_RESET);
+
+   //Dump PHY registers for debugging purpose
+   lan8740DumpPhyReg(interface);
+
+   //The PHY will generate interrupts when link status changes are detected
+   lan8740WritePhyReg(interface, LAN8740_PHY_REG_IMR, IMR_AN_COMPLETE | IMR_LINK_DOWN);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief LAN8740 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8740Tick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //No external interrupt line driver?
+   if(interface->extIntDriver == NULL)
+   {
+      //Read basic status register
+      value = lan8740ReadPhyReg(interface, LAN8740_PHY_REG_BMSR);
+      //Retrieve current link state
+      linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+      //Link up event?
+      if(linkState && !interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+      //Link down event?
+      else if(!linkState && interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8740EnableIrq(NetInterface *interface)
+{
+   //Enable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8740DisableIrq(NetInterface *interface)
+{
+   //Disable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief LAN8740 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8740EventHandler(NetInterface *interface)
+{
+   uint16_t value;
+
+   //Read status register to acknowledge the interrupt
+   value = lan8740ReadPhyReg(interface, LAN8740_PHY_REG_ISR);
+
+   //Link status change?
+   if(value & (IMR_AN_COMPLETE | IMR_LINK_DOWN))
+   {
+      //Any link failure condition is latched in the BMSR register... Reading
+      //the register twice will always return the actual link status
+      value = lan8740ReadPhyReg(interface, LAN8740_PHY_REG_BMSR);
+      value = lan8740ReadPhyReg(interface, LAN8740_PHY_REG_BMSR);
+
+      //Link is up?
+      if(value & BMSR_LINK_STATUS)
+      {
+         //Read PHY special control/status register
+         value = lan8740ReadPhyReg(interface, LAN8740_PHY_REG_PSCSR);
+
+         //Check current operation mode
+         switch(value & PSCSR_HCDSPEED_MASK)
+         {
+         //10BASE-T
+         case PSCSR_HCDSPEED_10BT:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //10BASE-T full-duplex
+         case PSCSR_HCDSPEED_10BT_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //100BASE-TX
+         case PSCSR_HCDSPEED_100BTX:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //100BASE-TX full-duplex
+         case PSCSR_HCDSPEED_100BTX_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //Unknown operation mode
+         default:
+            //Debug message
+            TRACE_WARNING("Invalid Duplex mode\r\n");
+            break;
+         }
+
+         //Update link state
+         interface->linkState = TRUE;
+
+         //Adjust MAC configuration parameters for proper operation
+         interface->nicDriver->updateMacConfig(interface);
+      }
+      else
+      {
+         //Update link state
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void lan8740WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = LAN8740_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t lan8740ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = LAN8740_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8740DumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, lan8740ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lan8740.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,304 @@
+/**
+ * @file lan8740.h
+ * @brief LAN8740 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _LAN8740_H
+#define _LAN8740_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#ifndef LAN8740_PHY_ADDR
+   #define LAN8740_PHY_ADDR 0
+#elif (LAN8740_PHY_ADDR < 0 || LAN8740_PHY_ADDR > 31)
+   #error LAN8740_PHY_ADDR parameter is not valid
+#endif
+
+//LAN8740 registers
+#define LAN8740_PHY_REG_BMCR        0x00
+#define LAN8740_PHY_REG_BMSR        0x01
+#define LAN8740_PHY_REG_PHYIDR1     0x02
+#define LAN8740_PHY_REG_PHYIDR2     0x03
+#define LAN8740_PHY_REG_ANAR        0x04
+#define LAN8740_PHY_REG_ANLPAR      0x05
+#define LAN8740_PHY_REG_ANER        0x06
+#define LAN8740_PHY_REG_ANNPTR      0x07
+#define LAN8740_PHY_REG_ANNPRR      0x08
+#define LAN8740_PHY_REG_MDDACR      0x0D
+#define LAN8740_PHY_REG_MDDAADR     0x0E
+#define LAN8740_PHY_REG_ENCTECR     0x10
+#define LAN8740_PHY_REG_MCSR        0x11
+#define LAN8740_PHY_REG_SMR         0x12
+#define LAN8740_PHY_REG_TDRPDCR     0x18
+#define LAN8740_PHY_REG_TDRCSR      0x19
+#define LAN8740_PHY_REG_SECR        0x1A
+#define LAN8740_PHY_REG_SCSIR       0x1B
+#define LAN8740_PHY_REG_CLR         0x1C
+#define LAN8740_PHY_REG_ISR         0x1D
+#define LAN8740_PHY_REG_IMR         0x1E
+#define LAN8740_PHY_REG_PSCSR       0x1F
+
+//BMCR register
+#define BMCR_RESET                  (1 << 15)
+#define BMCR_LOOPBACK               (1 << 14)
+#define BMCR_SPEED_SEL              (1 << 13)
+#define BMCR_AN_EN                  (1 << 12)
+#define BMCR_POWER_DOWN             (1 << 11)
+#define BMCR_ISOLATE                (1 << 10)
+#define BMCR_RESTART_AN             (1 << 9)
+#define BMCR_DUPLEX_MODE            (1 << 8)
+#define BMCR_COL_TEST               (1 << 7)
+
+//BMSR register
+#define BMSR_100BT4                 (1 << 15)
+#define BMSR_100BTX_FD              (1 << 14)
+#define BMSR_100BTX                 (1 << 13)
+#define BMSR_10BT_FD                (1 << 12)
+#define BMSR_10BT                   (1 << 11)
+#define BMSR_100BT2_FD              (1 << 10)
+#define BMSR_100BT2                 (1 << 9)
+#define BMSR_EXTENTED_STATUS        (1 << 8)
+#define BMSR_AN_COMPLETE            (1 << 5)
+#define BMSR_REMOTE_FAULT           (1 << 4)
+#define BMSR_AN_ABLE                (1 << 3)
+#define BMSR_LINK_STATUS            (1 << 2)
+#define BMSR_JABBER_DETECT          (1 << 1)
+#define BMSR_EXTENDED_CAP           (1 << 0)
+
+//ANAR register
+#define ANAR_NP                     (1 << 15)
+#define ANAR_RF                     (1 << 13)
+#define ANAR_PAUSE1                 (1 << 11)
+#define ANAR_PAUSE0                 (1 << 10)
+#define ANAR_100BTX_FD              (1 << 8)
+#define ANAR_100BTX                 (1 << 7)
+#define ANAR_10BT_FD                (1 << 6)
+#define ANAR_10BT                   (1 << 5)
+#define ANAR_SELECTOR4              (1 << 4)
+#define ANAR_SELECTOR3              (1 << 3)
+#define ANAR_SELECTOR2              (1 << 2)
+#define ANAR_SELECTOR1              (1 << 1)
+#define ANAR_SELECTOR0              (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NP                   (1 << 15)
+#define ANLPAR_ACK                  (1 << 14)
+#define ANLPAR_RF                   (1 << 13)
+#define ANLPAR_PAUSE1               (1 << 11)
+#define ANLPAR_PAUSE0               (1 << 10)
+#define ANLPAR_100BT4               (1 << 9)
+#define ANLPAR_100BTX_FD            (1 << 8)
+#define ANLPAR_100BTX               (1 << 7)
+#define ANLPAR_10BT_FD              (1 << 6)
+#define ANLPAR_10BT                 (1 << 5)
+#define ANLPAR_SELECTOR4            (1 << 4)
+#define ANLPAR_SELECTOR3            (1 << 3)
+#define ANLPAR_SELECTOR2            (1 << 2)
+#define ANLPAR_SELECTOR1            (1 << 1)
+#define ANLPAR_SELECTOR0            (1 << 0)
+
+//ANER register
+#define ANER_RX_NP_LOC_ABLE         (1 << 6)
+#define ANER_RX_NP_STOR_LOC         (1 << 5)
+#define ANER_PDF                    (1 << 4)
+#define ANER_LP_NP_ABLE             (1 << 3)
+#define ANER_NP_ABLE                (1 << 2)
+#define ANER_PAGE_RX                (1 << 1)
+#define ANER_LP_AN_ABLE             (1 << 0)
+
+//ANNPTR register
+#define ANNPTR_NEXT_PAGE            (1 << 15)
+#define ANNPTR_MSG_PAGE             (1 << 13)
+#define ANNPTR_ACK2                 (1 << 12)
+#define ANNPTR_TOGGLE               (1 << 11)
+#define ANNPTR_MESSAGE10            (1 << 10)
+#define ANNPTR_MESSAGE9             (1 << 9)
+#define ANNPTR_MESSAGE8             (1 << 8)
+#define ANNPTR_MESSAGE7             (1 << 7)
+#define ANNPTR_MESSAGE6             (1 << 6)
+#define ANNPTR_MESSAGE5             (1 << 5)
+#define ANNPTR_MESSAGE4             (1 << 4)
+#define ANNPTR_MESSAGE3             (1 << 3)
+#define ANNPTR_MESSAGE2             (1 << 2)
+#define ANNPTR_MESSAGE1             (1 << 1)
+#define ANNPTR_MESSAGE0             (1 << 0)
+
+//ANNPRR register
+#define ANNPRR_NEXT_PAGE            (1 << 15)
+#define ANNPRR_ACK                  (1 << 14)
+#define ANNPRR_MSG_PAGE             (1 << 13)
+#define ANNPRR_ACK2                 (1 << 12)
+#define ANNPRR_TOGGLE               (1 << 11)
+#define ANNPRR_MESSAGE10            (1 << 10)
+#define ANNPRR_MESSAGE9             (1 << 9)
+#define ANNPRR_MESSAGE8             (1 << 8)
+#define ANNPRR_MESSAGE7             (1 << 7)
+#define ANNPRR_MESSAGE6             (1 << 6)
+#define ANNPRR_MESSAGE5             (1 << 5)
+#define ANNPRR_MESSAGE4             (1 << 4)
+#define ANNPRR_MESSAGE3             (1 << 3)
+#define ANNPRR_MESSAGE2             (1 << 2)
+#define ANNPRR_MESSAGE1             (1 << 1)
+#define ANNPRR_MESSAGE0             (1 << 0)
+
+//MDDACR register
+#define MDDACR_MMD_FUNCTION1        (1 << 15)
+#define MDDACR_MMD_FUNCTION0        (1 << 14)
+#define MDDACR_MMD_DEVAD4           (1 << 4)
+#define MDDACR_MMD_DEVAD3           (1 << 3)
+#define MDDACR_MMD_DEVAD2           (1 << 2)
+#define MDDACR_MMD_DEVAD1           (1 << 1)
+#define MDDACR_MMD_DEVAD0           (1 << 0)
+
+//ENCTECR register
+#define ENCTECR_EDPD_TX_NLP_EN      (1 << 15)
+#define ENCTECR_EDPD_TX_NLP_ITS1    (1 << 14)
+#define ENCTECR_EDPD_TX_NLP_ITS0    (1 << 13)
+#define ENCTECR_EDPD_RX_NLP_WAKE_EN (1 << 12)
+#define ENCTECR_EDPD_RX_NLP_MIDS1   (1 << 11)
+#define ENCTECR_EDPD_RX_NLP_MIDS0   (1 << 10)
+#define ENCTECR_PHY_EEE_EN          (1 << 2)
+#define ENCTECR_EDPD_EXT_CROSSOVER  (1 << 1)
+#define ENCTECR_EXT_CROSSOVER_TIME  (1 << 0)
+
+//MCSR register
+#define MCSR_EDPWRDOWN              (1 << 13)
+#define MCSR_FARLOOPBACK            (1 << 9)
+#define MCSR_ALTINT                 (1 << 6)
+#define MCSR_ENERGYON               (1 << 1)
+
+//SMR register
+#define SMR_MIIMODE                 (1 << 14)
+#define SMR_MODE2                   (1 << 7)
+#define SMR_MODE1                   (1 << 6)
+#define SMR_MODE0                   (1 << 5)
+#define SMR_PHYAD4                  (1 << 4)
+#define SMR_PHYAD3                  (1 << 3)
+#define SMR_PHYAD2                  (1 << 2)
+#define SMR_PHYAD1                  (1 << 1)
+#define SMR_PHYAD0                  (1 << 0)
+
+//TDRPDCR register
+#define TDRPDCR_DELAY_IN            (1 << 15)
+#define TDRPDCR_LINE_BREAK_COUNTER2 (1 << 14)
+#define TDRPDCR_LINE_BREAK_COUNTER1 (1 << 13)
+#define TDRPDCR_LINE_BREAK_COUNTER0 (1 << 12)
+#define TDRPDCR_PATTERN_HIGH5       (1 << 11)
+#define TDRPDCR_PATTERN_HIGH4       (1 << 10)
+#define TDRPDCR_PATTERN_HIGH3       (1 << 9)
+#define TDRPDCR_PATTERN_HIGH2       (1 << 8)
+#define TDRPDCR_PATTERN_HIGH1       (1 << 7)
+#define TDRPDCR_PATTERN_HIGH0       (1 << 6)
+#define TDRPDCR_PATTERN_LOW5        (1 << 5)
+#define TDRPDCR_PATTERN_LOW4        (1 << 4)
+#define TDRPDCR_PATTERN_LOW3        (1 << 3)
+#define TDRPDCR_PATTERN_LOW2        (1 << 2)
+#define TDRPDCR_PATTERN_LOW1        (1 << 1)
+#define TDRPDCR_PATTERN_LOW0        (1 << 0)
+
+//TDRCSR register
+#define TDRCSR_EN                   (1 << 15)
+#define TDRCSR_AD_FILTER_EN         (1 << 14)
+#define TDRCSR_CH_CABLE_TYPE1       (1 << 10)
+#define TDRCSR_CH_CABLE_TYPE0       (1 << 9)
+#define TDRCSR_CH_STATUS            (1 << 8)
+#define TDRCSR_CH_LENGTH7           (1 << 7)
+#define TDRCSR_CH_LENGTH6           (1 << 6)
+#define TDRCSR_CH_LENGTH5           (1 << 5)
+#define TDRCSR_CH_LENGTH4           (1 << 4)
+#define TDRCSR_CH_LENGTH3           (1 << 3)
+#define TDRCSR_CH_LENGTH2           (1 << 2)
+#define TDRCSR_CH_LENGTH1           (1 << 1)
+#define TDRCSR_CH_LENGTH0           (1 << 0)
+
+//SCSIR register
+#define SCSIR_AMDIXCTRL             (1 << 15)
+#define SCSIR_CH_SELECT             (1 << 13)
+#define SCSIR_SQEOFF                (1 << 11)
+#define SCSIR_XPOL                  (1 << 4)
+
+//CLR register
+#define CLR_CBLN3                   (1 << 15)
+#define CLR_CBLN2                   (1 << 14)
+#define CLR_CBLN1                   (1 << 13)
+#define CLR_CBLN0                   (1 << 12)
+
+//ISR register
+#define ISR_WOL                     (1 << 8)
+#define ISR_ENERGYON                (1 << 7)
+#define ISR_AN_COMPLETE             (1 << 6)
+#define ISR_REMOTE_FAULT            (1 << 5)
+#define ISR_LINK_DOWN               (1 << 4)
+#define ISR_AN_LP_ACK               (1 << 3)
+#define ISR_PD_FAULT                (1 << 2)
+#define ISR_AN_PAGE_RECEIVED        (1 << 1)
+
+//IMR register
+#define IMR_WOL                     (1 << 8)
+#define IMR_ENERGYON                (1 << 7)
+#define IMR_AN_COMPLETE             (1 << 6)
+#define IMR_REMOTE_FAULT            (1 << 5)
+#define IMR_LINK_DOWN               (1 << 4)
+#define IMR_AN_LP_ACK               (1 << 3)
+#define IMR_PD_FAULT                (1 << 2)
+#define IMR_AN_PAGE_RECEIVED        (1 << 1)
+
+//PSCSR register
+#define PSCSR_AUTODONE              (1 << 12)
+#define PSCSR_ENABLE_4B5B           (1 << 6)
+#define PSCSR_HCDSPEED2             (1 << 4)
+#define PSCSR_HCDSPEED1             (1 << 3)
+#define PSCSR_HCDSPEED0             (1 << 2)
+
+//Speed indication
+#define PSCSR_HCDSPEED_MASK         (7 << 2)
+#define PSCSR_HCDSPEED_10BT         (1 << 2)
+#define PSCSR_HCDSPEED_100BTX       (2 << 2)
+#define PSCSR_HCDSPEED_10BT_FD      (5 << 2)
+#define PSCSR_HCDSPEED_100BTX_FD    (6 << 2)
+
+//LAN8740 Ethernet PHY driver
+extern const PhyDriver lan8740PhyDriver;
+
+//LAN8740 related functions
+error_t lan8740Init(NetInterface *interface);
+
+void lan8740Tick(NetInterface *interface);
+
+void lan8740EnableIrq(NetInterface *interface);
+void lan8740DisableIrq(NetInterface *interface);
+
+void lan8740EventHandler(NetInterface *interface);
+
+void lan8740WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t lan8740ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void lan8740DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lan8742.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,297 @@
+/**
+ * @file lan8742.c
+ * @brief LAN8742 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/lan8742.h"
+#include "debug.h"
+
+
+/**
+ * @brief LAN8742 Ethernet PHY driver
+ **/
+
+const PhyDriver lan8742PhyDriver =
+{
+   lan8742Init,
+   lan8742Tick,
+   lan8742EnableIrq,
+   lan8742DisableIrq,
+   lan8742EventHandler,
+};
+
+
+/**
+ * @brief LAN8742 PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lan8742Init(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing LAN8742...\r\n");
+
+   //Initialize external interrupt line driver
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->init();
+
+   //Reset PHY transceiver (soft reset)
+   lan8742WritePhyReg(interface, LAN8742_PHY_REG_BMCR, BMCR_RESET);
+   //Wait for the reset to complete
+   while(lan8742ReadPhyReg(interface, LAN8742_PHY_REG_BMCR) & BMCR_RESET);
+
+   //Restore default auto-negotiation advertisement parameters
+   lan8742WritePhyReg(interface, LAN8742_PHY_REG_ANAR, ANAR_100BTX_FD |
+      ANAR_100BTX | ANAR_10BT_FD | ANAR_10BT | ANAR_SELECTOR0);
+
+   //Enable auto-negotiation
+   lan8742WritePhyReg(interface, LAN8742_PHY_REG_BMCR, BMCR_AN_EN);
+
+   //Dump PHY registers for debugging purpose
+   lan8742DumpPhyReg(interface);
+
+   //The PHY will generate interrupts when link status changes are detected
+   lan8742WritePhyReg(interface, LAN8742_PHY_REG_IMR,
+      IMR_AN_COMPLETE | IMR_LINK_DOWN);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief LAN8742 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8742Tick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //No external interrupt line driver?
+   if(interface->extIntDriver == NULL)
+   {
+      //Read basic status register
+      value = lan8742ReadPhyReg(interface, LAN8742_PHY_REG_BMSR);
+      //Retrieve current link state
+      linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+      //Link up event?
+      if(linkState && !interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+      //Link down event?
+      else if(!linkState && interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8742EnableIrq(NetInterface *interface)
+{
+   //Enable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8742DisableIrq(NetInterface *interface)
+{
+   //Disable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief LAN8742 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8742EventHandler(NetInterface *interface)
+{
+   uint16_t value;
+
+   //Read status register to acknowledge the interrupt
+   value = lan8742ReadPhyReg(interface, LAN8742_PHY_REG_ISR);
+
+   //Link status change?
+   if(value & (IMR_AN_COMPLETE | IMR_LINK_DOWN))
+   {
+      //Any link failure condition is latched in the BMSR register... Reading
+      //the register twice will always return the actual link status
+      value = lan8742ReadPhyReg(interface, LAN8742_PHY_REG_BMSR);
+      value = lan8742ReadPhyReg(interface, LAN8742_PHY_REG_BMSR);
+
+      //Link is up?
+      if(value & BMSR_LINK_STATUS)
+      {
+         //Read PHY special control/status register
+         value = lan8742ReadPhyReg(interface, LAN8742_PHY_REG_PSCSR);
+
+         //Check current operation mode
+         switch(value & PSCSR_HCDSPEED_MASK)
+         {
+         //10BASE-T
+         case PSCSR_HCDSPEED_10BT:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //10BASE-T full-duplex
+         case PSCSR_HCDSPEED_10BT_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //100BASE-TX
+         case PSCSR_HCDSPEED_100BTX:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+            break;
+         //100BASE-TX full-duplex
+         case PSCSR_HCDSPEED_100BTX_FD:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+            break;
+         //Unknown operation mode
+         default:
+            //Debug message
+            TRACE_WARNING("Invalid Duplex mode\r\n");
+            break;
+         }
+
+         //Update link state
+         interface->linkState = TRUE;
+
+         //Adjust MAC configuration parameters for proper operation
+         interface->nicDriver->updateMacConfig(interface);
+      }
+      else
+      {
+         //Update link state
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void lan8742WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = LAN8742_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t lan8742ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = LAN8742_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan8742DumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, lan8742ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lan8742.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,301 @@
+/**
+ * @file lan8742.h
+ * @brief LAN8742 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _LAN8742_H
+#define _LAN8742_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#ifndef LAN8742_PHY_ADDR
+   #define LAN8742_PHY_ADDR 0
+#elif (LAN8742_PHY_ADDR < 0 || LAN8742_PHY_ADDR > 31)
+   #error LAN8742_PHY_ADDR parameter is not valid
+#endif
+
+//LAN8742 registers
+#define LAN8742_PHY_REG_BMCR        0x00
+#define LAN8742_PHY_REG_BMSR        0x01
+#define LAN8742_PHY_REG_PHYIDR1     0x02
+#define LAN8742_PHY_REG_PHYIDR2     0x03
+#define LAN8742_PHY_REG_ANAR        0x04
+#define LAN8742_PHY_REG_ANLPAR      0x05
+#define LAN8742_PHY_REG_ANER        0x06
+#define LAN8742_PHY_REG_ANNPTR      0x07
+#define LAN8742_PHY_REG_ANNPRR      0x08
+#define LAN8742_PHY_REG_MDDACR      0x0D
+#define LAN8742_PHY_REG_MDDAADR     0x0E
+#define LAN8742_PHY_REG_ENCTR       0x10
+#define LAN8742_PHY_REG_MCSR        0x11
+#define LAN8742_PHY_REG_SMR         0x12
+#define LAN8742_PHY_REG_TDRPDCR     0x18
+#define LAN8742_PHY_REG_TDRCSR      0x19
+#define LAN8742_PHY_REG_SECR        0x1A
+#define LAN8742_PHY_REG_SCSIR       0x1B
+#define LAN8742_PHY_REG_CLR         0x1C
+#define LAN8742_PHY_REG_ISR         0x1D
+#define LAN8742_PHY_REG_IMR         0x1E
+#define LAN8742_PHY_REG_PSCSR       0x1F
+
+//BMCR register
+#define BMCR_RESET                  (1 << 15)
+#define BMCR_LOOPBACK               (1 << 14)
+#define BMCR_SPEED_SEL              (1 << 13)
+#define BMCR_AN_EN                  (1 << 12)
+#define BMCR_POWER_DOWN             (1 << 11)
+#define BMCR_ISOLATE                (1 << 10)
+#define BMCR_RESTART_AN             (1 << 9)
+#define BMCR_DUPLEX_MODE            (1 << 8)
+#define BMCR_COL_TEST               (1 << 7)
+
+//BMSR register
+#define BMSR_100BT4                 (1 << 15)
+#define BMSR_100BTX_FD              (1 << 14)
+#define BMSR_100BTX                 (1 << 13)
+#define BMSR_10BT_FD                (1 << 12)
+#define BMSR_10BT                   (1 << 11)
+#define BMSR_100BT2_FD              (1 << 10)
+#define BMSR_100BT2                 (1 << 9)
+#define BMSR_EXTENTED_STATUS        (1 << 8)
+#define BMSR_AN_COMPLETE            (1 << 5)
+#define BMSR_REMOTE_FAULT           (1 << 4)
+#define BMSR_AN_ABLE                (1 << 3)
+#define BMSR_LINK_STATUS            (1 << 2)
+#define BMSR_JABBER_DETECT          (1 << 1)
+#define BMSR_EXTENDED_CAP           (1 << 0)
+
+//ANAR register
+#define ANAR_NP                     (1 << 15)
+#define ANAR_RF                     (1 << 13)
+#define ANAR_PAUSE1                 (1 << 11)
+#define ANAR_PAUSE0                 (1 << 10)
+#define ANAR_100BTX_FD              (1 << 8)
+#define ANAR_100BTX                 (1 << 7)
+#define ANAR_10BT_FD                (1 << 6)
+#define ANAR_10BT                   (1 << 5)
+#define ANAR_SELECTOR4              (1 << 4)
+#define ANAR_SELECTOR3              (1 << 3)
+#define ANAR_SELECTOR2              (1 << 2)
+#define ANAR_SELECTOR1              (1 << 1)
+#define ANAR_SELECTOR0              (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NP                   (1 << 15)
+#define ANLPAR_ACK                  (1 << 14)
+#define ANLPAR_RF                   (1 << 13)
+#define ANLPAR_PAUSE1               (1 << 11)
+#define ANLPAR_PAUSE0               (1 << 10)
+#define ANLPAR_100BT4               (1 << 9)
+#define ANLPAR_100BTX_FD            (1 << 8)
+#define ANLPAR_100BTX               (1 << 7)
+#define ANLPAR_10BT_FD              (1 << 6)
+#define ANLPAR_10BT                 (1 << 5)
+#define ANLPAR_SELECTOR4            (1 << 4)
+#define ANLPAR_SELECTOR3            (1 << 3)
+#define ANLPAR_SELECTOR2            (1 << 2)
+#define ANLPAR_SELECTOR1            (1 << 1)
+#define ANLPAR_SELECTOR0            (1 << 0)
+
+//ANER register
+#define ANER_RX_NP_LOC_ABLE         (1 << 6)
+#define ANER_RX_NP_STOR_LOC         (1 << 5)
+#define ANER_PDF                    (1 << 4)
+#define ANER_LP_NP_ABLE             (1 << 3)
+#define ANER_NP_ABLE                (1 << 2)
+#define ANER_PAGE_RX                (1 << 1)
+#define ANER_LP_AN_ABLE             (1 << 0)
+
+//ANNPTR register
+#define ANNPTR_NEXT_PAGE            (1 << 15)
+#define ANNPTR_MSG_PAGE             (1 << 13)
+#define ANNPTR_ACK2                 (1 << 12)
+#define ANNPTR_TOGGLE               (1 << 11)
+#define ANNPTR_MESSAGE10            (1 << 10)
+#define ANNPTR_MESSAGE9             (1 << 9)
+#define ANNPTR_MESSAGE8             (1 << 8)
+#define ANNPTR_MESSAGE7             (1 << 7)
+#define ANNPTR_MESSAGE6             (1 << 6)
+#define ANNPTR_MESSAGE5             (1 << 5)
+#define ANNPTR_MESSAGE4             (1 << 4)
+#define ANNPTR_MESSAGE3             (1 << 3)
+#define ANNPTR_MESSAGE2             (1 << 2)
+#define ANNPTR_MESSAGE1             (1 << 1)
+#define ANNPTR_MESSAGE0             (1 << 0)
+
+//ANNPRR register
+#define ANNPRR_NEXT_PAGE            (1 << 15)
+#define ANNPRR_ACK                  (1 << 14)
+#define ANNPRR_MSG_PAGE             (1 << 13)
+#define ANNPRR_ACK2                 (1 << 12)
+#define ANNPRR_TOGGLE               (1 << 11)
+#define ANNPRR_MESSAGE10            (1 << 10)
+#define ANNPRR_MESSAGE9             (1 << 9)
+#define ANNPRR_MESSAGE8             (1 << 8)
+#define ANNPRR_MESSAGE7             (1 << 7)
+#define ANNPRR_MESSAGE6             (1 << 6)
+#define ANNPRR_MESSAGE5             (1 << 5)
+#define ANNPRR_MESSAGE4             (1 << 4)
+#define ANNPRR_MESSAGE3             (1 << 3)
+#define ANNPRR_MESSAGE2             (1 << 2)
+#define ANNPRR_MESSAGE1             (1 << 1)
+#define ANNPRR_MESSAGE0             (1 << 0)
+
+//MDDACR register
+#define MDDACR_MMD_FUNCTION1        (1 << 15)
+#define MDDACR_MMD_FUNCTION0        (1 << 14)
+#define MDDACR_MMD_DEVAD4           (1 << 4)
+#define MDDACR_MMD_DEVAD3           (1 << 3)
+#define MDDACR_MMD_DEVAD2           (1 << 2)
+#define MDDACR_MMD_DEVAD1           (1 << 1)
+#define MDDACR_MMD_DEVAD0           (1 << 0)
+
+//ENCTR register
+#define ENCTR_EDPD_TX_NLP_EN        (1 << 15)
+#define ENCTR_EDPD_TX_NLP_ITS1      (1 << 14)
+#define ENCTR_EDPD_TX_NLP_ITS0      (1 << 13)
+#define ENCTR_EDPD_RX_NLP_WAKE_EN   (1 << 12)
+#define ENCTR_EDPD_RX_NLP_MIDS1     (1 << 11)
+#define ENCTR_EDPD_RX_NLP_MIDS0     (1 << 10)
+#define ENCTR_EDPD_EXT_CROSSOVER    (1 << 1)
+#define ENCTR_EXT_CROSSOVER_TIME    (1 << 0)
+
+//MCSR register
+#define MCSR_EDPWRDOWN              (1 << 13)
+#define MCSR_FARLOOPBACK            (1 << 9)
+#define MCSR_ALTINT                 (1 << 6)
+#define MCSR_ENERGYON               (1 << 1)
+
+//SMR register
+#define SMR_MODE2                   (1 << 7)
+#define SMR_MODE1                   (1 << 6)
+#define SMR_MODE0                   (1 << 5)
+#define SMR_PHYAD4                  (1 << 4)
+#define SMR_PHYAD3                  (1 << 3)
+#define SMR_PHYAD2                  (1 << 2)
+#define SMR_PHYAD1                  (1 << 1)
+#define SMR_PHYAD0                  (1 << 0)
+
+//TDRPDCR register
+#define TDRPDCR_DELAY_IN            (1 << 15)
+#define TDRPDCR_LINE_BREAK_COUNTER2 (1 << 14)
+#define TDRPDCR_LINE_BREAK_COUNTER1 (1 << 13)
+#define TDRPDCR_LINE_BREAK_COUNTER0 (1 << 12)
+#define TDRPDCR_PATTERN_HIGH5       (1 << 11)
+#define TDRPDCR_PATTERN_HIGH4       (1 << 10)
+#define TDRPDCR_PATTERN_HIGH3       (1 << 9)
+#define TDRPDCR_PATTERN_HIGH2       (1 << 8)
+#define TDRPDCR_PATTERN_HIGH1       (1 << 7)
+#define TDRPDCR_PATTERN_HIGH0       (1 << 6)
+#define TDRPDCR_PATTERN_LOW5        (1 << 5)
+#define TDRPDCR_PATTERN_LOW4        (1 << 4)
+#define TDRPDCR_PATTERN_LOW3        (1 << 3)
+#define TDRPDCR_PATTERN_LOW2        (1 << 2)
+#define TDRPDCR_PATTERN_LOW1        (1 << 1)
+#define TDRPDCR_PATTERN_LOW0        (1 << 0)
+
+//TDRCSR register
+#define TDRCSR_EN                   (1 << 15)
+#define TDRCSR_AD_FILTER_EN         (1 << 14)
+#define TDRCSR_CH_CABLE_TYPE1       (1 << 10)
+#define TDRCSR_CH_CABLE_TYPE0       (1 << 9)
+#define TDRCSR_CH_STATUS            (1 << 8)
+#define TDRCSR_CH_LENGTH7           (1 << 7)
+#define TDRCSR_CH_LENGTH6           (1 << 6)
+#define TDRCSR_CH_LENGTH5           (1 << 5)
+#define TDRCSR_CH_LENGTH4           (1 << 4)
+#define TDRCSR_CH_LENGTH3           (1 << 3)
+#define TDRCSR_CH_LENGTH2           (1 << 2)
+#define TDRCSR_CH_LENGTH1           (1 << 1)
+#define TDRCSR_CH_LENGTH0           (1 << 0)
+
+//SCSIR register
+#define SCSIR_AMDIXCTRL             (1 << 15)
+#define SCSIR_CH_SELECT             (1 << 13)
+#define SCSIR_SQEOFF                (1 << 11)
+#define SCSIR_XPOL                  (1 << 4)
+
+//CLR register
+#define CLR_CBLN3                   (1 << 15)
+#define CLR_CBLN2                   (1 << 14)
+#define CLR_CBLN1                   (1 << 13)
+#define CLR_CBLN0                   (1 << 12)
+
+//ISR register
+#define ISR_WOL                     (1 << 8)
+#define ISR_ENERGYON                (1 << 7)
+#define ISR_AN_COMPLETE             (1 << 6)
+#define ISR_REMOTE_FAULT            (1 << 5)
+#define ISR_LINK_DOWN               (1 << 4)
+#define ISR_AN_LP_ACK               (1 << 3)
+#define ISR_PD_FAULT                (1 << 2)
+#define ISR_AN_PAGE_RECEIVED        (1 << 1)
+
+//IMR register
+#define IMR_WOL                     (1 << 8)
+#define IMR_ENERGYON                (1 << 7)
+#define IMR_AN_COMPLETE             (1 << 6)
+#define IMR_REMOTE_FAULT            (1 << 5)
+#define IMR_LINK_DOWN               (1 << 4)
+#define IMR_AN_LP_ACK               (1 << 3)
+#define IMR_PD_FAULT                (1 << 2)
+#define IMR_AN_PAGE_RECEIVED        (1 << 1)
+
+//PSCSR register
+#define PSCSR_AUTODONE              (1 << 12)
+#define PSCSR_HCDSPEED2             (1 << 4)
+#define PSCSR_HCDSPEED1             (1 << 3)
+#define PSCSR_HCDSPEED0             (1 << 2)
+
+//Speed indication
+#define PSCSR_HCDSPEED_MASK         (7 << 2)
+#define PSCSR_HCDSPEED_10BT         (1 << 2)
+#define PSCSR_HCDSPEED_100BTX       (2 << 2)
+#define PSCSR_HCDSPEED_10BT_FD      (5 << 2)
+#define PSCSR_HCDSPEED_100BTX_FD    (6 << 2)
+
+//LAN8742 Ethernet PHY driver
+extern const PhyDriver lan8742PhyDriver;
+
+//LAN8742 related functions
+error_t lan8742Init(NetInterface *interface);
+
+void lan8742Tick(NetInterface *interface);
+
+void lan8742EnableIrq(NetInterface *interface);
+void lan8742DisableIrq(NetInterface *interface);
+
+void lan8742EventHandler(NetInterface *interface);
+
+void lan8742WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t lan8742ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void lan8742DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lan9303.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,384 @@
+/**
+ * @file lan9303.c
+ * @brief LAN9303 Ethernet switch
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/lan9303.h"
+#include "debug.h"
+
+
+/**
+ * @brief LAN9303 Ethernet switch driver
+ **/
+
+const PhyDriver lan9303PhyDriver =
+{
+   lan9303Init,
+   lan9303Tick,
+   lan9303EnableIrq,
+   lan9303DisableIrq,
+   lan9303EventHandler,
+};
+
+
+/**
+ * @brief LAN9303 Ethernet switch initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lan9303Init(NetInterface *interface)
+{
+   uint_t port;
+
+   //Debug message
+   TRACE_INFO("Initializing LAN9303...\r\n");
+
+   //Loop through ports
+   for(port = LAN9303_PORT1; port <= LAN9303_PORT2; port++)
+   {
+      //Debug message
+      TRACE_INFO("Port %u:\r\n", port);
+      //Dump PHY registers for debugging purpose
+      lan9303DumpPhyReg(interface, port);
+   }
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Get link state
+ * @param[in] interface Underlying network interface
+ * @param[in] port Port number
+ * @return Link state
+ **/
+
+bool_t lan9303GetLinkState(NetInterface *interface, uint8_t port)
+{
+   uint16_t status;
+   bool_t linkState;
+
+   //Check port number
+   if(port >= LAN9303_PORT1 && port <= LAN9303_PORT2)
+   {
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+      //Read status register
+      status = lan9303ReadPhyReg(interface, port, LAN9303_PHY_REG_BMSR);
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+
+      //Retrieve current link state
+      linkState = (status & BMSR_LINK_STATUS) ? TRUE : FALSE;
+   }
+   else
+   {
+      //The specified port number is not valid
+      linkState = FALSE;
+   }
+
+   //Return link status
+   return linkState;
+}
+
+
+/**
+ * @brief LAN9303 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan9303Tick(NetInterface *interface)
+{
+   uint_t port;
+   uint16_t status;
+   bool_t linkState;
+
+   //Initialize link state
+   linkState = FALSE;
+
+   //Loop through ports
+   for(port = LAN9303_PORT1; port <= LAN9303_PORT2; port++)
+   {
+      //Read status register
+      status = lan9303ReadPhyReg(interface, port, LAN9303_PHY_REG_BMSR);
+
+      //Retrieve current link state
+      if(status & BMSR_LINK_STATUS)
+         linkState = TRUE;
+   }
+
+   //Link up event?
+   if(linkState)
+   {
+      if(!interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+   //Link down event?
+   else
+   {
+      if(interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan9303EnableIrq(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan9303DisableIrq(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief LAN9303 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan9303EventHandler(NetInterface *interface)
+{
+   uint_t port;
+   uint16_t status;
+   bool_t linkState;
+
+   //Initialize link state
+   linkState = FALSE;
+
+   //Loop through ports
+   for(port = LAN9303_PORT1; port <= LAN9303_PORT2; port++)
+   {
+      //Read status register
+      status = lan9303ReadPhyReg(interface, port, LAN9303_PHY_REG_BMSR);
+
+      //Retrieve current link state
+      if(status & BMSR_LINK_STATUS)
+         linkState = TRUE;
+   }
+
+   //Link up event?
+   if(linkState)
+   {
+      //Set current speed
+      interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+      //Set duplex mode
+      interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+
+      //Update link state
+      interface->linkState = TRUE;
+
+      //Adjust MAC configuration parameters for proper operation
+      interface->nicDriver->updateMacConfig(interface);
+   }
+   else
+   {
+      //Update link state
+      interface->linkState = FALSE;
+   }
+
+   //Process link state change event
+   nicNotifyLinkChange(interface);
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] port Port number
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void lan9303WritePhyReg(NetInterface *interface,
+   uint8_t port, uint8_t address, uint16_t data)
+{
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(port, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] port Port number
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t lan9303ReadPhyReg(NetInterface *interface,
+   uint8_t port, uint8_t address)
+{
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(port, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ * @param[in] port Port number
+ **/
+
+void lan9303DumpPhyReg(NetInterface *interface, uint8_t port)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i,
+         lan9303ReadPhyReg(interface, port, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
+
+/**
+ * @brief Write SMI register
+ * @param[in] interface Underlying network interface
+ * @param[in] address System register address
+ * @param[in] data Register value
+ **/
+
+void lan9303WriteSmiReg(NetInterface *interface, uint16_t address,
+   uint32_t data)
+{
+   uint8_t phyAddr;
+   uint8_t regAddr;
+
+   //PHY address bit 4 is 1 for SMI commands. PHY address 3:0 form
+   //system register address bits 9:6
+   phyAddr = 0x10 | ((address >> 6) & 0x0F);
+
+   //Register address field forms register address bits 5:1
+   regAddr = (address >> 1) & 0x1F;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Write the low word of the SMI register
+   interface->nicDriver->writePhyReg(phyAddr, regAddr, data & 0xFFFF);
+   //Write the high word of the SMI register
+   interface->nicDriver->writePhyReg(phyAddr, regAddr + 1, (data >> 16) & 0xFFFF);
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+}
+
+
+/**
+ * @brief Read SMI register
+ * @param[in] interface Underlying network interface
+ * @param[in] address System register address
+ * @return Register value
+ **/
+
+uint32_t lan9303ReadSmiReg(NetInterface *interface, uint16_t address)
+{
+   uint8_t phyAddr;
+   uint8_t regAddr;
+   uint32_t data;
+
+   //PHY address bit 4 is 1 for SMI commands. PHY address 3:0 form
+   //system register address bits 9:6
+   phyAddr = 0x10 | ((address >> 6) & 0x0F);
+
+   //Register address field forms register address bits 5:1
+   regAddr = (address >> 1) & 0x1F;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Read the low word of the SMI register
+   data = interface->nicDriver->readPhyReg(phyAddr, regAddr);
+   //Read the high word of the SMI register
+   data |= interface->nicDriver->readPhyReg(phyAddr, regAddr + 1) << 16;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return register value
+   return data;
+}
+
+
+/**
+ * @brief Dump SMI registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void lan9303DumpSmiReg(NetInterface *interface)
+{
+   uint16_t i;
+
+   //Loop through SMI registers
+   for(i = 80; i < 512; i += 4)
+   {
+      //Display current SMI register
+      TRACE_DEBUG("0x%03" PRIX16 ": 0x%08" PRIX32 "\r\n", i,
+         lan9303ReadSmiReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lan9303.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,203 @@
+/**
+ * @file lan9303.h
+ * @brief LAN9303 Ethernet switch
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _LAN9303_H
+#define _LAN9303_H
+
+//Dependencies
+#include "core/nic.h"
+
+//LAN9303 ports
+#define LAN9303_PORT1               1
+#define LAN9303_PORT2               2
+
+//LAN9303 PHY registers
+#define LAN9303_PHY_REG_BMCR        0x00
+#define LAN9303_PHY_REG_BMSR        0x01
+#define LAN9303_PHY_REG_PHYIDR1     0x02
+#define LAN9303_PHY_REG_PHYIDR2     0x03
+#define LAN9303_PHY_REG_ANAR        0x04
+#define LAN9303_PHY_REG_ANLPAR      0x05
+#define LAN9303_PHY_REG_ANER        0x06
+#define LAN9303_PHY_REG_MCSR        0x11
+#define LAN9303_PHY_REG_SMR         0x12
+#define LAN9303_PHY_REG_SCSIR       0x1B
+#define LAN9303_PHY_REG_PISR        0x1D
+#define LAN9303_PHY_REG_PIMR        0x1E
+#define LAN9303_PHY_REG_PSCSR       0x1F
+
+//BMCR register
+#define BMCR_RESET                  (1 << 15)
+#define BMCR_LOOPBACK               (1 << 14)
+#define BMCR_SPEED_SEL              (1 << 13)
+#define BMCR_AN_EN                  (1 << 12)
+#define BMCR_POWER_DOWN             (1 << 11)
+#define BMCR_ISOLATE                (1 << 10)
+#define BMCR_RESTART_AN             (1 << 9)
+#define BMCR_DUPLEX_MODE            (1 << 8)
+#define BMCR_COL_TEST               (1 << 7)
+
+//BMSR register
+#define BMSR_100BT4                 (1 << 15)
+#define BMSR_100BTX_FD              (1 << 14)
+#define BMSR_100BTX                 (1 << 13)
+#define BMSR_10BT_FD                (1 << 12)
+#define BMSR_10BT                   (1 << 11)
+#define BMSR_100BT2_FD              (1 << 10)
+#define BMSR_100BT2                 (1 << 9)
+#define BMSR_EXTENTED_STATUS        (1 << 8)
+#define BMSR_AN_COMPLETE            (1 << 5)
+#define BMSR_REMOTE_FAULT           (1 << 4)
+#define BMSR_AN_ABLE                (1 << 3)
+#define BMSR_LINK_STATUS            (1 << 2)
+#define BMSR_JABBER_DETECT          (1 << 1)
+#define BMSR_EXTENDED_CAP           (1 << 0)
+
+//ANAR register
+#define ANAR_RF                     (1 << 13)
+#define ANAR_PAUSE1                 (1 << 11)
+#define ANAR_PAUSE0                 (1 << 10)
+#define ANAR_100BTX_FD              (1 << 8)
+#define ANAR_100BTX                 (1 << 7)
+#define ANAR_10BT_FD                (1 << 6)
+#define ANAR_10BT                   (1 << 5)
+#define ANAR_SELECTOR4              (1 << 4)
+#define ANAR_SELECTOR3              (1 << 3)
+#define ANAR_SELECTOR2              (1 << 2)
+#define ANAR_SELECTOR1              (1 << 1)
+#define ANAR_SELECTOR0              (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NP                   (1 << 15)
+#define ANLPAR_ACK                  (1 << 14)
+#define ANLPAR_RF                   (1 << 13)
+#define ANLPAR_PAUSE1               (1 << 11)
+#define ANLPAR_PAUSE0               (1 << 10)
+#define ANLPAR_100BT4               (1 << 9)
+#define ANLPAR_100BTX_FD            (1 << 8)
+#define ANLPAR_100BTX               (1 << 7)
+#define ANLPAR_10BT_FD              (1 << 6)
+#define ANLPAR_10BT                 (1 << 5)
+#define ANLPAR_SELECTOR4            (1 << 4)
+#define ANLPAR_SELECTOR3            (1 << 3)
+#define ANLPAR_SELECTOR2            (1 << 2)
+#define ANLPAR_SELECTOR1            (1 << 1)
+#define ANLPAR_SELECTOR0            (1 << 0)
+
+//ANER register
+#define ANER_PDF                    (1 << 4)
+#define ANER_LP_NP_ABLE             (1 << 3)
+#define ANER_NP_ABLE                (1 << 2)
+#define ANER_PAGE_RX                (1 << 1)
+#define ANER_LP_AN_ABLE             (1 << 0)
+
+//MCSR register
+#define MCSR_EDPWRDOWN              (1 << 13)
+#define MCSR_ENERGYON               (1 << 1)
+
+//SMR register
+#define SMR_MODE2                   (1 << 7)
+#define SMR_MODE1                   (1 << 6)
+#define SMR_MODE0                   (1 << 5)
+#define SMR_PHYAD4                  (1 << 4)
+#define SMR_PHYAD3                  (1 << 3)
+#define SMR_PHYAD2                  (1 << 2)
+#define SMR_PHYAD1                  (1 << 1)
+#define SMR_PHYAD0                  (1 << 0)
+
+//SCSIR register
+#define SCSIR_AMDIXCTRL             (1 << 15)
+#define SCSIR_AMDIXEN               (1 << 14)
+#define SCSIR_AMDIXSTATE            (1 << 13)
+#define SCSIR_SQEOFF                (1 << 11)
+#define SCSIR_VCOOFF_LP             (1 << 10)
+#define SCSIR_XPOL                  (1 << 4)
+
+//ISR register
+#define ISR_ENERGYON                (1 << 7)
+#define ISR_AN_COMPLETE             (1 << 6)
+#define ISR_REMOTE_FAULT            (1 << 5)
+#define ISR_LINK_DOWN               (1 << 4)
+#define ISR_AN_LP_ACK               (1 << 3)
+#define ISR_PD_FAULT                (1 << 2)
+#define ISR_AN_PAGE_RECEIVED        (1 << 1)
+
+//IMR register
+#define IMR_ENERGYON                (1 << 7)
+#define IMR_AN_COMPLETE             (1 << 6)
+#define IMR_REMOTE_FAULT            (1 << 5)
+#define IMR_LINK_DOWN               (1 << 4)
+#define IMR_AN_LP_ACK               (1 << 3)
+#define IMR_PD_FAULT                (1 << 2)
+#define IMR_AN_PAGE_RECEIVED        (1 << 1)
+
+//PSCSR register
+#define PSCSR_AUTODONE              (1 << 12)
+#define PSCSR_HCDSPEED2             (1 << 4)
+#define PSCSR_HCDSPEED1             (1 << 3)
+#define PSCSR_HCDSPEED0             (1 << 2)
+
+//Speed indication
+#define PSCSR_HCDSPEED_MASK         (7 << 2)
+#define PSCSR_HCDSPEED_10BT         (1 << 2)
+#define PSCSR_HCDSPEED_100BTX       (2 << 2)
+#define PSCSR_HCDSPEED_10BT_FD      (5 << 2)
+#define PSCSR_HCDSPEED_100BTX_FD    (6 << 2)
+
+//LAN9303 Ethernet switch driver
+extern const PhyDriver lan9303PhyDriver;
+
+//LAN9303 related functions
+error_t lan9303Init(NetInterface *interface);
+
+bool_t lan9303GetLinkState(NetInterface *interface, uint8_t port);
+
+void lan9303Tick(NetInterface *interface);
+
+void lan9303EnableIrq(NetInterface *interface);
+void lan9303DisableIrq(NetInterface *interface);
+
+void lan9303EventHandler(NetInterface *interface);
+
+void lan9303WritePhyReg(NetInterface *interface,
+   uint8_t port, uint8_t address, uint16_t data);
+
+uint16_t lan9303ReadPhyReg(NetInterface *interface,
+   uint8_t port, uint8_t address);
+
+void lan9303DumpPhyReg(NetInterface *interface, uint8_t port);
+
+void lan9303WriteSmiReg(NetInterface *interface, uint16_t address,
+   uint32_t data);
+
+uint32_t lan9303ReadSmiReg(NetInterface *interface, uint16_t address);
+
+void lan9303DumpSmiReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lm3s_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,624 @@
+/**
+ * @file lm3s_eth.c
+ * @brief Luminary Stellaris LM3S Ethernet controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//LM3S6965 device?
+#if defined(LM3S6965)
+   #include "lm3s6965.h"
+//LM3S9B92 device?
+#elif defined(LM3S9B92)
+   #include "lm3s9b92.h"
+#endif
+
+//Dependencies
+#include "inc/hw_ints.h"
+#include "inc/hw_memmap.h"
+#include "inc/hw_types.h"
+#include "driverlib/gpio.h"
+#include "driverlib/interrupt.h"
+#include "driverlib/sysctl.h"
+#include "core/net.h"
+#include "drivers/lm3s_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+static uint8_t txBuffer[ETH_MAX_FRAME_SIZE + 2];
+//Receive buffer
+#pragma data_alignment = 4
+static uint8_t rxBuffer[ETH_MAX_FRAME_SIZE];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[ETH_MAX_FRAME_SIZE + 2] __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[ETH_MAX_FRAME_SIZE] __attribute__((aligned(4)));
+
+#endif
+
+
+/**
+ * @brief Stellaris LM3S Ethernet driver
+ **/
+
+const NicDriver lm3sEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   lm3sEthInit,
+   lm3sEthTick,
+   lm3sEthEnableIrq,
+   lm3sEthDisableIrq,
+   lm3sEthEventHandler,
+   lm3sEthSendPacket,
+   lm3sEthSetMulticastFilter,
+   NULL,
+   NULL,
+   NULL,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief Stellaris LM3S Ethernet controller initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lm3sEthInit(NetInterface *interface)
+{
+   uint_t div;
+
+   //Debug message
+   TRACE_INFO("Initializing Stellaris LM3S Ethernet controller...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Enable Ethernet controller clock
+   SysCtlPeripheralEnable(SYSCTL_PERIPH_ETH);
+   //Reset Ethernet controller
+   SysCtlPeripheralReset(SYSCTL_PERIPH_ETH);
+
+   //GPIO configuration
+   lm3sEthInitGpio(interface);
+
+   //The MDC clock frequency cannot exceed 2.5MHz
+   div = SysCtlClockGet() / (2 * 2500000) - 1;
+   //Adjust MDC clock frequency
+   MAC_MDV_R = div & MAC_MDV_DIV_M;
+
+   //Reset PHY transceiver
+   lm3sEthWritePhyReg(PHY_MR0, PHY_MR0_RESET);
+   //Wait for the reset to complete
+   while(lm3sEthReadPhyReg(PHY_MR0) & PHY_MR0_RESET);
+
+   //Dump PHY registers for debugging purpose
+   lm3sEthDumpPhyReg();
+
+   //Configure LED0 and LED1
+   lm3sEthWritePhyReg(PHY_MR23, PHY_MR23_LED0_RXTX | PHY_MR23_LED1_LINK);
+
+   //Set the MAC address
+   MAC_IA0_R = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   MAC_IA1_R = interface->macAddr.w[2];
+
+   //Enable automatic CRC generation and packet padding
+   MAC_TCTL_R = MAC_TCTL_DUPLEX | MAC_TCTL_CRC | MAC_TCTL_PADEN;
+   //Flush the receive FIFO and enable CRC verification
+   MAC_RCTL_R = MAC_RCTL_RSTFIFO | MAC_RCTL_BADCRC;
+
+   //Configure Ethernet interrupts
+   MAC_IM_R = MAC_IM_PHYINTM | MAC_IM_TXEMPM | MAC_IM_RXINTM;
+   //Configure PHY interrupts
+   lm3sEthWritePhyReg(PHY_MR17, PHY_MR17_LSCHG_IE);
+
+   //Set priority grouping (3 bits for pre-emption priority, no bits for subpriority)
+   IntPriorityGroupingSet(LM3S_ETH_IRQ_PRIORITY_GROUPING);
+   //Configure Ethernet interrupt priority
+   IntPrioritySet(INT_ETH, LM3S_ETH_IRQ_PRIORITY);
+
+   //Enable transmitter
+   MAC_TCTL_R |= MAC_TCTL_TXEN;
+   //Enable receiver
+   MAC_RCTL_R |= MAC_RCTL_RXEN;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//EK-LM3S6965 evaluation board?
+#if defined(USE_EK_LM3S6965)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void lm3sEthInitGpio(NetInterface *interface)
+{
+   //Enable GPIO clock
+   SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
+
+   //Configure status LEDs
+   GPIOPinTypeEthernetLED(GPIO_PORTF_BASE, GPIO_PIN_2 | GPIO_PIN_3);
+}
+
+#endif
+
+
+/**
+ * @brief Stellaris LM3S Ethernet timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void lm3sEthTick(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lm3sEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet interrupts
+   IntEnable(INT_ETH);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lm3sEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet interrupts
+   IntDisable(INT_ETH);
+}
+
+
+/**
+ * @brief Stellaris LM3S Ethernet interrupt service routine
+ **/
+
+void ETH_IRQHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read interrupt status register
+   status = MAC_RIS_R;
+
+   //PHY interrupt?
+   if(status & MAC_RIS_PHYINT)
+   {
+      //Disable PHYINT interrupt
+      MAC_IM_R &= ~MAC_IM_PHYINTM;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Transmit FIFO empty?
+   if(status & MAC_RIS_TXEMP)
+   {
+      //Acknowledge TXEMP interrupt
+      MAC_IACK_R = MAC_IACK_TXEMP;
+
+      //Check whether the transmit FIFO is available for writing
+      if(!(MAC_TR_R & MAC_TR_NEWTX))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //Packet received?
+   if(status & MAC_RIS_RXINT)
+   {
+      //Disable RXINT interrupt
+      MAC_IM_R &= ~MAC_IM_RXINTM;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief Stellaris LM3S Ethernet event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void lm3sEthEventHandler(NetInterface *interface)
+{
+   uint32_t status;
+   uint16_t value;
+
+   //Read interrupt status register
+   status = MAC_RIS_R;
+
+   //PHY interrupt?
+   if(status & MAC_RIS_PHYINT)
+   {
+      //Acknowledge PHYINT interrupt
+      MAC_IACK_R = MAC_IACK_PHYINT;
+      //Read PHY interrupt status register
+      value = lm3sEthReadPhyReg(PHY_MR17);
+
+      //Check whether the link state has changed
+      if(value & PHY_MR17_LSCHG_IE)
+      {
+         //Read PHY status register
+         value = lm3sEthReadPhyReg(PHY_MR1);
+
+         //Check link state
+         if(value & PHY_MR1_LINK)
+         {
+            //Read PHY diagnostic register
+            value = lm3sEthReadPhyReg(PHY_MR18);
+
+            //Get current speed
+            if(value & PHY_MR18_RATE)
+            {
+               //100BASE-TX operation
+               interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            }
+            else
+            {
+               //10BASE-T operation
+               interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            }
+
+            //Get current duplex mode
+            if(value & PHY_MR18_DPLX)
+            {
+               //Full-Duplex mode
+               interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+               //Update MAC configuration
+               MAC_TCTL_R |= MAC_TCTL_DUPLEX;
+            }
+            else
+            {
+               //Half-Duplex mode
+               interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+               //Update MAC configuration
+               MAC_TCTL_R &= ~MAC_TCTL_DUPLEX;
+            }
+
+            //Update link state
+            interface->linkState = TRUE;
+         }
+         else
+         {
+            //Update link state
+            interface->linkState = FALSE;
+         }
+
+         //Process link state change event
+         nicNotifyLinkChange(interface);
+      }
+   }
+
+   //Packet received?
+   if(status & MAC_RIS_RXINT)
+   {
+      //Acknowledge RXINT interrupt
+      MAC_IACK_R = MAC_IACK_RXINT;
+
+      //Process all the pending packets
+      while(MAC_NP_R & MAC_NP_NPR_M)
+      {
+         //Read incoming packet
+         lm3sEthReceivePacket(interface);
+      }
+   }
+
+   //Re-enable Ethernet interrupts
+   MAC_IM_R = MAC_IM_PHYINTM | MAC_IM_TXEMPM | MAC_IM_RXINTM;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t lm3sEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t i;
+   size_t length;
+   uint32_t *p;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length < sizeof(EthHeader) || length > ETH_MAX_FRAME_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the transmit FIFO is available for writing
+   if(MAC_TR_R & MAC_TR_NEWTX)
+      return ERROR_FAILURE;
+
+   //Copy user data
+   netBufferRead(txBuffer + 2, buffer, offset, length);
+
+   //The packet is preceded by a 16-bit length field
+   txBuffer[0] = LSB(length - sizeof(EthHeader));
+   txBuffer[1] = MSB(length - sizeof(EthHeader));
+
+   //Point to the beginning of the packet
+   p = (uint32_t *) txBuffer;
+   //Compute the length of the packet in 32-bit words
+   length = (length + 5) / 4;
+
+   //Copy packet to transmit FIFO
+   for(i = 0; i < length; i++)
+      MAC_DATA_R = p[i];
+
+   //Start transmitting
+   MAC_TR_R = MAC_TR_NEWTX;
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @return Error code
+ **/
+
+error_t lm3sEthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t i;
+   size_t n;
+   size_t length;
+   uint32_t data;
+   uint16_t *p;
+
+   //Make sure the FIFO is not empty
+   if(MAC_NP_R & MAC_NP_NPR_M)
+   {
+      //Read the first word
+      data = MAC_DATA_R;
+      //Retrieve the total length of the packet
+      length = data & 0xFFFF;
+
+      //Make sure the length field is valid
+      if(length > 2)
+      {
+         //Point to the beginning of the buffer
+         p = (uint16_t *) rxBuffer;
+
+         //Retrieve the length of the frame
+         length -= 2;
+         //Limit the number of data to be read
+         n = MIN(length, ETH_MAX_FRAME_SIZE);
+
+         //Copy the first half word
+         if(n > 0)
+            *(p++) = (uint16_t) (data >> 16);
+
+         //Copy data from receive FIFO
+         for(i = 2; i < n; i += 4)
+         {
+            //Read a 32-bit word from the FIFO
+            data = MAC_DATA_R;
+            //Write the 32-bit to the receive buffer
+            *(p++) = (uint16_t) data;
+            *(p++) = (uint16_t) (data >> 16);
+         }
+
+         //Skip the remaining bytes
+         while(i < length)
+         {
+            //Read a 32-bit word from the FIFO
+            data = MAC_DATA_R;
+            //Increment byte counter
+            i += 4;
+         }
+
+         //Valid packet received
+         error = NO_ERROR;
+      }
+      else
+      {
+         //Disable receiver
+         MAC_RCTL_R &= ~MAC_RCTL_RXEN;
+         //Flush the receive FIFO
+         MAC_RCTL_R |= MAC_RCTL_RSTFIFO;
+         //Re-enable receiver
+         MAC_RCTL_R |= MAC_RCTL_RXEN;
+
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Check whether a valid packet has been received
+   if(!error)
+   {
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, rxBuffer, n);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lm3sEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   bool_t acceptMulticast;
+
+   //This flag will be set if multicast addresses should be accepted
+   acceptMulticast = FALSE;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Valid entry?
+      if(interface->macMulticastFilter[i].refCount > 0)
+      {
+         //Accept multicast addresses
+         acceptMulticast = TRUE;
+         //We are done
+         break;
+      }
+   }
+
+   //Enable the reception of multicast frames if necessary
+   if(acceptMulticast)
+      MAC_RCTL_R |= MAC_RCTL_AMUL;
+   else
+      MAC_RCTL_R &= ~MAC_RCTL_AMUL;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void lm3sEthWritePhyReg(uint8_t address, uint16_t data)
+{
+   //Data to be written in the PHY register
+   MAC_MTXD_R = data & MAC_MTXD_MDTX_M;
+   //Start a write operation
+   MAC_MCTL_R = (address << 3) | MAC_MCTL_WRITE | MAC_MCTL_START;
+
+   //Wait for the write to complete
+   while(MAC_MCTL_R & MAC_MCTL_START);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t lm3sEthReadPhyReg(uint8_t address)
+{
+   //Start a read operation
+   MAC_MCTL_R = (address << 3) | MAC_MCTL_START;
+
+   //Wait for the read to complete
+   while(MAC_MCTL_R & MAC_MCTL_START);
+
+   //Return PHY register contents
+   return MAC_MRXD_R & MAC_MRXD_MDRX_M;
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ **/
+
+void lm3sEthDumpPhyReg(void)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, lm3sEthReadPhyReg(i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lm3s_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,75 @@
+/**
+ * @file lm3s_eth.h
+ * @brief Luminary Stellaris LM3S Ethernet controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _LM3S_ETH_H
+#define _LM3S_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Interrupt priority grouping
+#ifndef LM3S_ETH_IRQ_PRIORITY_GROUPING
+   #define LM3S_ETH_IRQ_PRIORITY_GROUPING 3
+#elif (LM3S_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error LM3S_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt priority
+#ifndef LM3S_ETH_IRQ_PRIORITY
+   #define LM3S_ETH_IRQ_PRIORITY 192
+#elif (LM3S_ETH_IRQ_PRIORITY < 0)
+   #error LM3S_ETH_IRQ_PRIORITY parameter is not valid
+#endif
+
+//Stellaris LM3S Ethernet driver
+extern const NicDriver lm3sEthDriver;
+
+//Stellaris LM3S Ethernet related functions
+error_t lm3sEthInit(NetInterface *interface);
+void lm3sEthInitGpio(NetInterface *interface);
+
+void lm3sEthTick(NetInterface *interface);
+
+void lm3sEthEnableIrq(NetInterface *interface);
+void lm3sEthDisableIrq(NetInterface *interface);
+void lm3sEthEventHandler(NetInterface *interface);
+
+error_t lm3sEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t lm3sEthReceivePacket(NetInterface *interface);
+
+error_t lm3sEthSetMulticastFilter(NetInterface *interface);
+
+void lm3sEthWritePhyReg(uint8_t address, uint16_t data);
+uint16_t lm3sEthReadPhyReg(uint8_t address);
+
+void lm3sEthDumpPhyReg(void);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lpc175x_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,796 @@
+/**
+ * @file lpc175x_eth.c
+ * @brief LPC1758 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "lpc17xx.h"
+#include "core/net.h"
+#include "drivers/lpc175x_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+static uint8_t txBuffer[LPC175X_ETH_TX_BUFFER_COUNT][LPC175X_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+static uint8_t rxBuffer[LPC175X_ETH_RX_BUFFER_COUNT][LPC175X_ETH_RX_BUFFER_SIZE];
+//Transmit descriptors
+#pragma data_alignment = 4
+static Lpc175xTxDesc txDesc[LPC175X_ETH_TX_BUFFER_COUNT];
+//Transmit status array
+#pragma data_alignment = 4
+static Lpc175xTxStatus txStatus[LPC175X_ETH_TX_BUFFER_COUNT];
+//Receive descriptors
+#pragma data_alignment = 4
+static Lpc175xRxDesc rxDesc[LPC175X_ETH_RX_BUFFER_COUNT];
+//Receive status array
+#pragma data_alignment = 8
+static Lpc175xRxStatus rxStatus[LPC175X_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[LPC175X_ETH_TX_BUFFER_COUNT][LPC175X_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[LPC175X_ETH_RX_BUFFER_COUNT][LPC175X_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Transmit descriptors
+static Lpc175xTxDesc txDesc[LPC175X_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Transmit status array
+static Lpc175xTxStatus txStatus[LPC175X_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive descriptors
+static Lpc175xRxDesc rxDesc[LPC175X_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive status array
+static Lpc175xRxStatus rxStatus[LPC175X_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(8)));
+
+#endif
+
+
+/**
+ * @brief LPC175x Ethernet MAC driver
+ **/
+
+const NicDriver lpc175xEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   lpc175xEthInit,
+   lpc175xEthTick,
+   lpc175xEthEnableIrq,
+   lpc175xEthDisableIrq,
+   lpc175xEthEventHandler,
+   lpc175xEthSendPacket,
+   lpc175xEthSetMulticastFilter,
+   lpc175xEthUpdateMacConfig,
+   lpc175xEthWritePhyReg,
+   lpc175xEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief LPC175x Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc175xEthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing LPC175x Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Power up EMAC controller
+   LPC_SC->PCONP |= PCONP_PCENET;
+
+   //GPIO configuration
+   lpc175xEthInitGpio(interface);
+
+   //Reset host registers, transmit datapath and receive datapath
+   LPC_EMAC->Command = COMMAND_RX_RESET | COMMAND_TX_RESET | COMMAND_REG_RESET;
+
+   //Reset EMAC controller
+   LPC_EMAC->MAC1 = MAC1_SOFT_RESET | MAC1_SIMULATION_RESET |
+      MAC1_RESET_MCS_RX | MAC1_RESET_RX | MAC1_RESET_MCS_TX | MAC1_RESET_TX;
+
+   //Initialize MAC related registers
+   LPC_EMAC->MAC1 = 0;
+   LPC_EMAC->MAC2 = MAC2_PAD_CRC_ENABLE | MAC2_CRC_ENABLE;
+   LPC_EMAC->IPGR = IPGR_DEFAULT_VALUE;
+   LPC_EMAC->CLRT = CLRT_DEFAULT_VALUE;
+
+   //Select RMII mode
+   LPC_EMAC->Command = COMMAND_RMII;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Initialize TX and RX descriptor arrays
+   lpc175xEthInitDesc(interface);
+
+   //Set the MAC address
+   LPC_EMAC->SA0 = interface->macAddr.w[2];
+   LPC_EMAC->SA1 = interface->macAddr.w[1];
+   LPC_EMAC->SA2 = interface->macAddr.w[0];
+
+   //Initialize hash table
+   LPC_EMAC->HashFilterL = 0;
+   LPC_EMAC->HashFilterH = 0;
+
+   //Configure the receive filter
+   LPC_EMAC->RxFilterCtrl = RFC_ACCEPT_PERFECT_EN |
+      RFC_ACCEPT_MULTICAST_HASH_EN | RFC_ACCEPT_BROADCAST_EN;
+
+   //Program the MAXF register with the maximum frame length to be accepted
+   LPC_EMAC->MAXF = 1518;
+
+   //Reset EMAC interrupt flags
+   LPC_EMAC->IntClear  = 0xFFFF;
+   //Enable desired EMAC interrupts
+   LPC_EMAC->IntEnable = INT_TX_DONE | INT_RX_DONE;
+
+   //Set priority grouping (5 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(LPC175X_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure Ethernet interrupt priority
+   NVIC_SetPriority(ENET_IRQn, NVIC_EncodePriority(LPC175X_ETH_IRQ_PRIORITY_GROUPING,
+      LPC175X_ETH_IRQ_GROUP_PRIORITY, LPC175X_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable transmission and reception
+   LPC_EMAC->Command |= COMMAND_TX_ENABLE | COMMAND_RX_ENABLE;
+   //Allow frames to be received
+   LPC_EMAC->MAC1 |= MAC1_RECEIVE_ENABLE;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//MCB1758 evaluation board?
+#if defined(USE_MCB1758)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc175xEthInitGpio(NetInterface *interface)
+{
+   //Configure P1.0 (ENET_TXD0), P1.1 (ENET_TXD1), P1.4 (ENET_TX_EN), P1.8 (ENET_CRS),
+   //P1.9 (ENET_RXD0), P1.10 (ENET_RXD1), P1.14 (RX_ER) and P1.15 (ENET_REF_CLK)
+   LPC_PINCON->PINSEL2 &= ~(PINSEL2_P1_0_MASK | PINSEL2_P1_1_MASK |
+      PINSEL2_P1_4_MASK | PINSEL2_P1_8_MASK | PINSEL2_P1_9_MASK |
+      PINSEL2_P1_10_MASK | PINSEL2_P1_14_MASK | PINSEL2_P1_15_MASK);
+
+   LPC_PINCON->PINSEL2 |= PINSEL2_P1_0_ENET_TXD0 | PINSEL2_P1_1_ENET_TXD1 |
+      PINSEL2_P1_4_ENET_TX_EN | PINSEL2_P1_8_ENET_CRS | PINSEL2_P1_9_ENET_RXD0 |
+      PINSEL2_P1_10_ENET_RXD1 | PINSEL2_P1_14_ENET_RX_ER | PINSEL2_P1_15_ENET_REF_CLK;
+
+   //Configure P2.8 (ENET_MDC) and P2.9 (ENET_MDIO) as GPIOs
+   LPC_PINCON->PINSEL4 &= ~(PINSEL4_P2_8_MASK | PINSEL4_P2_9_MASK);
+   LPC_PINCON->PINSEL4 |= PINSEL4_P2_8_GPIO | PINSEL4_P2_9_GPIO;
+
+   //SMI software implementation to drive MDC and MDIO
+   LPC175X_ETH_MDC_GPIO->FIOCLR = LPC175X_ETH_MDC_MASK;
+   LPC175X_ETH_MDC_GPIO->FIODIR |= LPC175X_ETH_MDC_MASK;
+   LPC175X_ETH_MDIO_GPIO->FIODIR &= ~LPC175X_ETH_MDIO_MASK;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize TX and RX descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc175xEthInitDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX descriptors
+   for(i = 0; i < LPC175X_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Base address of the buffer containing transmit data
+      txDesc[i].packet = (uint32_t) txBuffer[i];
+      //Transmit descriptor control word
+      txDesc[i].control = 0;
+      //Transmit status information word
+      txStatus[i].info = 0;
+   }
+
+   //Initialize RX descriptors
+   for(i = 0; i < LPC175X_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Base address of the buffer for storing receive data
+      rxDesc[i].packet = (uint32_t) rxBuffer[i];
+      //Receive descriptor control word
+      rxDesc[i].control = RX_CTRL_INTERRUPT | (LPC175X_ETH_RX_BUFFER_SIZE - 1);
+      //Receive status information word
+      rxStatus[i].info = 0;
+      //Receive status HashCRC word
+      rxStatus[i].hashCrc = 0;
+   }
+
+   //Initialize EMAC transmit descriptor registers
+   LPC_EMAC->TxDescriptor = (uint32_t) txDesc;
+   LPC_EMAC->TxStatus = (uint32_t) txStatus;
+   LPC_EMAC->TxDescriptorNumber = LPC175X_ETH_TX_BUFFER_COUNT - 1;
+   LPC_EMAC->TxProduceIndex = 0;
+
+   //Initialize EMAC receive descriptor registers
+   LPC_EMAC->RxDescriptor = (uint32_t) rxDesc;
+   LPC_EMAC->RxStatus = (uint32_t) rxStatus;
+   LPC_EMAC->RxDescriptorNumber = LPC175X_ETH_RX_BUFFER_COUNT - 1;
+   LPC_EMAC->RxConsumeIndex = 0;
+}
+
+
+/**
+ * @brief LPC175x Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc175xEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc175xEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(ENET_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc175xEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(ENET_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief LPC175x Ethernet MAC interrupt service routine
+ **/
+
+void ENET_IRQHandler(void)
+{
+   uint_t i;
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read interrupt status register
+   status = LPC_EMAC->IntStatus;
+
+   //A packet has been transmitted?
+   if(status & INT_TX_DONE)
+   {
+      //Clear TxDone interrupt flag
+      LPC_EMAC->IntClear = INT_TX_DONE;
+
+      //Get the index of the next descriptor
+      i = LPC_EMAC->TxProduceIndex + 1;
+
+      //Wrap around if necessary
+      if(i >= LPC175X_ETH_TX_BUFFER_COUNT)
+         i = 0;
+
+      //Check whether the TX buffer is available for writing
+      if(i != LPC_EMAC->TxConsumeIndex)
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & INT_RX_DONE)
+   {
+      //Disable RxDone interrupts
+      LPC_EMAC->IntEnable &= ~INT_RX_DONE;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief LPC175x Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc175xEthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(LPC_EMAC->IntStatus & INT_RX_DONE)
+   {
+      //Clear RxDone interrupt flag
+      LPC_EMAC->IntClear = INT_RX_DONE;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = lpc175xEthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable TxDone and RxDone interrupts
+   LPC_EMAC->IntEnable = INT_TX_DONE | INT_RX_DONE;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t lpc175xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   uint_t i;
+   uint_t j;
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(!length)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //We are done since the buffer is empty
+      return NO_ERROR;
+   }
+   else if(length > LPC175X_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Get the index of the current descriptor
+   i = LPC_EMAC->TxProduceIndex;
+   //Get the index of the next descriptor
+   j = i + 1;
+
+   //Wrap around if necessary
+   if(j >= LPC175X_ETH_TX_BUFFER_COUNT)
+      j = 0;
+
+   //Check whether the transmit descriptor array is full
+   if(j == LPC_EMAC->TxConsumeIndex)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txDesc[i].packet, buffer, offset, length);
+
+   //Write the transmit control word
+   txDesc[i].control = TX_CTRL_INTERRUPT | TX_CTRL_LAST |
+      TX_CTRL_CRC | TX_CTRL_PAD | ((length - 1) & TX_CTRL_SIZE);
+
+   //Increment index and wrap around if necessary
+   if(++i >= LPC175X_ETH_TX_BUFFER_COUNT)
+      i = 0;
+
+   //Save the resulting value
+   LPC_EMAC->TxProduceIndex = i;
+
+   //Get the index of the next descriptor
+   j = i + 1;
+
+   //Wrap around if necessary
+   if(j >= LPC175X_ETH_TX_BUFFER_COUNT)
+      j = 0;
+
+   //Check whether the next buffer is available for writing
+   if(j != LPC_EMAC->TxConsumeIndex)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful write operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc175xEthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+   uint_t i;
+
+   //Point to the current descriptor
+   i = LPC_EMAC->RxConsumeIndex;
+
+   //Make sure the current buffer is available for reading
+   if(i != LPC_EMAC->RxProduceIndex)
+   {
+      //Retrieve the length of the frame
+      n = (rxStatus[i].info & RX_STATUS_SIZE) + 1;
+      //Limit the number of data to read
+      n = MIN(n, LPC175X_ETH_RX_BUFFER_SIZE);
+
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, (uint8_t *) rxDesc[i].packet, n);
+
+      //Increment index and wrap around if necessary
+      if(++i >= LPC175X_ETH_RX_BUFFER_COUNT)
+         i = 0;
+
+      //Save the resulting value
+      LPC_EMAC->RxConsumeIndex = i;
+
+      //Valid packet received
+      error = NO_ERROR;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc175xEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating LPC175x hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = lpc175xEthCalcCrc(&entry->addr, sizeof(MacAddr));
+         //Bits [28:23] are used to form the hash
+         k = (crc >> 23) & 0x3F;
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   LPC_EMAC->HashFilterL = hashTable[0];
+   LPC_EMAC->HashFilterH = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HashFilterL = %08" PRIX32 "\r\n", LPC_EMAC->HashFilterL);
+   TRACE_DEBUG("  HashFilterH = %08" PRIX32 "\r\n", LPC_EMAC->HashFilterH);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc175xEthUpdateMacConfig(NetInterface *interface)
+{
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      LPC_EMAC->SUPP = SUPP_SPEED;
+   else
+      LPC_EMAC->SUPP = 0;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+   {
+      //The MAC operates in full-duplex mode
+      LPC_EMAC->MAC2 |= MAC2_FULL_DUPLEX;
+      LPC_EMAC->Command |= COMMAND_FULL_DUPLEX;
+      //Configure Back-to-Back Inter-Packet Gap
+      LPC_EMAC->IPGT = IPGT_FULL_DUPLEX;
+   }
+   else
+   {
+      //The MAC operates in half-duplex mode
+      LPC_EMAC->MAC2 &= ~MAC2_FULL_DUPLEX;
+      LPC_EMAC->Command &= ~COMMAND_FULL_DUPLEX;
+      //Configure Back-to-Back Inter-Packet Gap
+      LPC_EMAC->IPGT = IPGT_HALF_DUPLEX;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void lpc175xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   //Synchronization pattern
+   lpc175xEthWriteSmi(SMI_SYNC, 32);
+   //Start of frame
+   lpc175xEthWriteSmi(SMI_START, 2);
+   //Set up a write operation
+   lpc175xEthWriteSmi(SMI_WRITE, 2);
+   //Write PHY address
+   lpc175xEthWriteSmi(phyAddr, 5);
+   //Write register address
+   lpc175xEthWriteSmi(regAddr, 5);
+   //Turnaround
+   lpc175xEthWriteSmi(SMI_TA, 2);
+   //Write register value
+   lpc175xEthWriteSmi(data, 16);
+   //Release MDIO
+   lpc175xEthReadSmi(1);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t lpc175xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint16_t data;
+
+   //Synchronization pattern
+   lpc175xEthWriteSmi(SMI_SYNC, 32);
+   //Start of frame
+   lpc175xEthWriteSmi(SMI_START, 2);
+   //Set up a read operation
+   lpc175xEthWriteSmi(SMI_READ, 2);
+   //Write PHY address
+   lpc175xEthWriteSmi(phyAddr, 5);
+   //Write register address
+   lpc175xEthWriteSmi(regAddr, 5);
+   //Turnaround to avoid contention
+   lpc175xEthReadSmi(1);
+   //Read register value
+   data = lpc175xEthReadSmi(16);
+   //Force the PHY to release the MDIO pin
+   lpc175xEthReadSmi(1);
+
+   //Return PHY register contents
+   return data;
+}
+
+
+/**
+ * @brief SMI write operation
+ * @param[in] data Raw data to be written
+ * @param[in] length Number of bits to be written
+ **/
+
+void lpc175xEthWriteSmi(uint32_t data, uint_t length)
+{
+   //Skip the most significant bits since they are meaningless
+   data <<= 32 - length;
+
+   //Configure MDIO as an output
+   LPC175X_ETH_MDIO_GPIO->FIODIR |= LPC175X_ETH_MDIO_MASK;
+
+   //Write the specified number of bits
+   while(length--)
+   {
+      //Write MDIO
+      if(data & 0x80000000)
+         LPC175X_ETH_MDIO_GPIO->FIOSET = LPC175X_ETH_MDIO_MASK;
+      else
+         LPC175X_ETH_MDIO_GPIO->FIOCLR = LPC175X_ETH_MDIO_MASK;
+
+      //Assert MDC
+      usleep(1);
+      LPC175X_ETH_MDC_GPIO->FIOSET = LPC175X_ETH_MDC_MASK;
+      //Deassert MDC
+      usleep(1);
+      LPC175X_ETH_MDC_GPIO->FIOCLR = LPC175X_ETH_MDC_MASK;
+
+      //Rotate data
+      data <<= 1;
+   }
+}
+
+
+/**
+ * @brief SMI read operation
+ * @param[in] length Number of bits to be read
+ * @return Data resulting from the MDIO read operation
+ **/
+
+uint32_t lpc175xEthReadSmi(uint_t length)
+{
+   uint32_t data = 0;
+
+   //Configure MDIO as an input
+   LPC175X_ETH_MDIO_GPIO->FIODIR &= ~LPC175X_ETH_MDIO_MASK;
+
+   //Read the specified number of bits
+   while(length--)
+   {
+      //Rotate data
+      data <<= 1;
+
+      //Assert MDC
+      LPC175X_ETH_MDC_GPIO->FIOSET = LPC175X_ETH_MDC_MASK;
+      usleep(1);
+      //Deassert MDC
+      LPC175X_ETH_MDC_GPIO->FIOCLR = LPC175X_ETH_MDC_MASK;
+      usleep(1);
+
+      //Check MDIO state
+      if(LPC175X_ETH_MDIO_GPIO->FIOPIN & LPC175X_ETH_MDIO_MASK)
+         data |= 0x00000001;
+   }
+
+   //Return the received data
+   return data;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t lpc175xEthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lpc175x_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,420 @@
+/**
+ * @file lpc175x_eth.h
+ * @brief LPC1758 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _LPC175X_ETH_H
+#define _LPC175X_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef LPC175X_ETH_TX_BUFFER_COUNT
+   #define LPC175X_ETH_TX_BUFFER_COUNT 2
+#elif (LPC175X_ETH_TX_BUFFER_COUNT < 1)
+   #error LPC175X_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef LPC175X_ETH_TX_BUFFER_SIZE
+   #define LPC175X_ETH_TX_BUFFER_SIZE 1536
+#elif (LPC175X_ETH_TX_BUFFER_SIZE != 1536)
+   #error LPC175X_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef LPC175X_ETH_RX_BUFFER_COUNT
+   #define LPC175X_ETH_RX_BUFFER_COUNT 4
+#elif (LPC175X_ETH_RX_BUFFER_COUNT < 1)
+   #error LPC175X_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef LPC175X_ETH_RX_BUFFER_SIZE
+   #define LPC175X_ETH_RX_BUFFER_SIZE 1536
+#elif (LPC175X_ETH_RX_BUFFER_SIZE != 1536)
+   #error LPC175X_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef LPC175X_ETH_IRQ_PRIORITY_GROUPING
+   #define LPC175X_ETH_IRQ_PRIORITY_GROUPING 2
+#elif (LPC175X_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error LPC175X_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef LPC175X_ETH_IRQ_GROUP_PRIORITY
+   #define LPC175X_ETH_IRQ_GROUP_PRIORITY 24
+#elif (LPC175X_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error LPC175X_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef LPC175X_ETH_IRQ_SUB_PRIORITY
+   #define LPC175X_ETH_IRQ_SUB_PRIORITY 0
+#elif (LPC175X_ETH_IRQ_SUB_PRIORITY < 0)
+   #error LPC175X_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//MDC pin
+#ifndef LPC175X_ETH_MDC_GPIO
+   #define LPC175X_ETH_MDC_GPIO LPC_GPIO2
+#endif
+
+#ifndef LPC175X_ETH_MDC_MASK
+   #define LPC175X_ETH_MDC_MASK (1 << 8)
+#endif
+
+//MDIO pin
+#ifndef LPC175X_ETH_MDIO_GPIO
+   #define LPC175X_ETH_MDIO_GPIO LPC_GPIO2
+#endif
+
+#ifndef LPC175X_ETH_MDIO_MASK
+   #define LPC175X_ETH_MDIO_MASK (1 << 9)
+#endif
+
+//MAC1 register
+#define MAC1_SOFT_RESET                0x00008000
+#define MAC1_SIMULATION_RESET          0x00004000
+#define MAC1_RESET_MCS_RX              0x00000800
+#define MAC1_RESET_RX                  0x00000400
+#define MAC1_RESET_MCS_TX              0x00000200
+#define MAC1_RESET_TX                  0x00000100
+#define MAC1_LOOPBACK                  0x00000010
+#define MAC1_TX_FLOW_CONTROL           0x00000008
+#define MAC1_RX_FLOW_CONTROL           0x00000004
+#define MAC1_PASS_ALL_FRAMES           0x00000002
+#define MAC1_RECEIVE_ENABLE            0x00000001
+
+//MAC2 register
+#define MAC2_EXCESS_DEFER              0x00004000
+#define MAC2_BACK_PRESSURE_NO_BACKOFF  0x00002000
+#define MAC2_NO_BACKOFF                0x00001000
+#define MAC2_LONG_PREAMBLE_ENFORCEMENT 0x00000200
+#define MAC2_PURE_PREAMBLE_ENFORCEMENT 0x00000100
+#define MAC2_AUTO_DETECT_PAD_ENABLE    0x00000080
+#define MAC2_VLAN_PAD_ENABLE           0x00000040
+#define MAC2_PAD_CRC_ENABLE            0x00000020
+#define MAC2_CRC_ENABLE                0x00000010
+#define MAC2_DELAYED_CRC               0x00000008
+#define MAC2_HUGE_FRAME_ENABLE         0x00000004
+#define MAC2_FRAME_LENGTH_CHECKING     0x00000002
+#define MAC2_FULL_DUPLEX               0x00000001
+
+//IPGT register
+#define IPGT_BACK_TO_BACK_IPG          0x0000007F
+#define IPGT_HALF_DUPLEX               0x00000012
+#define IPGT_FULL_DUPLEX               0x00000015
+
+//IPGR register
+#define IPGR_NON_BACK_TO_BACK_IPG1     0x00007F00
+#define IPGR_NON_BACK_TO_BACK_IPG2     0x0000007F
+#define IPGR_DEFAULT_VALUE             0x00000C12
+
+//CLRT register
+#define CLRT_COLLISION_WINDOW          0x00003F00
+#define CLRT_RETRANSMISSION_MAXIMUM    0x00003F00
+#define CLRT_DEFAULT_VALUE             0x0000370F
+
+//MAXF register
+#define MAXF_MAXIMUM_FRAME_LENGTH      0x0000FFFF
+
+//SUPP register
+#define SUPP_SPEED                     0x00000100
+
+//TEST register
+#define TEST_BACKPRESSURE              0x00000004
+#define TEST_PAUSE                     0x00000002
+#define TEST_SHORTCUT_PAUSE_QUANTA     0x00000001
+
+//MCFG register
+#define MCFG_RESET_MII_MGMT            0x00008000
+#define MCFG_CLOCK SELECT              0x0000003C
+#define MCFG_SUPPRESS_PREAMBLE         0x00000002
+#define MCFG_SCAN_INCREMENT            0x00000001
+
+#define MCFG_CLOCK_SELECT_DIV4         0x00000000
+#define MCFG_CLOCK_SELECT_DIV6         0x00000008
+#define MCFG_CLOCK_SELECT_DIV8         0x0000000C
+#define MCFG_CLOCK_SELECT_DIV10        0x00000010
+#define MCFG_CLOCK_SELECT_DIV14        0x00000014
+#define MCFG_CLOCK_SELECT_DIV20        0x00000018
+#define MCFG_CLOCK_SELECT_DIV28        0x0000001C
+#define MCFG_CLOCK_SELECT_DIV36        0x00000020
+#define MCFG_CLOCK_SELECT_DIV40        0x00000024
+#define MCFG_CLOCK_SELECT_DIV44        0x00000028
+#define MCFG_CLOCK_SELECT_DIV48        0x0000002C
+#define MCFG_CLOCK_SELECT_DIV52        0x00000030
+#define MCFG_CLOCK_SELECT_DIV56        0x00000034
+#define MCFG_CLOCK_SELECT_DIV60        0x00000038
+#define MCFG_CLOCK_SELECT_DIV64        0x0000003C
+
+//MCMD register
+#define MCMD_SCAN                      0x00000002
+#define MCMD_READ                      0x00000001
+
+//MADR register
+#define MADR_PHY_ADDRESS               0x00001F00
+#define MADR_REGISTER_ADDRESS          0x0000001F
+
+//MWTD register
+#define MWTD_WRITE_DATA                0x0000FFFF
+
+//MRDD register
+#define MRDD_READ_DATA                 0x0000FFFF
+
+//MIND register
+#define MIND_MII_LINK_FAIL             0x00000008
+#define MIND_NOT_VALID                 0x00000004
+#define MIND_SCANNING                  0x00000002
+#define MIND_BUSY                      0x00000001
+
+//Command register
+#define COMMAND_FULL_DUPLEX            0x00000400
+#define COMMAND_RMII                   0x00000200
+#define COMMAND_TX_FLOW_CONTROL        0x00000100
+#define COMMAND_PASS_RX_FILTER         0x00000080
+#define COMMAND_PASS_RUNT_FRAME        0x00000040
+#define COMMAND_RX_RESET               0x00000020
+#define COMMAND_TX_RESET               0x00000010
+#define COMMAND_REG_RESET              0x00000008
+#define COMMAND_TX_ENABLE              0x00000002
+#define COMMAND_RX_ENABLE              0x00000001
+
+//Status register
+#define STATUS_TX                      0x00000002
+#define STATUS_RX                      0x00000001
+
+//TSV0 register
+#define TSV0_VLAN                      0x80000000
+#define TSV0_BACKPRESSURE              0x40000000
+#define TSV0_PAUSE                     0x20000000
+#define TSV0_CONTROL_FRAME             0x10000000
+#define TSV0_TOTAL_BYTES               0x0FFFF000
+#define TSV0_UNDERRUN                  0x00000800
+#define TSV0_GIANT                     0x00000400
+#define TSV0_LATE_COLLISION            0x00000200
+#define TSV0_EXCESSIVE_COLLISION       0x00000100
+#define TSV0_EXCESSIVE_DEFER           0x00000080
+#define TSV0_PACKET_DEFER              0x00000040
+#define TSV0_BROADCAST                 0x00000020
+#define TSV0_MULTICAST                 0x00000010
+#define TSV0_DONE                      0x00000008
+#define TSV0_LENGTH_OUT_OF_RANGE       0x00000004
+#define TSV0_LENGTH_CHECK_ERROR        0x00000002
+#define TSV0_CRC_ERROR                 0x00000001
+
+//TSV1 register
+#define TSV1_TRANSMIT_COLLISION_COUNT  0x000F0000
+#define TSV1_TRANSMIT_BYTE_COUNT       0x0000FFFF
+
+//RSV register
+#define RSV_VLAN                       0x40000000
+#define RSV_UNSUPPORTED_OPCODE         0x20000000
+#define RSV_PAUSE                      0x10000000
+#define RSV_CONTROL_FRAME              0x08000000
+#define RSV_DRIBBLE_NIBBLE             0x04000000
+#define RSV_BROADCAST                  0x02000000
+#define RSV_MULTICAST                  0x01000000
+#define RSV_RECEIVE_OK                 0x00800000
+#define RSV_LENGTH_OUT_OF_RANGE        0x00400000
+#define RSV_LENGTH_CHECK_ERROR         0x00200000
+#define RSV_CRC_ERROR                  0x00100000
+#define RSV_RECEIVE_CODE_VIOLATION     0x00080000
+#define RSV_CARRIER_EVENT_PREV_SEEN    0x00040000
+#define RSV_RXDV_EVENT_PREV_SEEN       0x00020000
+#define RSV_PACKET_PREVIOUSLY_IGNORED  0x00010000
+#define RSV_RECEIVED_BYTE_COUNT        0x0000FFFF
+
+//FlowControlCounter register
+#define FCC_PAUSE_TIMER                0xFFFF0000
+#define FCC_MIRROR_COUNTER             0x0000FFFF
+
+//FlowControlStatus register
+#define FCS_MIRROR_COUNTER_CURRENT     0x0000FFFF
+
+//RxFilterCtrl register
+#define RFC_RX_FILTER_EN_WOL           0x00002000
+#define RFC_MAGIC_PACKET_EN_WOL        0x00001000
+#define RFC_ACCEPT_PERFECT_EN          0x00000020
+#define RFC_ACCEPT_MULTICAST_HASH_EN   0x00000010
+#define RFC_ACCEPT_UNICAST_HASH_EN     0x00000008
+#define RFC_ACCEPT_MULTICAST_EN        0x00000004
+#define RFC_ACCEPT_BROADCAST_EN        0x00000002
+#define RFC_ACCEPT_UNICAST_EN          0x00000001
+
+//RxFilterWoLStatus and RxFilterWoLClear registers
+#define RFWS_MAGIC_PACKET_WOL          0x00000100
+#define RFWS_RX_FILTER_WOL             0x00000080
+#define RFWS_ACCEPT_PERFECT_WOL        0x00000020
+#define RFWS_ACCEPT_MULTICAST_HASH_WOL 0x00000010
+#define RFWS_ACCEPT_UNICAST_HASH_WOL   0x00000008
+#define RFWS_ACCEPT_MULTICAST_WOL      0x00000004
+#define RFWS_ACCEPT_BROADCAST_WOL      0x00000002
+#define RFWS_ACCEPT_UNICAST_WOL        0x00000001
+
+//IntStatus, IntEnable, IntClear and IntSet registers
+#define INT_WAKEUP                     0x00002000
+#define INT_SOFT_INT                   0x00001000
+#define INT_TX_DONE                    0x00000080
+#define INT_TX_FINISHED                0x00000040
+#define INT_TX_ERROR                   0x00000020
+#define INT_TX_UNDERRUN                0x00000010
+#define INT_RX_DONE                    0x00000008
+#define INT_RX_FINISHED                0x00000004
+#define INT_RX_ERROR                   0x00000002
+#define INT_RX_OVERRUN                 0x00000001
+
+//Transmit descriptor control word
+#define TX_CTRL_INTERRUPT              0x80000000
+#define TX_CTRL_LAST                   0x40000000
+#define TX_CTRL_CRC                    0x20000000
+#define TX_CTRL_PAD                    0x10000000
+#define TX_CTRL_HUGE                   0x08000000
+#define TX_CTRL_OVERRIDE               0x04000000
+#define TX_CTRL_SIZE                   0x000007FF
+
+//Transmit status information word
+#define TX_STATUS_ERROR                0x80000000
+#define TX_STATUS_NO_DESCRIPTOR        0x40000000
+#define TX_STATUS_UNDERRUN             0x20000000
+#define TX_STATUS_LATE_COLLISION       0x10000000
+#define TX_STATUS_EXCESSIVE_COLLISION  0x08000000
+#define TX_STATUS_EXCESSIVE_DEFER      0x04000000
+#define TX_STATUS_DEFER                0x02000000
+#define TX_STATUS_COLLISION_COUNT      0x01E00000
+
+//Receive descriptor control word
+#define RX_CTRL_INTERRUPT              0x80000000
+#define RX_CTRL_SIZE                   0x000007FF
+
+//Receive status information word
+#define RX_STATUS_ERROR                0x80000000
+#define RX_STATUS_LAST_FLAG            0x40000000
+#define RX_STATUS_NO_DESCRIPTOR        0x20000000
+#define RX_STATUS_OVERRUN              0x10000000
+#define RX_STATUS_ALIGNMENT_ERROR      0x08000000
+#define RX_STATUS_RANGE_ERROR          0x04000000
+#define RX_STATUS_LENGTH_ERROR         0x02000000
+#define RX_STATUS_SYMBOL_ERROR         0x01000000
+#define RX_STATUS_CRC_ERROR            0x00800000
+#define RX_STATUS_BROADCAST            0x00400000
+#define RX_STATUS_MULTICAST            0x00200000
+#define RX_STATUS_FAIL_FILTER          0x00100000
+#define RX_STATUS_VLAN                 0x00080000
+#define RX_STATUS_CONTROL_FRAME        0x00040000
+#define RX_STATUS_SIZE                 0x000007FF
+
+//Receive status HashCRC word
+#define RX_HASH_CRC_DA                 0x001FF000
+#define RX_HASH_CRC_SA                 0x000001FF
+
+//Serial Management Interface
+#define SMI_SYNC                       0xFFFFFFFF
+#define SMI_START                      0x00000001
+#define SMI_WRITE                      0x00000001
+#define SMI_READ                       0x00000002
+#define SMI_TA                         0x00000002
+
+
+/**
+ * @brief Transmit descriptor
+ **/
+
+typedef struct
+{
+   uint32_t packet;
+   uint32_t control;
+} Lpc175xTxDesc;
+
+
+/**
+ * @brief Transmit status
+ **/
+
+typedef struct
+{
+   uint32_t info;
+} Lpc175xTxStatus;
+
+
+/**
+ * @brief Receive descriptor
+ **/
+
+typedef struct
+{
+   uint32_t packet;
+   uint32_t control;
+} Lpc175xRxDesc;
+
+
+/**
+ * @brief Receive status
+ **/
+
+typedef struct
+{
+   uint32_t info;
+   uint32_t hashCrc;
+} Lpc175xRxStatus;
+
+
+//LPC175x Ethernet MAC driver
+extern const NicDriver lpc175xEthDriver;
+
+//LPC175x Ethernet MAC related functions
+error_t lpc175xEthInit(NetInterface *interface);
+void lpc175xEthInitGpio(NetInterface *interface);
+void lpc175xEthInitDesc(NetInterface *interface);
+
+void lpc175xEthTick(NetInterface *interface);
+
+void lpc175xEthEnableIrq(NetInterface *interface);
+void lpc175xEthDisableIrq(NetInterface *interface);
+void lpc175xEthEventHandler(NetInterface *interface);
+
+error_t lpc175xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t lpc175xEthReceivePacket(NetInterface *interface);
+
+error_t lpc175xEthSetMulticastFilter(NetInterface *interface);
+error_t lpc175xEthUpdateMacConfig(NetInterface *interface);
+
+void lpc175xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t lpc175xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+void lpc175xEthWriteSmi(uint32_t data, uint_t length);
+uint32_t lpc175xEthReadSmi(uint_t length);
+
+uint32_t lpc175xEthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lpc176x_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,714 @@
+/**
+ * @file lpc176x_eth.c
+ * @brief LPC1764/66/67/68/69 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "lpc17xx.h"
+#include "core/net.h"
+#include "drivers/lpc176x_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+static uint8_t txBuffer[LPC176X_ETH_TX_BUFFER_COUNT][LPC176X_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+static uint8_t rxBuffer[LPC176X_ETH_RX_BUFFER_COUNT][LPC176X_ETH_RX_BUFFER_SIZE];
+//Transmit descriptors
+#pragma data_alignment = 4
+static Lpc176xTxDesc txDesc[LPC176X_ETH_TX_BUFFER_COUNT];
+//Transmit status array
+#pragma data_alignment = 4
+static Lpc176xTxStatus txStatus[LPC176X_ETH_TX_BUFFER_COUNT];
+//Receive descriptors
+#pragma data_alignment = 4
+static Lpc176xRxDesc rxDesc[LPC176X_ETH_RX_BUFFER_COUNT];
+//Receive status array
+#pragma data_alignment = 8
+static Lpc176xRxStatus rxStatus[LPC176X_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[LPC176X_ETH_TX_BUFFER_COUNT][LPC176X_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[LPC176X_ETH_RX_BUFFER_COUNT][LPC176X_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Transmit descriptors
+static Lpc176xTxDesc txDesc[LPC176X_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Transmit status array
+static Lpc176xTxStatus txStatus[LPC176X_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive descriptors
+static Lpc176xRxDesc rxDesc[LPC176X_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive status array
+static Lpc176xRxStatus rxStatus[LPC176X_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(8)));
+
+#endif
+
+
+/**
+ * @brief LPC176x Ethernet MAC driver
+ **/
+
+const NicDriver lpc176xEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   lpc176xEthInit,
+   lpc176xEthTick,
+   lpc176xEthEnableIrq,
+   lpc176xEthDisableIrq,
+   lpc176xEthEventHandler,
+   lpc176xEthSendPacket,
+   lpc176xEthSetMulticastFilter,
+   lpc176xEthUpdateMacConfig,
+   lpc176xEthWritePhyReg,
+   lpc176xEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief LPC176x Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc176xEthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing LPC176x Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Power up EMAC controller
+   LPC_SC->PCONP |= PCONP_PCENET;
+
+   //GPIO configuration
+   lpc176xEthInitGpio(interface);
+
+   //Reset host registers, transmit datapath and receive datapath
+   LPC_EMAC->Command = COMMAND_RX_RESET | COMMAND_TX_RESET | COMMAND_REG_RESET;
+
+   //Reset EMAC controller
+   LPC_EMAC->MAC1 = MAC1_SOFT_RESET | MAC1_SIMULATION_RESET |
+      MAC1_RESET_MCS_RX | MAC1_RESET_RX | MAC1_RESET_MCS_TX | MAC1_RESET_TX;
+
+   //Initialize MAC related registers
+   LPC_EMAC->MAC1 = 0;
+   LPC_EMAC->MAC2 = MAC2_PAD_CRC_ENABLE | MAC2_CRC_ENABLE;
+   LPC_EMAC->IPGR = IPGR_DEFAULT_VALUE;
+   LPC_EMAC->CLRT = CLRT_DEFAULT_VALUE;
+
+   //Select RMII mode
+   LPC_EMAC->Command = COMMAND_RMII;
+
+   //Configure MDC clock
+   LPC_EMAC->MCFG = MCFG_CLOCK_SELECT_DIV44;
+   //Reset MII management interface
+   LPC_EMAC->MCFG |= MCFG_RESET_MII_MGMT;
+   LPC_EMAC->MCFG &= ~MCFG_RESET_MII_MGMT;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Initialize TX and RX descriptor arrays
+   lpc176xEthInitDesc(interface);
+
+   //Set the MAC address
+   LPC_EMAC->SA0 = interface->macAddr.w[2];
+   LPC_EMAC->SA1 = interface->macAddr.w[1];
+   LPC_EMAC->SA2 = interface->macAddr.w[0];
+
+   //Initialize hash table
+   LPC_EMAC->HashFilterL = 0;
+   LPC_EMAC->HashFilterH = 0;
+
+   //Configure the receive filter
+   LPC_EMAC->RxFilterCtrl = RFC_ACCEPT_PERFECT_EN |
+      RFC_ACCEPT_MULTICAST_HASH_EN | RFC_ACCEPT_BROADCAST_EN;
+
+   //Program the MAXF register with the maximum frame length to be accepted
+   LPC_EMAC->MAXF = 1518;
+
+   //Reset EMAC interrupt flags
+   LPC_EMAC->IntClear  = 0xFFFF;
+   //Enable desired EMAC interrupts
+   LPC_EMAC->IntEnable = INT_TX_DONE | INT_RX_DONE;
+
+   //Set priority grouping (5 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(LPC176X_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure Ethernet interrupt priority
+   NVIC_SetPriority(ENET_IRQn, NVIC_EncodePriority(LPC176X_ETH_IRQ_PRIORITY_GROUPING,
+      LPC176X_ETH_IRQ_GROUP_PRIORITY, LPC176X_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable transmission and reception
+   LPC_EMAC->Command |= COMMAND_TX_ENABLE | COMMAND_RX_ENABLE;
+   //Allow frames to be received
+   LPC_EMAC->MAC1 |= MAC1_RECEIVE_ENABLE;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//LPC1766-STK evaluation board?
+#if defined(USE_LPC1766_STK)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc176xEthInitGpio(NetInterface *interface)
+{
+   //Configure P1.0 (ENET_TXD0), P1.1 (ENET_TXD1), P1.4 (ENET_TX_EN), P1.8 (ENET_CRS),
+   //P1.9 (ENET_RXD0), P1.10 (ENET_RXD1), P1.14 (RX_ER) and P1.15 (ENET_REF_CLK)
+   LPC_PINCON->PINSEL2 &= ~(PINSEL2_P1_0_MASK | PINSEL2_P1_1_MASK |
+      PINSEL2_P1_4_MASK | PINSEL2_P1_8_MASK | PINSEL2_P1_9_MASK |
+      PINSEL2_P1_10_MASK | PINSEL2_P1_14_MASK | PINSEL2_P1_15_MASK);
+
+   LPC_PINCON->PINSEL2 |= PINSEL2_P1_0_ENET_TXD0 | PINSEL2_P1_1_ENET_TXD1 |
+      PINSEL2_P1_4_ENET_TX_EN | PINSEL2_P1_8_ENET_CRS | PINSEL2_P1_9_ENET_RXD0 |
+      PINSEL2_P1_10_ENET_RXD1 | PINSEL2_P1_14_ENET_RX_ER | PINSEL2_P1_15_ENET_REF_CLK;
+
+   //Configure P1.16 (ENET_MDC) and P1.17 (ENET_MDIO)
+   LPC_PINCON->PINSEL3 &= ~(PINSEL3_P1_16_MASK | PINSEL3_P1_17_MASK);
+   LPC_PINCON->PINSEL3 |= PINSEL3_P1_16_ENET_MDC | PINSEL3_P1_17_ENET_MDIO;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize TX and RX descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc176xEthInitDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX descriptors
+   for(i = 0; i < LPC176X_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Base address of the buffer containing transmit data
+      txDesc[i].packet = (uint32_t) txBuffer[i];
+      //Transmit descriptor control word
+      txDesc[i].control = 0;
+      //Transmit status information word
+      txStatus[i].info = 0;
+   }
+
+   //Initialize RX descriptors
+   for(i = 0; i < LPC176X_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Base address of the buffer for storing receive data
+      rxDesc[i].packet = (uint32_t) rxBuffer[i];
+      //Receive descriptor control word
+      rxDesc[i].control = RX_CTRL_INTERRUPT | (LPC176X_ETH_RX_BUFFER_SIZE - 1);
+      //Receive status information word
+      rxStatus[i].info = 0;
+      //Receive status HashCRC word
+      rxStatus[i].hashCrc = 0;
+   }
+
+   //Initialize EMAC transmit descriptor registers
+   LPC_EMAC->TxDescriptor = (uint32_t) txDesc;
+   LPC_EMAC->TxStatus = (uint32_t) txStatus;
+   LPC_EMAC->TxDescriptorNumber = LPC176X_ETH_TX_BUFFER_COUNT - 1;
+   LPC_EMAC->TxProduceIndex = 0;
+
+   //Initialize EMAC receive descriptor registers
+   LPC_EMAC->RxDescriptor = (uint32_t) rxDesc;
+   LPC_EMAC->RxStatus = (uint32_t) rxStatus;
+   LPC_EMAC->RxDescriptorNumber = LPC176X_ETH_RX_BUFFER_COUNT - 1;
+   LPC_EMAC->RxConsumeIndex = 0;
+}
+
+
+/**
+ * @brief LPC176x Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc176xEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc176xEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(ENET_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc176xEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(ENET_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief LPC176x Ethernet MAC interrupt service routine
+ **/
+
+void ENET_IRQHandler(void)
+{
+   uint_t i;
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read interrupt status register
+   status = LPC_EMAC->IntStatus;
+
+   //A packet has been transmitted?
+   if(status & INT_TX_DONE)
+   {
+      //Clear TxDone interrupt flag
+      LPC_EMAC->IntClear = INT_TX_DONE;
+
+      //Get the index of the next descriptor
+      i = LPC_EMAC->TxProduceIndex + 1;
+
+      //Wrap around if necessary
+      if(i >= LPC176X_ETH_TX_BUFFER_COUNT)
+         i = 0;
+
+      //Check whether the TX buffer is available for writing
+      if(i != LPC_EMAC->TxConsumeIndex)
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & INT_RX_DONE)
+   {
+      //Disable RxDone interrupts
+      LPC_EMAC->IntEnable &= ~INT_RX_DONE;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief LPC176x Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc176xEthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(LPC_EMAC->IntStatus & INT_RX_DONE)
+   {
+      //Clear RxDone interrupt flag
+      LPC_EMAC->IntClear = INT_RX_DONE;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = lpc176xEthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable TxDone and RxDone interrupts
+   LPC_EMAC->IntEnable = INT_TX_DONE | INT_RX_DONE;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t lpc176xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   uint_t i;
+   uint_t j;
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(!length)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //We are done since the buffer is empty
+      return NO_ERROR;
+   }
+   else if(length > LPC176X_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Get the index of the current descriptor
+   i = LPC_EMAC->TxProduceIndex;
+   //Get the index of the next descriptor
+   j = i + 1;
+
+   //Wrap around if necessary
+   if(j >= LPC176X_ETH_TX_BUFFER_COUNT)
+      j = 0;
+
+   //Check whether the transmit descriptor array is full
+   if(j == LPC_EMAC->TxConsumeIndex)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txDesc[i].packet, buffer, offset, length);
+
+   //Write the transmit control word
+   txDesc[i].control = TX_CTRL_INTERRUPT | TX_CTRL_LAST |
+      TX_CTRL_CRC | TX_CTRL_PAD | ((length - 1) & TX_CTRL_SIZE);
+
+   //Increment index and wrap around if necessary
+   if(++i >= LPC176X_ETH_TX_BUFFER_COUNT)
+      i = 0;
+
+   //Save the resulting value
+   LPC_EMAC->TxProduceIndex = i;
+
+   //Get the index of the next descriptor
+   j = i + 1;
+
+   //Wrap around if necessary
+   if(j >= LPC176X_ETH_TX_BUFFER_COUNT)
+      j = 0;
+
+   //Check whether the next buffer is available for writing
+   if(j != LPC_EMAC->TxConsumeIndex)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful write operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc176xEthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+   uint_t i;
+
+   //Point to the current descriptor
+   i = LPC_EMAC->RxConsumeIndex;
+
+   //Make sure the current buffer is available for reading
+   if(i != LPC_EMAC->RxProduceIndex)
+   {
+      //Retrieve the length of the frame
+      n = (rxStatus[i].info & RX_STATUS_SIZE) + 1;
+      //Limit the number of data to read
+      n = MIN(n, LPC176X_ETH_RX_BUFFER_SIZE);
+
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, (uint8_t *) rxDesc[i].packet, n);
+
+      //Increment index and wrap around if necessary
+      if(++i >= LPC176X_ETH_RX_BUFFER_COUNT)
+         i = 0;
+
+      //Save the resulting value
+      LPC_EMAC->RxConsumeIndex = i;
+
+      //Valid packet received
+      error = NO_ERROR;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc176xEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating LPC176x hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = lpc176xEthCalcCrc(&entry->addr, sizeof(MacAddr));
+         //Bits [28:23] are used to form the hash
+         k = (crc >> 23) & 0x3F;
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   LPC_EMAC->HashFilterL = hashTable[0];
+   LPC_EMAC->HashFilterH = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HashFilterL = %08" PRIX32 "\r\n", LPC_EMAC->HashFilterL);
+   TRACE_DEBUG("  HashFilterH = %08" PRIX32 "\r\n", LPC_EMAC->HashFilterH);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc176xEthUpdateMacConfig(NetInterface *interface)
+{
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      LPC_EMAC->SUPP = SUPP_SPEED;
+   else
+      LPC_EMAC->SUPP = 0;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+   {
+      //The MAC operates in full-duplex mode
+      LPC_EMAC->MAC2 |= MAC2_FULL_DUPLEX;
+      LPC_EMAC->Command |= COMMAND_FULL_DUPLEX;
+      //Configure Back-to-Back Inter-Packet Gap
+      LPC_EMAC->IPGT = IPGT_FULL_DUPLEX;
+   }
+   else
+   {
+      //The MAC operates in half-duplex mode
+      LPC_EMAC->MAC2 &= ~MAC2_FULL_DUPLEX;
+      LPC_EMAC->Command &= ~COMMAND_FULL_DUPLEX;
+      //Configure Back-to-Back Inter-Packet Gap
+      LPC_EMAC->IPGT = IPGT_HALF_DUPLEX;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void lpc176xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   //Clear MCMD register
+   LPC_EMAC->MCMD = 0;
+
+   //PHY address
+   LPC_EMAC->MADR = (phyAddr << 8) & MADR_PHY_ADDRESS;
+   //Register address
+   LPC_EMAC->MADR |= regAddr & MADR_REGISTER_ADDRESS;
+   //Data to be written in the PHY register
+   LPC_EMAC->MWTD = data & MWTD_WRITE_DATA;
+
+   //Wait for the write to complete
+   while(LPC_EMAC->MIND & MIND_BUSY);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t lpc176xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   //PHY address
+   LPC_EMAC->MADR = (phyAddr << 8) & MADR_PHY_ADDRESS;
+   //Register address
+   LPC_EMAC->MADR |= regAddr & MADR_REGISTER_ADDRESS;
+
+   //Start a read operation
+   LPC_EMAC->MCMD = MCMD_READ;
+   //Wait for the read to complete
+   while(LPC_EMAC->MIND & MIND_BUSY);
+   //Clear MCMD register
+   LPC_EMAC->MCMD = 0;
+
+   //Return PHY register contents
+   return LPC_EMAC->MRDD & MRDD_READ_DATA;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t lpc176xEthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lpc176x_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,392 @@
+/**
+ * @file lpc176x_eth.h
+ * @brief LPC1764/66/67/68/69 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _LPC176X_ETH_H
+#define _LPC176X_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef LPC176X_ETH_TX_BUFFER_COUNT
+   #define LPC176X_ETH_TX_BUFFER_COUNT 2
+#elif (LPC176X_ETH_TX_BUFFER_COUNT < 1)
+   #error LPC176X_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef LPC176X_ETH_TX_BUFFER_SIZE
+   #define LPC176X_ETH_TX_BUFFER_SIZE 1536
+#elif (LPC176X_ETH_TX_BUFFER_SIZE != 1536)
+   #error LPC176X_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef LPC176X_ETH_RX_BUFFER_COUNT
+   #define LPC176X_ETH_RX_BUFFER_COUNT 4
+#elif (LPC176X_ETH_RX_BUFFER_COUNT < 1)
+   #error LPC176X_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef LPC176X_ETH_RX_BUFFER_SIZE
+   #define LPC176X_ETH_RX_BUFFER_SIZE 1536
+#elif (LPC176X_ETH_RX_BUFFER_SIZE != 1536)
+   #error LPC176X_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef LPC176X_ETH_IRQ_PRIORITY_GROUPING
+   #define LPC176X_ETH_IRQ_PRIORITY_GROUPING 2
+#elif (LPC176X_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error LPC176X_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef LPC176X_ETH_IRQ_GROUP_PRIORITY
+   #define LPC176X_ETH_IRQ_GROUP_PRIORITY 24
+#elif (LPC176X_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error LPC176X_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef LPC176X_ETH_IRQ_SUB_PRIORITY
+   #define LPC176X_ETH_IRQ_SUB_PRIORITY 0
+#elif (LPC176X_ETH_IRQ_SUB_PRIORITY < 0)
+   #error LPC176X_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//MAC1 register
+#define MAC1_SOFT_RESET                0x00008000
+#define MAC1_SIMULATION_RESET          0x00004000
+#define MAC1_RESET_MCS_RX              0x00000800
+#define MAC1_RESET_RX                  0x00000400
+#define MAC1_RESET_MCS_TX              0x00000200
+#define MAC1_RESET_TX                  0x00000100
+#define MAC1_LOOPBACK                  0x00000010
+#define MAC1_TX_FLOW_CONTROL           0x00000008
+#define MAC1_RX_FLOW_CONTROL           0x00000004
+#define MAC1_PASS_ALL_FRAMES           0x00000002
+#define MAC1_RECEIVE_ENABLE            0x00000001
+
+//MAC2 register
+#define MAC2_EXCESS_DEFER              0x00004000
+#define MAC2_BACK_PRESSURE_NO_BACKOFF  0x00002000
+#define MAC2_NO_BACKOFF                0x00001000
+#define MAC2_LONG_PREAMBLE_ENFORCEMENT 0x00000200
+#define MAC2_PURE_PREAMBLE_ENFORCEMENT 0x00000100
+#define MAC2_AUTO_DETECT_PAD_ENABLE    0x00000080
+#define MAC2_VLAN_PAD_ENABLE           0x00000040
+#define MAC2_PAD_CRC_ENABLE            0x00000020
+#define MAC2_CRC_ENABLE                0x00000010
+#define MAC2_DELAYED_CRC               0x00000008
+#define MAC2_HUGE_FRAME_ENABLE         0x00000004
+#define MAC2_FRAME_LENGTH_CHECKING     0x00000002
+#define MAC2_FULL_DUPLEX               0x00000001
+
+//IPGT register
+#define IPGT_BACK_TO_BACK_IPG          0x0000007F
+#define IPGT_HALF_DUPLEX               0x00000012
+#define IPGT_FULL_DUPLEX               0x00000015
+
+//IPGR register
+#define IPGR_NON_BACK_TO_BACK_IPG1     0x00007F00
+#define IPGR_NON_BACK_TO_BACK_IPG2     0x0000007F
+#define IPGR_DEFAULT_VALUE             0x00000C12
+
+//CLRT register
+#define CLRT_COLLISION_WINDOW          0x00003F00
+#define CLRT_RETRANSMISSION_MAXIMUM    0x00003F00
+#define CLRT_DEFAULT_VALUE             0x0000370F
+
+//MAXF register
+#define MAXF_MAXIMUM_FRAME_LENGTH      0x0000FFFF
+
+//SUPP register
+#define SUPP_SPEED                     0x00000100
+
+//TEST register
+#define TEST_BACKPRESSURE              0x00000004
+#define TEST_PAUSE                     0x00000002
+#define TEST_SHORTCUT_PAUSE_QUANTA     0x00000001
+
+//MCFG register
+#define MCFG_RESET_MII_MGMT            0x00008000
+#define MCFG_CLOCK SELECT              0x0000003C
+#define MCFG_SUPPRESS_PREAMBLE         0x00000002
+#define MCFG_SCAN_INCREMENT            0x00000001
+
+#define MCFG_CLOCK_SELECT_DIV4         0x00000000
+#define MCFG_CLOCK_SELECT_DIV6         0x00000008
+#define MCFG_CLOCK_SELECT_DIV8         0x0000000C
+#define MCFG_CLOCK_SELECT_DIV10        0x00000010
+#define MCFG_CLOCK_SELECT_DIV14        0x00000014
+#define MCFG_CLOCK_SELECT_DIV20        0x00000018
+#define MCFG_CLOCK_SELECT_DIV28        0x0000001C
+#define MCFG_CLOCK_SELECT_DIV36        0x00000020
+#define MCFG_CLOCK_SELECT_DIV40        0x00000024
+#define MCFG_CLOCK_SELECT_DIV44        0x00000028
+#define MCFG_CLOCK_SELECT_DIV48        0x0000002C
+#define MCFG_CLOCK_SELECT_DIV52        0x00000030
+#define MCFG_CLOCK_SELECT_DIV56        0x00000034
+#define MCFG_CLOCK_SELECT_DIV60        0x00000038
+#define MCFG_CLOCK_SELECT_DIV64        0x0000003C
+
+//MCMD register
+#define MCMD_SCAN                      0x00000002
+#define MCMD_READ                      0x00000001
+
+//MADR register
+#define MADR_PHY_ADDRESS               0x00001F00
+#define MADR_REGISTER_ADDRESS          0x0000001F
+
+//MWTD register
+#define MWTD_WRITE_DATA                0x0000FFFF
+
+//MRDD register
+#define MRDD_READ_DATA                 0x0000FFFF
+
+//MIND register
+#define MIND_MII_LINK_FAIL             0x00000008
+#define MIND_NOT_VALID                 0x00000004
+#define MIND_SCANNING                  0x00000002
+#define MIND_BUSY                      0x00000001
+
+//Command register
+#define COMMAND_FULL_DUPLEX            0x00000400
+#define COMMAND_RMII                   0x00000200
+#define COMMAND_TX_FLOW_CONTROL        0x00000100
+#define COMMAND_PASS_RX_FILTER         0x00000080
+#define COMMAND_PASS_RUNT_FRAME        0x00000040
+#define COMMAND_RX_RESET               0x00000020
+#define COMMAND_TX_RESET               0x00000010
+#define COMMAND_REG_RESET              0x00000008
+#define COMMAND_TX_ENABLE              0x00000002
+#define COMMAND_RX_ENABLE              0x00000001
+
+//Status register
+#define STATUS_TX                      0x00000002
+#define STATUS_RX                      0x00000001
+
+//TSV0 register
+#define TSV0_VLAN                      0x80000000
+#define TSV0_BACKPRESSURE              0x40000000
+#define TSV0_PAUSE                     0x20000000
+#define TSV0_CONTROL_FRAME             0x10000000
+#define TSV0_TOTAL_BYTES               0x0FFFF000
+#define TSV0_UNDERRUN                  0x00000800
+#define TSV0_GIANT                     0x00000400
+#define TSV0_LATE_COLLISION            0x00000200
+#define TSV0_EXCESSIVE_COLLISION       0x00000100
+#define TSV0_EXCESSIVE_DEFER           0x00000080
+#define TSV0_PACKET_DEFER              0x00000040
+#define TSV0_BROADCAST                 0x00000020
+#define TSV0_MULTICAST                 0x00000010
+#define TSV0_DONE                      0x00000008
+#define TSV0_LENGTH_OUT_OF_RANGE       0x00000004
+#define TSV0_LENGTH_CHECK_ERROR        0x00000002
+#define TSV0_CRC_ERROR                 0x00000001
+
+//TSV1 register
+#define TSV1_TRANSMIT_COLLISION_COUNT  0x000F0000
+#define TSV1_TRANSMIT_BYTE_COUNT       0x0000FFFF
+
+//RSV register
+#define RSV_VLAN                       0x40000000
+#define RSV_UNSUPPORTED_OPCODE         0x20000000
+#define RSV_PAUSE                      0x10000000
+#define RSV_CONTROL_FRAME              0x08000000
+#define RSV_DRIBBLE_NIBBLE             0x04000000
+#define RSV_BROADCAST                  0x02000000
+#define RSV_MULTICAST                  0x01000000
+#define RSV_RECEIVE_OK                 0x00800000
+#define RSV_LENGTH_OUT_OF_RANGE        0x00400000
+#define RSV_LENGTH_CHECK_ERROR         0x00200000
+#define RSV_CRC_ERROR                  0x00100000
+#define RSV_RECEIVE_CODE_VIOLATION     0x00080000
+#define RSV_CARRIER_EVENT_PREV_SEEN    0x00040000
+#define RSV_RXDV_EVENT_PREV_SEEN       0x00020000
+#define RSV_PACKET_PREVIOUSLY_IGNORED  0x00010000
+#define RSV_RECEIVED_BYTE_COUNT        0x0000FFFF
+
+//FlowControlCounter register
+#define FCC_PAUSE_TIMER                0xFFFF0000
+#define FCC_MIRROR_COUNTER             0x0000FFFF
+
+//FlowControlStatus register
+#define FCS_MIRROR_COUNTER_CURRENT     0x0000FFFF
+
+//RxFilterCtrl register
+#define RFC_RX_FILTER_EN_WOL           0x00002000
+#define RFC_MAGIC_PACKET_EN_WOL        0x00001000
+#define RFC_ACCEPT_PERFECT_EN          0x00000020
+#define RFC_ACCEPT_MULTICAST_HASH_EN   0x00000010
+#define RFC_ACCEPT_UNICAST_HASH_EN     0x00000008
+#define RFC_ACCEPT_MULTICAST_EN        0x00000004
+#define RFC_ACCEPT_BROADCAST_EN        0x00000002
+#define RFC_ACCEPT_UNICAST_EN          0x00000001
+
+//RxFilterWoLStatus and RxFilterWoLClear registers
+#define RFWS_MAGIC_PACKET_WOL          0x00000100
+#define RFWS_RX_FILTER_WOL             0x00000080
+#define RFWS_ACCEPT_PERFECT_WOL        0x00000020
+#define RFWS_ACCEPT_MULTICAST_HASH_WOL 0x00000010
+#define RFWS_ACCEPT_UNICAST_HASH_WOL   0x00000008
+#define RFWS_ACCEPT_MULTICAST_WOL      0x00000004
+#define RFWS_ACCEPT_BROADCAST_WOL      0x00000002
+#define RFWS_ACCEPT_UNICAST_WOL        0x00000001
+
+//IntStatus, IntEnable, IntClear and IntSet registers
+#define INT_WAKEUP                     0x00002000
+#define INT_SOFT_INT                   0x00001000
+#define INT_TX_DONE                    0x00000080
+#define INT_TX_FINISHED                0x00000040
+#define INT_TX_ERROR                   0x00000020
+#define INT_TX_UNDERRUN                0x00000010
+#define INT_RX_DONE                    0x00000008
+#define INT_RX_FINISHED                0x00000004
+#define INT_RX_ERROR                   0x00000002
+#define INT_RX_OVERRUN                 0x00000001
+
+//Transmit descriptor control word
+#define TX_CTRL_INTERRUPT              0x80000000
+#define TX_CTRL_LAST                   0x40000000
+#define TX_CTRL_CRC                    0x20000000
+#define TX_CTRL_PAD                    0x10000000
+#define TX_CTRL_HUGE                   0x08000000
+#define TX_CTRL_OVERRIDE               0x04000000
+#define TX_CTRL_SIZE                   0x000007FF
+
+//Transmit status information word
+#define TX_STATUS_ERROR                0x80000000
+#define TX_STATUS_NO_DESCRIPTOR        0x40000000
+#define TX_STATUS_UNDERRUN             0x20000000
+#define TX_STATUS_LATE_COLLISION       0x10000000
+#define TX_STATUS_EXCESSIVE_COLLISION  0x08000000
+#define TX_STATUS_EXCESSIVE_DEFER      0x04000000
+#define TX_STATUS_DEFER                0x02000000
+#define TX_STATUS_COLLISION_COUNT      0x01E00000
+
+//Receive descriptor control word
+#define RX_CTRL_INTERRUPT              0x80000000
+#define RX_CTRL_SIZE                   0x000007FF
+
+//Receive status information word
+#define RX_STATUS_ERROR                0x80000000
+#define RX_STATUS_LAST_FLAG            0x40000000
+#define RX_STATUS_NO_DESCRIPTOR        0x20000000
+#define RX_STATUS_OVERRUN              0x10000000
+#define RX_STATUS_ALIGNMENT_ERROR      0x08000000
+#define RX_STATUS_RANGE_ERROR          0x04000000
+#define RX_STATUS_LENGTH_ERROR         0x02000000
+#define RX_STATUS_SYMBOL_ERROR         0x01000000
+#define RX_STATUS_CRC_ERROR            0x00800000
+#define RX_STATUS_BROADCAST            0x00400000
+#define RX_STATUS_MULTICAST            0x00200000
+#define RX_STATUS_FAIL_FILTER          0x00100000
+#define RX_STATUS_VLAN                 0x00080000
+#define RX_STATUS_CONTROL_FRAME        0x00040000
+#define RX_STATUS_SIZE                 0x000007FF
+
+//Receive status HashCRC word
+#define RX_HASH_CRC_DA                 0x001FF000
+#define RX_HASH_CRC_SA                 0x000001FF
+
+
+/**
+ * @brief Transmit descriptor
+ **/
+
+typedef struct
+{
+   uint32_t packet;
+   uint32_t control;
+} Lpc176xTxDesc;
+
+
+/**
+ * @brief Transmit status
+ **/
+
+typedef struct
+{
+   uint32_t info;
+} Lpc176xTxStatus;
+
+
+/**
+ * @brief Receive descriptor
+ **/
+
+typedef struct
+{
+   uint32_t packet;
+   uint32_t control;
+} Lpc176xRxDesc;
+
+
+/**
+ * @brief Receive status
+ **/
+
+typedef struct
+{
+   uint32_t info;
+   uint32_t hashCrc;
+} Lpc176xRxStatus;
+
+
+//LPC176x Ethernet MAC driver
+extern const NicDriver lpc176xEthDriver;
+
+//LPC176x Ethernet MAC related functions
+error_t lpc176xEthInit(NetInterface *interface);
+void lpc176xEthInitGpio(NetInterface *interface);
+void lpc176xEthInitDesc(NetInterface *interface);
+
+void lpc176xEthTick(NetInterface *interface);
+
+void lpc176xEthEnableIrq(NetInterface *interface);
+void lpc176xEthDisableIrq(NetInterface *interface);
+void lpc176xEthEventHandler(NetInterface *interface);
+
+error_t lpc176xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t lpc176xEthReceivePacket(NetInterface *interface);
+
+error_t lpc176xEthSetMulticastFilter(NetInterface *interface);
+error_t lpc176xEthUpdateMacConfig(NetInterface *interface);
+
+void lpc176xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t lpc176xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t lpc176xEthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lpc178x_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,724 @@
+/**
+ * @file lpc178x_eth.c
+ * @brief LPC1786/88 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "lpc177x_8x.h"
+#include "core/net.h"
+#include "drivers/lpc178x_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+static uint8_t txBuffer[LPC178X_ETH_TX_BUFFER_COUNT][LPC178X_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+static uint8_t rxBuffer[LPC178X_ETH_RX_BUFFER_COUNT][LPC178X_ETH_RX_BUFFER_SIZE];
+//Transmit descriptors
+#pragma data_alignment = 4
+static Lpc178xTxDesc txDesc[LPC178X_ETH_TX_BUFFER_COUNT];
+//Transmit status array
+#pragma data_alignment = 4
+static Lpc178xTxStatus txStatus[LPC178X_ETH_TX_BUFFER_COUNT];
+//Receive descriptors
+#pragma data_alignment = 4
+static Lpc178xRxDesc rxDesc[LPC178X_ETH_RX_BUFFER_COUNT];
+//Receive status array
+#pragma data_alignment = 8
+static Lpc178xRxStatus rxStatus[LPC178X_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[LPC178X_ETH_TX_BUFFER_COUNT][LPC178X_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[LPC178X_ETH_RX_BUFFER_COUNT][LPC178X_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Transmit descriptors
+static Lpc178xTxDesc txDesc[LPC178X_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Transmit status array
+static Lpc178xTxStatus txStatus[LPC178X_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive descriptors
+static Lpc178xRxDesc rxDesc[LPC178X_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive status array
+static Lpc178xRxStatus rxStatus[LPC178X_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(8)));
+
+#endif
+
+
+/**
+ * @brief LPC178x Ethernet MAC driver
+ **/
+
+const NicDriver lpc178xEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   lpc178xEthInit,
+   lpc178xEthTick,
+   lpc178xEthEnableIrq,
+   lpc178xEthDisableIrq,
+   lpc178xEthEventHandler,
+   lpc178xEthSendPacket,
+   lpc178xEthSetMulticastFilter,
+   lpc178xEthUpdateMacConfig,
+   lpc178xEthWritePhyReg,
+   lpc178xEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief LPC178x Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc178xEthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing LPC178x Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Power up EMAC controller
+   LPC_SC->PCONP |= PCONP_PCENET;
+
+   //GPIO configuration
+   lpc178xEthInitGpio(interface);
+
+   //Reset host registers, transmit datapath and receive datapath
+   LPC_EMAC->Command = COMMAND_RX_RESET | COMMAND_TX_RESET | COMMAND_REG_RESET;
+
+   //Reset EMAC controller
+   LPC_EMAC->MAC1 = MAC1_SOFT_RESET | MAC1_SIMULATION_RESET |
+      MAC1_RESET_MCS_RX | MAC1_RESET_RX | MAC1_RESET_MCS_TX | MAC1_RESET_TX;
+
+   //Initialize MAC related registers
+   LPC_EMAC->MAC1 = 0;
+   LPC_EMAC->MAC2 = MAC2_PAD_CRC_ENABLE | MAC2_CRC_ENABLE;
+   LPC_EMAC->IPGR = IPGR_DEFAULT_VALUE;
+   LPC_EMAC->CLRT = CLRT_DEFAULT_VALUE;
+
+   //Select RMII mode
+   LPC_EMAC->Command = COMMAND_RMII;
+
+   //Configure MDC clock
+   LPC_EMAC->MCFG = MCFG_CLOCK_SELECT_DIV48;
+   //Reset MII management interface
+   LPC_EMAC->MCFG |= MCFG_RESET_MII_MGMT;
+   LPC_EMAC->MCFG &= ~MCFG_RESET_MII_MGMT;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Initialize TX and RX descriptor arrays
+   lpc178xEthInitDesc(interface);
+
+   //Set the MAC address
+   LPC_EMAC->SA0 = interface->macAddr.w[2];
+   LPC_EMAC->SA1 = interface->macAddr.w[1];
+   LPC_EMAC->SA2 = interface->macAddr.w[0];
+
+   //Initialize hash table
+   LPC_EMAC->HashFilterL = 0;
+   LPC_EMAC->HashFilterH = 0;
+
+   //Configure the receive filter
+   LPC_EMAC->RxFilterCtrl = RFC_ACCEPT_PERFECT_EN |
+      RFC_ACCEPT_MULTICAST_HASH_EN | RFC_ACCEPT_BROADCAST_EN;
+
+   //Program the MAXF register with the maximum frame length to be accepted
+   LPC_EMAC->MAXF = 1518;
+
+   //Reset EMAC interrupt flags
+   LPC_EMAC->IntClear  = 0xFFFF;
+   //Enable desired EMAC interrupts
+   LPC_EMAC->IntEnable = INT_TX_DONE | INT_RX_DONE;
+
+   //Set priority grouping (5 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(LPC178X_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure Ethernet interrupt priority
+   NVIC_SetPriority(ENET_IRQn, NVIC_EncodePriority(LPC178X_ETH_IRQ_PRIORITY_GROUPING,
+      LPC178X_ETH_IRQ_GROUP_PRIORITY, LPC178X_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable transmission and reception
+   LPC_EMAC->Command |= COMMAND_TX_ENABLE | COMMAND_RX_ENABLE;
+   //Allow frames to be received
+   LPC_EMAC->MAC1 |= MAC1_RECEIVE_ENABLE;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//LPC1788-32 Developer's Kit?
+#if defined(USE_LPC1788_32_DEV_KIT)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc178xEthInitGpio(NetInterface *interface)
+{
+   //Power up GPIO
+   LPC_SC->PCONP |= PCONP_PCGPIO;
+
+   //Configure P1.0 (ENET_TXD0)
+   LPC_IOCON->P1_0 = IOCON_SLEW | IOCON_FUNC_1;
+   //Configure P1.1 (ENET_TXD1)
+   LPC_IOCON->P1_1 = IOCON_SLEW | IOCON_FUNC_1;
+   //Configure P1.4 (ENET_TX_EN)
+   LPC_IOCON->P1_4 = IOCON_SLEW | IOCON_FUNC_1;
+   //Configure P1.8 (ENET_CRS)
+   LPC_IOCON->P1_8 = IOCON_SLEW | IOCON_FUNC_1;
+   //Configure P1.9 (ENET_RXD0)
+   LPC_IOCON->P1_9 = IOCON_SLEW | IOCON_FUNC_1;
+   //Configure P1.10 (ENET_RXD1)
+   LPC_IOCON->P1_10 = IOCON_SLEW | IOCON_FUNC_1;
+   //Configure P1.14 (RX_ER)
+   LPC_IOCON->P1_14 = IOCON_SLEW | IOCON_FUNC_1;
+   //Configure P1.15 (ENET_REF_CLK)
+   LPC_IOCON->P1_15 = IOCON_SLEW | IOCON_FUNC_1;
+   //Configure P1.16 (ENET_MDC)
+   LPC_IOCON->P1_16 = IOCON_MODE_PULL_UP | IOCON_FUNC_1;
+   //Configure P1.17 (ENET_MDIO)
+   LPC_IOCON->P1_17 = IOCON_MODE_PULL_UP | IOCON_FUNC_1;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize TX and RX descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc178xEthInitDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX descriptors
+   for(i = 0; i < LPC178X_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Base address of the buffer containing transmit data
+      txDesc[i].packet = (uint32_t) txBuffer[i];
+      //Transmit descriptor control word
+      txDesc[i].control = 0;
+      //Transmit status information word
+      txStatus[i].info = 0;
+   }
+
+   //Initialize RX descriptors
+   for(i = 0; i < LPC178X_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Base address of the buffer for storing receive data
+      rxDesc[i].packet = (uint32_t) rxBuffer[i];
+      //Receive descriptor control word
+      rxDesc[i].control = RX_CTRL_INTERRUPT | (LPC178X_ETH_RX_BUFFER_SIZE - 1);
+      //Receive status information word
+      rxStatus[i].info = 0;
+      //Receive status HashCRC word
+      rxStatus[i].hashCrc = 0;
+   }
+
+   //Initialize EMAC transmit descriptor registers
+   LPC_EMAC->TxDescriptor = (uint32_t) txDesc;
+   LPC_EMAC->TxStatus = (uint32_t) txStatus;
+   LPC_EMAC->TxDescriptorNumber = LPC178X_ETH_TX_BUFFER_COUNT - 1;
+   LPC_EMAC->TxProduceIndex = 0;
+
+   //Initialize EMAC receive descriptor registers
+   LPC_EMAC->RxDescriptor = (uint32_t) rxDesc;
+   LPC_EMAC->RxStatus = (uint32_t) rxStatus;
+   LPC_EMAC->RxDescriptorNumber = LPC178X_ETH_RX_BUFFER_COUNT - 1;
+   LPC_EMAC->RxConsumeIndex = 0;
+}
+
+
+/**
+ * @brief LPC178x Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc178xEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc178xEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(ENET_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc178xEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(ENET_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief LPC178x Ethernet MAC interrupt service routine
+ **/
+
+void ENET_IRQHandler(void)
+{
+   uint_t i;
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read interrupt status register
+   status = LPC_EMAC->IntStatus;
+
+   //A packet has been transmitted?
+   if(status & INT_TX_DONE)
+   {
+      //Clear TxDone interrupt flag
+      LPC_EMAC->IntClear = INT_TX_DONE;
+
+      //Get the index of the next descriptor
+      i = LPC_EMAC->TxProduceIndex + 1;
+
+      //Wrap around if necessary
+      if(i >= LPC178X_ETH_TX_BUFFER_COUNT)
+         i = 0;
+
+      //Check whether the TX buffer is available for writing
+      if(i != LPC_EMAC->TxConsumeIndex)
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & INT_RX_DONE)
+   {
+      //Disable RxDone interrupts
+      LPC_EMAC->IntEnable &= ~INT_RX_DONE;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief LPC178x Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc178xEthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(LPC_EMAC->IntStatus & INT_RX_DONE)
+   {
+      //Clear RxDone interrupt flag
+      LPC_EMAC->IntClear = INT_RX_DONE;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = lpc178xEthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable TxDone and RxDone interrupts
+   LPC_EMAC->IntEnable = INT_TX_DONE | INT_RX_DONE;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t lpc178xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   uint_t i;
+   uint_t j;
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(!length)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //We are done since the buffer is empty
+      return NO_ERROR;
+   }
+   else if(length > LPC178X_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Get the index of the current descriptor
+   i = LPC_EMAC->TxProduceIndex;
+   //Get the index of the next descriptor
+   j = i + 1;
+
+   //Wrap around if necessary
+   if(j >= LPC178X_ETH_TX_BUFFER_COUNT)
+      j = 0;
+
+   //Check whether the transmit descriptor array is full
+   if(j == LPC_EMAC->TxConsumeIndex)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txDesc[i].packet, buffer, offset, length);
+
+   //Write the transmit control word
+   txDesc[i].control = TX_CTRL_INTERRUPT | TX_CTRL_LAST |
+      TX_CTRL_CRC | TX_CTRL_PAD | ((length - 1) & TX_CTRL_SIZE);
+
+   //Increment index and wrap around if necessary
+   if(++i >= LPC178X_ETH_TX_BUFFER_COUNT)
+      i = 0;
+
+   //Save the resulting value
+   LPC_EMAC->TxProduceIndex = i;
+
+   //Get the index of the next descriptor
+   j = i + 1;
+
+   //Wrap around if necessary
+   if(j >= LPC178X_ETH_TX_BUFFER_COUNT)
+      j = 0;
+
+   //Check whether the next buffer is available for writing
+   if(j != LPC_EMAC->TxConsumeIndex)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful write operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc178xEthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+   uint_t i;
+
+   //Point to the current descriptor
+   i = LPC_EMAC->RxConsumeIndex;
+
+   //Make sure the current buffer is available for reading
+   if(i != LPC_EMAC->RxProduceIndex)
+   {
+      //Retrieve the length of the frame
+      n = (rxStatus[i].info & RX_STATUS_SIZE) + 1;
+      //Limit the number of data to read
+      n = MIN(n, LPC178X_ETH_RX_BUFFER_SIZE);
+
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, (uint8_t *) rxDesc[i].packet, n);
+
+      //Increment index and wrap around if necessary
+      if(++i >= LPC178X_ETH_RX_BUFFER_COUNT)
+         i = 0;
+
+      //Save the resulting value
+      LPC_EMAC->RxConsumeIndex = i;
+
+      //Valid packet received
+      error = NO_ERROR;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc178xEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating LPC178x hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = lpc178xEthCalcCrc(&entry->addr, sizeof(MacAddr));
+         //Bits [28:23] are used to form the hash
+         k = (crc >> 23) & 0x3F;
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   LPC_EMAC->HashFilterL = hashTable[0];
+   LPC_EMAC->HashFilterH = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HashFilterL = %08" PRIX32 "\r\n", LPC_EMAC->HashFilterL);
+   TRACE_DEBUG("  HashFilterH = %08" PRIX32 "\r\n", LPC_EMAC->HashFilterH);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc178xEthUpdateMacConfig(NetInterface *interface)
+{
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      LPC_EMAC->SUPP = SUPP_SPEED;
+   else
+      LPC_EMAC->SUPP = 0;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+   {
+      //The MAC operates in full-duplex mode
+      LPC_EMAC->MAC2 |= MAC2_FULL_DUPLEX;
+      LPC_EMAC->Command |= COMMAND_FULL_DUPLEX;
+      //Configure Back-to-Back Inter-Packet Gap
+      LPC_EMAC->IPGT = IPGT_FULL_DUPLEX;
+   }
+   else
+   {
+      //The MAC operates in half-duplex mode
+      LPC_EMAC->MAC2 &= ~MAC2_FULL_DUPLEX;
+      LPC_EMAC->Command &= ~COMMAND_FULL_DUPLEX;
+      //Configure Back-to-Back Inter-Packet Gap
+      LPC_EMAC->IPGT = IPGT_HALF_DUPLEX;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void lpc178xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   //Clear MCMD register
+   LPC_EMAC->MCMD = 0;
+
+   //PHY address
+   LPC_EMAC->MADR = (phyAddr << 8) & MADR_PHY_ADDRESS;
+   //Register address
+   LPC_EMAC->MADR |= regAddr & MADR_REGISTER_ADDRESS;
+   //Data to be written in the PHY register
+   LPC_EMAC->MWTD = data & MWTD_WRITE_DATA;
+
+   //Wait for the write to complete
+   while(LPC_EMAC->MIND & MIND_BUSY);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t lpc178xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   //PHY address
+   LPC_EMAC->MADR = (phyAddr << 8) & MADR_PHY_ADDRESS;
+   //Register address
+   LPC_EMAC->MADR |= regAddr & MADR_REGISTER_ADDRESS;
+
+   //Start a read operation
+   LPC_EMAC->MCMD = MCMD_READ;
+   //Wait for the read to complete
+   while(LPC_EMAC->MIND & MIND_BUSY);
+   //Clear MCMD register
+   LPC_EMAC->MCMD = 0;
+
+   //Return PHY register contents
+   return LPC_EMAC->MRDD & MRDD_READ_DATA;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t lpc178xEthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lpc178x_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,392 @@
+/**
+ * @file lpc178x_eth.h
+ * @brief LPC1786/88 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _LPC178X_ETH_H
+#define _LPC178X_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef LPC178X_ETH_TX_BUFFER_COUNT
+   #define LPC178X_ETH_TX_BUFFER_COUNT 3
+#elif (LPC178X_ETH_TX_BUFFER_COUNT < 1)
+   #error LPC178X_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef LPC178X_ETH_TX_BUFFER_SIZE
+   #define LPC178X_ETH_TX_BUFFER_SIZE 1536
+#elif (LPC178X_ETH_TX_BUFFER_SIZE != 1536)
+   #error LPC178X_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef LPC178X_ETH_RX_BUFFER_COUNT
+   #define LPC178X_ETH_RX_BUFFER_COUNT 6
+#elif (LPC178X_ETH_RX_BUFFER_COUNT < 1)
+   #error LPC178X_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef LPC178X_ETH_RX_BUFFER_SIZE
+   #define LPC178X_ETH_RX_BUFFER_SIZE 1536
+#elif (LPC178X_ETH_RX_BUFFER_SIZE != 1536)
+   #error LPC178X_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef LPC178X_ETH_IRQ_PRIORITY_GROUPING
+   #define LPC178X_ETH_IRQ_PRIORITY_GROUPING 2
+#elif (LPC178X_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error LPC178X_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef LPC178X_ETH_IRQ_GROUP_PRIORITY
+   #define LPC178X_ETH_IRQ_GROUP_PRIORITY 24
+#elif (LPC178X_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error LPC178X_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef LPC178X_ETH_IRQ_SUB_PRIORITY
+   #define LPC178X_ETH_IRQ_SUB_PRIORITY 0
+#elif (LPC178X_ETH_IRQ_SUB_PRIORITY < 0)
+   #error LPC178X_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//MAC1 register
+#define MAC1_SOFT_RESET                0x00008000
+#define MAC1_SIMULATION_RESET          0x00004000
+#define MAC1_RESET_MCS_RX              0x00000800
+#define MAC1_RESET_RX                  0x00000400
+#define MAC1_RESET_MCS_TX              0x00000200
+#define MAC1_RESET_TX                  0x00000100
+#define MAC1_LOOPBACK                  0x00000010
+#define MAC1_TX_FLOW_CONTROL           0x00000008
+#define MAC1_RX_FLOW_CONTROL           0x00000004
+#define MAC1_PASS_ALL_FRAMES           0x00000002
+#define MAC1_RECEIVE_ENABLE            0x00000001
+
+//MAC2 register
+#define MAC2_EXCESS_DEFER              0x00004000
+#define MAC2_BACK_PRESSURE_NO_BACKOFF  0x00002000
+#define MAC2_NO_BACKOFF                0x00001000
+#define MAC2_LONG_PREAMBLE_ENFORCEMENT 0x00000200
+#define MAC2_PURE_PREAMBLE_ENFORCEMENT 0x00000100
+#define MAC2_AUTO_DETECT_PAD_ENABLE    0x00000080
+#define MAC2_VLAN_PAD_ENABLE           0x00000040
+#define MAC2_PAD_CRC_ENABLE            0x00000020
+#define MAC2_CRC_ENABLE                0x00000010
+#define MAC2_DELAYED_CRC               0x00000008
+#define MAC2_HUGE_FRAME_ENABLE         0x00000004
+#define MAC2_FRAME_LENGTH_CHECKING     0x00000002
+#define MAC2_FULL_DUPLEX               0x00000001
+
+//IPGT register
+#define IPGT_BACK_TO_BACK_IPG          0x0000007F
+#define IPGT_HALF_DUPLEX               0x00000012
+#define IPGT_FULL_DUPLEX               0x00000015
+
+//IPGR register
+#define IPGR_NON_BACK_TO_BACK_IPG1     0x00007F00
+#define IPGR_NON_BACK_TO_BACK_IPG2     0x0000007F
+#define IPGR_DEFAULT_VALUE             0x00000C12
+
+//CLRT register
+#define CLRT_COLLISION_WINDOW          0x00003F00
+#define CLRT_RETRANSMISSION_MAXIMUM    0x00003F00
+#define CLRT_DEFAULT_VALUE             0x0000370F
+
+//MAXF register
+#define MAXF_MAXIMUM_FRAME_LENGTH      0x0000FFFF
+
+//SUPP register
+#define SUPP_SPEED                     0x00000100
+
+//TEST register
+#define TEST_BACKPRESSURE              0x00000004
+#define TEST_PAUSE                     0x00000002
+#define TEST_SHORTCUT_PAUSE_QUANTA     0x00000001
+
+//MCFG register
+#define MCFG_RESET_MII_MGMT            0x00008000
+#define MCFG_CLOCK SELECT              0x0000003C
+#define MCFG_SUPPRESS_PREAMBLE         0x00000002
+#define MCFG_SCAN_INCREMENT            0x00000001
+
+#define MCFG_CLOCK_SELECT_DIV4         0x00000000
+#define MCFG_CLOCK_SELECT_DIV6         0x00000008
+#define MCFG_CLOCK_SELECT_DIV8         0x0000000C
+#define MCFG_CLOCK_SELECT_DIV10        0x00000010
+#define MCFG_CLOCK_SELECT_DIV14        0x00000014
+#define MCFG_CLOCK_SELECT_DIV20        0x00000018
+#define MCFG_CLOCK_SELECT_DIV28        0x0000001C
+#define MCFG_CLOCK_SELECT_DIV36        0x00000020
+#define MCFG_CLOCK_SELECT_DIV40        0x00000024
+#define MCFG_CLOCK_SELECT_DIV44        0x00000028
+#define MCFG_CLOCK_SELECT_DIV48        0x0000002C
+#define MCFG_CLOCK_SELECT_DIV52        0x00000030
+#define MCFG_CLOCK_SELECT_DIV56        0x00000034
+#define MCFG_CLOCK_SELECT_DIV60        0x00000038
+#define MCFG_CLOCK_SELECT_DIV64        0x0000003C
+
+//MCMD register
+#define MCMD_SCAN                      0x00000002
+#define MCMD_READ                      0x00000001
+
+//MADR register
+#define MADR_PHY_ADDRESS               0x00001F00
+#define MADR_REGISTER_ADDRESS          0x0000001F
+
+//MWTD register
+#define MWTD_WRITE_DATA                0x0000FFFF
+
+//MRDD register
+#define MRDD_READ_DATA                 0x0000FFFF
+
+//MIND register
+#define MIND_MII_LINK_FAIL             0x00000008
+#define MIND_NOT_VALID                 0x00000004
+#define MIND_SCANNING                  0x00000002
+#define MIND_BUSY                      0x00000001
+
+//Command register
+#define COMMAND_FULL_DUPLEX            0x00000400
+#define COMMAND_RMII                   0x00000200
+#define COMMAND_TX_FLOW_CONTROL        0x00000100
+#define COMMAND_PASS_RX_FILTER         0x00000080
+#define COMMAND_PASS_RUNT_FRAME        0x00000040
+#define COMMAND_RX_RESET               0x00000020
+#define COMMAND_TX_RESET               0x00000010
+#define COMMAND_REG_RESET              0x00000008
+#define COMMAND_TX_ENABLE              0x00000002
+#define COMMAND_RX_ENABLE              0x00000001
+
+//Status register
+#define STATUS_TX                      0x00000002
+#define STATUS_RX                      0x00000001
+
+//TSV0 register
+#define TSV0_VLAN                      0x80000000
+#define TSV0_BACKPRESSURE              0x40000000
+#define TSV0_PAUSE                     0x20000000
+#define TSV0_CONTROL_FRAME             0x10000000
+#define TSV0_TOTAL_BYTES               0x0FFFF000
+#define TSV0_UNDERRUN                  0x00000800
+#define TSV0_GIANT                     0x00000400
+#define TSV0_LATE_COLLISION            0x00000200
+#define TSV0_EXCESSIVE_COLLISION       0x00000100
+#define TSV0_EXCESSIVE_DEFER           0x00000080
+#define TSV0_PACKET_DEFER              0x00000040
+#define TSV0_BROADCAST                 0x00000020
+#define TSV0_MULTICAST                 0x00000010
+#define TSV0_DONE                      0x00000008
+#define TSV0_LENGTH_OUT_OF_RANGE       0x00000004
+#define TSV0_LENGTH_CHECK_ERROR        0x00000002
+#define TSV0_CRC_ERROR                 0x00000001
+
+//TSV1 register
+#define TSV1_TRANSMIT_COLLISION_COUNT  0x000F0000
+#define TSV1_TRANSMIT_BYTE_COUNT       0x0000FFFF
+
+//RSV register
+#define RSV_VLAN                       0x40000000
+#define RSV_UNSUPPORTED_OPCODE         0x20000000
+#define RSV_PAUSE                      0x10000000
+#define RSV_CONTROL_FRAME              0x08000000
+#define RSV_DRIBBLE_NIBBLE             0x04000000
+#define RSV_BROADCAST                  0x02000000
+#define RSV_MULTICAST                  0x01000000
+#define RSV_RECEIVE_OK                 0x00800000
+#define RSV_LENGTH_OUT_OF_RANGE        0x00400000
+#define RSV_LENGTH_CHECK_ERROR         0x00200000
+#define RSV_CRC_ERROR                  0x00100000
+#define RSV_RECEIVE_CODE_VIOLATION     0x00080000
+#define RSV_CARRIER_EVENT_PREV_SEEN    0x00040000
+#define RSV_RXDV_EVENT_PREV_SEEN       0x00020000
+#define RSV_PACKET_PREVIOUSLY_IGNORED  0x00010000
+#define RSV_RECEIVED_BYTE_COUNT        0x0000FFFF
+
+//FlowControlCounter register
+#define FCC_PAUSE_TIMER                0xFFFF0000
+#define FCC_MIRROR_COUNTER             0x0000FFFF
+
+//FlowControlStatus register
+#define FCS_MIRROR_COUNTER_CURRENT     0x0000FFFF
+
+//RxFilterCtrl register
+#define RFC_RX_FILTER_EN_WOL           0x00002000
+#define RFC_MAGIC_PACKET_EN_WOL        0x00001000
+#define RFC_ACCEPT_PERFECT_EN          0x00000020
+#define RFC_ACCEPT_MULTICAST_HASH_EN   0x00000010
+#define RFC_ACCEPT_UNICAST_HASH_EN     0x00000008
+#define RFC_ACCEPT_MULTICAST_EN        0x00000004
+#define RFC_ACCEPT_BROADCAST_EN        0x00000002
+#define RFC_ACCEPT_UNICAST_EN          0x00000001
+
+//RxFilterWoLStatus and RxFilterWoLClear registers
+#define RFWS_MAGIC_PACKET_WOL          0x00000100
+#define RFWS_RX_FILTER_WOL             0x00000080
+#define RFWS_ACCEPT_PERFECT_WOL        0x00000020
+#define RFWS_ACCEPT_MULTICAST_HASH_WOL 0x00000010
+#define RFWS_ACCEPT_UNICAST_HASH_WOL   0x00000008
+#define RFWS_ACCEPT_MULTICAST_WOL      0x00000004
+#define RFWS_ACCEPT_BROADCAST_WOL      0x00000002
+#define RFWS_ACCEPT_UNICAST_WOL        0x00000001
+
+//IntStatus, IntEnable, IntClear and IntSet registers
+#define INT_WAKEUP                     0x00002000
+#define INT_SOFT_INT                   0x00001000
+#define INT_TX_DONE                    0x00000080
+#define INT_TX_FINISHED                0x00000040
+#define INT_TX_ERROR                   0x00000020
+#define INT_TX_UNDERRUN                0x00000010
+#define INT_RX_DONE                    0x00000008
+#define INT_RX_FINISHED                0x00000004
+#define INT_RX_ERROR                   0x00000002
+#define INT_RX_OVERRUN                 0x00000001
+
+//Transmit descriptor control word
+#define TX_CTRL_INTERRUPT              0x80000000
+#define TX_CTRL_LAST                   0x40000000
+#define TX_CTRL_CRC                    0x20000000
+#define TX_CTRL_PAD                    0x10000000
+#define TX_CTRL_HUGE                   0x08000000
+#define TX_CTRL_OVERRIDE               0x04000000
+#define TX_CTRL_SIZE                   0x000007FF
+
+//Transmit status information word
+#define TX_STATUS_ERROR                0x80000000
+#define TX_STATUS_NO_DESCRIPTOR        0x40000000
+#define TX_STATUS_UNDERRUN             0x20000000
+#define TX_STATUS_LATE_COLLISION       0x10000000
+#define TX_STATUS_EXCESSIVE_COLLISION  0x08000000
+#define TX_STATUS_EXCESSIVE_DEFER      0x04000000
+#define TX_STATUS_DEFER                0x02000000
+#define TX_STATUS_COLLISION_COUNT      0x01E00000
+
+//Receive descriptor control word
+#define RX_CTRL_INTERRUPT              0x80000000
+#define RX_CTRL_SIZE                   0x000007FF
+
+//Receive status information word
+#define RX_STATUS_ERROR                0x80000000
+#define RX_STATUS_LAST_FLAG            0x40000000
+#define RX_STATUS_NO_DESCRIPTOR        0x20000000
+#define RX_STATUS_OVERRUN              0x10000000
+#define RX_STATUS_ALIGNMENT_ERROR      0x08000000
+#define RX_STATUS_RANGE_ERROR          0x04000000
+#define RX_STATUS_LENGTH_ERROR         0x02000000
+#define RX_STATUS_SYMBOL_ERROR         0x01000000
+#define RX_STATUS_CRC_ERROR            0x00800000
+#define RX_STATUS_BROADCAST            0x00400000
+#define RX_STATUS_MULTICAST            0x00200000
+#define RX_STATUS_FAIL_FILTER          0x00100000
+#define RX_STATUS_VLAN                 0x00080000
+#define RX_STATUS_CONTROL_FRAME        0x00040000
+#define RX_STATUS_SIZE                 0x000007FF
+
+//Receive status HashCRC word
+#define RX_HASH_CRC_DA                 0x001FF000
+#define RX_HASH_CRC_SA                 0x000001FF
+
+
+/**
+ * @brief Transmit descriptor
+ **/
+
+typedef struct
+{
+   uint32_t packet;
+   uint32_t control;
+} Lpc178xTxDesc;
+
+
+/**
+ * @brief Transmit status
+ **/
+
+typedef struct
+{
+   uint32_t info;
+} Lpc178xTxStatus;
+
+
+/**
+ * @brief Receive descriptor
+ **/
+
+typedef struct
+{
+   uint32_t packet;
+   uint32_t control;
+} Lpc178xRxDesc;
+
+
+/**
+ * @brief Receive status
+ **/
+
+typedef struct
+{
+   uint32_t info;
+   uint32_t hashCrc;
+} Lpc178xRxStatus;
+
+
+//LPC178x Ethernet MAC driver
+extern const NicDriver lpc178xEthDriver;
+
+//LPC178x Ethernet MAC related functions
+error_t lpc178xEthInit(NetInterface *interface);
+void lpc178xEthInitGpio(NetInterface *interface);
+void lpc178xEthInitDesc(NetInterface *interface);
+
+void lpc178xEthTick(NetInterface *interface);
+
+void lpc178xEthEnableIrq(NetInterface *interface);
+void lpc178xEthDisableIrq(NetInterface *interface);
+void lpc178xEthEventHandler(NetInterface *interface);
+
+error_t lpc178xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t lpc178xEthReceivePacket(NetInterface *interface);
+
+error_t lpc178xEthSetMulticastFilter(NetInterface *interface);
+error_t lpc178xEthUpdateMacConfig(NetInterface *interface);
+
+void lpc178xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t lpc178xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t lpc178xEthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lpc18xx_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,752 @@
+/**
+ * @file lpc18xx_eth.c
+ * @brief LPC1800 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "lpc18xx.h"
+#include "core/net.h"
+#include "drivers/lpc18xx_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+static uint8_t txBuffer[LPC18XX_ETH_TX_BUFFER_COUNT][LPC18XX_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+static uint8_t rxBuffer[LPC18XX_ETH_RX_BUFFER_COUNT][LPC18XX_ETH_RX_BUFFER_SIZE];
+//Transmit DMA descriptors
+#pragma data_alignment = 4
+static Lpc18xxTxDmaDesc txDmaDesc[LPC18XX_ETH_TX_BUFFER_COUNT];
+//Receive DMA descriptors
+#pragma data_alignment = 4
+static Lpc18xxRxDmaDesc rxDmaDesc[LPC18XX_ETH_RX_BUFFER_COUNT];
+
+//ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[LPC18XX_ETH_TX_BUFFER_COUNT][LPC18XX_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[LPC18XX_ETH_RX_BUFFER_COUNT][LPC18XX_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Transmit DMA descriptors
+static Lpc18xxTxDmaDesc txDmaDesc[LPC18XX_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive DMA descriptors
+static Lpc18xxRxDmaDesc rxDmaDesc[LPC18XX_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+
+#endif
+
+//Pointer to the current TX DMA descriptor
+static Lpc18xxTxDmaDesc *txCurDmaDesc;
+//Pointer to the current RX DMA descriptor
+static Lpc18xxRxDmaDesc *rxCurDmaDesc;
+
+
+/**
+ * @brief LPC18xx Ethernet MAC driver
+ **/
+
+const NicDriver lpc18xxEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   lpc18xxEthInit,
+   lpc18xxEthTick,
+   lpc18xxEthEnableIrq,
+   lpc18xxEthDisableIrq,
+   lpc18xxEthEventHandler,
+   lpc18xxEthSendPacket,
+   lpc18xxEthSetMulticastFilter,
+   lpc18xxEthUpdateMacConfig,
+   lpc18xxEthWritePhyReg,
+   lpc18xxEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief LPC18xx Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc18xxEthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing LPC18xx Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Enable Ethernet peripheral clock
+   LPC_CCU1->CLK_M3_ETHERNET_CFG |= CCU1_CLK_M3_ETHERNET_CFG_RUN_Msk;
+   while(!(LPC_CCU1->CLK_M3_ETHERNET_STAT & CCU1_CLK_M3_ETHERNET_STAT_RUN_Msk));
+
+   //Reset DMA
+   LPC_RGU->RESET_EXT_STAT19 |= RGU_RESET_EXT_STAT19_MASTER_RESET_Msk;
+   LPC_RGU->RESET_EXT_STAT19 &= ~RGU_RESET_EXT_STAT19_MASTER_RESET_Msk;
+
+   //Reset Ethernet peripheral
+   LPC_RGU->RESET_EXT_STAT22 |= RGU_RESET_EXT_STAT22_MASTER_RESET_Msk;
+   LPC_RGU->RESET_EXT_STAT22 &= ~RGU_RESET_EXT_STAT22_MASTER_RESET_Msk;
+
+   //GPIO configuration
+   lpc18xxEthInitGpio(interface);
+
+   //Reset Ethernet peripheral
+   LPC_RGU->RESET_CTRL0 = RGU_RESET_CTRL0_ETHERNET_RST_Msk;
+   while(!(LPC_RGU->RESET_ACTIVE_STATUS0 & RGU_RESET_ACTIVE_STATUS0_ETHERNET_RST_Msk));
+
+   //Perform a software reset
+   LPC_ETHERNET->DMA_BUS_MODE |= ETHERNET_DMA_BUS_MODE_SWR_Msk;
+   //Wait for the reset to complete
+   while(LPC_ETHERNET->DMA_BUS_MODE & ETHERNET_DMA_BUS_MODE_SWR_Msk);
+
+   //Adjust MDC clock range
+   LPC_ETHERNET->MAC_MII_ADDR = ETHERNET_MAC_MII_ADDR_CR_DIV62;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Use default MAC configuration
+   LPC_ETHERNET->MAC_CONFIG = ETHERNET_MAC_CONFIG_DO_Msk;
+
+   //Set the MAC address
+   LPC_ETHERNET->MAC_ADDR0_LOW = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   LPC_ETHERNET->MAC_ADDR0_HIGH = interface->macAddr.w[2];
+
+   //Initialize hash table
+   LPC_ETHERNET->MAC_HASHTABLE_LOW = 0;
+   LPC_ETHERNET->MAC_HASHTABLE_HIGH = 0;
+
+   //Configure the receive filter
+   LPC_ETHERNET->MAC_FRAME_FILTER = ETHERNET_MAC_FRAME_FILTER_HPF_Msk |
+      ETHERNET_MAC_FRAME_FILTER_HMC_Msk;
+
+   //Disable flow control
+   LPC_ETHERNET->MAC_FLOW_CTRL = 0;
+   //Set the threshold level of the transmit and receive FIFOs
+   LPC_ETHERNET->DMA_OP_MODE = ETHERNET_DMA_OP_MODE_TTC_64 | ETHERNET_DMA_OP_MODE_RTC_32;
+
+   //Configure DMA bus mode
+   LPC_ETHERNET->DMA_BUS_MODE = ETHERNET_DMA_BUS_MODE_AAL_Msk | ETHERNET_DMA_BUS_MODE_USP_Msk |
+      ETHERNET_DMA_BUS_MODE_RPBL_1 | ETHERNET_DMA_BUS_MODE_PR_1_1 |
+      ETHERNET_DMA_BUS_MODE_PBL_1 | ETHERNET_DMA_BUS_MODE_ATDS_Msk;
+
+   //Initialize DMA descriptor lists
+   lpc18xxEthInitDmaDesc(interface);
+
+   //Disable MAC interrupts
+   LPC_ETHERNET->MAC_INTR_MASK = ETHERNET_MAC_INTR_MASK_TSIM_Msk |
+      ETHERNET_MAC_INTR_MASK_PMTIM_Msk;
+
+   //Enable the desired DMA interrupts
+   LPC_ETHERNET->DMA_INT_EN = ETHERNET_DMA_INT_EN_NIE_Msk |
+      ETHERNET_DMA_INT_EN_RIE_Msk | ETHERNET_DMA_INT_EN_TIE_Msk;
+
+   //Set priority grouping (3 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(LPC18XX_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure Ethernet interrupt priority
+   NVIC_SetPriority(ETHERNET_IRQn, NVIC_EncodePriority(LPC18XX_ETH_IRQ_PRIORITY_GROUPING,
+      LPC18XX_ETH_IRQ_GROUP_PRIORITY, LPC18XX_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable MAC transmission and reception
+   LPC_ETHERNET->MAC_CONFIG |= ETHERNET_MAC_CONFIG_TE_Msk | ETHERNET_MAC_CONFIG_RE_Msk;
+   //Enable DMA transmission and reception
+   LPC_ETHERNET->DMA_OP_MODE |= ETHERNET_DMA_OP_MODE_ST_Msk | ETHERNET_DMA_OP_MODE_SR_Msk;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//LPC1830-Xplorer evaluation board?
+#if defined(USE_LPC1830_XPLORER)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc18xxEthInitGpio(NetInterface *interface)
+{
+   //Enable GPIO peripheral clock
+   LPC_CCU1->CLK_M3_GPIO_CFG |= CCU1_CLK_M3_GPIO_CFG_RUN_Msk;
+   while(!(LPC_CCU1->CLK_M3_GPIO_STAT & CCU1_CLK_M3_GPIO_STAT_RUN_Msk));
+
+   //Select RMII operation mode
+   LPC_CREG->CREG6 &= ~CREG_CREG6_ETHMODE_Msk;
+   LPC_CREG->CREG6 |= CREG6_ETHMODE_RMII;
+
+   //Configure P0.0 (ENET_RXD1)
+   LPC_SCU->SFSP0_0 = SCU_SFSP0_0_EZI_Msk | SCU_SFSP0_0_EHS_Msk | (2 & SCU_SFSP0_0_MODE_Msk);
+   //Configure P0.1 (ENET_TX_EN)
+   LPC_SCU->SFSP0_1 = SCU_SFSP0_1_EHS_Msk | (6 & SCU_SFSP0_1_MODE_Msk);
+
+   //Configure P1.15 (ENET_RXD0)
+   LPC_SCU->SFSP1_15 = SCU_SFSP1_15_EZI_Msk | SCU_SFSP1_15_EHS_Msk | (3 & SCU_SFSP1_15_MODE_Msk);
+   //Configure P1.16 (ENET_RX_DV)
+   LPC_SCU->SFSP1_16 = SCU_SFSP1_16_EZI_Msk | SCU_SFSP1_16_EHS_Msk | (7 & SCU_SFSP1_16_MODE_Msk);
+   //Configure P1.17 (ENET_MDIO)
+   LPC_SCU->SFSP1_17 = SCU_SFSP1_17_EZI_Msk | (3 & SCU_SFSP1_17_MODE_Msk);
+   //Configure P1.18 (ENET_TXD0)
+   LPC_SCU->SFSP1_18 = SCU_SFSP1_18_EHS_Msk | (3 & SCU_SFSP1_18_MODE_Msk);
+   //Configure P1.19 (ENET_REF_CLK)
+   LPC_SCU->SFSP1_19 = SCU_SFSP1_19_EZI_Msk | SCU_SFSP1_19_EHS_Msk | (0 & SCU_SFSP1_19_MODE_Msk);
+   //Configure P1.20 (ENET_TXD1)
+   LPC_SCU->SFSP1_20 = SCU_SFSP1_20_EHS_Msk | (3 & SCU_SFSP1_20_MODE_Msk);
+
+   //Configure P2.0 (ENET_MDC)
+   LPC_SCU->SFSP2_0 = (7 & SCU_SFSP2_0_MODE_Msk);
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc18xxEthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX DMA descriptor list
+   for(i = 0; i < LPC18XX_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Use chain structure rather than ring structure
+      txDmaDesc[i].tdes0 = ETH_TDES0_IC | ETH_TDES0_TCH;
+      //Initialize transmit buffer size
+      txDmaDesc[i].tdes1 = 0;
+      //Transmit buffer address
+      txDmaDesc[i].tdes2 = (uint32_t) txBuffer[i];
+      //Next descriptor address
+      txDmaDesc[i].tdes3 = (uint32_t) &txDmaDesc[i + 1];
+      //Reserved fields
+      txDmaDesc[i].tdes4 = 0;
+      txDmaDesc[i].tdes5 = 0;
+      //Transmit frame time stamp
+      txDmaDesc[i].tdes6 = 0;
+      txDmaDesc[i].tdes7 = 0;
+   }
+
+   //The last descriptor is chained to the first entry
+   txDmaDesc[i - 1].tdes3 = (uint32_t) &txDmaDesc[0];
+   //Point to the very first descriptor
+   txCurDmaDesc = &txDmaDesc[0];
+
+   //Initialize RX DMA descriptor list
+   for(i = 0; i < LPC18XX_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the DMA
+      rxDmaDesc[i].rdes0 = ETH_RDES0_OWN;
+      //Use chain structure rather than ring structure
+      rxDmaDesc[i].rdes1 = ETH_RDES1_RCH | (LPC18XX_ETH_RX_BUFFER_SIZE & ETH_RDES1_RBS1);
+      //Receive buffer address
+      rxDmaDesc[i].rdes2 = (uint32_t) rxBuffer[i];
+      //Next descriptor address
+      rxDmaDesc[i].rdes3 = (uint32_t) &rxDmaDesc[i + 1];
+      //Extended status
+      rxDmaDesc[i].rdes4 = 0;
+      //Reserved field
+      rxDmaDesc[i].rdes5 = 0;
+      //Receive frame time stamp
+      rxDmaDesc[i].rdes6 = 0;
+      rxDmaDesc[i].rdes7 = 0;
+   }
+
+   //The last descriptor is chained to the first entry
+   rxDmaDesc[i - 1].rdes3 = (uint32_t) &rxDmaDesc[0];
+   //Point to the very first descriptor
+   rxCurDmaDesc = &rxDmaDesc[0];
+
+   //Start location of the TX descriptor list
+   LPC_ETHERNET->DMA_TRANS_DES_ADDR = (uint32_t) txDmaDesc;
+   //Start location of the RX descriptor list
+   LPC_ETHERNET->DMA_REC_DES_ADDR = (uint32_t) rxDmaDesc;
+}
+
+
+/**
+ * @brief LPC18xx Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc18xxEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc18xxEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(ETHERNET_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc18xxEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(ETHERNET_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief LPC18xx Ethernet MAC interrupt service routine
+ **/
+
+void ETHERNET_IRQHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read DMA status register
+   status = LPC_ETHERNET->DMA_STAT;
+
+   //A packet has been transmitted?
+   if(status & ETHERNET_DMA_STAT_TI_Msk)
+   {
+      //Clear TI interrupt flag
+      LPC_ETHERNET->DMA_STAT = ETHERNET_DMA_STAT_TI_Msk;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & ETHERNET_DMA_STAT_RI_Msk)
+   {
+      //Disable RIE interrupt
+      LPC_ETHERNET->DMA_INT_EN &= ~ETHERNET_DMA_INT_EN_RIE_Msk;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Clear NIS interrupt flag
+   LPC_ETHERNET->DMA_STAT = ETHERNET_DMA_STAT_NIS_Msk;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief LPC18xx Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc18xxEthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(LPC_ETHERNET->DMA_STAT & ETHERNET_DMA_STAT_RI_Msk)
+   {
+      //Clear interrupt flag
+      LPC_ETHERNET->DMA_STAT = ETHERNET_DMA_STAT_RI_Msk;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = lpc18xxEthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable DMA interrupts
+   LPC_ETHERNET->DMA_INT_EN |= ETHERNET_DMA_INT_EN_NIE_Msk |
+      ETHERNET_DMA_INT_EN_RIE_Msk | ETHERNET_DMA_INT_EN_TIE_Msk;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t lpc18xxEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > LPC18XX_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurDmaDesc->tdes0 & ETH_TDES0_OWN)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txCurDmaDesc->tdes2, buffer, offset, length);
+
+   //Write the number of bytes to send
+   txCurDmaDesc->tdes1 = length & ETH_TDES1_TBS1;
+   //Set LS and FS flags as the data fits in a single buffer
+   txCurDmaDesc->tdes0 |= ETH_TDES0_LS | ETH_TDES0_FS;
+   //Give the ownership of the descriptor to the DMA
+   txCurDmaDesc->tdes0 |= ETH_TDES0_OWN;
+
+   //Clear TU flag to resume processing
+   LPC_ETHERNET->DMA_STAT = ETHERNET_DMA_STAT_TU_Msk;
+   //Instruct the DMA to poll the transmit descriptor list
+   LPC_ETHERNET->DMA_TRANS_POLL_DEMAND = 0;
+
+   //Point to the next descriptor in the list
+   txCurDmaDesc = (Lpc18xxTxDmaDesc *) txCurDmaDesc->tdes3;
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc18xxEthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_OWN))
+   {
+      //FS and LS flags should be set
+      if((rxCurDmaDesc->rdes0 & ETH_RDES0_FS) && (rxCurDmaDesc->rdes0 & ETH_RDES0_LS))
+      {
+         //Make sure no error occurred
+         if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_ES))
+         {
+            //Retrieve the length of the frame
+            n = (rxCurDmaDesc->rdes0 & ETH_RDES0_FL) >> 16;
+            //Limit the number of data to read
+            n = MIN(n, LPC18XX_ETH_RX_BUFFER_SIZE);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, (uint8_t *) rxCurDmaDesc->rdes2, n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Give the ownership of the descriptor back to the DMA
+      rxCurDmaDesc->rdes0 = ETH_RDES0_OWN;
+      //Point to the next descriptor in the list
+      rxCurDmaDesc = (Lpc18xxRxDmaDesc *) rxCurDmaDesc->rdes3;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Clear RU flag to resume processing
+   LPC_ETHERNET->DMA_STAT = ETHERNET_DMA_STAT_RU_Msk;
+   //Instruct the DMA to poll the receive descriptor list
+   LPC_ETHERNET->DMA_REC_POLL_DEMAND = 0;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc18xxEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating LPC18xx hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = lpc18xxEthCalcCrc(&entry->addr, sizeof(MacAddr));
+
+         //The upper 6 bits in the CRC register are used to index the
+         //contents of the hash table
+         k = (crc >> 26) & 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   LPC_ETHERNET->MAC_HASHTABLE_LOW = hashTable[0];
+   LPC_ETHERNET->MAC_HASHTABLE_HIGH = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  MAC_HASHTABLE_LOW = %08" PRIX32 "\r\n", LPC_ETHERNET->MAC_HASHTABLE_LOW);
+   TRACE_DEBUG("  MAC_HASHTABLE_HIGH = %08" PRIX32 "\r\n", LPC_ETHERNET->MAC_HASHTABLE_HIGH);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc18xxEthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read current MAC configuration
+   config = LPC_ETHERNET->MAC_CONFIG;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= ETHERNET_MAC_CONFIG_FES_Msk;
+   else
+      config &= ~ETHERNET_MAC_CONFIG_FES_Msk;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= ETHERNET_MAC_CONFIG_DM_Msk;
+   else
+      config &= ~ETHERNET_MAC_CONFIG_DM_Msk;
+
+   //Update MAC configuration register
+   LPC_ETHERNET->MAC_CONFIG = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void lpc18xxEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = LPC_ETHERNET->MAC_MII_ADDR & ETHERNET_MAC_MII_ADDR_CR_Msk;
+   //Set up a write operation
+   value |= ETHERNET_MAC_MII_ADDR_W_Msk | ETHERNET_MAC_MII_ADDR_GB_Msk;
+   //PHY address
+   value |= (phyAddr << ETHERNET_MAC_MII_ADDR_PA_Pos) & ETHERNET_MAC_MII_ADDR_PA_Msk;
+   //Register address
+   value |= (regAddr << ETHERNET_MAC_MII_ADDR_GR_Pos) & ETHERNET_MAC_MII_ADDR_GR_Msk;
+
+   //Data to be written in the PHY register
+   LPC_ETHERNET->MAC_MII_DATA = data & ETHERNET_MAC_MII_DATA_GD_Msk;
+
+   //Start a write operation
+   LPC_ETHERNET->MAC_MII_ADDR = value;
+   //Wait for the write to complete
+   while(LPC_ETHERNET->MAC_MII_ADDR & ETHERNET_MAC_MII_ADDR_GB_Msk);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t lpc18xxEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = LPC_ETHERNET->MAC_MII_ADDR & ETHERNET_MAC_MII_ADDR_CR_Msk;
+   //Set up a read operation
+   value |= ETHERNET_MAC_MII_ADDR_GB_Msk;
+   //PHY address
+   value |= (phyAddr << ETHERNET_MAC_MII_ADDR_PA_Pos) & ETHERNET_MAC_MII_ADDR_PA_Msk;
+   //Register address
+   value |= (regAddr << ETHERNET_MAC_MII_ADDR_GR_Pos) & ETHERNET_MAC_MII_ADDR_GR_Msk;
+
+   //Start a read operation
+   LPC_ETHERNET->MAC_MII_ADDR = value;
+   //Wait for the read to complete
+   while(LPC_ETHERNET->MAC_MII_ADDR & ETHERNET_MAC_MII_ADDR_GB_Msk);
+
+   //Return PHY register contents
+   return LPC_ETHERNET->MAC_MII_DATA & ETHERNET_MAC_MII_DATA_GD_Msk;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t lpc18xxEthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return ~crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lpc18xx_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,260 @@
+/**
+ * @file lpc18xx_eth.h
+ * @brief LPC1800 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _LPC18XX_ETH_H
+#define _LPC18XX_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef LPC18XX_ETH_TX_BUFFER_COUNT
+   #define LPC18XX_ETH_TX_BUFFER_COUNT 3
+#elif (LPC18XX_ETH_TX_BUFFER_COUNT < 1)
+   #error LPC18XX_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef LPC18XX_ETH_TX_BUFFER_SIZE
+   #define LPC18XX_ETH_TX_BUFFER_SIZE 1536
+#elif (LPC18XX_ETH_TX_BUFFER_SIZE != 1536)
+   #error LPC18XX_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef LPC18XX_ETH_RX_BUFFER_COUNT
+   #define LPC18XX_ETH_RX_BUFFER_COUNT 6
+#elif (LPC18XX_ETH_RX_BUFFER_COUNT < 1)
+   #error LPC18XX_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef LPC18XX_ETH_RX_BUFFER_SIZE
+   #define LPC18XX_ETH_RX_BUFFER_SIZE 1536
+#elif (LPC18XX_ETH_RX_BUFFER_SIZE != 1536)
+   #error LPC18XX_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef LPC18XX_ETH_IRQ_PRIORITY_GROUPING
+   #define LPC18XX_ETH_IRQ_PRIORITY_GROUPING 4
+#elif (LPC18XX_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error LPC18XX_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef LPC18XX_ETH_IRQ_GROUP_PRIORITY
+   #define LPC18XX_ETH_IRQ_GROUP_PRIORITY 6
+#elif (LPC18XX_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error LPC18XX_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef LPC18XX_ETH_IRQ_SUB_PRIORITY
+   #define LPC18XX_ETH_IRQ_SUB_PRIORITY 0
+#elif (LPC18XX_ETH_IRQ_SUB_PRIORITY < 0)
+   #error LPC18XX_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//CREG6 register
+#define CREG6_ETHMODE_MII               (0 << CREG_CREG6_ETHMODE_Pos)
+#define CREG6_ETHMODE_RMII              (4 << CREG_CREG6_ETHMODE_Pos)
+
+//MAC_MII_ADDR register
+#define ETHERNET_MAC_MII_ADDR_CR_DIV42  (0 << ETHERNET_MAC_MII_ADDR_CR_Pos)
+#define ETHERNET_MAC_MII_ADDR_CR_DIV62  (1 << ETHERNET_MAC_MII_ADDR_CR_Pos)
+#define ETHERNET_MAC_MII_ADDR_CR_DIV16  (2 << ETHERNET_MAC_MII_ADDR_CR_Pos)
+#define ETHERNET_MAC_MII_ADDR_CR_DIV26  (3 << ETHERNET_MAC_MII_ADDR_CR_Pos)
+#define ETHERNET_MAC_MII_ADDR_CR_DIV102 (4 << ETHERNET_MAC_MII_ADDR_CR_Pos)
+#define ETHERNET_MAC_MII_ADDR_CR_DIV124 (5 << ETHERNET_MAC_MII_ADDR_CR_Pos)
+
+//DMA_BUS_MODE register
+#define ETHERNET_DMA_BUS_MODE_RPBL_1    (1 << ETHERNET_DMA_BUS_MODE_RPBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_RPBL_2    (2 << ETHERNET_DMA_BUS_MODE_RPBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_RPBL_4    (4 << ETHERNET_DMA_BUS_MODE_RPBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_RPBL_8    (8 << ETHERNET_DMA_BUS_MODE_RPBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_RPBL_16   (16 << ETHERNET_DMA_BUS_MODE_RPBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_RPBL_32   (32 << ETHERNET_DMA_BUS_MODE_RPBL_Pos)
+
+#define ETHERNET_DMA_BUS_MODE_PR_1_1    (0 << ETHERNET_DMA_BUS_MODE_PR_Pos)
+#define ETHERNET_DMA_BUS_MODE_PR_2_1    (1 << ETHERNET_DMA_BUS_MODE_PR_Pos)
+#define ETHERNET_DMA_BUS_MODE_PR_3_1    (2 << ETHERNET_DMA_BUS_MODE_PR_Pos)
+#define ETHERNET_DMA_BUS_MODE_PR_4_1    (3 << ETHERNET_DMA_BUS_MODE_PR_Pos)
+
+#define ETHERNET_DMA_BUS_MODE_PBL_1     (1 << ETHERNET_DMA_BUS_MODE_PBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_PBL_2     (2 << ETHERNET_DMA_BUS_MODE_PBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_PBL_4     (4 << ETHERNET_DMA_BUS_MODE_PBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_PBL_8     (8 << ETHERNET_DMA_BUS_MODE_PBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_PBL_16    (16 << ETHERNET_DMA_BUS_MODE_PBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_PBL_32    (32 << ETHERNET_DMA_BUS_MODE_PBL_Pos)
+
+//DMA_OP_MODE register
+#define ETHERNET_DMA_OP_MODE_TTC_64     (0 << ETHERNET_DMA_OP_MODE_TTC_Pos)
+#define ETHERNET_DMA_OP_MODE_TTC_128    (1 << ETHERNET_DMA_OP_MODE_TTC_Pos)
+#define ETHERNET_DMA_OP_MODE_TTC_192    (2 << ETHERNET_DMA_OP_MODE_TTC_Pos)
+#define ETHERNET_DMA_OP_MODE_TTC_256    (3 << ETHERNET_DMA_OP_MODE_TTC_Pos)
+#define ETHERNET_DMA_OP_MODE_TTC_40     (4 << ETHERNET_DMA_OP_MODE_TTC_Pos)
+#define ETHERNET_DMA_OP_MODE_TTC_32     (5 << ETHERNET_DMA_OP_MODE_TTC_Pos)
+#define ETHERNET_DMA_OP_MODE_TTC_24     (6 << ETHERNET_DMA_OP_MODE_TTC_Pos)
+#define ETHERNET_DMA_OP_MODE_TTC_16     (7 << ETHERNET_DMA_OP_MODE_TTC_Pos)
+
+#define ETHERNET_DMA_OP_MODE_RTC_64     (0 << ETHERNET_DMA_OP_MODE_RTC_Pos)
+#define ETHERNET_DMA_OP_MODE_RTC_32     (1 << ETHERNET_DMA_OP_MODE_RTC_Pos)
+#define ETHERNET_DMA_OP_MODE_RTC_96     (2 << ETHERNET_DMA_OP_MODE_RTC_Pos)
+#define ETHERNET_DMA_OP_MODE_RTC_128    (3 << ETHERNET_DMA_OP_MODE_RTC_Pos)
+
+//Transmit DMA descriptor flags
+#define ETH_TDES0_OWN        0x80000000
+#define ETH_TDES0_IC         0x40000000
+#define ETH_TDES0_LS         0x20000000
+#define ETH_TDES0_FS         0x10000000
+#define ETH_TDES0_DC         0x08000000
+#define ETH_TDES0_DP         0x04000000
+#define ETH_TDES0_TTSE       0x02000000
+#define ETH_TDES0_TER        0x00200000
+#define ETH_TDES0_TCH        0x00100000
+#define ETH_TDES0_TTSS       0x00020000
+#define ETH_TDES0_IHE        0x00010000
+#define ETH_TDES0_ES         0x00008000
+#define ETH_TDES0_JT         0x00004000
+#define ETH_TDES0_FF         0x00002000
+#define ETH_TDES0_IPE        0x00001000
+#define ETH_TDES0_LCA        0x00000800
+#define ETH_TDES0_NC         0x00000400
+#define ETH_TDES0_LCO        0x00000200
+#define ETH_TDES0_EC         0x00000100
+#define ETH_TDES0_VF         0x00000080
+#define ETH_TDES0_CC         0x00000078
+#define ETH_TDES0_ED         0x00000004
+#define ETH_TDES0_UF         0x00000002
+#define ETH_TDES0_DB         0x00000001
+#define ETH_TDES1_TBS2       0x1FFF0000
+#define ETH_TDES1_TBS1       0x00001FFF
+#define ETH_TDES2_B1ADD      0xFFFFFFFF
+#define ETH_TDES3_B2ADD      0xFFFFFFFF
+#define ETH_TDES6_TTSL       0xFFFFFFFF
+#define ETH_TDES7_TTSH       0xFFFFFFFF
+
+//Receive DMA descriptor flags
+#define ETH_RDES0_OWN        0x80000000
+#define ETH_RDES0_AFM        0x40000000
+#define ETH_RDES0_FL         0x3FFF0000
+#define ETH_RDES0_ES         0x00008000
+#define ETH_RDES0_DE         0x00004000
+#define ETH_RDES0_SAF        0x00002000
+#define ETH_RDES0_LE         0x00001000
+#define ETH_RDES0_OE         0x00000800
+#define ETH_RDES0_VLAN       0x00000400
+#define ETH_RDES0_FS         0x00000200
+#define ETH_RDES0_LS         0x00000100
+#define ETH_RDES0_TSA        0x00000080
+#define ETH_RDES0_LCO        0x00000040
+#define ETH_RDES0_FT         0x00000020
+#define ETH_RDES0_RWT        0x00000010
+#define ETH_RDES0_RE         0x00000008
+#define ETH_RDES0_DBE        0x00000004
+#define ETH_RDES0_CE         0x00000002
+#define ETH_RDES0_ESA        0x00000001
+#define ETH_RDES1_RBS2       0x1FFF0000
+#define ETH_RDES1_RER        0x00008000
+#define ETH_RDES1_RCH        0x00004000
+#define ETH_RDES1_RBS1       0x00001FFF
+#define ETH_RDES2_B1ADD      0xFFFFFFFF
+#define ETH_RDES3_B2ADD      0xFFFFFFFF
+#define ETH_RDES4_PTPVERSION 0x00002000
+#define ETH_RDES4_PTPTYPE    0x00001000
+#define ETH_RDES4_MT         0x00000F00
+#define ETH_RDES4_IPV6       0x00000080
+#define ETH_RDES4_IPV4       0x00000040
+#define ETH_RDES6_RTSL       0xFFFFFFFF
+#define ETH_RDES7_RTSH       0xFFFFFFFF
+
+
+/**
+ * @brief Enhanced TX DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t tdes0;
+   uint32_t tdes1;
+   uint32_t tdes2;
+   uint32_t tdes3;
+   uint32_t tdes4;
+   uint32_t tdes5;
+   uint32_t tdes6;
+   uint32_t tdes7;
+} Lpc18xxTxDmaDesc;
+
+
+/**
+ * @brief Enhanced RX DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t rdes0;
+   uint32_t rdes1;
+   uint32_t rdes2;
+   uint32_t rdes3;
+   uint32_t rdes4;
+   uint32_t rdes5;
+   uint32_t rdes6;
+   uint32_t rdes7;
+} Lpc18xxRxDmaDesc;
+
+
+//LPC18xx Ethernet MAC driver
+extern const NicDriver lpc18xxEthDriver;
+
+//LPC18xx Ethernet MAC related functions
+error_t lpc18xxEthInit(NetInterface *interface);
+void lpc18xxEthInitGpio(NetInterface *interface);
+void lpc18xxEthInitDmaDesc(NetInterface *interface);
+
+void lpc18xxEthTick(NetInterface *interface);
+
+void lpc18xxEthEnableIrq(NetInterface *interface);
+void lpc18xxEthDisableIrq(NetInterface *interface);
+void lpc18xxEthEventHandler(NetInterface *interface);
+
+error_t lpc18xxEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t lpc18xxEthReceivePacket(NetInterface *interface);
+
+error_t lpc18xxEthSetMulticastFilter(NetInterface *interface);
+error_t lpc18xxEthUpdateMacConfig(NetInterface *interface);
+
+void lpc18xxEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t lpc18xxEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t lpc18xxEthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lpc23xx_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,714 @@
+/**
+ * @file lpc23xx_eth.c
+ * @brief LPC2300 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "lpc23xx.h"
+#include "core/net.h"
+#include "drivers/lpc23xx_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+static uint8_t txBuffer[LPC23XX_ETH_TX_BUFFER_COUNT][LPC23XX_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+static uint8_t rxBuffer[LPC23XX_ETH_RX_BUFFER_COUNT][LPC23XX_ETH_RX_BUFFER_SIZE];
+//Transmit descriptors
+#pragma data_alignment = 4
+static Lpc23xxTxDesc txDesc[LPC23XX_ETH_TX_BUFFER_COUNT];
+//Transmit status array
+#pragma data_alignment = 4
+static Lpc23xxTxStatus txStatus[LPC23XX_ETH_TX_BUFFER_COUNT];
+//Receive descriptors
+#pragma data_alignment = 4
+static Lpc23xxRxDesc rxDesc[LPC23XX_ETH_RX_BUFFER_COUNT];
+//Receive status array
+#pragma data_alignment = 8
+static Lpc23xxRxStatus rxStatus[LPC23XX_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[LPC23XX_ETH_TX_BUFFER_COUNT][LPC23XX_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[LPC23XX_ETH_RX_BUFFER_COUNT][LPC23XX_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Transmit descriptors
+static Lpc23xxTxDesc txDesc[LPC23XX_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Transmit status array
+static Lpc23xxTxStatus txStatus[LPC23XX_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive descriptors
+static Lpc23xxRxDesc rxDesc[LPC23XX_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive status array
+static Lpc23xxRxStatus rxStatus[LPC23XX_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(8)));
+
+#endif
+
+
+/**
+ * @brief LPC23xx Ethernet MAC driver
+ **/
+
+const NicDriver lpc23xxEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   lpc23xxEthInit,
+   lpc23xxEthTick,
+   lpc23xxEthEnableIrq,
+   lpc23xxEthDisableIrq,
+   lpc23xxEthEventHandler,
+   lpc23xxEthSendPacket,
+   lpc23xxEthSetMulticastFilter,
+   lpc23xxEthUpdateMacConfig,
+   lpc23xxEthWritePhyReg,
+   lpc23xxEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief LPC23xx Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc23xxEthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing LPC23xx Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Power up EMAC controller
+   PCONP |= PCONP_PCENET;
+
+   //GPIO configuration
+   lpc23xxEthInitGpio(interface);
+
+   //Reset host registers, transmit datapath and receive datapath
+   MAC_COMMAND = COMMAND_RX_RESET | COMMAND_TX_RESET | COMMAND_REG_RESET;
+
+   //Reset EMAC controller
+   MAC_MAC1 = MAC1_SOFT_RESET | MAC1_SIMULATION_RESET |
+      MAC1_RESET_MCS_RX | MAC1_RESET_RX | MAC1_RESET_MCS_TX | MAC1_RESET_TX;
+
+   //Initialize MAC related registers
+   MAC_MAC1 = 0;
+   MAC_MAC2 = MAC2_PAD_CRC_ENABLE | MAC2_CRC_ENABLE;
+   MAC_IPGR = IPGR_DEFAULT_VALUE;
+   MAC_CLRT = CLRT_DEFAULT_VALUE;
+
+   //Select RMII mode
+   MAC_COMMAND = COMMAND_RMII;
+
+   //Configure MDC clock
+   MAC_MCFG = MCFG_CLOCK_SELECT_DIV28;
+   //Reset MII management interface
+   MAC_MCFG |= MCFG_RESET_MII_MGMT;
+   MAC_MCFG &= ~MCFG_RESET_MII_MGMT;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Initialize TX and RX descriptor arrays
+   lpc23xxEthInitDesc(interface);
+
+   //Set the MAC address
+   MAC_SA0 = interface->macAddr.w[2];
+   MAC_SA1 = interface->macAddr.w[1];
+   MAC_SA2 = interface->macAddr.w[0];
+
+   //Initialize hash table
+   MAC_HASHFILTERL = 0;
+   MAC_HASHFILTERH = 0;
+
+   //Configure the receive filter
+   MAC_RXFILTERCTRL = RFC_ACCEPT_PERFECT_EN |
+      RFC_ACCEPT_MULTICAST_HASH_EN | RFC_ACCEPT_BROADCAST_EN;
+
+   //Program the MAXF register with the maximum frame length to be accepted
+   MAC_MAXF = 1518;
+
+   //Reset EMAC interrupt flags
+   MAC_INTCLEAR  = 0xFFFF;
+   //Enable desired EMAC interrupts
+   MAC_INTENABLE = INT_TX_DONE | INT_RX_DONE;
+
+   //The interrupt request is assigned to the IRQ category
+   VICIntSelect &= ~VIC_INT_ETHERNET;
+   //Register interrupt handler
+   VICVectAddr21 = (uint32_t) lpc23xxEthIrqHandler;
+   //Configure interrupt priority
+   VICVectPriority21 = LPC23XX_ETH_IRQ_PRIORITY;
+
+   //Enable transmission and reception
+   MAC_COMMAND |= COMMAND_TX_ENABLE | COMMAND_RX_ENABLE;
+   //Allow frames to be received
+   MAC_MAC1 |= MAC1_RECEIVE_ENABLE;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//LPC2378-STK evaluation board?
+#if defined(USE_LPC2378_STK)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc23xxEthInitGpio(NetInterface *interface)
+{
+   //Configure P1.0 (ENET_TXD0), P1.1 (ENET_TXD1), P1.4 (ENET_TX_EN), P1.8 (ENET_CRS),
+   //P1.9 (ENET_RXD0), P1.10 (ENET_RXD1), P1.14 (RX_ER) and P1.15 (ENET_REF_CLK)
+   PINSEL2 &= ~(PINSEL2_P1_0_MASK | PINSEL2_P1_1_MASK |
+      PINSEL2_P1_4_MASK | PINSEL2_P1_8_MASK | PINSEL2_P1_9_MASK |
+      PINSEL2_P1_10_MASK | PINSEL2_P1_14_MASK | PINSEL2_P1_15_MASK);
+
+   PINSEL2 |= PINSEL2_P1_0_ENET_TXD0 | PINSEL2_P1_1_ENET_TXD1 |
+      PINSEL2_P1_4_ENET_TX_EN | PINSEL2_P1_8_ENET_CRS | PINSEL2_P1_9_ENET_RXD0 |
+      PINSEL2_P1_10_ENET_RXD1 | PINSEL2_P1_14_ENET_RX_ER | PINSEL2_P1_15_ENET_REF_CLK;
+
+   //Configure P1.16 (ENET_MDC) and P1.17 (ENET_MDIO)
+   PINSEL3 &= ~(PINSEL3_P1_16_MASK | PINSEL3_P1_17_MASK);
+   PINSEL3 |= PINSEL3_P1_16_ENET_MDC | PINSEL3_P1_17_ENET_MDIO;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize TX and RX descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc23xxEthInitDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX descriptors
+   for(i = 0; i < LPC23XX_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Base address of the buffer containing transmit data
+      txDesc[i].packet = (uint32_t) txBuffer[i];
+      //Transmit descriptor control word
+      txDesc[i].control = 0;
+      //Transmit status information word
+      txStatus[i].info = 0;
+   }
+
+   //Initialize RX descriptors
+   for(i = 0; i < LPC23XX_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Base address of the buffer for storing receive data
+      rxDesc[i].packet = (uint32_t) rxBuffer[i];
+      //Receive descriptor control word
+      rxDesc[i].control = RX_CTRL_INTERRUPT | (LPC23XX_ETH_RX_BUFFER_SIZE - 1);
+      //Receive status information word
+      rxStatus[i].info = 0;
+      //Receive status HashCRC word
+      rxStatus[i].hashCrc = 0;
+   }
+
+   //Initialize EMAC transmit descriptor registers
+   MAC_TXDESCRIPTOR = (uint32_t) txDesc;
+   MAC_TXSTATUS = (uint32_t) txStatus;
+   MAC_TXDESCRIPTORNUM = LPC23XX_ETH_TX_BUFFER_COUNT - 1;
+   MAC_TXPRODUCEINDEX = 0;
+
+   //Initialize EMAC receive descriptor registers
+   MAC_RXDESCRIPTOR = (uint32_t) rxDesc;
+   MAC_RXSTATUS = (uint32_t) rxStatus;
+   MAC_RXDESCRIPTORNUM = LPC23XX_ETH_RX_BUFFER_COUNT - 1;
+   MAC_RXCONSUMEINDEX = 0;
+}
+
+
+/**
+ * @brief LPC23xx Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc23xxEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc23xxEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   VICIntEnable = VIC_INT_ETHERNET;
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc23xxEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   VICIntEnClr = VIC_INT_ETHERNET;
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief LPC23xx Ethernet MAC interrupt service routine
+ **/
+
+__irq void lpc23xxEthIrqHandler(void)
+{
+   uint_t i;
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read interrupt status register
+   status = MAC_INTSTATUS;
+
+   //A packet has been transmitted?
+   if(status & INT_TX_DONE)
+   {
+      //Clear TxDone interrupt flag
+      MAC_INTCLEAR = INT_TX_DONE;
+
+      //Get the index of the next descriptor
+      i = MAC_TXPRODUCEINDEX + 1;
+
+      //Wrap around if necessary
+      if(i >= LPC23XX_ETH_TX_BUFFER_COUNT)
+         i = 0;
+
+      //Check whether the TX buffer is available for writing
+      if(i != MAC_TXCONSUMEINDEX)
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & INT_RX_DONE)
+   {
+      //Disable RxDone interrupts
+      MAC_INTENABLE &= ~INT_RX_DONE;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief LPC23xx Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc23xxEthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(MAC_INTSTATUS & INT_RX_DONE)
+   {
+      //Clear RxDone interrupt flag
+      MAC_INTCLEAR = INT_RX_DONE;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = lpc23xxEthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable TxDone and RxDone interrupts
+   MAC_INTENABLE = INT_TX_DONE | INT_RX_DONE;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t lpc23xxEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   uint_t i;
+   uint_t j;
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(!length)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //We are done since the buffer is empty
+      return NO_ERROR;
+   }
+   else if(length > LPC23XX_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Get the index of the current descriptor
+   i = MAC_TXPRODUCEINDEX;
+   //Get the index of the next descriptor
+   j = i + 1;
+
+   //Wrap around if necessary
+   if(j >= LPC23XX_ETH_TX_BUFFER_COUNT)
+      j = 0;
+
+   //Check whether the transmit descriptor array is full
+   if(j == MAC_TXCONSUMEINDEX)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txDesc[i].packet, buffer, offset, length);
+
+   //Write the transmit control word
+   txDesc[i].control = TX_CTRL_INTERRUPT | TX_CTRL_LAST |
+      TX_CTRL_CRC | TX_CTRL_PAD | ((length - 1) & TX_CTRL_SIZE);
+
+   //Increment index and wrap around if necessary
+   if(++i >= LPC23XX_ETH_TX_BUFFER_COUNT)
+      i = 0;
+
+   //Save the resulting value
+   MAC_TXPRODUCEINDEX = i;
+
+   //Get the index of the next descriptor
+   j = i + 1;
+
+   //Wrap around if necessary
+   if(j >= LPC23XX_ETH_TX_BUFFER_COUNT)
+      j = 0;
+
+   //Check whether the next buffer is available for writing
+   if(j != MAC_TXCONSUMEINDEX)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful write operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc23xxEthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+   uint_t i;
+
+   //Point to the current descriptor
+   i = MAC_RXCONSUMEINDEX;
+
+   //Make sure the current buffer is available for reading
+   if(i != MAC_RXPRODUCEINDEX)
+   {
+      //Retrieve the length of the frame
+      n = (rxStatus[i].info & RX_STATUS_SIZE) + 1;
+      //Limit the number of data to read
+      n = MIN(n, LPC23XX_ETH_RX_BUFFER_SIZE);
+
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, (uint8_t *) rxDesc[i].packet, n);
+
+      //Increment index and wrap around if necessary
+      if(++i >= LPC23XX_ETH_RX_BUFFER_COUNT)
+         i = 0;
+
+      //Save the resulting value
+      MAC_RXCONSUMEINDEX = i;
+
+      //Valid packet received
+      error = NO_ERROR;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc23xxEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating LPC23xx hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = lpc23xxEthCalcCrc(&entry->addr, sizeof(MacAddr));
+         //Bits [28:23] are used to form the hash
+         k = (crc >> 23) & 0x3F;
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   MAC_HASHFILTERL = hashTable[0];
+   MAC_HASHFILTERH = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HashFilterL = %08" PRIX32 "\r\n", MAC_HASHFILTERL);
+   TRACE_DEBUG("  HashFilterH = %08" PRIX32 "\r\n", MAC_HASHFILTERH);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc23xxEthUpdateMacConfig(NetInterface *interface)
+{
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      MAC_SUPP = SUPP_SPEED;
+   else
+      MAC_SUPP = 0;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+   {
+      //The MAC operates in full-duplex mode
+      MAC_MAC2 |= MAC2_FULL_DUPLEX;
+      MAC_COMMAND |= COMMAND_FULL_DUPLEX;
+      //Configure Back-to-Back Inter-Packet Gap
+      MAC_IPGT = IPGT_FULL_DUPLEX;
+   }
+   else
+   {
+      //The MAC operates in half-duplex mode
+      MAC_MAC2 &= ~MAC2_FULL_DUPLEX;
+      MAC_COMMAND &= ~COMMAND_FULL_DUPLEX;
+      //Configure Back-to-Back Inter-Packet Gap
+      MAC_IPGT = IPGT_HALF_DUPLEX;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void lpc23xxEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   //Clear MCMD register
+   MAC_MCMD = 0;
+
+   //PHY address
+   MAC_MADR = (phyAddr << 8) & MADR_PHY_ADDRESS;
+   //Register address
+   MAC_MADR |= regAddr & MADR_REGISTER_ADDRESS;
+   //Data to be written in the PHY register
+   MAC_MWTD = data & MWTD_WRITE_DATA;
+
+   //Wait for the write to complete
+   while(MAC_MIND & MIND_BUSY);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t lpc23xxEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   //PHY address
+   MAC_MADR = (phyAddr << 8) & MADR_PHY_ADDRESS;
+   //Register address
+   MAC_MADR |= regAddr & MADR_REGISTER_ADDRESS;
+
+   //Start a read operation
+   MAC_MCMD = MCMD_READ;
+   //Wait for the read to complete
+   while(MAC_MIND & MIND_BUSY);
+   //Clear MCMD register
+   MAC_MCMD = 0;
+
+   //Return PHY register contents
+   return MAC_MRDD & MRDD_READ_DATA;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t lpc23xxEthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lpc23xx_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,371 @@
+/**
+ * @file lpc23xx_eth.h
+ * @brief LPC2300 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _LPC23XX_ETH_H
+#define _LPC23XX_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef LPC23XX_ETH_TX_BUFFER_COUNT
+   #define LPC23XX_ETH_TX_BUFFER_COUNT 2
+#elif (LPC23XX_ETH_TX_BUFFER_COUNT < 1)
+   #error LPC23XX_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef LPC23XX_ETH_TX_BUFFER_SIZE
+   #define LPC23XX_ETH_TX_BUFFER_SIZE 1536
+#elif (LPC23XX_ETH_TX_BUFFER_SIZE != 1536)
+   #error LPC23XX_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef LPC23XX_ETH_RX_BUFFER_COUNT
+   #define LPC23XX_ETH_RX_BUFFER_COUNT 4
+#elif (LPC23XX_ETH_RX_BUFFER_COUNT < 1)
+   #error LPC23XX_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef LPC23XX_ETH_RX_BUFFER_SIZE
+   #define LPC23XX_ETH_RX_BUFFER_SIZE 1536
+#elif (LPC23XX_ETH_RX_BUFFER_SIZE != 1536)
+   #error LPC23XX_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Ethernet interrupt priority
+#ifndef LPC23XX_ETH_IRQ_PRIORITY
+   #define LPC23XX_ETH_IRQ_PRIORITY 15
+#elif (LPC23XX_ETH_IRQ_PRIORITY < 0)
+   #error LPC23XX_ETH_IRQ_PRIORITY parameter is not valid
+#endif
+
+//MAC1 register
+#define MAC1_SOFT_RESET                0x00008000
+#define MAC1_SIMULATION_RESET          0x00004000
+#define MAC1_RESET_MCS_RX              0x00000800
+#define MAC1_RESET_RX                  0x00000400
+#define MAC1_RESET_MCS_TX              0x00000200
+#define MAC1_RESET_TX                  0x00000100
+#define MAC1_LOOPBACK                  0x00000010
+#define MAC1_TX_FLOW_CONTROL           0x00000008
+#define MAC1_RX_FLOW_CONTROL           0x00000004
+#define MAC1_PASS_ALL_FRAMES           0x00000002
+#define MAC1_RECEIVE_ENABLE            0x00000001
+
+//MAC2 register
+#define MAC2_EXCESS_DEFER              0x00004000
+#define MAC2_BACK_PRESSURE_NO_BACKOFF  0x00002000
+#define MAC2_NO_BACKOFF                0x00001000
+#define MAC2_LONG_PREAMBLE_ENFORCEMENT 0x00000200
+#define MAC2_PURE_PREAMBLE_ENFORCEMENT 0x00000100
+#define MAC2_AUTO_DETECT_PAD_ENABLE    0x00000080
+#define MAC2_VLAN_PAD_ENABLE           0x00000040
+#define MAC2_PAD_CRC_ENABLE            0x00000020
+#define MAC2_CRC_ENABLE                0x00000010
+#define MAC2_DELAYED_CRC               0x00000008
+#define MAC2_HUGE_FRAME_ENABLE         0x00000004
+#define MAC2_FRAME_LENGTH_CHECKING     0x00000002
+#define MAC2_FULL_DUPLEX               0x00000001
+
+//IPGT register
+#define IPGT_BACK_TO_BACK_IPG          0x0000007F
+#define IPGT_HALF_DUPLEX               0x00000012
+#define IPGT_FULL_DUPLEX               0x00000015
+
+//IPGR register
+#define IPGR_NON_BACK_TO_BACK_IPG1     0x00007F00
+#define IPGR_NON_BACK_TO_BACK_IPG2     0x0000007F
+#define IPGR_DEFAULT_VALUE             0x00000C12
+
+//CLRT register
+#define CLRT_COLLISION_WINDOW          0x00003F00
+#define CLRT_RETRANSMISSION_MAXIMUM    0x00003F00
+#define CLRT_DEFAULT_VALUE             0x0000370F
+
+//MAXF register
+#define MAXF_MAXIMUM_FRAME_LENGTH      0x0000FFFF
+
+//SUPP register
+#define SUPP_SPEED                     0x00000100
+
+//TEST register
+#define TEST_BACKPRESSURE              0x00000004
+#define TEST_PAUSE                     0x00000002
+#define TEST_SHORTCUT_PAUSE_QUANTA     0x00000001
+
+//MCFG register
+#define MCFG_RESET_MII_MGMT            0x00008000
+#define MCFG_CLOCK SELECT              0x0000001C
+#define MCFG_SUPPRESS_PREAMBLE         0x00000002
+#define MCFG_SCAN_INCREMENT            0x00000001
+
+#define MCFG_CLOCK_SELECT_DIV4         0x00000000
+#define MCFG_CLOCK_SELECT_DIV6         0x00000008
+#define MCFG_CLOCK_SELECT_DIV8         0x0000000C
+#define MCFG_CLOCK_SELECT_DIV10        0x00000010
+#define MCFG_CLOCK_SELECT_DIV14        0x00000014
+#define MCFG_CLOCK_SELECT_DIV20        0x00000018
+#define MCFG_CLOCK_SELECT_DIV28        0x0000001C
+
+//MCMD register
+#define MCMD_SCAN                      0x00000002
+#define MCMD_READ                      0x00000001
+
+//MADR register
+#define MADR_PHY_ADDRESS               0x00001F00
+#define MADR_REGISTER_ADDRESS          0x0000001F
+
+//MWTD register
+#define MWTD_WRITE_DATA                0x0000FFFF
+
+//MRDD register
+#define MRDD_READ_DATA                 0x0000FFFF
+
+//MIND register
+#define MIND_MII_LINK_FAIL             0x00000008
+#define MIND_NOT_VALID                 0x00000004
+#define MIND_SCANNING                  0x00000002
+#define MIND_BUSY                      0x00000001
+
+//Command register
+#define COMMAND_FULL_DUPLEX            0x00000400
+#define COMMAND_RMII                   0x00000200
+#define COMMAND_TX_FLOW_CONTROL        0x00000100
+#define COMMAND_PASS_RX_FILTER         0x00000080
+#define COMMAND_PASS_RUNT_FRAME        0x00000040
+#define COMMAND_RX_RESET               0x00000020
+#define COMMAND_TX_RESET               0x00000010
+#define COMMAND_REG_RESET              0x00000008
+#define COMMAND_TX_ENABLE              0x00000002
+#define COMMAND_RX_ENABLE              0x00000001
+
+//Status register
+#define STATUS_TX                      0x00000002
+#define STATUS_RX                      0x00000001
+
+//TSV0 register
+#define TSV0_VLAN                      0x80000000
+#define TSV0_BACKPRESSURE              0x40000000
+#define TSV0_PAUSE                     0x20000000
+#define TSV0_CONTROL_FRAME             0x10000000
+#define TSV0_TOTAL_BYTES               0x0FFFF000
+#define TSV0_UNDERRUN                  0x00000800
+#define TSV0_GIANT                     0x00000400
+#define TSV0_LATE_COLLISION            0x00000200
+#define TSV0_EXCESSIVE_COLLISION       0x00000100
+#define TSV0_EXCESSIVE_DEFER           0x00000080
+#define TSV0_PACKET_DEFER              0x00000040
+#define TSV0_BROADCAST                 0x00000020
+#define TSV0_MULTICAST                 0x00000010
+#define TSV0_DONE                      0x00000008
+#define TSV0_LENGTH_OUT_OF_RANGE       0x00000004
+#define TSV0_LENGTH_CHECK_ERROR        0x00000002
+#define TSV0_CRC_ERROR                 0x00000001
+
+//TSV1 register
+#define TSV1_TRANSMIT_COLLISION_COUNT  0x000F0000
+#define TSV1_TRANSMIT_BYTE_COUNT       0x0000FFFF
+
+//RSV register
+#define RSV_VLAN                       0x40000000
+#define RSV_UNSUPPORTED_OPCODE         0x20000000
+#define RSV_PAUSE                      0x10000000
+#define RSV_CONTROL_FRAME              0x08000000
+#define RSV_DRIBBLE_NIBBLE             0x04000000
+#define RSV_BROADCAST                  0x02000000
+#define RSV_MULTICAST                  0x01000000
+#define RSV_RECEIVE_OK                 0x00800000
+#define RSV_LENGTH_OUT_OF_RANGE        0x00400000
+#define RSV_LENGTH_CHECK_ERROR         0x00200000
+#define RSV_CRC_ERROR                  0x00100000
+#define RSV_RECEIVE_CODE_VIOLATION     0x00080000
+#define RSV_CARRIER_EVENT_PREV_SEEN    0x00040000
+#define RSV_RXDV_EVENT_PREV_SEEN       0x00020000
+#define RSV_PACKET_PREVIOUSLY_IGNORED  0x00010000
+#define RSV_RECEIVED_BYTE_COUNT        0x0000FFFF
+
+//FlowControlCounter register
+#define FCC_PAUSE_TIMER                0xFFFF0000
+#define FCC_MIRROR_COUNTER             0x0000FFFF
+
+//FlowControlStatus register
+#define FCS_MIRROR_COUNTER_CURRENT     0x0000FFFF
+
+//RxFilterCtrl register
+#define RFC_RX_FILTER_EN_WOL           0x00002000
+#define RFC_MAGIC_PACKET_EN_WOL        0x00001000
+#define RFC_ACCEPT_PERFECT_EN          0x00000020
+#define RFC_ACCEPT_MULTICAST_HASH_EN   0x00000010
+#define RFC_ACCEPT_UNICAST_HASH_EN     0x00000008
+#define RFC_ACCEPT_MULTICAST_EN        0x00000004
+#define RFC_ACCEPT_BROADCAST_EN        0x00000002
+#define RFC_ACCEPT_UNICAST_EN          0x00000001
+
+//RxFilterWoLStatus and RxFilterWoLClear registers
+#define RFWS_MAGIC_PACKET_WOL          0x00000100
+#define RFWS_RX_FILTER_WOL             0x00000080
+#define RFWS_ACCEPT_PERFECT_WOL        0x00000020
+#define RFWS_ACCEPT_MULTICAST_HASH_WOL 0x00000010
+#define RFWS_ACCEPT_UNICAST_HASH_WOL   0x00000008
+#define RFWS_ACCEPT_MULTICAST_WOL      0x00000004
+#define RFWS_ACCEPT_BROADCAST_WOL      0x00000002
+#define RFWS_ACCEPT_UNICAST_WOL        0x00000001
+
+//IntStatus, IntEnable, IntClear and IntSet registers
+#define INT_WAKEUP                     0x00002000
+#define INT_SOFT_INT                   0x00001000
+#define INT_TX_DONE                    0x00000080
+#define INT_TX_FINISHED                0x00000040
+#define INT_TX_ERROR                   0x00000020
+#define INT_TX_UNDERRUN                0x00000010
+#define INT_RX_DONE                    0x00000008
+#define INT_RX_FINISHED                0x00000004
+#define INT_RX_ERROR                   0x00000002
+#define INT_RX_OVERRUN                 0x00000001
+
+//Transmit descriptor control word
+#define TX_CTRL_INTERRUPT              0x80000000
+#define TX_CTRL_LAST                   0x40000000
+#define TX_CTRL_CRC                    0x20000000
+#define TX_CTRL_PAD                    0x10000000
+#define TX_CTRL_HUGE                   0x08000000
+#define TX_CTRL_OVERRIDE               0x04000000
+#define TX_CTRL_SIZE                   0x000007FF
+
+//Transmit status information word
+#define TX_STATUS_ERROR                0x80000000
+#define TX_STATUS_NO_DESCRIPTOR        0x40000000
+#define TX_STATUS_UNDERRUN             0x20000000
+#define TX_STATUS_LATE_COLLISION       0x10000000
+#define TX_STATUS_EXCESSIVE_COLLISION  0x08000000
+#define TX_STATUS_EXCESSIVE_DEFER      0x04000000
+#define TX_STATUS_DEFER                0x02000000
+#define TX_STATUS_COLLISION_COUNT      0x01E00000
+
+//Receive descriptor control word
+#define RX_CTRL_INTERRUPT              0x80000000
+#define RX_CTRL_SIZE                   0x000007FF
+
+//Receive status information word
+#define RX_STATUS_ERROR                0x80000000
+#define RX_STATUS_LAST_FLAG            0x40000000
+#define RX_STATUS_NO_DESCRIPTOR        0x20000000
+#define RX_STATUS_OVERRUN              0x10000000
+#define RX_STATUS_ALIGNMENT_ERROR      0x08000000
+#define RX_STATUS_RANGE_ERROR          0x04000000
+#define RX_STATUS_LENGTH_ERROR         0x02000000
+#define RX_STATUS_SYMBOL_ERROR         0x01000000
+#define RX_STATUS_CRC_ERROR            0x00800000
+#define RX_STATUS_BROADCAST            0x00400000
+#define RX_STATUS_MULTICAST            0x00200000
+#define RX_STATUS_FAIL_FILTER          0x00100000
+#define RX_STATUS_VLAN                 0x00080000
+#define RX_STATUS_CONTROL_FRAME        0x00040000
+#define RX_STATUS_SIZE                 0x000007FF
+
+//Receive status HashCRC word
+#define RX_HASH_CRC_DA                 0x001FF000
+#define RX_HASH_CRC_SA                 0x000001FF
+
+
+/**
+ * @brief Transmit descriptor
+ **/
+
+typedef struct
+{
+   uint32_t packet;
+   uint32_t control;
+} Lpc23xxTxDesc;
+
+
+/**
+ * @brief Transmit status
+ **/
+
+typedef struct
+{
+   uint32_t info;
+} Lpc23xxTxStatus;
+
+
+/**
+ * @brief Receive descriptor
+ **/
+
+typedef struct
+{
+   uint32_t packet;
+   uint32_t control;
+} Lpc23xxRxDesc;
+
+
+/**
+ * @brief Receive status
+ **/
+
+typedef struct
+{
+   uint32_t info;
+   uint32_t hashCrc;
+} Lpc23xxRxStatus;
+
+
+//LPC23xx Ethernet MAC driver
+extern const NicDriver lpc23xxEthDriver;
+
+//LPC23xx Ethernet MAC related functions
+error_t lpc23xxEthInit(NetInterface *interface);
+void lpc23xxEthInitGpio(NetInterface *interface);
+void lpc23xxEthInitDesc(NetInterface *interface);
+
+void lpc23xxEthTick(NetInterface *interface);
+
+void lpc23xxEthEnableIrq(NetInterface *interface);
+void lpc23xxEthDisableIrq(NetInterface *interface);
+__irq void lpc23xxEthIrqHandler(void);
+void lpc23xxEthEventHandler(NetInterface *interface);
+
+error_t lpc23xxEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t lpc23xxEthReceivePacket(NetInterface *interface);
+
+error_t lpc23xxEthSetMulticastFilter(NetInterface *interface);
+error_t lpc23xxEthUpdateMacConfig(NetInterface *interface);
+
+void lpc23xxEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t lpc23xxEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t lpc23xxEthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lpc43xx_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,752 @@
+/**
+ * @file lpc43xx_eth.c
+ * @brief LPC4300 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "lpc43xx.h"
+#include "core/net.h"
+#include "drivers/lpc43xx_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+static uint8_t txBuffer[LPC43XX_ETH_TX_BUFFER_COUNT][LPC43XX_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+static uint8_t rxBuffer[LPC43XX_ETH_RX_BUFFER_COUNT][LPC43XX_ETH_RX_BUFFER_SIZE];
+//Transmit DMA descriptors
+#pragma data_alignment = 4
+static Lpc43xxTxDmaDesc txDmaDesc[LPC43XX_ETH_TX_BUFFER_COUNT];
+//Receive DMA descriptors
+#pragma data_alignment = 4
+static Lpc43xxRxDmaDesc rxDmaDesc[LPC43XX_ETH_RX_BUFFER_COUNT];
+
+//ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[LPC43XX_ETH_TX_BUFFER_COUNT][LPC43XX_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[LPC43XX_ETH_RX_BUFFER_COUNT][LPC43XX_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Transmit DMA descriptors
+static Lpc43xxTxDmaDesc txDmaDesc[LPC43XX_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive DMA descriptors
+static Lpc43xxRxDmaDesc rxDmaDesc[LPC43XX_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+
+#endif
+
+//Pointer to the current TX DMA descriptor
+static Lpc43xxTxDmaDesc *txCurDmaDesc;
+//Pointer to the current RX DMA descriptor
+static Lpc43xxRxDmaDesc *rxCurDmaDesc;
+
+
+/**
+ * @brief LPC43xx Ethernet MAC driver
+ **/
+
+const NicDriver lpc43xxEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   lpc43xxEthInit,
+   lpc43xxEthTick,
+   lpc43xxEthEnableIrq,
+   lpc43xxEthDisableIrq,
+   lpc43xxEthEventHandler,
+   lpc43xxEthSendPacket,
+   lpc43xxEthSetMulticastFilter,
+   lpc43xxEthUpdateMacConfig,
+   lpc43xxEthWritePhyReg,
+   lpc43xxEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief LPC43xx Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc43xxEthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing LPC43xx Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Enable Ethernet peripheral clock
+   LPC_CCU1->CLK_M4_ETHERNET_CFG |= CCU1_CLK_M4_ETHERNET_CFG_RUN_Msk;
+   while(!(LPC_CCU1->CLK_M4_ETHERNET_STAT & CCU1_CLK_M4_ETHERNET_STAT_RUN_Msk));
+
+   //Reset DMA
+   LPC_RGU->RESET_EXT_STAT19 |= RGU_RESET_EXT_STAT19_MASTER_RESET_Msk;
+   LPC_RGU->RESET_EXT_STAT19 &= ~RGU_RESET_EXT_STAT19_MASTER_RESET_Msk;
+
+   //Reset Ethernet peripheral
+   LPC_RGU->RESET_EXT_STAT22 |= RGU_RESET_EXT_STAT22_MASTER_RESET_Msk;
+   LPC_RGU->RESET_EXT_STAT22 &= ~RGU_RESET_EXT_STAT22_MASTER_RESET_Msk;
+
+   //GPIO configuration
+   lpc43xxEthInitGpio(interface);
+
+   //Reset Ethernet peripheral
+   LPC_RGU->RESET_CTRL0 = RGU_RESET_CTRL0_ETHERNET_RST_Msk;
+   while(!(LPC_RGU->RESET_ACTIVE_STATUS0 & RGU_RESET_ACTIVE_STATUS0_ETHERNET_RST_Msk));
+
+   //Perform a software reset
+   LPC_ETHERNET->DMA_BUS_MODE |= ETHERNET_DMA_BUS_MODE_SWR_Msk;
+   //Wait for the reset to complete
+   while(LPC_ETHERNET->DMA_BUS_MODE & ETHERNET_DMA_BUS_MODE_SWR_Msk);
+
+   //Adjust MDC clock range
+   LPC_ETHERNET->MAC_MII_ADDR = ETHERNET_MAC_MII_ADDR_CR_DIV62;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Use default MAC configuration
+   LPC_ETHERNET->MAC_CONFIG = ETHERNET_MAC_CONFIG_DO_Msk;
+
+   //Set the MAC address
+   LPC_ETHERNET->MAC_ADDR0_LOW = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   LPC_ETHERNET->MAC_ADDR0_HIGH = interface->macAddr.w[2];
+
+   //Initialize hash table
+   LPC_ETHERNET->MAC_HASHTABLE_LOW = 0;
+   LPC_ETHERNET->MAC_HASHTABLE_HIGH = 0;
+
+   //Configure the receive filter
+   LPC_ETHERNET->MAC_FRAME_FILTER = ETHERNET_MAC_FRAME_FILTER_HPF_Msk |
+      ETHERNET_MAC_FRAME_FILTER_HMC_Msk;
+
+   //Disable flow control
+   LPC_ETHERNET->MAC_FLOW_CTRL = 0;
+   //Set the threshold level of the transmit and receive FIFOs
+   LPC_ETHERNET->DMA_OP_MODE = ETHERNET_DMA_OP_MODE_TTC_64 | ETHERNET_DMA_OP_MODE_RTC_32;
+
+   //Configure DMA bus mode
+   LPC_ETHERNET->DMA_BUS_MODE = ETHERNET_DMA_BUS_MODE_AAL_Msk | ETHERNET_DMA_BUS_MODE_USP_Msk |
+      ETHERNET_DMA_BUS_MODE_RPBL_1 | ETHERNET_DMA_BUS_MODE_PR_1_1 |
+      ETHERNET_DMA_BUS_MODE_PBL_1 | ETHERNET_DMA_BUS_MODE_ATDS_Msk;
+
+   //Initialize DMA descriptor lists
+   lpc43xxEthInitDmaDesc(interface);
+
+   //Disable MAC interrupts
+   LPC_ETHERNET->MAC_INTR_MASK = ETHERNET_MAC_INTR_MASK_TSIM_Msk |
+      ETHERNET_MAC_INTR_MASK_PMTIM_Msk;
+
+   //Enable the desired DMA interrupts
+   LPC_ETHERNET->DMA_INT_EN = ETHERNET_DMA_INT_EN_NIE_Msk |
+      ETHERNET_DMA_INT_EN_RIE_Msk | ETHERNET_DMA_INT_EN_TIE_Msk;
+
+   //Set priority grouping (3 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(LPC43XX_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure Ethernet interrupt priority
+   NVIC_SetPriority(ETHERNET_IRQn, NVIC_EncodePriority(LPC43XX_ETH_IRQ_PRIORITY_GROUPING,
+      LPC43XX_ETH_IRQ_GROUP_PRIORITY, LPC43XX_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable MAC transmission and reception
+   LPC_ETHERNET->MAC_CONFIG |= ETHERNET_MAC_CONFIG_TE_Msk | ETHERNET_MAC_CONFIG_RE_Msk;
+   //Enable DMA transmission and reception
+   LPC_ETHERNET->DMA_OP_MODE |= ETHERNET_DMA_OP_MODE_ST_Msk | ETHERNET_DMA_OP_MODE_SR_Msk;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//LPC4330-Xplorer or LPCXpresso4337 evaluation board?
+#if defined(USE_LPC4330_XPLORER) || defined(USE_LPCXPRESSO_4337)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc43xxEthInitGpio(NetInterface *interface)
+{
+   //Enable GPIO peripheral clock
+   LPC_CCU1->CLK_M4_GPIO_CFG |= CCU1_CLK_M4_GPIO_CFG_RUN_Msk;
+   while(!(LPC_CCU1->CLK_M4_GPIO_STAT & CCU1_CLK_M4_GPIO_STAT_RUN_Msk));
+
+   //Select RMII operation mode
+   LPC_CREG->CREG6 &= ~CREG_CREG6_ETHMODE_Msk;
+   LPC_CREG->CREG6 |= CREG6_ETHMODE_RMII;
+
+   //Configure P0.0 (ENET_RXD1)
+   LPC_SCU->SFSP0_0 = SCU_SFSP0_0_EZI_Msk | SCU_SFSP0_0_EHS_Msk | (2 & SCU_SFSP0_0_MODE_Msk);
+   //Configure P0.1 (ENET_TX_EN)
+   LPC_SCU->SFSP0_1 = SCU_SFSP0_1_EHS_Msk | (6 & SCU_SFSP0_1_MODE_Msk);
+
+   //Configure P1.15 (ENET_RXD0)
+   LPC_SCU->SFSP1_15 = SCU_SFSP1_15_EZI_Msk | SCU_SFSP1_15_EHS_Msk | (3 & SCU_SFSP1_15_MODE_Msk);
+   //Configure P1.16 (ENET_RX_DV)
+   LPC_SCU->SFSP1_16 = SCU_SFSP1_16_EZI_Msk | SCU_SFSP1_16_EHS_Msk | (7 & SCU_SFSP1_16_MODE_Msk);
+   //Configure P1.17 (ENET_MDIO)
+   LPC_SCU->SFSP1_17 = SCU_SFSP1_17_EZI_Msk | (3 & SCU_SFSP1_17_MODE_Msk);
+   //Configure P1.18 (ENET_TXD0)
+   LPC_SCU->SFSP1_18 = SCU_SFSP1_18_EHS_Msk | (3 & SCU_SFSP1_18_MODE_Msk);
+   //Configure P1.19 (ENET_REF_CLK)
+   LPC_SCU->SFSP1_19 = SCU_SFSP1_19_EZI_Msk | SCU_SFSP1_19_EHS_Msk | (0 & SCU_SFSP1_19_MODE_Msk);
+   //Configure P1.20 (ENET_TXD1)
+   LPC_SCU->SFSP1_20 = SCU_SFSP1_20_EHS_Msk | (3 & SCU_SFSP1_20_MODE_Msk);
+
+   //Configure P2.0 (ENET_MDC)
+   LPC_SCU->SFSP2_0 = (7 & SCU_SFSP2_0_MODE_Msk);
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc43xxEthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX DMA descriptor list
+   for(i = 0; i < LPC43XX_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Use chain structure rather than ring structure
+      txDmaDesc[i].tdes0 = ETH_TDES0_IC | ETH_TDES0_TCH;
+      //Initialize transmit buffer size
+      txDmaDesc[i].tdes1 = 0;
+      //Transmit buffer address
+      txDmaDesc[i].tdes2 = (uint32_t) txBuffer[i];
+      //Next descriptor address
+      txDmaDesc[i].tdes3 = (uint32_t) &txDmaDesc[i + 1];
+      //Reserved fields
+      txDmaDesc[i].tdes4 = 0;
+      txDmaDesc[i].tdes5 = 0;
+      //Transmit frame time stamp
+      txDmaDesc[i].tdes6 = 0;
+      txDmaDesc[i].tdes7 = 0;
+   }
+
+   //The last descriptor is chained to the first entry
+   txDmaDesc[i - 1].tdes3 = (uint32_t) &txDmaDesc[0];
+   //Point to the very first descriptor
+   txCurDmaDesc = &txDmaDesc[0];
+
+   //Initialize RX DMA descriptor list
+   for(i = 0; i < LPC43XX_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the DMA
+      rxDmaDesc[i].rdes0 = ETH_RDES0_OWN;
+      //Use chain structure rather than ring structure
+      rxDmaDesc[i].rdes1 = ETH_RDES1_RCH | (LPC43XX_ETH_RX_BUFFER_SIZE & ETH_RDES1_RBS1);
+      //Receive buffer address
+      rxDmaDesc[i].rdes2 = (uint32_t) rxBuffer[i];
+      //Next descriptor address
+      rxDmaDesc[i].rdes3 = (uint32_t) &rxDmaDesc[i + 1];
+      //Extended status
+      rxDmaDesc[i].rdes4 = 0;
+      //Reserved field
+      rxDmaDesc[i].rdes5 = 0;
+      //Receive frame time stamp
+      rxDmaDesc[i].rdes6 = 0;
+      rxDmaDesc[i].rdes7 = 0;
+   }
+
+   //The last descriptor is chained to the first entry
+   rxDmaDesc[i - 1].rdes3 = (uint32_t) &rxDmaDesc[0];
+   //Point to the very first descriptor
+   rxCurDmaDesc = &rxDmaDesc[0];
+
+   //Start location of the TX descriptor list
+   LPC_ETHERNET->DMA_TRANS_DES_ADDR = (uint32_t) txDmaDesc;
+   //Start location of the RX descriptor list
+   LPC_ETHERNET->DMA_REC_DES_ADDR = (uint32_t) rxDmaDesc;
+}
+
+
+/**
+ * @brief LPC43xx Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc43xxEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc43xxEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(ETHERNET_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc43xxEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(ETHERNET_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief LPC43xx Ethernet MAC interrupt service routine
+ **/
+
+void ETHERNET_IRQHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read DMA status register
+   status = LPC_ETHERNET->DMA_STAT;
+
+   //A packet has been transmitted?
+   if(status & ETHERNET_DMA_STAT_TI_Msk)
+   {
+      //Clear TI interrupt flag
+      LPC_ETHERNET->DMA_STAT = ETHERNET_DMA_STAT_TI_Msk;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & ETHERNET_DMA_STAT_RI_Msk)
+   {
+      //Disable RIE interrupt
+      LPC_ETHERNET->DMA_INT_EN &= ~ETHERNET_DMA_INT_EN_RIE_Msk;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Clear NIS interrupt flag
+   LPC_ETHERNET->DMA_STAT = ETHERNET_DMA_STAT_NIS_Msk;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief LPC43xx Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void lpc43xxEthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(LPC_ETHERNET->DMA_STAT & ETHERNET_DMA_STAT_RI_Msk)
+   {
+      //Clear interrupt flag
+      LPC_ETHERNET->DMA_STAT = ETHERNET_DMA_STAT_RI_Msk;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = lpc43xxEthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable DMA interrupts
+   LPC_ETHERNET->DMA_INT_EN |= ETHERNET_DMA_INT_EN_NIE_Msk |
+      ETHERNET_DMA_INT_EN_RIE_Msk | ETHERNET_DMA_INT_EN_TIE_Msk;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t lpc43xxEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > LPC43XX_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurDmaDesc->tdes0 & ETH_TDES0_OWN)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txCurDmaDesc->tdes2, buffer, offset, length);
+
+   //Write the number of bytes to send
+   txCurDmaDesc->tdes1 = length & ETH_TDES1_TBS1;
+   //Set LS and FS flags as the data fits in a single buffer
+   txCurDmaDesc->tdes0 |= ETH_TDES0_LS | ETH_TDES0_FS;
+   //Give the ownership of the descriptor to the DMA
+   txCurDmaDesc->tdes0 |= ETH_TDES0_OWN;
+
+   //Clear TU flag to resume processing
+   LPC_ETHERNET->DMA_STAT = ETHERNET_DMA_STAT_TU_Msk;
+   //Instruct the DMA to poll the transmit descriptor list
+   LPC_ETHERNET->DMA_TRANS_POLL_DEMAND = 0;
+
+   //Point to the next descriptor in the list
+   txCurDmaDesc = (Lpc43xxTxDmaDesc *) txCurDmaDesc->tdes3;
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc43xxEthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_OWN))
+   {
+      //FS and LS flags should be set
+      if((rxCurDmaDesc->rdes0 & ETH_RDES0_FS) && (rxCurDmaDesc->rdes0 & ETH_RDES0_LS))
+      {
+         //Make sure no error occurred
+         if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_ES))
+         {
+            //Retrieve the length of the frame
+            n = (rxCurDmaDesc->rdes0 & ETH_RDES0_FL) >> 16;
+            //Limit the number of data to read
+            n = MIN(n, LPC43XX_ETH_RX_BUFFER_SIZE);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, (uint8_t *) rxCurDmaDesc->rdes2, n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Give the ownership of the descriptor back to the DMA
+      rxCurDmaDesc->rdes0 = ETH_RDES0_OWN;
+      //Point to the next descriptor in the list
+      rxCurDmaDesc = (Lpc43xxRxDmaDesc *) rxCurDmaDesc->rdes3;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Clear RU flag to resume processing
+   LPC_ETHERNET->DMA_STAT = ETHERNET_DMA_STAT_RU_Msk;
+   //Instruct the DMA to poll the receive descriptor list
+   LPC_ETHERNET->DMA_REC_POLL_DEMAND = 0;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc43xxEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating LPC43xx hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = lpc43xxEthCalcCrc(&entry->addr, sizeof(MacAddr));
+
+         //The upper 6 bits in the CRC register are used to index the
+         //contents of the hash table
+         k = (crc >> 26) & 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   LPC_ETHERNET->MAC_HASHTABLE_LOW = hashTable[0];
+   LPC_ETHERNET->MAC_HASHTABLE_HIGH = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  MAC_HASHTABLE_LOW = %08" PRIX32 "\r\n", LPC_ETHERNET->MAC_HASHTABLE_LOW);
+   TRACE_DEBUG("  MAC_HASHTABLE_HIGH = %08" PRIX32 "\r\n", LPC_ETHERNET->MAC_HASHTABLE_HIGH);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t lpc43xxEthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read current MAC configuration
+   config = LPC_ETHERNET->MAC_CONFIG;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= ETHERNET_MAC_CONFIG_FES_Msk;
+   else
+      config &= ~ETHERNET_MAC_CONFIG_FES_Msk;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= ETHERNET_MAC_CONFIG_DM_Msk;
+   else
+      config &= ~ETHERNET_MAC_CONFIG_DM_Msk;
+
+   //Update MAC configuration register
+   LPC_ETHERNET->MAC_CONFIG = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void lpc43xxEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = LPC_ETHERNET->MAC_MII_ADDR & ETHERNET_MAC_MII_ADDR_CR_Msk;
+   //Set up a write operation
+   value |= ETHERNET_MAC_MII_ADDR_W_Msk | ETHERNET_MAC_MII_ADDR_GB_Msk;
+   //PHY address
+   value |= (phyAddr << ETHERNET_MAC_MII_ADDR_PA_Pos) & ETHERNET_MAC_MII_ADDR_PA_Msk;
+   //Register address
+   value |= (regAddr << ETHERNET_MAC_MII_ADDR_GR_Pos) & ETHERNET_MAC_MII_ADDR_GR_Msk;
+
+   //Data to be written in the PHY register
+   LPC_ETHERNET->MAC_MII_DATA = data & ETHERNET_MAC_MII_DATA_GD_Msk;
+
+   //Start a write operation
+   LPC_ETHERNET->MAC_MII_ADDR = value;
+   //Wait for the write to complete
+   while(LPC_ETHERNET->MAC_MII_ADDR & ETHERNET_MAC_MII_ADDR_GB_Msk);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t lpc43xxEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = LPC_ETHERNET->MAC_MII_ADDR & ETHERNET_MAC_MII_ADDR_CR_Msk;
+   //Set up a read operation
+   value |= ETHERNET_MAC_MII_ADDR_GB_Msk;
+   //PHY address
+   value |= (phyAddr << ETHERNET_MAC_MII_ADDR_PA_Pos) & ETHERNET_MAC_MII_ADDR_PA_Msk;
+   //Register address
+   value |= (regAddr << ETHERNET_MAC_MII_ADDR_GR_Pos) & ETHERNET_MAC_MII_ADDR_GR_Msk;
+
+   //Start a read operation
+   LPC_ETHERNET->MAC_MII_ADDR = value;
+   //Wait for the read to complete
+   while(LPC_ETHERNET->MAC_MII_ADDR & ETHERNET_MAC_MII_ADDR_GB_Msk);
+
+   //Return PHY register contents
+   return LPC_ETHERNET->MAC_MII_DATA & ETHERNET_MAC_MII_DATA_GD_Msk;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t lpc43xxEthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return ~crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/lpc43xx_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,260 @@
+/**
+ * @file lpc43xx_eth.h
+ * @brief LPC4300 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _LPC43XX_ETH_H
+#define _LPC43XX_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef LPC43XX_ETH_TX_BUFFER_COUNT
+   #define LPC43XX_ETH_TX_BUFFER_COUNT 3
+#elif (LPC43XX_ETH_TX_BUFFER_COUNT < 1)
+   #error LPC43XX_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef LPC43XX_ETH_TX_BUFFER_SIZE
+   #define LPC43XX_ETH_TX_BUFFER_SIZE 1536
+#elif (LPC43XX_ETH_TX_BUFFER_SIZE != 1536)
+   #error LPC43XX_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef LPC43XX_ETH_RX_BUFFER_COUNT
+   #define LPC43XX_ETH_RX_BUFFER_COUNT 6
+#elif (LPC43XX_ETH_RX_BUFFER_COUNT < 1)
+   #error LPC43XX_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef LPC43XX_ETH_RX_BUFFER_SIZE
+   #define LPC43XX_ETH_RX_BUFFER_SIZE 1536
+#elif (LPC43XX_ETH_RX_BUFFER_SIZE != 1536)
+   #error LPC43XX_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef LPC43XX_ETH_IRQ_PRIORITY_GROUPING
+   #define LPC43XX_ETH_IRQ_PRIORITY_GROUPING 4
+#elif (LPC43XX_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error LPC43XX_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef LPC43XX_ETH_IRQ_GROUP_PRIORITY
+   #define LPC43XX_ETH_IRQ_GROUP_PRIORITY 6
+#elif (LPC43XX_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error LPC43XX_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef LPC43XX_ETH_IRQ_SUB_PRIORITY
+   #define LPC43XX_ETH_IRQ_SUB_PRIORITY 0
+#elif (LPC43XX_ETH_IRQ_SUB_PRIORITY < 0)
+   #error LPC43XX_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//CREG6 register
+#define CREG6_ETHMODE_MII               (0 << CREG_CREG6_ETHMODE_Pos)
+#define CREG6_ETHMODE_RMII              (4 << CREG_CREG6_ETHMODE_Pos)
+
+//MAC_MII_ADDR register
+#define ETHERNET_MAC_MII_ADDR_CR_DIV42  (0 << ETHERNET_MAC_MII_ADDR_CR_Pos)
+#define ETHERNET_MAC_MII_ADDR_CR_DIV62  (1 << ETHERNET_MAC_MII_ADDR_CR_Pos)
+#define ETHERNET_MAC_MII_ADDR_CR_DIV16  (2 << ETHERNET_MAC_MII_ADDR_CR_Pos)
+#define ETHERNET_MAC_MII_ADDR_CR_DIV26  (3 << ETHERNET_MAC_MII_ADDR_CR_Pos)
+#define ETHERNET_MAC_MII_ADDR_CR_DIV102 (4 << ETHERNET_MAC_MII_ADDR_CR_Pos)
+#define ETHERNET_MAC_MII_ADDR_CR_DIV124 (5 << ETHERNET_MAC_MII_ADDR_CR_Pos)
+
+//DMA_BUS_MODE register
+#define ETHERNET_DMA_BUS_MODE_RPBL_1    (1 << ETHERNET_DMA_BUS_MODE_RPBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_RPBL_2    (2 << ETHERNET_DMA_BUS_MODE_RPBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_RPBL_4    (4 << ETHERNET_DMA_BUS_MODE_RPBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_RPBL_8    (8 << ETHERNET_DMA_BUS_MODE_RPBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_RPBL_16   (16 << ETHERNET_DMA_BUS_MODE_RPBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_RPBL_32   (32 << ETHERNET_DMA_BUS_MODE_RPBL_Pos)
+
+#define ETHERNET_DMA_BUS_MODE_PR_1_1    (0 << ETHERNET_DMA_BUS_MODE_PR_Pos)
+#define ETHERNET_DMA_BUS_MODE_PR_2_1    (1 << ETHERNET_DMA_BUS_MODE_PR_Pos)
+#define ETHERNET_DMA_BUS_MODE_PR_3_1    (2 << ETHERNET_DMA_BUS_MODE_PR_Pos)
+#define ETHERNET_DMA_BUS_MODE_PR_4_1    (3 << ETHERNET_DMA_BUS_MODE_PR_Pos)
+
+#define ETHERNET_DMA_BUS_MODE_PBL_1     (1 << ETHERNET_DMA_BUS_MODE_PBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_PBL_2     (2 << ETHERNET_DMA_BUS_MODE_PBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_PBL_4     (4 << ETHERNET_DMA_BUS_MODE_PBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_PBL_8     (8 << ETHERNET_DMA_BUS_MODE_PBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_PBL_16    (16 << ETHERNET_DMA_BUS_MODE_PBL_Pos)
+#define ETHERNET_DMA_BUS_MODE_PBL_32    (32 << ETHERNET_DMA_BUS_MODE_PBL_Pos)
+
+//DMA_OP_MODE register
+#define ETHERNET_DMA_OP_MODE_TTC_64     (0 << ETHERNET_DMA_OP_MODE_TTC_Pos)
+#define ETHERNET_DMA_OP_MODE_TTC_128    (1 << ETHERNET_DMA_OP_MODE_TTC_Pos)
+#define ETHERNET_DMA_OP_MODE_TTC_192    (2 << ETHERNET_DMA_OP_MODE_TTC_Pos)
+#define ETHERNET_DMA_OP_MODE_TTC_256    (3 << ETHERNET_DMA_OP_MODE_TTC_Pos)
+#define ETHERNET_DMA_OP_MODE_TTC_40     (4 << ETHERNET_DMA_OP_MODE_TTC_Pos)
+#define ETHERNET_DMA_OP_MODE_TTC_32     (5 << ETHERNET_DMA_OP_MODE_TTC_Pos)
+#define ETHERNET_DMA_OP_MODE_TTC_24     (6 << ETHERNET_DMA_OP_MODE_TTC_Pos)
+#define ETHERNET_DMA_OP_MODE_TTC_16     (7 << ETHERNET_DMA_OP_MODE_TTC_Pos)
+
+#define ETHERNET_DMA_OP_MODE_RTC_64     (0 << ETHERNET_DMA_OP_MODE_RTC_Pos)
+#define ETHERNET_DMA_OP_MODE_RTC_32     (1 << ETHERNET_DMA_OP_MODE_RTC_Pos)
+#define ETHERNET_DMA_OP_MODE_RTC_96     (2 << ETHERNET_DMA_OP_MODE_RTC_Pos)
+#define ETHERNET_DMA_OP_MODE_RTC_128    (3 << ETHERNET_DMA_OP_MODE_RTC_Pos)
+
+//Transmit DMA descriptor flags
+#define ETH_TDES0_OWN        0x80000000
+#define ETH_TDES0_IC         0x40000000
+#define ETH_TDES0_LS         0x20000000
+#define ETH_TDES0_FS         0x10000000
+#define ETH_TDES0_DC         0x08000000
+#define ETH_TDES0_DP         0x04000000
+#define ETH_TDES0_TTSE       0x02000000
+#define ETH_TDES0_TER        0x00200000
+#define ETH_TDES0_TCH        0x00100000
+#define ETH_TDES0_TTSS       0x00020000
+#define ETH_TDES0_IHE        0x00010000
+#define ETH_TDES0_ES         0x00008000
+#define ETH_TDES0_JT         0x00004000
+#define ETH_TDES0_FF         0x00002000
+#define ETH_TDES0_IPE        0x00001000
+#define ETH_TDES0_LCA        0x00000800
+#define ETH_TDES0_NC         0x00000400
+#define ETH_TDES0_LCO        0x00000200
+#define ETH_TDES0_EC         0x00000100
+#define ETH_TDES0_VF         0x00000080
+#define ETH_TDES0_CC         0x00000078
+#define ETH_TDES0_ED         0x00000004
+#define ETH_TDES0_UF         0x00000002
+#define ETH_TDES0_DB         0x00000001
+#define ETH_TDES1_TBS2       0x1FFF0000
+#define ETH_TDES1_TBS1       0x00001FFF
+#define ETH_TDES2_B1ADD      0xFFFFFFFF
+#define ETH_TDES3_B2ADD      0xFFFFFFFF
+#define ETH_TDES6_TTSL       0xFFFFFFFF
+#define ETH_TDES7_TTSH       0xFFFFFFFF
+
+//Receive DMA descriptor flags
+#define ETH_RDES0_OWN        0x80000000
+#define ETH_RDES0_AFM        0x40000000
+#define ETH_RDES0_FL         0x3FFF0000
+#define ETH_RDES0_ES         0x00008000
+#define ETH_RDES0_DE         0x00004000
+#define ETH_RDES0_SAF        0x00002000
+#define ETH_RDES0_LE         0x00001000
+#define ETH_RDES0_OE         0x00000800
+#define ETH_RDES0_VLAN       0x00000400
+#define ETH_RDES0_FS         0x00000200
+#define ETH_RDES0_LS         0x00000100
+#define ETH_RDES0_TSA        0x00000080
+#define ETH_RDES0_LCO        0x00000040
+#define ETH_RDES0_FT         0x00000020
+#define ETH_RDES0_RWT        0x00000010
+#define ETH_RDES0_RE         0x00000008
+#define ETH_RDES0_DBE        0x00000004
+#define ETH_RDES0_CE         0x00000002
+#define ETH_RDES0_ESA        0x00000001
+#define ETH_RDES1_RBS2       0x1FFF0000
+#define ETH_RDES1_RER        0x00008000
+#define ETH_RDES1_RCH        0x00004000
+#define ETH_RDES1_RBS1       0x00001FFF
+#define ETH_RDES2_B1ADD      0xFFFFFFFF
+#define ETH_RDES3_B2ADD      0xFFFFFFFF
+#define ETH_RDES4_PTPVERSION 0x00002000
+#define ETH_RDES4_PTPTYPE    0x00001000
+#define ETH_RDES4_MT         0x00000F00
+#define ETH_RDES4_IPV6       0x00000080
+#define ETH_RDES4_IPV4       0x00000040
+#define ETH_RDES6_RTSL       0xFFFFFFFF
+#define ETH_RDES7_RTSH       0xFFFFFFFF
+
+
+/**
+ * @brief Enhanced TX DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t tdes0;
+   uint32_t tdes1;
+   uint32_t tdes2;
+   uint32_t tdes3;
+   uint32_t tdes4;
+   uint32_t tdes5;
+   uint32_t tdes6;
+   uint32_t tdes7;
+} Lpc43xxTxDmaDesc;
+
+
+/**
+ * @brief Enhanced RX DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t rdes0;
+   uint32_t rdes1;
+   uint32_t rdes2;
+   uint32_t rdes3;
+   uint32_t rdes4;
+   uint32_t rdes5;
+   uint32_t rdes6;
+   uint32_t rdes7;
+} Lpc43xxRxDmaDesc;
+
+
+//LPC43xx Ethernet MAC driver
+extern const NicDriver lpc43xxEthDriver;
+
+//LPC43xx Ethernet MAC related functions
+error_t lpc43xxEthInit(NetInterface *interface);
+void lpc43xxEthInitGpio(NetInterface *interface);
+void lpc43xxEthInitDmaDesc(NetInterface *interface);
+
+void lpc43xxEthTick(NetInterface *interface);
+
+void lpc43xxEthEnableIrq(NetInterface *interface);
+void lpc43xxEthDisableIrq(NetInterface *interface);
+void lpc43xxEthEventHandler(NetInterface *interface);
+
+error_t lpc43xxEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t lpc43xxEthReceivePacket(NetInterface *interface);
+
+error_t lpc43xxEthSetMulticastFilter(NetInterface *interface);
+error_t lpc43xxEthUpdateMacConfig(NetInterface *interface);
+
+void lpc43xxEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t lpc43xxEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t lpc43xxEthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/m2sxxx_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,636 @@
+/**
+ * @file m2sxxx_eth.c
+ * @brief SmartFusion2 (M2Sxxx) Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "m2sxxx.h"
+#include "core/net.h"
+#include "drivers/m2sxxx_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+static uint8_t txBuffer[M2SXXX_ETH_TX_BUFFER_COUNT][M2SXXX_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+static uint8_t rxBuffer[M2SXXX_ETH_RX_BUFFER_COUNT][M2SXXX_ETH_RX_BUFFER_SIZE];
+//Transmit DMA descriptors
+#pragma data_alignment = 4
+static M2sxxxTxDmaDesc txDmaDesc[M2SXXX_ETH_TX_BUFFER_COUNT];
+//Receive DMA descriptors
+#pragma data_alignment = 4
+static M2sxxxRxDmaDesc rxDmaDesc[M2SXXX_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[M2SXXX_ETH_TX_BUFFER_COUNT][M2SXXX_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[M2SXXX_ETH_RX_BUFFER_COUNT][M2SXXX_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Transmit DMA descriptors
+static M2sxxxTxDmaDesc txDmaDesc[M2SXXX_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive DMA descriptors
+static M2sxxxRxDmaDesc rxDmaDesc[M2SXXX_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+
+#endif
+
+//Pointer to the current TX DMA descriptor
+static M2sxxxTxDmaDesc *txCurDmaDesc;
+//Pointer to the current RX DMA descriptor
+static M2sxxxRxDmaDesc *rxCurDmaDesc;
+
+
+/**
+ * @brief M2Sxxx Ethernet MAC driver
+ **/
+
+const NicDriver m2sxxxEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   m2sxxxEthInit,
+   m2sxxxEthTick,
+   m2sxxxEthEnableIrq,
+   m2sxxxEthDisableIrq,
+   m2sxxxEthEventHandler,
+   m2sxxxEthSendPacket,
+   m2sxxxEthSetMulticastFilter,
+   m2sxxxEthUpdateMacConfig,
+   m2sxxxEthWritePhyReg,
+   m2sxxxEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief M2Sxxx Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t m2sxxxEthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing M2Sxxx Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Disable EDAC feature
+   SYSREG->EDAC_CR &= ~(EDAC_CR_MAC_EDAC_RX_EN | EDAC_CR_MAC_EDAC_TX_EN);
+
+   //Reset the MAC module
+   MAC->CFG1 = CFG1_SOFT_RESET | CFG1_RESET_RX_MAC_CTRL |
+      CFG1_RESET_TX_MAC_CTRL | CFG1_RESET_RX_FUNCTION | CFG1_RESET_TX_FUNCTION;
+
+   //Reset the interface module
+   MAC->INTERFACE_CTRL = INTERFACE_CTRL_RESET;
+
+   //Reset FIFOs
+   MAC->FIFO_CFG0 = FIFO_CFG0_HSTRSTFT | FIFO_CFG0_HSTRSTST |
+      FIFO_CFG0_HSTRSTFR | FIFO_CFG0_HSTRSTSR | FIFO_CFG0_HSTRSTWT;
+
+   //Take the MAC module out of reset
+   MAC->CFG1 = 0;
+   //Take the interface module out of reset
+   MAC->INTERFACE_CTRL = 0;
+   //Take the FIFOs out of reset
+   MAC->FIFO_CFG0 = 0;
+
+   //Select interface mode (MII, RMII, GMII or TBI)
+   m2sxxxEthInitGpio(interface);
+
+   //Select the proper divider for the MDC clock
+   MAC->MII_CONFIG = MII_CONFIG_CLKSEL_DIV28;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Set the upper 16 bits of the MAC address
+   MAC->STATION_ADDRESS2 = (interface->macAddr.b[0] << 16) |
+      (interface->macAddr.b[1] << 24);
+
+   //Set the lower 32 bits of the MAC address
+   MAC->STATION_ADDRESS1 = interface->macAddr.b[2] |
+      (interface->macAddr.b[3] << 8) |
+      (interface->macAddr.b[4] << 16) |
+      (interface->macAddr.b[5] << 24);
+
+   //Maximum frame length to be accepted
+   MAC->MAX_FRAME_LENGTH = 1518;
+
+   //Disable flow control
+   MAC->CFG1 = 0;
+
+   //All short frames will be zero-padded to 60 bytes and a valid CRC is then appended
+   MAC->CFG2 = CFG2_PREAMBLE_7 | CFG2_INTERFACE_MODE_NIBBLE |
+      CFG2_LENGTH_FIELD_CHECK | CFG2_PAD_CRC_EN | CFG2_CRC_EN;
+
+   //Enable TX and RX FIFOs
+   MAC->FIFO_CFG0 = FIFO_CFG0_FTFENREQ | FIFO_CFG0_STFENREQ |
+      FIFO_CFG0_FRFENREQ | FIFO_CFG0_SRFENREQ | FIFO_CFG0_WTMENREQ;
+
+   //Use default FIFO configuration
+   MAC->FIFO_CFG1 = FIFO_CFG1_DEFAULT_VALUE;
+   MAC->FIFO_CFG2 = FIFO_CFG2_DEFAULT_VALUE;
+   MAC->FIFO_CFG3 = FIFO_CFG3_DEFAULT_VALUE;
+
+   //Drop frames less than 64 bytes
+   MAC->FIFO_CFG5 = FIFO_CFG5_HSTDRPLT64 | FIFO_CFG5_HSTFLTRFRMDC;
+
+   //Specify the statistics vectors that will be checked
+   MAC->FIFO_CFG5 &= ~(FIFO_CFG5_TRUNCATED | FIFO_CFG5_RECEPTION_OK |
+      FIFO_CFG5_INVALID_CRC | FIFO_CFG5_RECEIVE_ERROR);
+
+   //Configure frame filtering
+   MAC->FIFO_CFG4 = FIFO_CFG4_TRUNCATED |
+      FIFO_CFG4_INVALID_CRC | FIFO_CFG4_RECEIVE_ERROR;
+
+   //Initialize DMA descriptor lists
+   m2sxxxEthInitDmaDesc(interface);
+
+   //Enable the desired Ethernet interrupts
+   MAC->DMA_IRQ_MASK = DMA_IRQ_MASK_RX_PKT_RECEIVED | DMA_IRQ_MASK_TX_PKT_SENT;
+
+   //Set priority grouping (4 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(M2SXXX_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure Ethernet interrupt priority
+   NVIC_SetPriority(EthernetMAC_IRQn, NVIC_EncodePriority(M2SXXX_ETH_IRQ_PRIORITY_GROUPING,
+      M2SXXX_ETH_IRQ_GROUP_PRIORITY, M2SXXX_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable transmission and reception
+   MAC->CFG1 |= CFG1_TX_EN | CFG1_RX_EN;
+   //Enable the DMA transfer of received packets
+   MAC->DMA_RX_CTRL = DMA_RX_CTRL_RX_EN;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//SF2-STARTER-KIT-ES-2 evaluation board?
+#if defined(USE_SF2_STARTER_KIT_ES_2)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void m2sxxxEthInitGpio(NetInterface *interface)
+{
+   //Select MII interface mode
+   SYSREG->MAC_CR = MAC_CR_ETH_PHY_MODE_MII;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void m2sxxxEthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX DMA descriptor list
+   for(i = 0; i < M2SXXX_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Transmit buffer address
+      txDmaDesc[i].addr = (uint32_t) txBuffer[i];
+      //The descriptor is initially owned by the user
+      txDmaDesc[i].size = DMA_DESC_EMPTY_FLAG;
+      //Next descriptor address
+      txDmaDesc[i].next = (uint32_t) &txDmaDesc[i + 1];
+   }
+
+   //The last descriptor is chained to the first entry
+   txDmaDesc[i - 1].next = (uint32_t) &txDmaDesc[0];
+   //Point to the very first descriptor
+   txCurDmaDesc = &txDmaDesc[0];
+
+   //Initialize RX DMA descriptor list
+   for(i = 0; i < M2SXXX_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Receive buffer address
+      rxDmaDesc[i].addr = (uint32_t) rxBuffer[i];
+      //The descriptor is initially owned by the DMA
+      rxDmaDesc[i].size = DMA_DESC_EMPTY_FLAG;
+      //Next descriptor address
+      rxDmaDesc[i].next = (uint32_t) &rxDmaDesc[i + 1];
+   }
+
+   //The last descriptor is chained to the first entry
+   rxDmaDesc[i - 1].next = (uint32_t) &rxDmaDesc[0];
+   //Point to the very first descriptor
+   rxCurDmaDesc = &rxDmaDesc[0];
+
+   //Start location of the TX descriptor list
+   MAC->DMA_TX_DESC = (uint32_t) txDmaDesc;
+   //Start location of the RX descriptor list
+   MAC->DMA_RX_DESC = (uint32_t) rxDmaDesc;
+}
+
+
+/**
+ * @brief M2Sxxx Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void m2sxxxEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void m2sxxxEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(EthernetMAC_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void m2sxxxEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(EthernetMAC_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief M2Sxxx Ethernet MAC interrupt service routine
+ **/
+
+void EthernetMAC_IRQHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read interrupt status register
+   status = MAC->DMA_IRQ;
+
+   //A packet has been transmitted?
+   if(status & DMA_IRQ_TX_PKT_SENT)
+   {
+      //Clear TX interrupt flag
+      MAC->DMA_TX_STATUS = DMA_TX_STATUS_TX_PKT_SENT;
+
+      //Check whether the TX buffer is available for writing
+      if(txCurDmaDesc->size & DMA_DESC_EMPTY_FLAG)
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & DMA_IRQ_RX_PKT_RECEIVED)
+   {
+      //Disable RX interrupt
+      MAC->DMA_IRQ_MASK &= ~DMA_IRQ_MASK_RX_PKT_RECEIVED;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief M2Sxxx Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void m2sxxxEthEventHandler(NetInterface *interface)
+{
+   //Packet received?
+   if(MAC->DMA_RX_STATUS & DMA_RX_STATUS_RX_PKT_RECEIVED)
+   {
+      //Process all the pending packets
+      while(MAC->DMA_RX_STATUS & DMA_RX_STATUS_RX_PKT_RECEIVED)
+      {
+         //Clear RX interrupt flag
+         MAC->DMA_RX_STATUS = DMA_RX_STATUS_RX_PKT_RECEIVED;
+         //Read incoming packet
+         m2sxxxEthReceivePacket(interface);
+      }
+   }
+
+   //Re-enable Ethernet interrupts
+   MAC->DMA_IRQ_MASK = DMA_IRQ_MASK_RX_PKT_RECEIVED | DMA_IRQ_MASK_TX_PKT_SENT;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t m2sxxxEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > M2SXXX_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(!(txCurDmaDesc->size & DMA_DESC_EMPTY_FLAG))
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txCurDmaDesc->addr, buffer, offset, length);
+
+   //Set the packet length and give the ownership of the descriptor to the DMA
+   txCurDmaDesc->size = length & DMA_DESC_SIZE_MASK;
+
+   //Check whether DMA transfers are suspended
+   if(!(MAC->DMA_TX_CTRL & DMA_TX_CTRL_TX_EN))
+   {
+      //Set the start position in the ring buffer
+      MAC->DMA_TX_DESC = (uint32_t) txCurDmaDesc;
+   }
+
+   //Instruct the DMA controller to transfer the packet
+   MAC->DMA_TX_CTRL = DMA_TX_CTRL_TX_EN;
+
+   //Point to the next descriptor in the list
+   txCurDmaDesc = (M2sxxxTxDmaDesc *) txCurDmaDesc->next;
+
+   //Check whether the next buffer is available for writing
+   if(txCurDmaDesc->size & DMA_DESC_EMPTY_FLAG)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t m2sxxxEthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxCurDmaDesc->size & DMA_DESC_EMPTY_FLAG))
+   {
+      //Retrieve the length of the frame
+      n = rxCurDmaDesc->size & DMA_DESC_SIZE_MASK;
+      //Limit the number of data to read
+      n = MIN(n, M2SXXX_ETH_RX_BUFFER_SIZE);
+
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, (uint8_t *) rxCurDmaDesc->addr, n);
+
+      //Give the ownership of the descriptor back to the DMA
+      rxCurDmaDesc->size = DMA_DESC_EMPTY_FLAG;
+
+      //Check whether DMA transfers are suspended
+      if(!(MAC->DMA_RX_CTRL & DMA_RX_CTRL_RX_EN))
+      {
+         //Set the start position in the ring buffer
+         MAC->DMA_RX_DESC = (uint32_t) rxCurDmaDesc;
+      }
+
+      //Enable the DMA transfer of received packets
+      MAC->DMA_RX_CTRL = DMA_RX_CTRL_RX_EN;
+      //Point to the next descriptor in the list
+      rxCurDmaDesc = (M2sxxxRxDmaDesc *) rxCurDmaDesc->next;
+
+      //Valid packet received
+      error = NO_ERROR;
+   }
+   else
+   {
+      //Check whether DMA transfers are suspended
+      if(!(MAC->DMA_RX_CTRL & DMA_RX_CTRL_RX_EN))
+      {
+         //Set the start position in the ring buffer
+         MAC->DMA_RX_DESC = (uint32_t) rxCurDmaDesc;
+      }
+
+      //Enable the DMA transfer of received packets
+      MAC->DMA_RX_CTRL = DMA_RX_CTRL_RX_EN;
+
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t m2sxxxEthSetMulticastFilter(NetInterface *interface)
+{
+   //SmartFusion2 Ethernet MAC does not implement any hash table
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t m2sxxxEthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t temp;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+   {
+      //The link operates at 100 Mbps
+      temp = SYSREG->MAC_CR & ~MAC_CR_ETH_LINE_SPEED;
+      SYSREG->MAC_CR = temp | MAC_CR_ETH_LINE_SPEED_100MBPS;
+
+      //Configure the RMII module with the current operating speed
+      MAC->INTERFACE_CTRL |= INTERFACE_CTRL_SPEED;
+
+      //Use nibble mode
+      temp = MAC->CFG2 & ~CFG2_INTERFACE_MODE;
+      MAC->CFG2 = temp | CFG2_INTERFACE_MODE_NIBBLE;
+   }
+   else
+   {
+      //The link operates at 10 Mbps
+      temp = SYSREG->MAC_CR & ~MAC_CR_ETH_LINE_SPEED;
+      SYSREG->MAC_CR = temp | MAC_CR_ETH_LINE_SPEED_10MBPS;
+
+      //Configure the RMII module with the current operating speed
+      MAC->INTERFACE_CTRL &= ~INTERFACE_CTRL_SPEED;
+
+      //Use nibble mode
+      temp = MAC->CFG2 & ~CFG2_INTERFACE_MODE;
+      MAC->CFG2 = temp | CFG2_INTERFACE_MODE_NIBBLE;
+   }
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+   {
+      //Configure MAC to operate in full-duplex mode
+      MAC->CFG2 |= CFG2_FULL_DUPLEX;
+      MAC->FIFO_CFG5 &= ~FIFO_CFG5_CFGHDPLX;
+   }
+   else
+   {
+      //Configure MAC to operate in half-duplex mode
+      MAC->CFG2 &= ~CFG2_FULL_DUPLEX;
+      MAC->FIFO_CFG5 |= FIFO_CFG5_CFGHDPLX;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void m2sxxxEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   //Set PHY address and register address
+   MAC->MII_ADDRESS = (phyAddr << MII_ADDRESS_PHY_ADDR_POS) | regAddr;
+   //Start a write operation
+   MAC->MII_CTRL = data;
+
+   //Wait for the write to complete
+   while(MAC->MII_INDICATORS & MII_INDICATORS_BUSY);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t m2sxxxEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   //Set PHY address and register address
+   MAC->MII_ADDRESS = (phyAddr << MII_ADDRESS_PHY_ADDR_POS) | regAddr;
+   //Start a read operation
+   MAC->MII_COMMAND = MII_COMMAND_READ;
+
+   //Wait for the read to complete
+   while(MAC->MII_INDICATORS & MII_INDICATORS_BUSY);
+
+   //Clear command register
+   MAC->MII_COMMAND = 0;
+   //Return PHY register contents
+   return MAC->MII_STATUS;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/m2sxxx_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,344 @@
+/**
+ * @file m2sxxx_eth.h
+ * @brief SmartFusion2 (M2Sxxx) Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _M2SXXX_ETH_H
+#define _M2SXXX_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef M2SXXX_ETH_TX_BUFFER_COUNT
+   #define M2SXXX_ETH_TX_BUFFER_COUNT 2
+#elif (M2SXXX_ETH_TX_BUFFER_COUNT < 1)
+   #error M2SXXX_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef M2SXXX_ETH_TX_BUFFER_SIZE
+   #define M2SXXX_ETH_TX_BUFFER_SIZE 1536
+#elif (M2SXXX_ETH_TX_BUFFER_SIZE != 1536)
+   #error M2SXXX_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef M2SXXX_ETH_RX_BUFFER_COUNT
+   #define M2SXXX_ETH_RX_BUFFER_COUNT 4
+#elif (M2SXXX_ETH_RX_BUFFER_COUNT < 1)
+   #error M2SXXX_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef M2SXXX_ETH_RX_BUFFER_SIZE
+   #define M2SXXX_ETH_RX_BUFFER_SIZE 1536
+#elif (M2SXXX_ETH_RX_BUFFER_SIZE != 1536)
+   #error M2SXXX_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef M2SXXX_ETH_IRQ_PRIORITY_GROUPING
+   #define M2SXXX_ETH_IRQ_PRIORITY_GROUPING 3
+#elif (M2SXXX_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error M2SXXX_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef M2SXXX_ETH_IRQ_GROUP_PRIORITY
+   #define M2SXXX_ETH_IRQ_GROUP_PRIORITY 12
+#elif (M2SXXX_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error M2SXXX_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef M2SXXX_ETH_IRQ_SUB_PRIORITY
+   #define M2SXXX_ETH_IRQ_SUB_PRIORITY 0
+#elif (M2SXXX_ETH_IRQ_SUB_PRIORITY < 0)
+   #error M2SXXX_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//EDAC_CR register
+#define EDAC_CR_CAN_EDAC_EN               0x00000040
+#define EDAC_CR_USB_EDAC_EN               0x00000020
+#define EDAC_CR_MAC_EDAC_RX_EN            0x00000010
+#define EDAC_CR_MAC_EDAC_TX_EN            0x00000008
+#define EDAC_CR_ESRAM1_EDAC_EN            0x00000002
+#define EDAC_CR_ESRAM0_EDAC_EN            0x00000001
+
+//MAC_CR register
+#define MAC_CR_RGMII_TXC_DELAY_SEL        0x000001E0
+#define MAC_CR_ETH_PHY_MODE               0x0000001C
+#define MAC_CR_ETH_LINE_SPEED             0x00000003
+
+#define MAC_CR_ETH_PHY_MODE_RMII          0x00000000
+#define MAC_CR_ETH_PHY_MODE_TBI           0x00000008
+#define MAC_CR_ETH_PHY_MODE_MII           0x0000000C
+#define MAC_CR_ETH_PHY_MODE_GMII          0x00000010
+
+#define MAC_CR_ETH_LINE_SPEED_10MBPS      0x00000000
+#define MAC_CR_ETH_LINE_SPEED_100MBPS     0x00000001
+#define MAC_CR_ETH_LINE_SPEED_1000MBPS    0x00000002
+
+//DMA_TX_CTRL register
+#define DMA_TX_CTRL_TX_EN                 0x00000001
+
+//DMA_TX_STATUS register
+#define DMA_TX_STATUS_TX_PKT_COUNT        0x00FF0000
+#define DMA_TX_STATUS_TX_BUS_ERROR        0x00000008
+#define DMA_TX_STATUS_TX_UNDERRUN         0x00000002
+#define DMA_TX_STATUS_TX_PKT_SENT         0x00000001
+
+//DMA_RX_CTRL register
+#define DMA_RX_CTRL_RX_EN                 0x00000001
+
+//DMA_RX_STATUS register
+#define DMA_RX_STATUS_RX_PKT_COUNT        0x00FF0000
+#define DMA_RX_STATUS_RX_BUS_ERROR        0x00000008
+#define DMA_RX_STATUS_RX_OVERFLOW         0x00000004
+#define DMA_RX_STATUS_RX_PKT_RECEIVED     0x00000001
+
+//DMA_IRQ_MASK register
+#define DMA_IRQ_MASK_RX_BUS_ERROR         0x00000080
+#define DMA_IRQ_MASK_RX_OVERFLOW          0x00000040
+#define DMA_IRQ_MASK_RX_PKT_RECEIVED      0x00000010
+#define DMA_IRQ_MASK_TX_BUS_ERROR         0x00000008
+#define DMA_IRQ_MASK_TX_UNDERRUN          0x00000002
+#define DMA_IRQ_MASK_TX_PKT_SENT          0x00000001
+
+//DMA_IRQ register
+#define DMA_IRQ_RX_BUS_ERROR              0x00000080
+#define DMA_IRQ_RX_OVERFLOW               0x00000040
+#define DMA_IRQ_RX_PKT_RECEIVED           0x00000010
+#define DMA_IRQ_TX_BUS_ERROR              0x00000008
+#define DMA_IRQ_TX_UNDERRUN               0x00000002
+#define DMA_IRQ_TX_PKT_SENT               0x00000001
+
+//CFG1 register
+#define CFG1_SOFT_RESET                   0x80000000
+#define CFG1_SIMULATION_RESET             0x40000000
+#define CFG1_RESET_RX_MAC_CTRL            0x00080000
+#define CFG1_RESET_TX_MAC_CTRL            0x00040000
+#define CFG1_RESET_RX_FUNCTION            0x00020000
+#define CFG1_RESET_TX_FUNCTION            0x00010000
+#define CFG1_LOOP_BACK                    0x00000100
+#define CFG1_RX_FLOW_CTRL_EN              0x00000020
+#define CFG1_TX_FLOW_CTRL_EN              0x00000010
+#define CFG1_SYNC_RX_EN                   0x00000008
+#define CFG1_RX_EN                        0x00000004
+#define CFG1_SYNC_TX_EN                   0x00000002
+#define CFG1_TX_EN                        0x00000001
+
+//CFG2 register
+#define CFG2_PREAMBLE_LENGTH              0x0000F000
+#define CFG2_INTERFACE_MODE               0x00000300
+#define CFG2_HUGE FRAME_EN                0x00000020
+#define CFG2_LENGTH_FIELD_CHECK           0x00000010
+#define CFG2_PAD_CRC_EN                   0x00000004
+#define CFG2_CRC_EN                       0x00000002
+#define CFG2_FULL_DUPLEX                  0x00000001
+
+#define CFG2_PREAMBLE_7                   0x00007000
+
+#define CFG2_INTERFACE_MODE_NIBBLE        0x00000100
+#define CFG2_INTERFACE_MODE_BYTE          0x00000200
+
+//MII_CONFIG register
+#define MII_CONFIG_CLKSEL_DIV4            0x00000000
+#define MII_CONFIG_CLKSEL_DIV6            0x00000002
+#define MII_CONFIG_CLKSEL_DIV8            0x00000003
+#define MII_CONFIG_CLKSEL_DIV10           0x00000004
+#define MII_CONFIG_CLKSEL_DIV14           0x00000005
+#define MII_CONFIG_CLKSEL_DIV20           0x00000006
+#define MII_CONFIG_CLKSEL_DIV28           0x00000007
+
+//MII_COMMAND register
+#define MII_COMMAND_SCAN                  0x00000002
+#define MII_COMMAND_READ                  0x00000001
+
+//MII_ADDRESS register
+#define MII_ADDRESS_PHY_ADDR              0x00001F00
+#define MII_ADDRESS_REG_ADDR              0x0000001F
+
+#define MII_ADDRESS_PHY_ADDR_POS          8
+#define MII_ADDRESS_REG_ADDR_POS          0
+
+//MII_INDICATORS register
+#define MII_INDICATORS_NOT_VALID          0x00000004
+#define MII_INDICATORS_SCANNING           0x00000002
+#define MII_INDICATORS_BUSY               0x00000001
+
+//INTERFACE_CTRL register
+#define INTERFACE_CTRL_RESET              0x80000000
+#define INTERFACE_CTRL_TBI_MODE           0x08000000
+#define INTERFACE_CTRL_GHD_MODE           0x04000000
+#define INTERFACE_CTRL_LHD_MODE           0x02000000
+#define INTERFACE_CTRL_PHY_MODE           0x01000000
+#define INTERFACE_CTRL_RESET_PERMII       0x00800000
+#define INTERFACE_CTRL_SPEED              0x00010000
+#define INTERFACE_CTRL_RESET_PE100X       0x00008000
+#define INTERFACE_CTRL_FORCE_QUIET        0x00000400
+#define INTERFACE_CTRL_NO_CIPHER          0x00000200
+#define INTERFACE_CTRL_DISABLE_LINK_FAIL  0x00000100
+#define INTERFACE_CTRL_EN_JABBER_PROTECT  0x00000001
+
+//FIFO_CFG0 register
+#define FIFO_CFG0_STFENRPLY               0x00080000
+#define FIFO_CFG0_FRFENRPLY               0x00040000
+#define FIFO_CFG0_SRFENRPLY               0x00020000
+#define FIFO_CFG0_WTMENRPLY               0x00010000
+#define FIFO_CFG0_FTFENREQ                0x00001000
+#define FIFO_CFG0_STFENREQ                0x00000800
+#define FIFO_CFG0_FRFENREQ                0x00000400
+#define FIFO_CFG0_SRFENREQ                0x00000200
+#define FIFO_CFG0_WTMENREQ                0x00000100
+#define FIFO_CFG0_HSTRSTFT                0x00000010
+#define FIFO_CFG0_HSTRSTST                0x00000008
+#define FIFO_CFG0_HSTRSTFR                0x00000004
+#define FIFO_CFG0_HSTRSTSR                0x00000002
+#define FIFO_CFG0_HSTRSTWT                0x00000001
+
+//FIFO_CFG1 register
+#define FIFO_CFG1_CFGSRTH                 0x0FFF0000
+#define FIFO_CFG1_CFGXOFFRTX              0x0000FFFF
+
+#define FIFO_CFG1_DEFAULT_VALUE           0x0FFF0000
+
+//FIFO_CFG2 register
+#define FIFO_CFG2_CFGHWM                  0x1FFF0000
+#define FIFO_CFG2_CFGLWM                  0x00001FFF
+
+#define FIFO_CFG2_DEFAULT_VALUE           0x04000180
+
+//FIFO_CFG3 register
+#define FIFO_CFG3_CFGHWMFT                0x0FFF0000
+#define FIFO_CFG3_CFGFTTH                 0x00000FFF
+
+#define FIFO_CFG3_DEFAULT_VALUE           0x0258FFFF
+
+//FIFO_CFG4 register
+#define FIFO_CFG4_HSTFLTRFRM              0x0003FFFF
+#define FIFO_CFG4_RECEIVE_LONG_EVENT      0x00020000
+#define FIFO_CFG4_VLAN                    0x00010000
+#define FIFO_CFG4_CONTROL_NOT_PAUSE       0x00008000
+#define FIFO_CFG4_CONTROL_PAUSE           0x00004000
+#define FIFO_CFG4_CONTROL                 0x00002000
+#define FIFO_CFG4_TRUNCATED               0x00001000
+#define FIFO_CFG4_LONG_EVENT              0x00000800
+#define FIFO_CFG4_DRIBBLE_NIBBLE          0x00000400
+#define FIFO_CFG4_BROADCAST               0x00000200
+#define FIFO_CFG4_MULTICAST               0x00000100
+#define FIFO_CFG4_RECEPTION_OK            0x00000080
+#define FIFO_CFG4_TYPE_ERROR              0x00000040
+#define FIFO_CFG4_LENGTH_ERROR            0x00000020
+#define FIFO_CFG4_INVALID_CRC             0x00000010
+#define FIFO_CFG4_RECEIVE_ERROR           0x00000008
+#define FIFO_CFG4_FALSE_CARRIER           0x00000004
+#define FIFO_CFG4_RX_DV_EVENT             0x00000002
+#define FIFO_CFG4_PRIOR_PKT_DROPPED       0x00000001
+
+//FIFO_CFG5 register
+#define FIFO_CFG5_CFGHDPLX                0x00400000
+#define FIFO_CFG5_SRFULL                  0x00200000
+#define FIFO_CFG5_HSTSRFULLCLR            0x00100000
+#define FIFO_CFG5_CFGBYTMODE              0x00080000
+#define FIFO_CFG5_HSTDRPLT64              0x00040000
+#define FIFO_CFG5_HSTFLTRFRMDC            0x0003FFFF
+#define FIFO_CFG5_RECEIVE_LONG_EVENT      0x00020000
+#define FIFO_CFG5_VLAN                    0x00010000
+#define FIFO_CFG5_CONTROL_NOT_PAUSE       0x00008000
+#define FIFO_CFG5_CONTROL_PAUSE           0x00004000
+#define FIFO_CFG5_CONTROL                 0x00002000
+#define FIFO_CFG5_TRUNCATED               0x00001000
+#define FIFO_CFG5_LONG_EVENT              0x00000800
+#define FIFO_CFG5_DRIBBLE_NIBBLE          0x00000400
+#define FIFO_CFG5_BROADCAST               0x00000200
+#define FIFO_CFG5_MULTICAST               0x00000100
+#define FIFO_CFG5_RECEPTION_OK            0x00000080
+#define FIFO_CFG5_TYPE_ERROR              0x00000040
+#define FIFO_CFG5_LENGTH_ERROR            0x00000020
+#define FIFO_CFG5_INVALID_CRC             0x00000010
+#define FIFO_CFG5_RECEIVE_ERROR           0x00000008
+#define FIFO_CFG5_FALSE_CARRIER           0x00000004
+#define FIFO_CFG5_RX_DV_EVENT             0x00000002
+#define FIFO_CFG5_PRIOR_PKT_DROPPED       0x00000001
+
+//DMA descriptor flags
+#define DMA_DESC_EMPTY_FLAG               0x80000000
+#define DMA_DESC_SIZE_MASK                0x00000FFF
+
+
+/**
+ * @brief Transmit DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t addr;
+   uint32_t size;
+   uint32_t next;
+} M2sxxxTxDmaDesc;
+
+
+/**
+ * @brief Receive DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t addr;
+   uint32_t size;
+   uint32_t next;
+} M2sxxxRxDmaDesc;
+
+
+//M2Sxxx Ethernet MAC driver
+extern const NicDriver m2sxxxEthDriver;
+
+//M2Sxxx Ethernet MAC related functions
+error_t m2sxxxEthInit(NetInterface *interface);
+void m2sxxxEthInitGpio(NetInterface *interface);
+void m2sxxxEthInitDmaDesc(NetInterface *interface);
+
+void m2sxxxEthTick(NetInterface *interface);
+
+void m2sxxxEthEnableIrq(NetInterface *interface);
+void m2sxxxEthDisableIrq(NetInterface *interface);
+void m2sxxxEthEventHandler(NetInterface *interface);
+
+error_t m2sxxxEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t m2sxxxEthReceivePacket(NetInterface *interface);
+
+error_t m2sxxxEthSetMulticastFilter(NetInterface *interface);
+error_t m2sxxxEthUpdateMacConfig(NetInterface *interface);
+
+void m2sxxxEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t m2sxxxEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/mcf5225x_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,782 @@
+/**
+ * @file mcf5225x_eth.c
+ * @brief Coldfire V2 MCF5225x Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "mcf52259.h"
+#include "core/net.h"
+#include "drivers/mcf5225x_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//TX buffer
+static uint8_t txBuffer[MCF5225X_ETH_TX_BUFFER_COUNT][MCF5225X_ETH_TX_BUFFER_SIZE];
+//RX buffer
+static uint8_t rxBuffer[MCF5225X_ETH_RX_BUFFER_COUNT][MCF5225X_ETH_RX_BUFFER_SIZE];
+//TX buffer descriptors
+static Mcf5225xTxBufferDesc txBufferDesc[MCF5225X_ETH_TX_BUFFER_COUNT];
+//RX buffer descriptors
+static Mcf5225xRxBufferDesc rxBufferDesc[MCF5225X_ETH_RX_BUFFER_COUNT];
+
+//TX buffer index
+static uint_t txBufferIndex;
+//RX buffer index
+static uint_t rxBufferIndex;
+
+
+/**
+ * @brief MCF5225x Ethernet MAC driver
+ **/
+
+const NicDriver mcf5225xEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   mcf5225xEthInit,
+   mcf5225xEthTick,
+   mcf5225xEthEnableIrq,
+   mcf5225xEthDisableIrq,
+   mcf5225xEthEventHandler,
+   mcf5225xEthSendPacket,
+   mcf5225xEthSetMulticastFilter,
+   mcf5225xEthUpdateMacConfig,
+   mcf5225xEthWritePhyReg,
+   mcf5225xEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief MCF5225x Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mcf5225xEthInit(NetInterface *interface)
+{
+   error_t error;
+   uint_t i;
+   uint32_t value;
+
+   //Debug message
+   TRACE_INFO("Initializing MCF5225x Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //GPIO configuration
+   mcf5225xEthInitGpio(interface);
+
+   //Reset FEC module
+   MCF_FEC_ECR = MCF_FEC_ECR_RESET;
+   //Wait for the reset to complete
+   while(MCF_FEC_ECR & MCF_FEC_ECR_RESET);
+
+   //Reveive control register
+   MCF_FEC_RCR = MCF_FEC_RCR_MAX_FL(1518) | MCF_FEC_RCR_MII_MODE;
+   //Transmit control register
+   MCF_FEC_TCR = 0;
+   //Configure MDC clock frequency
+   MCF_FEC_MSCR = MCF_FEC_MSCR_MII_SPEED(19);
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Set the MAC address (upper 16 bits)
+   value = interface->macAddr.b[5];
+   value |= (interface->macAddr.b[4] << 8);
+   MCF_FEC_PAUR = MCF_FEC_PAUR_PADDR2(value) | MCF_FEC_PAUR_TYPE(0x8808);
+
+   //Set the MAC address (lower 32 bits)
+   value = interface->macAddr.b[3];
+   value |= (interface->macAddr.b[2] << 8);
+   value |= (interface->macAddr.b[1] << 16);
+   value |= (interface->macAddr.b[0] << 24);
+   MCF_FEC_PALR = MCF_FEC_PALR_PADDR1(value);
+
+   //Hash table for unicast address filtering
+   MCF_FEC_IALR = 0;
+   MCF_FEC_IAUR = 0;
+   //Hash table for multicast address filtering
+   MCF_FEC_GALR = 0;
+   MCF_FEC_GAUR = 0;
+
+   //Initialize buffer descriptors
+   mcf5225xEthInitBufferDesc(interface);
+
+   //Clear any pending interrupts
+   MCF_FEC_EIR = MCF_FEC_EIR_CLEAR_ALL;
+
+   //Enable desired interrupts
+   MCF_FEC_EIMR = MCF_FEC_EIMR_TXF | MCF_FEC_EIMR_TXB |
+      MCF_FEC_EIMR_RXF | MCF_FEC_EIMR_RXB | MCF_FEC_EIMR_EBERR;
+
+   //Set the priority of FEC interrupts
+   for(i = 23; i <= 35; i++)
+   {
+      MCF_INTC0_ICR(i) = MCF_INTC_ICR_IL(MCF5225X_ETH_IRQ_LEVEL) |
+         MCF_INTC_ICR_IP(MCF5225X_ETH_IRQ_PRIORITY);
+   }
+
+   //Enable Ethernet MAC
+   MCF_FEC_ECR |= MCF_FEC_ECR_ETHER_EN;
+   //Instruct the DMA to poll the receive descriptor list
+   MCF_FEC_RDAR = MCF_FEC_RDAR_R_DES_ACTIVE;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//TWR-MCF5225X evaluation board?
+#if defined(USE_TWR_MCF5225X)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void mcf5225xEthInitGpio(NetInterface *interface)
+{
+   uint8_t temp;
+
+   //Configure FEC_COL (PTI0), FEC_CRS (PTI1), FEC_RXCLK (PTI2), FEC_RXD0 (PTI3),
+   //FEC_RXD1 (PTI4), FEC_RXD2 (PTI5), FEC_RXD3 (PTI6) and FEC_RXDV (PTI7)
+   MCF_GPIO_PTIPAR |= MCF_GPIO_PTIPAR_FEC_COL_FEC_COL | MCF_GPIO_PTIPAR_FEC_CRS_FEC_CRS |
+      MCF_GPIO_PTIPAR_FEC_RXCLK_FEC_RXCLK | MCF_GPIO_PTIPAR_FEC_RXD0_FEC_RXD0 |
+      MCF_GPIO_PTIPAR_FEC_RXD1_FEC_RXD1 | MCF_GPIO_PTIPAR_FEC_RXD2_FEC_RXD2 |
+      MCF_GPIO_PTIPAR_FEC_RXD3_FEC_RXD3 | MCF_GPIO_PTIPAR_FEC_RXDV_FEC_RXDV;
+
+   //Configure FEC_RXER (PTJ0), FEC_TXCLK (PTJ1), FEC_TXD0 (PTJ2), FEC_TXD1 (PTJ3)
+   //FEC_TXD2 (PTJ4), FEC_TXD3 (PTJ5), FEC_TXEN (PTJ6) and FEC_TXER (PTJ7)
+   MCF_GPIO_PTJPAR |= MCF_GPIO_PTJPAR_FEC_RXER_FEC_RXER | MCF_GPIO_PTJPAR_FEC_TXCLK_FEC_TXCLK |
+      MCF_GPIO_PTJPAR_FEC_TXD0_FEC_TXD0 | MCF_GPIO_PTJPAR_FEC_TXD1_FEC_TXD1 |
+      MCF_GPIO_PTJPAR_FEC_TXD2_FEC_TXD2 | MCF_GPIO_PTJPAR_FEC_TXD3_FEC_TXD3 |
+      MCF_GPIO_PTJPAR_FEC_TXEN_FEC_TXEN | MCF_GPIO_PTJPAR_FEC_TXER_FEC_TXER;
+
+   //Configure FEC_MDIO (PNQ3)
+   temp = MCF_GPIO_PNQPAR & ~MCF_GPIO_PNQPAR_PNQPAR3(3);
+   MCF_GPIO_PNQPAR = temp | MCF_GPIO_PNQPAR_IRQ3_FEC_MDIO;
+
+   //Configure FEC_MDC (PNQ5)
+   temp = MCF_GPIO_PNQPAR & ~MCF_GPIO_PNQPAR_PNQPAR5(3);
+   MCF_GPIO_PNQPAR = temp | MCF_GPIO_PNQPAR_IRQ5_FEC_MDC;
+
+   //Reset PHY transceiver
+   MCF_RCM_RCR |= MCF_RCM_RCR_FRCRSTOUT;
+   sleep(10);
+
+   //Take the PHY transceiver out of reset
+   MCF_RCM_RCR &= ~MCF_RCM_RCR_FRCRSTOUT;
+   sleep(10);
+}
+
+#endif
+
+
+/**
+ * @brief Initialize buffer descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void mcf5225xEthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX buffer descriptors
+   for(i = 0; i < MCF5225X_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the software
+      txBufferDesc[i].status = 0;
+      //Transmit buffer length
+      txBufferDesc[i].length = 0;
+      //Transmit buffer address
+      txBufferDesc[i].address = (uint32_t) FEC_ALIGN16(txBuffer[i]);
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   txBufferDesc[i - 1].status |= FEC_TX_BD_W;
+   //Initialize TX buffer index
+   txBufferIndex = 0;
+
+   //Initialize RX buffer descriptors
+   for(i = 0; i < MCF5225X_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the DMA
+      rxBufferDesc[i].status = FEC_RX_BD_E;
+      //Receive buffer length
+      rxBufferDesc[i].length = 0;
+      //Receive buffer address
+      rxBufferDesc[i].address = (uint32_t) FEC_ALIGN16(rxBuffer[i]);
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   rxBufferDesc[i - 1].status |= FEC_RX_BD_W;
+   //Initialize RX buffer index
+   rxBufferIndex = 0;
+
+   //Start location of the TX descriptor list
+   MCF_FEC_ETSDR = (uint32_t) txBufferDesc;
+   //Start location of the RX descriptor list
+   MCF_FEC_ERDSR = (uint32_t) rxBufferDesc;
+   //Maximum receive buffer size
+   MCF_FEC_EMRBR = MCF5225X_ETH_RX_BUFFER_SIZE;
+}
+
+
+/**
+ * @brief MCF5225x Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void mcf5225xEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void mcf5225xEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   MCF_INTC0_IMRL &= ~(MCF_INTC_IMRL_INT_MASK23 |
+      MCF_INTC_IMRL_INT_MASK24 | MCF_INTC_IMRL_INT_MASK25 |
+      MCF_INTC_IMRL_INT_MASK26 | MCF_INTC_IMRL_INT_MASK27 |
+      MCF_INTC_IMRL_INT_MASK28 | MCF_INTC_IMRL_INT_MASK29 |
+      MCF_INTC_IMRL_INT_MASK30 | MCF_INTC_IMRL_INT_MASK31);
+
+   MCF_INTC0_IMRH &= ~(MCF_INTC_IMRH_INT_MASK33 |
+      MCF_INTC_IMRH_INT_MASK34| MCF_INTC_IMRH_INT_MASK35);
+
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void mcf5225xEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   MCF_INTC0_IMRL |= MCF_INTC_IMRL_INT_MASK23 |
+      MCF_INTC_IMRL_INT_MASK24 | MCF_INTC_IMRL_INT_MASK25 |
+      MCF_INTC_IMRL_INT_MASK26 | MCF_INTC_IMRL_INT_MASK27 |
+      MCF_INTC_IMRL_INT_MASK28 | MCF_INTC_IMRL_INT_MASK29 |
+      MCF_INTC_IMRL_INT_MASK30 | MCF_INTC_IMRL_INT_MASK31;
+
+   MCF_INTC0_IMRH |= MCF_INTC_IMRH_INT_MASK33 |
+      MCF_INTC_IMRH_INT_MASK34| MCF_INTC_IMRH_INT_MASK35;
+
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief Ethernet MAC interrupt
+ **/
+
+__declspec(interrupt) void mcf5225xEthIrqHandler(void)
+{
+   bool_t flag;
+   uint32_t events;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+   //Read interrupt event register
+   events = MCF_FEC_EIR;
+
+   //A packet has been transmitted?
+   if(events & (MCF_FEC_EIR_TXF | MCF_FEC_EIR_TXB))
+   {
+      //Clear TXF and TXB interrupt flags
+      MCF_FEC_EIR = MCF_FEC_EIR_TXF | MCF_FEC_EIR_TXB;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txBufferDesc[txBufferIndex].status & FEC_TX_BD_R))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+
+      //Instruct the DMA to poll the transmit descriptor list
+      MCF_FEC_TDAR = MCF_FEC_TDAR_X_DES_ACTIVE;
+   }
+
+   //A packet has been received?
+   if(events & (MCF_FEC_EIR_RXF | MCF_FEC_EIR_RXB))
+   {
+      //Disable RXF and RXB interrupts
+      MCF_FEC_EIMR &= ~(MCF_FEC_EIMR_RXF | MCF_FEC_EIMR_RXB);
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //System bus error?
+   if(events & MCF_FEC_EIR_EBERR)
+   {
+      //Disable EBERR interrupt
+      MCF_FEC_EIMR &= ~MCF_FEC_EIMR_EBERR;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Any other event?
+   if(events & (MCF_FEC_EIR_HBERR | MCF_FEC_EIR_BABR | MCF_FEC_EIR_BABT | MCF_FEC_EIR_GRA |
+      MCF_FEC_EIR_MII | MCF_FEC_EIR_LC | MCF_FEC_EIR_RL | MCF_FEC_EIR_UN))
+   {
+      //Clear interrupt flags
+      MCF_FEC_EIR = MCF_FEC_EIR_HBERR | MCF_FEC_EIR_BABR | MCF_FEC_EIR_BABT | MCF_FEC_EIR_GRA |
+         MCF_FEC_EIR_MII | MCF_FEC_EIR_LC | MCF_FEC_EIR_RL | MCF_FEC_EIR_UN;
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief MCF5225x Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void mcf5225xEthEventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint32_t status;
+
+   //Read interrupt event register
+   status = MCF_FEC_EIR;
+
+   //Packet received?
+   if(status & (MCF_FEC_EIR_RXF | MCF_FEC_EIR_RXB))
+   {
+      //Clear RXF and RXB interrupt flag
+      MCF_FEC_EIR = MCF_FEC_EIR_RXF | MCF_FEC_EIR_RXB;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = mcf5225xEthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //System bus error?
+   if(status & MCF_FEC_EIR_EBERR)
+   {
+      //Clear EBERR interrupt flag
+      MCF_FEC_EIR = MCF_FEC_EIR_EBERR;
+
+      //Disable Ethernet MAC
+      MCF_FEC_ECR &= ~MCF_FEC_ECR_ETHER_EN;
+      //Reset buffer descriptors
+      mcf5225xEthInitBufferDesc(interface);
+      //Resume normal operation
+      MCF_FEC_ECR |= MCF_FEC_ECR_ETHER_EN;
+      //Instruct the DMA to poll the receive descriptor list
+      MCF_FEC_RDAR = MCF_FEC_RDAR_R_DES_ACTIVE;
+   }
+
+   //Re-enable Ethernet MAC interrupts
+   MCF_FEC_EIMR = MCF_FEC_EIMR_TXF | MCF_FEC_EIMR_TXB |
+      MCF_FEC_EIMR_RXF | MCF_FEC_EIMR_RXB | MCF_FEC_EIMR_EBERR;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t mcf5225xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > MCF5225X_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txBufferDesc[txBufferIndex].status & FEC_TX_BD_R)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(FEC_ALIGN16(txBuffer[txBufferIndex]), buffer, offset, length);
+
+   //Set frame length
+   txBufferDesc[txBufferIndex].length = length;
+
+   //Check current index
+   if(txBufferIndex < (MCF5225X_ETH_TX_BUFFER_COUNT - 1))
+   {
+      //Give the ownership of the descriptor to the DMA engine
+      txBufferDesc[txBufferIndex].status = FEC_TX_BD_R |
+         FEC_TX_BD_L | FEC_TX_BD_TC;
+
+      //Point to the next buffer
+      txBufferIndex++;
+   }
+   else
+   {
+      //Give the ownership of the descriptor to the DMA engine
+      txBufferDesc[txBufferIndex].status = FEC_TX_BD_R |
+         FEC_TX_BD_W | FEC_TX_BD_L | FEC_TX_BD_TC;
+
+      //Wrap around
+      txBufferIndex = 0;
+   }
+
+   //Instruct the DMA to poll the transmit descriptor list
+   MCF_FEC_TDAR = MCF_FEC_TDAR_X_DES_ACTIVE;
+
+   //Check whether the next buffer is available for writing
+   if(!(txBufferDesc[txBufferIndex].status & FEC_TX_BD_R))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mcf5225xEthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+
+   //Make sure the current buffer is available for reading
+   if(!(rxBufferDesc[rxBufferIndex].status & FEC_RX_BD_E))
+   {
+      //The frame should not span multiple buffers
+      if(rxBufferDesc[rxBufferIndex].status & FEC_RX_BD_L)
+      {
+         //Check whether an error occurred
+         if(!(rxBufferDesc[rxBufferIndex].status & (FEC_RX_BD_LG |
+            FEC_RX_BD_NO | FEC_RX_BD_CR | FEC_RX_BD_OV | FEC_RX_BD_TR)))
+         {
+            //Retrieve the length of the frame
+            n = rxBufferDesc[rxBufferIndex].length;
+            //Limit the number of data to read
+            n = MIN(n, MCF5225X_ETH_RX_BUFFER_SIZE);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, FEC_ALIGN16(rxBuffer[rxBufferIndex]), n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Check current index
+      if(rxBufferIndex < (MCF5225X_ETH_RX_BUFFER_COUNT - 1))
+      {
+         //Give the ownership of the descriptor back to the DMA engine
+         rxBufferDesc[rxBufferIndex].status = FEC_RX_BD_E;
+         //Point to the next buffer
+         rxBufferIndex++;
+      }
+      else
+      {
+         //Give the ownership of the descriptor back to the DMA engine
+         rxBufferDesc[rxBufferIndex].status = FEC_RX_BD_E | FEC_RX_BD_W;
+         //Wrap around
+         rxBufferIndex = 0;
+      }
+
+      //Instruct the DMA to poll the receive descriptor list
+      MCF_FEC_RDAR = MCF_FEC_RDAR_R_DES_ACTIVE;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mcf5225xEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating MCF5225x hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = mcf5225xEthCalcCrc(&entry->addr, sizeof(MacAddr));
+
+         //The upper 6 bits in the CRC register are used to index the
+         //contents of the hash table
+         k = (crc >> 26) & 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   MCF_FEC_GALR = hashTable[0];
+   MCF_FEC_GAUR = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  GALR = %08" PRIX32 "\r\n", MCF_FEC_GALR);
+   TRACE_DEBUG("  GAUR = %08" PRIX32 "\r\n", MCF_FEC_GAUR);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mcf5225xEthUpdateMacConfig(NetInterface *interface)
+{
+   //Disable Ethernet MAC while modifying configuration registers
+   MCF_FEC_ECR &= ~MCF_FEC_ECR_ETHER_EN;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+   {
+      //Full-duplex mode
+      MCF_FEC_TCR |= MCF_FEC_TCR_FDEN;
+      //Receive path operates independently of transmit
+      MCF_FEC_RCR &= ~MCF_FEC_RCR_DRT;
+   }
+   else
+   {
+      //Half-duplex mode
+      MCF_FEC_TCR &= ~MCF_FEC_TCR_FDEN;
+      //Disable reception of frames while transmitting
+      MCF_FEC_RCR |= MCF_FEC_RCR_DRT;
+   }
+
+   //Reset buffer descriptors
+   mcf5225xEthInitBufferDesc(interface);
+
+   //Re-enable Ethernet MAC
+   MCF_FEC_ECR |= MCF_FEC_ECR_ETHER_EN;
+   //Instruct the DMA to poll the receive descriptor list
+   MCF_FEC_RDAR = MCF_FEC_RDAR_R_DES_ACTIVE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void mcf5225xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Set up a write operation
+   value = MCF_FEC_MMFR_ST(1) | MCF_FEC_MMFR_OP(1) | MCF_FEC_MMFR_TA(2);
+   //PHY address
+   value |= MCF_FEC_MMFR_PA(phyAddr);
+   //Register address
+   value |= MCF_FEC_MMFR_RA(regAddr);
+   //Register value
+   value |= MCF_FEC_MMFR_DATA(data);
+
+   //Clear MII interrupt flag
+   MCF_FEC_EIR = MCF_FEC_EIR_MII;
+   //Start a write operation
+   MCF_FEC_MMFR = value;
+   //Wait for the write to complete
+   while(!(MCF_FEC_EIR & MCF_FEC_EIR_MII));
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t mcf5225xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Set up a read operation
+   value = MCF_FEC_MMFR_ST(1) | MCF_FEC_MMFR_OP(2) | MCF_FEC_MMFR_TA(2);
+   //PHY address
+   value |= MCF_FEC_MMFR_PA(phyAddr);
+   //Register address
+   value |= MCF_FEC_MMFR_RA(regAddr);
+
+   //Clear MII interrupt flag
+   MCF_FEC_EIR = MCF_FEC_EIR_MII;
+   //Start a read operation
+   MCF_FEC_MMFR = value;
+   //Wait for the read to complete
+   while(!(MCF_FEC_EIR & MCF_FEC_EIR_MII));
+
+   //Return PHY register contents
+   return MCF_FEC_MMFR;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t mcf5225xEthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //Update CRC value
+      crc ^= p[i];
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         if(crc & 0x00000001)
+            crc = (crc >> 1) ^ 0xEDB88320;
+         else
+            crc = crc >> 1;
+      }
+   }
+
+   //Return CRC value
+   return crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/mcf5225x_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,154 @@
+/**
+ * @file mcf5225x_eth.h
+ * @brief Coldfire V2 MCF5225x Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MCF5225X_ETH_H
+#define _MCF5225X_ETH_H
+
+//Number of TX buffers
+#ifndef MCF5225X_ETH_TX_BUFFER_COUNT
+   #define MCF5225X_ETH_TX_BUFFER_COUNT 2
+#elif (MCF5225X_ETH_TX_BUFFER_COUNT < 1)
+   #error MCF5225X_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef MCF5225X_ETH_TX_BUFFER_SIZE
+   #define MCF5225X_ETH_TX_BUFFER_SIZE 1536
+#elif (MCF5225X_ETH_TX_BUFFER_SIZE != 1536)
+   #error MCF5225X_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef MCF5225X_ETH_RX_BUFFER_COUNT
+   #define MCF5225X_ETH_RX_BUFFER_COUNT 4
+#elif (MCF5225X_ETH_RX_BUFFER_COUNT < 1)
+   #error MCF5225X_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef MCF5225X_ETH_RX_BUFFER_SIZE
+   #define MCF5225X_ETH_RX_BUFFER_SIZE 1536
+#elif (MCF5225X_ETH_RX_BUFFER_SIZE != 1536)
+   #error MCF5225X_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Ethernet interrupt level
+#ifndef MCF5225X_ETH_IRQ_LEVEL
+   #define MCF5225X_ETH_IRQ_LEVEL 4
+#elif (MCF5225X_ETH_IRQ_LEVEL < 0)
+   #error MCF5225X_ETH_IRQ_LEVEL parameter is not valid
+#endif
+
+//Ethernet interrupt priority
+#ifndef MCF5225X_ETH_IRQ_PRIORITY
+   #define MCF5225X_ETH_IRQ_PRIORITY 1
+#elif (MCF5225X_ETH_IRQ_PRIORITY < 0)
+   #error MCF5225X_ETH_IRQ_PRIORITY parameter is not valid
+#endif
+
+//Align to 16-byte boundary
+#define FEC_ALIGN16(p) ((void *) ((((uint32_t) (p)) + 15) & 0xFFFFFFF0))
+
+//Transmit buffer descriptor
+#define FEC_TX_BD_R   0x8000
+#define FEC_TX_BD_TO1 0x4000
+#define FEC_TX_BD_W   0x2000
+#define FEC_TX_BD_TO2 0x1000
+#define FEC_TX_BD_L   0x0800
+#define FEC_TX_BD_TC  0x0400
+#define FEC_TX_BD_ABC 0x0200
+
+//Receive buffer descriptor
+#define FEC_RX_BD_E   0x8000
+#define FEC_RX_BD_RO1 0x4000
+#define FEC_RX_BD_W   0x2000
+#define FEC_RX_BD_RO2 0x1000
+#define FEC_RX_BD_L   0x0800
+#define FEC_RX_BD_M   0x0100
+#define FEC_RX_BD_BC  0x0080
+#define FEC_RX_BD_MC  0x0040
+#define FEC_RX_BD_LG  0x0020
+#define FEC_RX_BD_NO  0x0010
+#define FEC_RX_BD_CR  0x0004
+#define FEC_RX_BD_OV  0x0002
+#define FEC_RX_BD_TR  0x0001
+
+
+/**
+ * @brief Transmit buffer descriptor
+ **/
+
+typedef struct
+{
+   uint16_t status;
+   uint16_t length;
+   uint32_t address;
+} Mcf5225xTxBufferDesc;
+
+
+/**
+ * @brief Receive buffer descriptor
+ **/
+
+typedef struct
+{
+   uint16_t status;
+   uint16_t length;
+   uint32_t address;
+} Mcf5225xRxBufferDesc;
+
+
+//MCF5225x Ethernet MAC driver
+extern const NicDriver mcf5225xEthDriver;
+
+//MCF5225x Ethernet MAC related functions
+error_t mcf5225xEthInit(NetInterface *interface);
+void mcf5225xEthInitGpio(NetInterface *interface);
+void mcf5225xEthInitBufferDesc(NetInterface *interface);
+
+void mcf5225xEthTick(NetInterface *interface);
+
+void mcf5225xEthEnableIrq(NetInterface *interface);
+void mcf5225xEthDisableIrq(NetInterface *interface);
+void mcf5225xEthEventHandler(NetInterface *interface);
+
+error_t mcf5225xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t mcf5225xEthReceivePacket(NetInterface *interface);
+
+error_t mcf5225xEthSetMulticastFilter(NetInterface *interface);
+error_t mcf5225xEthUpdateMacConfig(NetInterface *interface);
+
+void mcf5225xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t mcf5225xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t mcf5225xEthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/mk6x_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,975 @@
+/**
+ * @file mk6x_eth.c
+ * @brief Freescale Kinetis K60/K64/K65/K66 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//MK60N512MD100 device?
+#if defined(MK60N512MD100)
+   #include "mk60n512md100.h"
+//MK60D10 device?
+#elif defined(MK60D10)
+   #include "mk60d10.h"
+//MK60F12 device?
+#elif defined(MK60F12)
+   #include "mk60f12.h"
+//MK64F12 device?
+#elif defined(MK64F12)
+   #include "mk64f12.h"
+//MK65F18 device?
+#elif defined(MK65F18)
+   #include "mk65f18.h"
+//MK66F18 device?
+#elif defined(MK66F18)
+   #include "mk66f18.h"
+#endif
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/mk6x_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//TX buffer
+#pragma data_alignment = 16
+static uint8_t txBuffer[MK6X_ETH_TX_BUFFER_COUNT][MK6X_ETH_TX_BUFFER_SIZE];
+//RX buffer
+#pragma data_alignment = 16
+static uint8_t rxBuffer[MK6X_ETH_RX_BUFFER_COUNT][MK6X_ETH_RX_BUFFER_SIZE];
+//TX buffer descriptors
+#pragma data_alignment = 16
+static uint16_t txBufferDesc[MK6X_ETH_TX_BUFFER_COUNT][16];
+//RX buffer descriptors
+#pragma data_alignment = 16
+static uint16_t rxBufferDesc[MK6X_ETH_RX_BUFFER_COUNT][16];
+
+//ARM or GCC compiler?
+#else
+
+//TX buffer
+static uint8_t txBuffer[MK6X_ETH_TX_BUFFER_COUNT][MK6X_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(16)));
+//RX buffer
+static uint8_t rxBuffer[MK6X_ETH_RX_BUFFER_COUNT][MK6X_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(16)));
+//TX buffer descriptors
+static uint16_t txBufferDesc[MK6X_ETH_TX_BUFFER_COUNT][16]
+   __attribute__((aligned(16)));
+//RX buffer descriptors
+static uint16_t rxBufferDesc[MK6X_ETH_RX_BUFFER_COUNT][16]
+   __attribute__((aligned(16)));
+
+#endif
+
+//TX buffer index
+static uint_t txBufferIndex;
+//RX buffer index
+static uint_t rxBufferIndex;
+
+
+/**
+ * @brief Kinetis K6x Ethernet MAC driver
+ **/
+
+const NicDriver mk6xEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   mk6xEthInit,
+   mk6xEthTick,
+   mk6xEthEnableIrq,
+   mk6xEthDisableIrq,
+   mk6xEthEventHandler,
+   mk6xEthSendPacket,
+   mk6xEthSetMulticastFilter,
+   mk6xEthUpdateMacConfig,
+   mk6xEthWritePhyReg,
+   mk6xEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief Kinetis K6x Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mk6xEthInit(NetInterface *interface)
+{
+   error_t error;
+   uint32_t value;
+
+   //Debug message
+   TRACE_INFO("Initializing Kinetis K6x Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Disable MPU
+   MPU->CESR &= ~MPU_CESR_VLD_MASK;
+
+   //Enable external reference clock
+   OSC->CR |= OSC_CR_ERCLKEN_MASK;
+   //Enable ENET peripheral clock
+   SIM->SCGC2 |= SIM_SCGC2_ENET_MASK;
+
+   //GPIO configuration
+   mk6xEthInitGpio(interface);
+
+   //Reset ENET module
+   ENET->ECR = ENET_ECR_RESET_MASK;
+   //Wait for the reset to complete
+   while(ENET->ECR & ENET_ECR_RESET_MASK);
+
+   //Receive control register
+   ENET->RCR = ENET_RCR_MAX_FL(1518) | ENET_RCR_RMII_MODE_MASK | ENET_RCR_MII_MODE_MASK;
+   //Transmit control register
+   ENET->TCR = 0;
+   //Configure MDC clock frequency
+   ENET->MSCR = ENET_MSCR_MII_SPEED(59);
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Set the MAC address (upper 16 bits)
+   value = interface->macAddr.b[5];
+   value |= (interface->macAddr.b[4] << 8);
+   ENET->PAUR = ENET_PAUR_PADDR2(value) | ENET_PAUR_TYPE(0x8808);
+
+   //Set the MAC address (lower 32 bits)
+   value = interface->macAddr.b[3];
+   value |= (interface->macAddr.b[2] << 8);
+   value |= (interface->macAddr.b[1] << 16);
+   value |= (interface->macAddr.b[0] << 24);
+   ENET->PALR = ENET_PALR_PADDR1(value);
+
+   //Hash table for unicast address filtering
+   ENET->IALR = 0;
+   ENET->IAUR = 0;
+   //Hash table for multicast address filtering
+   ENET->GALR = 0;
+   ENET->GAUR = 0;
+
+   //Disable transmit accelerator functions
+   ENET->TACC = 0;
+   //Disable receive accelerator functions
+   ENET->RACC = 0;
+
+   //Use enhanced buffer descriptors
+   ENET->ECR = ENET_ECR_EN1588_MASK;
+   //Clear MIC counters
+   ENET->MIBC = ENET_MIBC_MIB_CLEAR_MASK;
+
+   //Initialize buffer descriptors
+   mk6xEthInitBufferDesc(interface);
+
+   //Clear any pending interrupts
+   ENET->EIR = 0xFFFFFFFF;
+   //Enable desired interrupts
+   ENET->EIMR = ENET_EIMR_TXF_MASK | ENET_EIMR_RXF_MASK | ENET_EIMR_EBERR_MASK;
+
+   //Set priority grouping (4 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(MK6X_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure ENET transmit interrupt priority
+   NVIC_SetPriority(ENET_Transmit_IRQn, NVIC_EncodePriority(MK6X_ETH_IRQ_PRIORITY_GROUPING,
+      MK6X_ETH_IRQ_GROUP_PRIORITY, MK6X_ETH_IRQ_SUB_PRIORITY));
+
+   //Configure ENET receive interrupt priority
+   NVIC_SetPriority(ENET_Receive_IRQn, NVIC_EncodePriority(MK6X_ETH_IRQ_PRIORITY_GROUPING,
+      MK6X_ETH_IRQ_GROUP_PRIORITY, MK6X_ETH_IRQ_SUB_PRIORITY));
+
+   //Configure ENET error interrupt priority
+   NVIC_SetPriority(ENET_Error_IRQn, NVIC_EncodePriority(MK6X_ETH_IRQ_PRIORITY_GROUPING,
+      MK6X_ETH_IRQ_GROUP_PRIORITY, MK6X_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable Ethernet MAC
+   ENET->ECR |= ENET_ECR_ETHEREN_MASK;
+   //Instruct the DMA to poll the receive descriptor list
+   ENET->RDAR = ENET_RDAR_RDAR_MASK;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//FRDM-K64F, FRDM-K66F, TWR-K60N512, TWR-K60F120M,
+//TWR-K64F120M or TWR-K65F180M evaluation board?
+#if defined(USE_FRDM_K64F) || defined(USE_FRDM_K66F) || \
+   defined(USE_TWR_K60N512) || defined(USE_TWR_K60F120M) || \
+   defined(USE_TWR_K64F120M) || defined(USE_TWR_K65F180M)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void mk6xEthInitGpio(NetInterface *interface)
+{
+//TWR-K60N512 or TWR-K60F120M evaluation board?
+#if defined(USE_TWR_K60N512) || defined(USE_TWR_K60F120M)
+   //Enable PORTA and PORTB peripheral clocks
+   SIM->SCGC5 |= SIM_SCGC5_PORTA_MASK | SIM_SCGC5_PORTB_MASK;
+
+   //Configure RMII0_RXER (PTA5)
+   PORTA->PCR[5] = PORT_PCR_MUX(4) | PORT_PCR_PE_MASK;
+   //Configure RMII0_RXD1 (PTA12)
+   PORTA->PCR[12] = PORT_PCR_MUX(4);
+   //Configure RMII0_RXD0 (PTA13)
+   PORTA->PCR[13] = PORT_PCR_MUX(4);
+   //Configure RMII0_CRS_DV (PTA14)
+   PORTA->PCR[14] = PORT_PCR_MUX(4);
+   //Configure RMII0_TXEN (PTA15)
+   PORTA->PCR[15] = PORT_PCR_MUX(4);
+   //Configure RMII0_TXD0 (PTA16)
+   PORTA->PCR[16] = PORT_PCR_MUX(4);
+   //Configure RMII0_TXD1 (PTA17)
+   PORTA->PCR[17] = PORT_PCR_MUX(4);
+
+   //Configure RMII0_MDIO (PTB0)
+   PORTB->PCR[0] = PORT_PCR_MUX(4) | PORT_PCR_PE_MASK | PORT_PCR_PS_MASK;
+   //Configure RMII0_MDC (PTB1)
+   PORTB->PCR[1] = PORT_PCR_MUX(4);
+
+//FRDM-K64F or TWR-K64F120M evaluation board?
+#elif defined(USE_FRDM_K64F) || defined(USE_TWR_K64F120M)
+   //Enable PORTA and PORTB peripheral clocks
+   SIM->SCGC5 |= SIM_SCGC5_PORTA_MASK | SIM_SCGC5_PORTB_MASK;
+
+   //Configure RMII0_RXER (PTA5)
+   PORTA->PCR[5] = PORT_PCR_MUX(4) | PORT_PCR_PE_MASK;
+   //Configure RMII0_RXD1 (PTA12)
+   PORTA->PCR[12] = PORT_PCR_MUX(4);
+   //Configure RMII0_RXD0 (PTA13)
+   PORTA->PCR[13] = PORT_PCR_MUX(4);
+   //Configure RMII0_CRS_DV (PTA14)
+   PORTA->PCR[14] = PORT_PCR_MUX(4);
+   //Configure RMII0_TXEN (PTA15)
+   PORTA->PCR[15] = PORT_PCR_MUX(4);
+   //Configure RMII0_TXD0 (PTA16)
+   PORTA->PCR[16] = PORT_PCR_MUX(4);
+   //Configure RMII0_TXD1 (PTA17)
+   PORTA->PCR[17] = PORT_PCR_MUX(4);
+
+   //Configure RMII0_MDIO (PTB0)
+   PORTB->PCR[0] = PORT_PCR_MUX(4) | PORT_PCR_PE_MASK | PORT_PCR_PS_MASK;
+   //Configure RMII0_MDC (PTB1)
+   PORTB->PCR[1] = PORT_PCR_MUX(4);
+
+   //Select RMII clock source (EXTAL)
+   SIM->SOPT2 &= ~SIM_SOPT2_RMIISRC_MASK;
+
+//TWR-K65F180M evaluation board?
+#elif defined(USE_TWR_K65F180M)
+   //Enable PORTA and PORTE peripheral clocks
+   SIM->SCGC5 |= SIM_SCGC5_PORTA_MASK | SIM_SCGC5_PORTE_MASK;
+
+   //Configure RMII0_RXER (PTA5)
+   PORTA->PCR[5] = PORT_PCR_MUX(4) | PORT_PCR_PE_MASK;
+   //Configure RMII0_MDIO (PTA7)
+   PORTA->PCR[7] = PORT_PCR_MUX(5) | PORT_PCR_PE_MASK | PORT_PCR_PS_MASK;
+   //Configure RMII0_MDC (PTA8)
+   PORTA->PCR[8] = PORT_PCR_MUX(5);
+   //Configure RMII0_RXD1 (PTA12)
+   PORTA->PCR[12] = PORT_PCR_MUX(4);
+   //Configure RMII0_RXD0 (PTA13)
+   PORTA->PCR[13] = PORT_PCR_MUX(4);
+   //Configure RMII0_CRS_DV (PTA14)
+   PORTA->PCR[14] = PORT_PCR_MUX(4);
+   //Configure RMII0_TXEN (PTA15)
+   PORTA->PCR[15] = PORT_PCR_MUX(4);
+   //Configure RMII0_TXD0 (PTA16)
+   PORTA->PCR[16] = PORT_PCR_MUX(4);
+   //Configure RMII0_TXD1 (PTA17)
+   PORTA->PCR[17] = PORT_PCR_MUX(4);
+
+   //Configure ENET_1588_CLKIN (PTE26)
+   PORTE->PCR[26] = PORT_PCR_MUX(2);
+
+   //Select RMII clock source (ENET_1588_CLKIN)
+   SIM->SOPT2 |= SIM_SOPT2_RMIISRC_MASK;
+
+//FRDM-K66F evaluation board?
+#elif defined(USE_FRDM_K66F)
+//Enable PORTA, PORTB and PORTE peripheral clocks
+   SIM->SCGC5 |= SIM_SCGC5_PORTA_MASK |
+      SIM_SCGC5_PORTB_MASK | SIM_SCGC5_PORTE_MASK;
+
+   //Configure RMII0_RXER (PTA5)
+   PORTA->PCR[5] = PORT_PCR_MUX(4) | PORT_PCR_PE_MASK;
+   //Configure RMII0_RXD1 (PTA12)
+   PORTA->PCR[12] = PORT_PCR_MUX(4);
+   //Configure RMII0_RXD0 (PTA13)
+   PORTA->PCR[13] = PORT_PCR_MUX(4);
+   //Configure RMII0_CRS_DV (PTA14)
+   PORTA->PCR[14] = PORT_PCR_MUX(4);
+   //Configure RMII0_TXEN (PTA15)
+   PORTA->PCR[15] = PORT_PCR_MUX(4);
+   //Configure RMII0_TXD0 (PTA16)
+   PORTA->PCR[16] = PORT_PCR_MUX(4);
+   //Configure RMII0_TXD1 (PTA17)
+   PORTA->PCR[17] = PORT_PCR_MUX(4);
+
+   //Configure RMII0_MDIO (PTB0)
+   PORTB->PCR[0] = PORT_PCR_MUX(4) | PORT_PCR_PE_MASK | PORT_PCR_PS_MASK;
+   //Configure RMII0_MDC (PTB1)
+   PORTB->PCR[1] = PORT_PCR_MUX(4);
+
+   //Configure ENET_1588_CLKIN (PTE26)
+   PORTE->PCR[26] = PORT_PCR_MUX(2);
+
+   //Select RMII clock source (ENET_1588_CLKIN)
+   SIM->SOPT2 |= SIM_SOPT2_RMIISRC_MASK;
+#endif
+}
+
+#endif
+
+
+/**
+ * @brief Initialize buffer descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void mk6xEthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+   uint32_t address;
+
+   //Clear TX and RX buffer descriptors
+   memset(txBufferDesc, 0, sizeof(txBufferDesc));
+   memset(rxBufferDesc, 0, sizeof(rxBufferDesc));
+
+   //Initialize TX buffer descriptors
+   for(i = 0; i < MK6X_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current TX buffer
+      address = (uint32_t) txBuffer[i];
+      //Transmit buffer address
+      txBufferDesc[i][2] = htobe16(address >> 16);
+      txBufferDesc[i][3] = htobe16(address & 0xFFFF);
+      //Generate interrupts
+      txBufferDesc[i][4] = HTOBE16(ENET_TBD4_INT);
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   txBufferDesc[i - 1][0] |= HTOBE16(ENET_TBD0_W);
+   //Initialize TX buffer index
+   txBufferIndex = 0;
+
+   //Initialize RX buffer descriptors
+   for(i = 0; i < MK6X_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current RX buffer
+      address = (uint32_t) rxBuffer[i];
+      //The descriptor is initially owned by the DMA
+      rxBufferDesc[i][0] = HTOBE16(ENET_RBD0_E);
+      //Receive buffer address
+      rxBufferDesc[i][2] = htobe16(address >> 16);
+      rxBufferDesc[i][3] = htobe16(address & 0xFFFF);
+      //Generate interrupts
+      rxBufferDesc[i][4] = HTOBE16(ENET_RBD4_INT);
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   rxBufferDesc[i - 1][0] |= HTOBE16(ENET_RBD0_W);
+   //Initialize RX buffer index
+   rxBufferIndex = 0;
+
+   //Start location of the TX descriptor list
+   ENET->TDSR = (uint32_t) txBufferDesc;
+   //Start location of the RX descriptor list
+   ENET->RDSR = (uint32_t) rxBufferDesc;
+   //Maximum receive buffer size
+   ENET->MRBR = MK6X_ETH_RX_BUFFER_SIZE;
+}
+
+
+/**
+ * @brief Kinetis K6x Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void mk6xEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void mk6xEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(ENET_Transmit_IRQn);
+   NVIC_EnableIRQ(ENET_Receive_IRQn);
+   NVIC_EnableIRQ(ENET_Error_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void mk6xEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(ENET_Transmit_IRQn);
+   NVIC_DisableIRQ(ENET_Receive_IRQn);
+   NVIC_DisableIRQ(ENET_Error_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief Ethernet MAC transmit interrupt
+ **/
+
+void ENET_Transmit_IRQHandler(void)
+{
+   bool_t flag;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //A packet has been transmitted?
+   if(ENET->EIR & ENET_EIR_TXF_MASK)
+   {
+      //Clear TXF interrupt flag
+      ENET->EIR = ENET_EIR_TXF_MASK;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txBufferDesc[txBufferIndex][0] & HTOBE16(ENET_TBD0_R)))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag = osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+
+      //Instruct the DMA to poll the transmit descriptor list
+      ENET->TDAR = ENET_TDAR_TDAR_MASK;
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief Ethernet MAC receive interrupt
+ **/
+
+void ENET_Receive_IRQHandler(void)
+{
+   bool_t flag;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //A packet has been received?
+   if(ENET->EIR & ENET_EIR_RXF_MASK)
+   {
+      //Disable RXF interrupt
+      ENET->EIMR &= ~ENET_EIMR_RXF_MASK;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag = osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief Ethernet MAC error interrupt
+ **/
+
+void ENET_Error_IRQHandler(void)
+{
+   bool_t flag;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //System bus error?
+   if(ENET->EIR & ENET_EIR_EBERR_MASK)
+   {
+      //Disable EBERR interrupt
+      ENET->EIMR &= ~ENET_EIMR_EBERR_MASK;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief Kinetis K6x Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void mk6xEthEventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint32_t status;
+
+   //Read interrupt event register
+   status = ENET->EIR;
+
+   //Packet received?
+   if(status & ENET_EIR_RXF_MASK)
+   {
+      //Clear RXF interrupt flag
+      ENET->EIR = ENET_EIR_RXF_MASK;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = mk6xEthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //System bus error?
+   if(status & ENET_EIR_EBERR_MASK)
+   {
+      //Clear EBERR interrupt flag
+      ENET->EIR = ENET_EIR_EBERR_MASK;
+
+      //Disable Ethernet MAC
+      ENET->ECR &= ~ENET_ECR_ETHEREN_MASK;
+      //Reset buffer descriptors
+      mk6xEthInitBufferDesc(interface);
+      //Resume normal operation
+      ENET->ECR |= ENET_ECR_ETHEREN_MASK;
+      //Instruct the DMA to poll the receive descriptor list
+      ENET->RDAR = ENET_RDAR_RDAR_MASK;
+   }
+
+   //Re-enable Ethernet MAC interrupts
+   ENET->EIMR = ENET_EIMR_TXF_MASK | ENET_EIMR_RXF_MASK | ENET_EIMR_EBERR_MASK;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t mk6xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > MK6X_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txBufferDesc[txBufferIndex][0] & HTOBE16(ENET_TBD0_R))
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(txBuffer[txBufferIndex], buffer, offset, length);
+
+   //Set frame length
+   txBufferDesc[txBufferIndex][1] = HTOBE16(length);
+   //Clear BDU flag
+   txBufferDesc[txBufferIndex][8] = 0;
+
+   //Check current index
+   if(txBufferIndex < (MK6X_ETH_TX_BUFFER_COUNT - 1))
+   {
+      //Give the ownership of the descriptor to the DMA engine
+      txBufferDesc[txBufferIndex][0] = HTOBE16(ENET_TBD0_R |
+         ENET_TBD0_L | ENET_TBD0_TC);
+
+      //Point to the next buffer
+      txBufferIndex++;
+   }
+   else
+   {
+      //Give the ownership of the descriptor to the DMA engine
+      txBufferDesc[txBufferIndex][0] = HTOBE16(ENET_TBD0_R |
+         ENET_TBD0_W | ENET_TBD0_L | ENET_TBD0_TC);
+
+      //Wrap around
+      txBufferIndex = 0;
+   }
+
+   //Instruct the DMA to poll the transmit descriptor list
+   ENET->TDAR = ENET_TDAR_TDAR_MASK;
+
+   //Check whether the next buffer is available for writing
+   if(!(txBufferDesc[txBufferIndex][0] & HTOBE16(ENET_TBD0_R)))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mk6xEthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+
+   //Make sure the current buffer is available for reading
+   if(!(rxBufferDesc[rxBufferIndex][0] & HTOBE16(ENET_RBD0_E)))
+   {
+      //The frame should not span multiple buffers
+      if(rxBufferDesc[rxBufferIndex][0] & HTOBE16(ENET_RBD0_L))
+      {
+         //Check whether an error occurred
+         if(!(rxBufferDesc[rxBufferIndex][0] & HTOBE16(ENET_RBD0_LG |
+            ENET_RBD0_NO | ENET_RBD0_CR | ENET_RBD0_OV | ENET_RBD0_TR)))
+         {
+            //Retrieve the length of the frame
+            n = betoh16(rxBufferDesc[rxBufferIndex][1]);
+            //Limit the number of data to read
+            n = MIN(n, MK6X_ETH_RX_BUFFER_SIZE);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, rxBuffer[rxBufferIndex], n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Clear BDU flag
+      rxBufferDesc[rxBufferIndex][8] = 0;
+
+      //Check current index
+      if(rxBufferIndex < (MK6X_ETH_RX_BUFFER_COUNT - 1))
+      {
+         //Give the ownership of the descriptor back to the DMA engine
+         rxBufferDesc[rxBufferIndex][0] = HTOBE16(ENET_RBD0_E);
+         //Point to the next buffer
+         rxBufferIndex++;
+      }
+      else
+      {
+         //Give the ownership of the descriptor back to the DMA engine
+         rxBufferDesc[rxBufferIndex][0] = HTOBE16(ENET_RBD0_E | ENET_RBD0_W);
+         //Wrap around
+         rxBufferIndex = 0;
+      }
+
+      //Instruct the DMA to poll the receive descriptor list
+      ENET->RDAR = ENET_RDAR_RDAR_MASK;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mk6xEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating Kinetis K6x hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = mk6xEthCalcCrc(&entry->addr, sizeof(MacAddr));
+
+         //The upper 6 bits in the CRC register are used to index the
+         //contents of the hash table
+         k = (crc >> 26) & 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   ENET->GALR = hashTable[0];
+   ENET->GAUR = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  GALR = %08" PRIX32 "\r\n", ENET->GALR);
+   TRACE_DEBUG("  GAUR = %08" PRIX32 "\r\n", ENET->GAUR);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mk6xEthUpdateMacConfig(NetInterface *interface)
+{
+   //Disable Ethernet MAC while modifying configuration registers
+   ENET->ECR &= ~ENET_ECR_ETHEREN_MASK;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+   {
+      //100 Mbps operation
+      ENET->RCR &= ~ENET_RCR_RMII_10T_MASK;
+   }
+   else
+   {
+      //10 Mbps operation
+      ENET->RCR |= ENET_RCR_RMII_10T_MASK;
+   }
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+   {
+      //Full-duplex mode
+      ENET->TCR |= ENET_TCR_FDEN_MASK;
+      //Receive path operates independently of transmit
+      ENET->RCR &= ~ENET_RCR_DRT_MASK;
+   }
+   else
+   {
+      //Half-duplex mode
+      ENET->TCR &= ~ENET_TCR_FDEN_MASK;
+      //Disable reception of frames while transmitting
+      ENET->RCR |= ENET_RCR_DRT_MASK;
+   }
+
+   //Reset buffer descriptors
+   mk6xEthInitBufferDesc(interface);
+
+   //Re-enable Ethernet MAC
+   ENET->ECR |= ENET_ECR_ETHEREN_MASK;
+   //Instruct the DMA to poll the receive descriptor list
+   ENET->RDAR = ENET_RDAR_RDAR_MASK;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void mk6xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Set up a write operation
+   value = ENET_MMFR_ST(1) | ENET_MMFR_OP(1) | ENET_MMFR_TA(2);
+   //PHY address
+   value |= ENET_MMFR_PA(phyAddr);
+   //Register address
+   value |= ENET_MMFR_RA(regAddr);
+   //Register value
+   value |= ENET_MMFR_DATA(data);
+
+   //Clear MII interrupt flag
+   ENET->EIR = ENET_EIR_MII_MASK;
+   //Start a write operation
+   ENET->MMFR = value;
+   //Wait for the write to complete
+   while(!(ENET->EIR & ENET_EIR_MII_MASK));
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t mk6xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Set up a read operation
+   value = ENET_MMFR_ST(1) | ENET_MMFR_OP(2) | ENET_MMFR_TA(2);
+   //PHY address
+   value |= ENET_MMFR_PA(phyAddr);
+   //Register address
+   value |= ENET_MMFR_RA(regAddr);
+
+   //Clear MII interrupt flag
+   ENET->EIR = ENET_EIR_MII_MASK;
+   //Start a read operation
+   ENET->MMFR = value;
+   //Wait for the read to complete
+   while(!(ENET->EIR & ENET_EIR_MII_MASK));
+
+   //Return PHY register contents
+   return ENET->MMFR & ENET_MMFR_DATA_MASK;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t mk6xEthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //Update CRC value
+      crc ^= p[i];
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         if(crc & 0x00000001)
+            crc = (crc >> 1) ^ 0xEDB88320;
+         else
+            crc = crc >> 1;
+      }
+   }
+
+   //Return CRC value
+   return crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/mk6x_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,169 @@
+/**
+ * @file mk6x_eth.h
+ * @brief Freescale Kinetis K60/K64/K65/K66 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MK6X_ETH_H
+#define _MK6X_ETH_H
+
+//Number of TX buffers
+#ifndef MK6X_ETH_TX_BUFFER_COUNT
+   #define MK6X_ETH_TX_BUFFER_COUNT 3
+#elif (MK6X_ETH_TX_BUFFER_COUNT < 1)
+   #error MK6X_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef MK6X_ETH_TX_BUFFER_SIZE
+   #define MK6X_ETH_TX_BUFFER_SIZE 1536
+#elif (MK6X_ETH_TX_BUFFER_SIZE != 1536)
+   #error MK6X_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef MK6X_ETH_RX_BUFFER_COUNT
+   #define MK6X_ETH_RX_BUFFER_COUNT 6
+#elif (MK6X_ETH_RX_BUFFER_COUNT < 1)
+   #error MK6X_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef MK6X_ETH_RX_BUFFER_SIZE
+   #define MK6X_ETH_RX_BUFFER_SIZE 1536
+#elif (MK6X_ETH_RX_BUFFER_SIZE != 1536)
+   #error MK6X_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef MK6X_ETH_IRQ_PRIORITY_GROUPING
+   #define MK6X_ETH_IRQ_PRIORITY_GROUPING 3
+#elif (MK6X_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error MK6X_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef MK6X_ETH_IRQ_GROUP_PRIORITY
+   #define MK6X_ETH_IRQ_GROUP_PRIORITY 12
+#elif (MK6X_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error MK6X_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef MK6X_ETH_IRQ_SUB_PRIORITY
+   #define MK6X_ETH_IRQ_SUB_PRIORITY 0
+#elif (MK6X_ETH_IRQ_SUB_PRIORITY < 0)
+   #error MK6X_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//Enhanced transmit buffer descriptor
+#define ENET_TBD0_R                0x8000
+#define ENET_TBD0_TO1              0x4000
+#define ENET_TBD0_W                0x2000
+#define ENET_TBD0_TO2              0x1000
+#define ENET_TBD0_L                0x0800
+#define ENET_TBD0_TC               0x0400
+#define ENET_TBD1_DATA_LENGTH      0xFFFF
+#define ENET_TBD2_DATA_POINTER_H   0xFFFF
+#define ENET_TBD3_DATA_POINTER_L   0xFFFF
+#define ENET_TBD4_INT              0x4000
+#define ENET_TBD4_TS               0x2000
+#define ENET_TBD4_PINS             0x1000
+#define ENET_TBD4_IINS             0x0800
+#define ENET_TBD5_TXE              0x8000
+#define ENET_TBD5_UE               0x2000
+#define ENET_TBD5_EE               0x1000
+#define ENET_TBD5_FE               0x0800
+#define ENET_TBD5_LCE              0x0400
+#define ENET_TBD5_OE               0x0200
+#define ENET_TBD5_TSE              0x0100
+#define ENET_TBD8_BDU              0x8000
+#define ENET_TBD10_TIMESTAMP_H     0xFFFF
+#define ENET_TBD11_TIMESTAMP_L     0xFFFF
+
+//Enhanced receive buffer descriptor
+#define ENET_RBD0_E                0x8000
+#define ENET_RBD0_RO1              0x4000
+#define ENET_RBD0_W                0x2000
+#define ENET_RBD0_RO2              0x1000
+#define ENET_RBD0_L                0x0800
+#define ENET_RBD0_M                0x0100
+#define ENET_RBD0_BC               0x0080
+#define ENET_RBD0_MC               0x0040
+#define ENET_RBD0_LG               0x0020
+#define ENET_RBD0_NO               0x0010
+#define ENET_RBD0_CR               0x0004
+#define ENET_RBD0_OV               0x0002
+#define ENET_RBD0_TR               0x0001
+#define ENET_RBD1_DATA_LENGTH      0xFFFF
+#define ENET_RBD2_DATA_POINTER_H   0xFFFF
+#define ENET_RBD3_DATA_POINTER_L   0xFFFF
+#define ENET_RBD4_ME               0x8000
+#define ENET_RBD4_PE               0x0400
+#define ENET_RBD4_CE               0x0200
+#define ENET_RBD4_UC               0x0100
+#define ENET_RBD4_INT              0x0080
+#define ENET_RBD5_VPCP             0xE000
+#define ENET_RBD5_ICE              0x0020
+#define ENET_RBD5_PCR              0x0010
+#define ENET_RBD5_VLAN             0x0004
+#define ENET_RBD5_IPV6             0x0002
+#define ENET_RBD5_FRAG             0x0001
+#define ENET_RBD6_HEADER_LENGTH    0xF800
+#define ENET_RBD6_PROTOCOL_TYPE    0x00FF
+#define ENET_RBD7_PAYLOAD_CHECKSUM 0xFFFF
+#define ENET_RBD8_BDU              0x8000
+#define ENET_RBD10_TIMESTAMP_H     0xFFFF
+#define ENET_RBD11_TIMESTAMP_L     0xFFFF
+
+//Kinetis K6x Ethernet MAC driver
+extern const NicDriver mk6xEthDriver;
+
+//Kinetis K6x Ethernet MAC related functions
+error_t mk6xEthInit(NetInterface *interface);
+void mk6xEthInitGpio(NetInterface *interface);
+void mk6xEthInitBufferDesc(NetInterface *interface);
+
+void mk6xEthTick(NetInterface *interface);
+
+void mk6xEthEnableIrq(NetInterface *interface);
+void mk6xEthDisableIrq(NetInterface *interface);
+void mk6xEthEventHandler(NetInterface *interface);
+
+error_t mk6xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t mk6xEthReceivePacket(NetInterface *interface);
+
+error_t mk6xEthSetMulticastFilter(NetInterface *interface);
+error_t mk6xEthUpdateMacConfig(NetInterface *interface);
+
+void mk6xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t mk6xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t mk6xEthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/mk7x_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,867 @@
+/**
+ * @file mk7x_eth.c
+ * @brief Freescale Kinetis K70 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//MK70F12 device?
+#if defined(MK70F12)
+   #include "mk70f12.h"
+//MK70F15 device?
+#elif defined(MK70F15)
+   #include "mk70f15.h"
+#endif
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/mk7x_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//TX buffer
+#pragma data_alignment = 16
+static uint8_t txBuffer[MK7X_ETH_TX_BUFFER_COUNT][MK7X_ETH_TX_BUFFER_SIZE];
+//RX buffer
+#pragma data_alignment = 16
+static uint8_t rxBuffer[MK7X_ETH_RX_BUFFER_COUNT][MK7X_ETH_RX_BUFFER_SIZE];
+//TX buffer descriptors
+#pragma data_alignment = 16
+static uint16_t txBufferDesc[MK7X_ETH_TX_BUFFER_COUNT][16];
+//RX buffer descriptors
+#pragma data_alignment = 16
+static uint16_t rxBufferDesc[MK7X_ETH_RX_BUFFER_COUNT][16];
+
+//ARM or GCC compiler?
+#else
+
+//TX buffer
+static uint8_t txBuffer[MK7X_ETH_TX_BUFFER_COUNT][MK7X_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(16)));
+//RX buffer
+static uint8_t rxBuffer[MK7X_ETH_RX_BUFFER_COUNT][MK7X_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(16)));
+//TX buffer descriptors
+static uint16_t txBufferDesc[MK7X_ETH_TX_BUFFER_COUNT][16]
+   __attribute__((aligned(16)));
+//RX buffer descriptors
+static uint16_t rxBufferDesc[MK7X_ETH_RX_BUFFER_COUNT][16]
+   __attribute__((aligned(16)));
+
+#endif
+
+//TX buffer index
+static uint_t txBufferIndex;
+//RX buffer index
+static uint_t rxBufferIndex;
+
+
+/**
+ * @brief Kinetis K7x Ethernet MAC driver
+ **/
+
+const NicDriver mk7xEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   mk7xEthInit,
+   mk7xEthTick,
+   mk7xEthEnableIrq,
+   mk7xEthDisableIrq,
+   mk7xEthEventHandler,
+   mk7xEthSendPacket,
+   mk7xEthSetMulticastFilter,
+   mk7xEthUpdateMacConfig,
+   mk7xEthWritePhyReg,
+   mk7xEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief Kinetis K7x Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mk7xEthInit(NetInterface *interface)
+{
+   error_t error;
+   uint32_t value;
+
+   //Debug message
+   TRACE_INFO("Initializing Kinetis K7x Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Disable MPU
+   MPU->CESR &= ~MPU_CESR_VLD_MASK;
+
+   //Enable external reference clock
+   OSC0->CR |= OSC_CR_ERCLKEN_MASK;
+   //Enable ENET peripheral clock
+   SIM->SCGC2 |= SIM_SCGC2_ENET_MASK;
+
+   //GPIO configuration
+   mk7xEthInitGpio(interface);
+
+   //Reset ENET module
+   ENET->ECR = ENET_ECR_RESET_MASK;
+   //Wait for the reset to complete
+   while(ENET->ECR & ENET_ECR_RESET_MASK);
+
+   //Reveive control register
+   ENET->RCR = ENET_RCR_MAX_FL(1518) | ENET_RCR_RMII_MODE_MASK | ENET_RCR_MII_MODE_MASK;
+   //Transmit control register
+   ENET->TCR = 0;
+   //Configure MDC clock frequency
+   ENET->MSCR = ENET_MSCR_MII_SPEED(59);
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Set the MAC address (upper 16 bits)
+   value = interface->macAddr.b[5];
+   value |= (interface->macAddr.b[4] << 8);
+   ENET->PAUR = ENET_PAUR_PADDR2(value) | ENET_PAUR_TYPE(0x8808);
+
+   //Set the MAC address (lower 32 bits)
+   value = interface->macAddr.b[3];
+   value |= (interface->macAddr.b[2] << 8);
+   value |= (interface->macAddr.b[1] << 16);
+   value |= (interface->macAddr.b[0] << 24);
+   ENET->PALR = ENET_PALR_PADDR1(value);
+
+   //Hash table for unicast address filtering
+   ENET->IALR = 0;
+   ENET->IAUR = 0;
+   //Hash table for multicast address filtering
+   ENET->GALR = 0;
+   ENET->GAUR = 0;
+
+   //Disable transmit accelerator functions
+   ENET->TACC = 0;
+   //Disable receive accelerator functions
+   ENET->RACC = 0;
+
+   //Use enhanced buffer descriptors
+   ENET->ECR = ENET_ECR_EN1588_MASK;
+   //Clear MIC counters
+   ENET->MIBC = ENET_MIBC_MIB_CLEAR_MASK;
+
+   //Initialize buffer descriptors
+   mk7xEthInitBufferDesc(interface);
+
+   //Clear any pending interrupts
+   ENET->EIR = 0xFFFFFFFF;
+   //Enable desired interrupts
+   ENET->EIMR = ENET_EIMR_TXF_MASK | ENET_EIMR_RXF_MASK | ENET_EIMR_EBERR_MASK;
+
+   //Set priority grouping (4 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(MK7X_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure ENET transmit interrupt priority
+   NVIC_SetPriority(ENET_Transmit_IRQn, NVIC_EncodePriority(MK7X_ETH_IRQ_PRIORITY_GROUPING,
+      MK7X_ETH_IRQ_GROUP_PRIORITY, MK7X_ETH_IRQ_SUB_PRIORITY));
+
+   //Configure ENET receive interrupt priority
+   NVIC_SetPriority(ENET_Receive_IRQn, NVIC_EncodePriority(MK7X_ETH_IRQ_PRIORITY_GROUPING,
+      MK7X_ETH_IRQ_GROUP_PRIORITY, MK7X_ETH_IRQ_SUB_PRIORITY));
+
+   //Configure ENET error interrupt priority
+   NVIC_SetPriority(ENET_Error_IRQn, NVIC_EncodePriority(MK7X_ETH_IRQ_PRIORITY_GROUPING,
+      MK7X_ETH_IRQ_GROUP_PRIORITY, MK7X_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable Ethernet MAC
+   ENET->ECR |= ENET_ECR_ETHEREN_MASK;
+   //Instruct the DMA to poll the receive descriptor list
+   ENET->RDAR = ENET_RDAR_RDAR_MASK;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//TWR-K70F120M evaluation board?
+#if defined(USE_TWR_K70F120M)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void mk7xEthInitGpio(NetInterface *interface)
+{
+   //Enable PORTA and PORTB peripheral clocks
+   SIM->SCGC5 |= SIM_SCGC5_PORTA_MASK | SIM_SCGC5_PORTB_MASK;
+
+   //Configure RMII0_RXER (PTA5)
+   PORTA->PCR[5] = PORT_PCR_MUX(4) | PORT_PCR_PE_MASK;
+   //Configure RMII0_RXD1 (PTA12)
+   PORTA->PCR[12] = PORT_PCR_MUX(4);
+   //Configure RMII0_RXD0 (PTA13)
+   PORTA->PCR[13] = PORT_PCR_MUX(4);
+   //Configure RMII0_CRS_DV (PTA14)
+   PORTA->PCR[14] = PORT_PCR_MUX(4);
+   //Configure RMII0_TXEN (PTA15)
+   PORTA->PCR[15] = PORT_PCR_MUX(4);
+   //Configure RMII0_TXD0 (PTA16)
+   PORTA->PCR[16] = PORT_PCR_MUX(4);
+   //Configure RMII0_TXD1 (PTA17)
+   PORTA->PCR[17] = PORT_PCR_MUX(4);
+
+   //Configure RMII0_MDIO (PTB0)
+   PORTB->PCR[0] = PORT_PCR_MUX(4) | PORT_PCR_PE_MASK | PORT_PCR_PS_MASK;
+   //Configure RMII0_MDC (PTB1)
+   PORTB->PCR[1] = PORT_PCR_MUX(4);
+}
+
+#endif
+
+
+/**
+ * @brief Initialize buffer descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void mk7xEthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+   uint32_t address;
+
+   //Clear TX and RX buffer descriptors
+   memset(txBufferDesc, 0, sizeof(txBufferDesc));
+   memset(rxBufferDesc, 0, sizeof(rxBufferDesc));
+
+   //Initialize TX buffer descriptors
+   for(i = 0; i < MK7X_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current TX buffer
+      address = (uint32_t) txBuffer[i];
+      //Transmit buffer address
+      txBufferDesc[i][2] = htobe16(address >> 16);
+      txBufferDesc[i][3] = htobe16(address & 0xFFFF);
+      //Generate interrupts
+      txBufferDesc[i][4] = HTOBE16(ENET_TBD4_INT);
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   txBufferDesc[i - 1][0] |= HTOBE16(ENET_TBD0_W);
+   //Initialize TX buffer index
+   txBufferIndex = 0;
+
+   //Initialize RX buffer descriptors
+   for(i = 0; i < MK7X_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current RX buffer
+      address = (uint32_t) rxBuffer[i];
+      //The descriptor is initially owned by the DMA
+      rxBufferDesc[i][0] = HTOBE16(ENET_RBD0_E);
+      //Receive buffer address
+      rxBufferDesc[i][2] = htobe16(address >> 16);
+      rxBufferDesc[i][3] = htobe16(address & 0xFFFF);
+      //Generate interrupts
+      rxBufferDesc[i][4] = HTOBE16(ENET_RBD4_INT);
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   rxBufferDesc[i - 1][0] |= HTOBE16(ENET_RBD0_W);
+   //Initialize RX buffer index
+   rxBufferIndex = 0;
+
+   //Start location of the TX descriptor list
+   ENET->TDSR = (uint32_t) txBufferDesc;
+   //Start location of the RX descriptor list
+   ENET->RDSR = (uint32_t) rxBufferDesc;
+   //Maximum receive buffer size
+   ENET->MRBR = MK7X_ETH_RX_BUFFER_SIZE;
+}
+
+
+/**
+ * @brief Kinetis K7x Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void mk7xEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void mk7xEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(ENET_Transmit_IRQn);
+   NVIC_EnableIRQ(ENET_Receive_IRQn);
+   NVIC_EnableIRQ(ENET_Error_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void mk7xEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(ENET_Transmit_IRQn);
+   NVIC_DisableIRQ(ENET_Receive_IRQn);
+   NVIC_DisableIRQ(ENET_Error_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief Ethernet MAC transmit interrupt
+ **/
+
+void ENET_Transmit_IRQHandler(void)
+{
+   bool_t flag;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //A packet has been transmitted?
+   if(ENET->EIR & ENET_EIR_TXF_MASK)
+   {
+      //Clear TXF interrupt flag
+      ENET->EIR = ENET_EIR_TXF_MASK;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txBufferDesc[txBufferIndex][0] & HTOBE16(ENET_TBD0_R)))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag = osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+
+      //Instruct the DMA to poll the transmit descriptor list
+      ENET->TDAR = ENET_TDAR_TDAR_MASK;
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief Ethernet MAC receive interrupt
+ **/
+
+void ENET_Receive_IRQHandler(void)
+{
+   bool_t flag;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //A packet has been received?
+   if(ENET->EIR & ENET_EIR_RXF_MASK)
+   {
+      //Disable RXF interrupt
+      ENET->EIMR &= ~ENET_EIMR_RXF_MASK;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag = osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief Ethernet MAC error interrupt
+ **/
+
+void ENET_Error_IRQHandler(void)
+{
+   bool_t flag;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //System bus error?
+   if(ENET->EIR & ENET_EIR_EBERR_MASK)
+   {
+      //Disable EBERR interrupt
+      ENET->EIMR &= ~ENET_EIMR_EBERR_MASK;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief Kinetis K7x Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void mk7xEthEventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint32_t status;
+
+   //Read interrupt event register
+   status = ENET->EIR;
+
+   //Packet received?
+   if(status & ENET_EIR_RXF_MASK)
+   {
+      //Clear RXF interrupt flag
+      ENET->EIR = ENET_EIR_RXF_MASK;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = mk7xEthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //System bus error?
+   if(status & ENET_EIR_EBERR_MASK)
+   {
+      //Clear EBERR interrupt flag
+      ENET->EIR = ENET_EIR_EBERR_MASK;
+
+      //Disable Ethernet MAC
+      ENET->ECR &= ~ENET_ECR_ETHEREN_MASK;
+      //Reset buffer descriptors
+      mk7xEthInitBufferDesc(interface);
+      //Resume normal operation
+      ENET->ECR |= ENET_ECR_ETHEREN_MASK;
+      //Instruct the DMA to poll the receive descriptor list
+      ENET->RDAR = ENET_RDAR_RDAR_MASK;
+   }
+
+   //Re-enable Ethernet MAC interrupts
+   ENET->EIMR = ENET_EIMR_TXF_MASK | ENET_EIMR_RXF_MASK | ENET_EIMR_EBERR_MASK;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t mk7xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > MK7X_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txBufferDesc[txBufferIndex][0] & HTOBE16(ENET_TBD0_R))
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(txBuffer[txBufferIndex], buffer, offset, length);
+
+   //Set frame length
+   txBufferDesc[txBufferIndex][1] = HTOBE16(length);
+   //Clear BDU flag
+   txBufferDesc[txBufferIndex][8] = 0;
+
+   //Check current index
+   if(txBufferIndex < (MK7X_ETH_TX_BUFFER_COUNT - 1))
+   {
+      //Give the ownership of the descriptor to the DMA engine
+      txBufferDesc[txBufferIndex][0] = HTOBE16(ENET_TBD0_R |
+         ENET_TBD0_L | ENET_TBD0_TC);
+
+      //Point to the next buffer
+      txBufferIndex++;
+   }
+   else
+   {
+      //Give the ownership of the descriptor to the DMA engine
+      txBufferDesc[txBufferIndex][0] = HTOBE16(ENET_TBD0_R |
+         ENET_TBD0_W | ENET_TBD0_L | ENET_TBD0_TC);
+
+      //Wrap around
+      txBufferIndex = 0;
+   }
+
+   //Instruct the DMA to poll the transmit descriptor list
+   ENET->TDAR = ENET_TDAR_TDAR_MASK;
+
+   //Check whether the next buffer is available for writing
+   if(!(txBufferDesc[txBufferIndex][0] & HTOBE16(ENET_TBD0_R)))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mk7xEthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+
+   //Make sure the current buffer is available for reading
+   if(!(rxBufferDesc[rxBufferIndex][0] & HTOBE16(ENET_RBD0_E)))
+   {
+      //The frame should not span multiple buffers
+      if(rxBufferDesc[rxBufferIndex][0] & HTOBE16(ENET_RBD0_L))
+      {
+         //Check whether an error occurred
+         if(!(rxBufferDesc[rxBufferIndex][0] & HTOBE16(ENET_RBD0_LG |
+            ENET_RBD0_NO | ENET_RBD0_CR | ENET_RBD0_OV | ENET_RBD0_TR)))
+         {
+            //Retrieve the length of the frame
+            n = betoh16(rxBufferDesc[rxBufferIndex][1]);
+            //Limit the number of data to read
+            n = MIN(n, MK7X_ETH_RX_BUFFER_SIZE);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, rxBuffer[rxBufferIndex], n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Clear BDU flag
+      rxBufferDesc[rxBufferIndex][8] = 0;
+
+      //Check current index
+      if(rxBufferIndex < (MK7X_ETH_RX_BUFFER_COUNT - 1))
+      {
+         //Give the ownership of the descriptor back to the DMA engine
+         rxBufferDesc[rxBufferIndex][0] = HTOBE16(ENET_RBD0_E);
+         //Point to the next buffer
+         rxBufferIndex++;
+      }
+      else
+      {
+         //Give the ownership of the descriptor back to the DMA engine
+         rxBufferDesc[rxBufferIndex][0] = HTOBE16(ENET_RBD0_E | ENET_RBD0_W);
+         //Wrap around
+         rxBufferIndex = 0;
+      }
+
+      //Instruct the DMA to poll the receive descriptor list
+      ENET->RDAR = ENET_RDAR_RDAR_MASK;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mk7xEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating Kinetis K7x hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = mk7xEthCalcCrc(&entry->addr, sizeof(MacAddr));
+
+         //The upper 6 bits in the CRC register are used to index the
+         //contents of the hash table
+         k = (crc >> 26) & 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   ENET->GALR = hashTable[0];
+   ENET->GAUR = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  GALR = %08" PRIX32 "\r\n", ENET->GALR);
+   TRACE_DEBUG("  GAUR = %08" PRIX32 "\r\n", ENET->GAUR);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mk7xEthUpdateMacConfig(NetInterface *interface)
+{
+   //Disable Ethernet MAC while modifying configuration registers
+   ENET->ECR &= ~ENET_ECR_ETHEREN_MASK;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+   {
+      //100 Mbps operation
+      ENET->RCR &= ~ENET_RCR_RMII_10T_MASK;
+   }
+   else
+   {
+      //10 Mbps operation
+      ENET->RCR |= ENET_RCR_RMII_10T_MASK;
+   }
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+   {
+      //Full-duplex mode
+      ENET->TCR |= ENET_TCR_FDEN_MASK;
+      //Receive path operates independently of transmit
+      ENET->RCR &= ~ENET_RCR_DRT_MASK;
+   }
+   else
+   {
+      //Half-duplex mode
+      ENET->TCR &= ~ENET_TCR_FDEN_MASK;
+      //Disable reception of frames while transmitting
+      ENET->RCR |= ENET_RCR_DRT_MASK;
+   }
+
+   //Reset buffer descriptors
+   mk7xEthInitBufferDesc(interface);
+
+   //Re-enable Ethernet MAC
+   ENET->ECR |= ENET_ECR_ETHEREN_MASK;
+   //Instruct the DMA to poll the receive descriptor list
+   ENET->RDAR = ENET_RDAR_RDAR_MASK;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void mk7xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Set up a write operation
+   value = ENET_MMFR_ST(1) | ENET_MMFR_OP(1) | ENET_MMFR_TA(2);
+   //PHY address
+   value |= ENET_MMFR_PA(phyAddr);
+   //Register address
+   value |= ENET_MMFR_RA(regAddr);
+   //Register value
+   value |= ENET_MMFR_DATA(data);
+
+   //Clear MII interrupt flag
+   ENET->EIR = ENET_EIR_MII_MASK;
+   //Start a write operation
+   ENET->MMFR = value;
+   //Wait for the write to complete
+   while(!(ENET->EIR & ENET_EIR_MII_MASK));
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t mk7xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Set up a read operation
+   value = ENET_MMFR_ST(1) | ENET_MMFR_OP(2) | ENET_MMFR_TA(2);
+   //PHY address
+   value |= ENET_MMFR_PA(phyAddr);
+   //Register address
+   value |= ENET_MMFR_RA(regAddr);
+
+   //Clear MII interrupt flag
+   ENET->EIR = ENET_EIR_MII_MASK;
+   //Start a read operation
+   ENET->MMFR = value;
+   //Wait for the read to complete
+   while(!(ENET->EIR & ENET_EIR_MII_MASK));
+
+   //Return PHY register contents
+   return ENET->MMFR & ENET_MMFR_DATA_MASK;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t mk7xEthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //Update CRC value
+      crc ^= p[i];
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         if(crc & 0x00000001)
+            crc = (crc >> 1) ^ 0xEDB88320;
+         else
+            crc = crc >> 1;
+      }
+   }
+
+   //Return CRC value
+   return crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/mk7x_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,169 @@
+/**
+ * @file mk7x_eth.h
+ * @brief Freescale Kinetis K70 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MK7X_ETH_H
+#define _MK7X_ETH_H
+
+//Number of TX buffers
+#ifndef MK7X_ETH_TX_BUFFER_COUNT
+   #define MK7X_ETH_TX_BUFFER_COUNT 3
+#elif (MK7X_ETH_TX_BUFFER_COUNT < 1)
+   #error MK7X_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef MK7X_ETH_TX_BUFFER_SIZE
+   #define MK7X_ETH_TX_BUFFER_SIZE 1536
+#elif (MK7X_ETH_TX_BUFFER_SIZE != 1536)
+   #error MK7X_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef MK7X_ETH_RX_BUFFER_COUNT
+   #define MK7X_ETH_RX_BUFFER_COUNT 6
+#elif (MK7X_ETH_RX_BUFFER_COUNT < 1)
+   #error MK7X_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef MK7X_ETH_RX_BUFFER_SIZE
+   #define MK7X_ETH_RX_BUFFER_SIZE 1536
+#elif (MK7X_ETH_RX_BUFFER_SIZE != 1536)
+   #error MK7X_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef MK7X_ETH_IRQ_PRIORITY_GROUPING
+   #define MK7X_ETH_IRQ_PRIORITY_GROUPING 3
+#elif (MK7X_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error MK7X_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef MK7X_ETH_IRQ_GROUP_PRIORITY
+   #define MK7X_ETH_IRQ_GROUP_PRIORITY 12
+#elif (MK7X_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error MK7X_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef MK7X_ETH_IRQ_SUB_PRIORITY
+   #define MK7X_ETH_IRQ_SUB_PRIORITY 0
+#elif (MK7X_ETH_IRQ_SUB_PRIORITY < 0)
+   #error MK7X_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//Enhanced transmit buffer descriptor
+#define ENET_TBD0_R                0x8000
+#define ENET_TBD0_TO1              0x4000
+#define ENET_TBD0_W                0x2000
+#define ENET_TBD0_TO2              0x1000
+#define ENET_TBD0_L                0x0800
+#define ENET_TBD0_TC               0x0400
+#define ENET_TBD1_DATA_LENGTH      0xFFFF
+#define ENET_TBD2_DATA_POINTER_H   0xFFFF
+#define ENET_TBD3_DATA_POINTER_L   0xFFFF
+#define ENET_TBD4_INT              0x4000
+#define ENET_TBD4_TS               0x2000
+#define ENET_TBD4_PINS             0x1000
+#define ENET_TBD4_IINS             0x0800
+#define ENET_TBD5_TXE              0x8000
+#define ENET_TBD5_UE               0x2000
+#define ENET_TBD5_EE               0x1000
+#define ENET_TBD5_FE               0x0800
+#define ENET_TBD5_LCE              0x0400
+#define ENET_TBD5_OE               0x0200
+#define ENET_TBD5_TSE              0x0100
+#define ENET_TBD8_BDU              0x8000
+#define ENET_TBD10_TIMESTAMP_H     0xFFFF
+#define ENET_TBD11_TIMESTAMP_L     0xFFFF
+
+//Enhanced receive buffer descriptor
+#define ENET_RBD0_E                0x8000
+#define ENET_RBD0_RO1              0x4000
+#define ENET_RBD0_W                0x2000
+#define ENET_RBD0_RO2              0x1000
+#define ENET_RBD0_L                0x0800
+#define ENET_RBD0_M                0x0100
+#define ENET_RBD0_BC               0x0080
+#define ENET_RBD0_MC               0x0040
+#define ENET_RBD0_LG               0x0020
+#define ENET_RBD0_NO               0x0010
+#define ENET_RBD0_CR               0x0004
+#define ENET_RBD0_OV               0x0002
+#define ENET_RBD0_TR               0x0001
+#define ENET_RBD1_DATA_LENGTH      0xFFFF
+#define ENET_RBD2_DATA_POINTER_H   0xFFFF
+#define ENET_RBD3_DATA_POINTER_L   0xFFFF
+#define ENET_RBD4_ME               0x8000
+#define ENET_RBD4_PE               0x0400
+#define ENET_RBD4_CE               0x0200
+#define ENET_RBD4_UC               0x0100
+#define ENET_RBD4_INT              0x0080
+#define ENET_RBD5_VPCP             0xE000
+#define ENET_RBD5_ICE              0x0020
+#define ENET_RBD5_PCR              0x0010
+#define ENET_RBD5_VLAN             0x0004
+#define ENET_RBD5_IPV6             0x0002
+#define ENET_RBD5_FRAG             0x0001
+#define ENET_RBD6_HEADER_LENGTH    0xF800
+#define ENET_RBD6_PROTOCOL_TYPE    0x00FF
+#define ENET_RBD7_PAYLOAD_CHECKSUM 0xFFFF
+#define ENET_RBD8_BDU              0x8000
+#define ENET_RBD10_TIMESTAMP_H     0xFFFF
+#define ENET_RBD11_TIMESTAMP_L     0xFFFF
+
+//Kinetis K7x Ethernet MAC driver
+extern const NicDriver mk7xEthDriver;
+
+//Kinetis K7x Ethernet MAC related functions
+error_t mk7xEthInit(NetInterface *interface);
+void mk7xEthInitGpio(NetInterface *interface);
+void mk7xEthInitBufferDesc(NetInterface *interface);
+
+void mk7xEthTick(NetInterface *interface);
+
+void mk7xEthEnableIrq(NetInterface *interface);
+void mk7xEthDisableIrq(NetInterface *interface);
+void mk7xEthEventHandler(NetInterface *interface);
+
+error_t mk7xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t mk7xEthReceivePacket(NetInterface *interface);
+
+error_t mk7xEthSetMulticastFilter(NetInterface *interface);
+error_t mk7xEthUpdateMacConfig(NetInterface *interface);
+
+void mk7xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t mk7xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t mk7xEthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/mkv5x_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,884 @@
+/**
+ * @file mkv5x_eth.c
+ * @brief Freescale Kinetis KV5x Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//MKV58F12 device?
+#if defined(MKV58F22)
+   #include "mkv58f22.h"
+#endif
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/mkv5x_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//TX buffer
+#pragma data_alignment = 16
+static uint8_t txBuffer[MKV5X_ETH_TX_BUFFER_COUNT][MKV5X_ETH_TX_BUFFER_SIZE];
+//RX buffer
+#pragma data_alignment = 16
+static uint8_t rxBuffer[MKV5X_ETH_RX_BUFFER_COUNT][MKV5X_ETH_RX_BUFFER_SIZE];
+//TX buffer descriptors
+#pragma data_alignment = 16
+static uint16_t txBufferDesc[MKV5X_ETH_TX_BUFFER_COUNT][16];
+//RX buffer descriptors
+#pragma data_alignment = 16
+static uint16_t rxBufferDesc[MKV5X_ETH_RX_BUFFER_COUNT][16];
+
+//ARM or GCC compiler?
+#else
+
+//TX buffer
+static uint8_t txBuffer[MKV5X_ETH_TX_BUFFER_COUNT][MKV5X_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(16)));
+//RX buffer
+static uint8_t rxBuffer[MKV5X_ETH_RX_BUFFER_COUNT][MKV5X_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(16)));
+//TX buffer descriptors
+static uint16_t txBufferDesc[MKV5X_ETH_TX_BUFFER_COUNT][16]
+   __attribute__((aligned(16)));
+//RX buffer descriptors
+static uint16_t rxBufferDesc[MKV5X_ETH_RX_BUFFER_COUNT][16]
+   __attribute__((aligned(16)));
+
+#endif
+
+//TX buffer index
+static uint_t txBufferIndex;
+//RX buffer index
+static uint_t rxBufferIndex;
+
+
+/**
+ * @brief Kinetis KV5x Ethernet MAC driver
+ **/
+
+const NicDriver mkv5xEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   mkv5xEthInit,
+   mkv5xEthTick,
+   mkv5xEthEnableIrq,
+   mkv5xEthDisableIrq,
+   mkv5xEthEventHandler,
+   mkv5xEthSendPacket,
+   mkv5xEthSetMulticastFilter,
+   mkv5xEthUpdateMacConfig,
+   mkv5xEthWritePhyReg,
+   mkv5xEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief Kinetis KV5x Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mkv5xEthInit(NetInterface *interface)
+{
+   error_t error;
+   uint32_t value;
+
+   //Debug message
+   TRACE_INFO("Initializing Kinetis KV5x Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Disable MPU
+   MPU->CESR &= ~MPU_CESR_VLD_MASK;
+
+   //Enable ENET peripheral clock
+   SIM->SCGC2 |= SIM_SCGC2_ENET_MASK;
+
+   //GPIO configuration
+   mkv5xEthInitGpio(interface);
+
+   //Reset ENET module
+   ENET->ECR = ENET_ECR_RESET_MASK;
+   //Wait for the reset to complete
+   while(ENET->ECR & ENET_ECR_RESET_MASK);
+
+   //Receive control register
+   ENET->RCR = ENET_RCR_MAX_FL(1518) | ENET_RCR_MII_MODE_MASK;
+   //Transmit control register
+   ENET->TCR = 0;
+   //Configure MDC clock frequency
+   ENET->MSCR = ENET_MSCR_MII_SPEED(49);
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Set the MAC address (upper 16 bits)
+   value = interface->macAddr.b[5];
+   value |= (interface->macAddr.b[4] << 8);
+   ENET->PAUR = ENET_PAUR_PADDR2(value) | ENET_PAUR_TYPE(0x8808);
+
+   //Set the MAC address (lower 32 bits)
+   value = interface->macAddr.b[3];
+   value |= (interface->macAddr.b[2] << 8);
+   value |= (interface->macAddr.b[1] << 16);
+   value |= (interface->macAddr.b[0] << 24);
+   ENET->PALR = ENET_PALR_PADDR1(value);
+
+   //Hash table for unicast address filtering
+   ENET->IALR = 0;
+   ENET->IAUR = 0;
+   //Hash table for multicast address filtering
+   ENET->GALR = 0;
+   ENET->GAUR = 0;
+
+   //Disable transmit accelerator functions
+   ENET->TACC = 0;
+   //Disable receive accelerator functions
+   ENET->RACC = 0;
+
+   //Use enhanced buffer descriptors
+   ENET->ECR = ENET_ECR_EN1588_MASK;
+   //Clear MIC counters
+   ENET->MIBC = ENET_MIBC_MIB_CLEAR_MASK;
+
+   //Initialize buffer descriptors
+   mkv5xEthInitBufferDesc(interface);
+
+   //Clear any pending interrupts
+   ENET->EIR = 0xFFFFFFFF;
+   //Enable desired interrupts
+   ENET->EIMR = ENET_EIMR_TXF_MASK | ENET_EIMR_RXF_MASK | ENET_EIMR_EBERR_MASK;
+
+   //Set priority grouping (4 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(MKV5X_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure ENET transmit interrupt priority
+   NVIC_SetPriority(ENET_Transmit_IRQn, NVIC_EncodePriority(MKV5X_ETH_IRQ_PRIORITY_GROUPING,
+      MKV5X_ETH_IRQ_GROUP_PRIORITY, MKV5X_ETH_IRQ_SUB_PRIORITY));
+
+   //Configure ENET receive interrupt priority
+   NVIC_SetPriority(ENET_Receive_IRQn, NVIC_EncodePriority(MKV5X_ETH_IRQ_PRIORITY_GROUPING,
+      MKV5X_ETH_IRQ_GROUP_PRIORITY, MKV5X_ETH_IRQ_SUB_PRIORITY));
+
+   //Configure ENET error interrupt priority
+   NVIC_SetPriority(ENET_Error_IRQn, NVIC_EncodePriority(MKV5X_ETH_IRQ_PRIORITY_GROUPING,
+      MKV5X_ETH_IRQ_GROUP_PRIORITY, MKV5X_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable Ethernet MAC
+   ENET->ECR |= ENET_ECR_ETHEREN_MASK;
+   //Instruct the DMA to poll the receive descriptor list
+   ENET->RDAR = ENET_RDAR_RDAR_MASK;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//TWR-KV58F220M evaluation board?
+#if defined(USE_TWR_KV58F220M)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void mkv5xEthInitGpio(NetInterface *interface)
+{
+   //Enable PORTA peripheral clock
+   SIM->SCGC5 |= SIM_SCGC5_PORTA_MASK;
+
+   //Configure MII0_RXER (PTA5)
+   PORTA->PCR[5] = PORT_PCR_MUX(4) | PORT_PCR_PE_MASK;
+   //Configure MII0_MDIO (PTA7)
+   PORTA->PCR[7] = PORT_PCR_MUX(5) | PORT_PCR_PE_MASK | PORT_PCR_PS_MASK;
+   //Configure MII0_MDC (PTA8)
+   PORTA->PCR[8] = PORT_PCR_MUX(5);
+   //Configure MII0_RXD3 (PTA9)
+   PORTA->PCR[9] = PORT_PCR_MUX(5);
+   //Configure MII0_RXD2 (PTA10)
+   PORTA->PCR[10] = PORT_PCR_MUX(5);
+   //Configure MII0_RXCLK (PTA11)
+   PORTA->PCR[11] = PORT_PCR_MUX(5);
+   //Configure MII0_RXD1 (PTA12)
+   PORTA->PCR[12] = PORT_PCR_MUX(5);
+   //Configure MII0_RXD0 (PTA13)
+   PORTA->PCR[13] = PORT_PCR_MUX(5);
+   //Configure MII0_RXDV (PTA14)
+   PORTA->PCR[14] = PORT_PCR_MUX(5);
+   //Configure MII0_TXEN (PTA15)
+   PORTA->PCR[15] = PORT_PCR_MUX(5);
+   //Configure MII0_TXD0 (PTA16)
+   PORTA->PCR[16] = PORT_PCR_MUX(5);
+   //Configure MII0_TXD1 (PTA17)
+   PORTA->PCR[17] = PORT_PCR_MUX(5);
+   //Configure MII0_TXD2 (PTA24)
+   PORTA->PCR[24] = PORT_PCR_MUX(5);
+   //Configure MII0_TXCLK (PTA25)
+   PORTA->PCR[25] = PORT_PCR_MUX(5);
+   //Configure MII0_TXD3 (PTA26)
+   PORTA->PCR[26] = PORT_PCR_MUX(5);
+   //Configure MII0_CRS (PTA27)
+   PORTA->PCR[27] = PORT_PCR_MUX(5);
+   //Configure MII0_COL (PTA29)
+   PORTA->PCR[29] = PORT_PCR_MUX(5);
+}
+
+#endif
+
+
+/**
+ * @brief Initialize buffer descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void mkv5xEthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+   uint32_t address;
+
+   //Clear TX and RX buffer descriptors
+   memset(txBufferDesc, 0, sizeof(txBufferDesc));
+   memset(rxBufferDesc, 0, sizeof(rxBufferDesc));
+
+   //Initialize TX buffer descriptors
+   for(i = 0; i < MKV5X_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current TX buffer
+      address = (uint32_t) txBuffer[i];
+      //Transmit buffer address
+      txBufferDesc[i][2] = htobe16(address >> 16);
+      txBufferDesc[i][3] = htobe16(address & 0xFFFF);
+      //Generate interrupts
+      txBufferDesc[i][4] = HTOBE16(ENET_TBD4_INT);
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   txBufferDesc[i - 1][0] |= HTOBE16(ENET_TBD0_W);
+   //Initialize TX buffer index
+   txBufferIndex = 0;
+
+   //Initialize RX buffer descriptors
+   for(i = 0; i < MKV5X_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current RX buffer
+      address = (uint32_t) rxBuffer[i];
+      //The descriptor is initially owned by the DMA
+      rxBufferDesc[i][0] = HTOBE16(ENET_RBD0_E);
+      //Receive buffer address
+      rxBufferDesc[i][2] = htobe16(address >> 16);
+      rxBufferDesc[i][3] = htobe16(address & 0xFFFF);
+      //Generate interrupts
+      rxBufferDesc[i][4] = HTOBE16(ENET_RBD4_INT);
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   rxBufferDesc[i - 1][0] |= HTOBE16(ENET_RBD0_W);
+   //Initialize RX buffer index
+   rxBufferIndex = 0;
+
+   //Start location of the TX descriptor list
+   ENET->TDSR = (uint32_t) txBufferDesc;
+   //Start location of the RX descriptor list
+   ENET->RDSR = (uint32_t) rxBufferDesc;
+   //Maximum receive buffer size
+   ENET->MRBR = MKV5X_ETH_RX_BUFFER_SIZE;
+}
+
+
+/**
+ * @brief Kinetis KV5x Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void mkv5xEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void mkv5xEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(ENET_Transmit_IRQn);
+   NVIC_EnableIRQ(ENET_Receive_IRQn);
+   NVIC_EnableIRQ(ENET_Error_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void mkv5xEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(ENET_Transmit_IRQn);
+   NVIC_DisableIRQ(ENET_Receive_IRQn);
+   NVIC_DisableIRQ(ENET_Error_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief Ethernet MAC transmit interrupt
+ **/
+
+void ENET_Transmit_IRQHandler(void)
+{
+   bool_t flag;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //A packet has been transmitted?
+   if(ENET->EIR & ENET_EIR_TXF_MASK)
+   {
+      //Clear TXF interrupt flag
+      ENET->EIR = ENET_EIR_TXF_MASK;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txBufferDesc[txBufferIndex][0] & HTOBE16(ENET_TBD0_R)))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag = osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+
+      //Instruct the DMA to poll the transmit descriptor list
+      ENET->TDAR = ENET_TDAR_TDAR_MASK;
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief Ethernet MAC receive interrupt
+ **/
+
+void ENET_Receive_IRQHandler(void)
+{
+   bool_t flag;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //A packet has been received?
+   if(ENET->EIR & ENET_EIR_RXF_MASK)
+   {
+      //Disable RXF interrupt
+      ENET->EIMR &= ~ENET_EIMR_RXF_MASK;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag = osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief Ethernet MAC error interrupt
+ **/
+
+void ENET_Error_IRQHandler(void)
+{
+   bool_t flag;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //System bus error?
+   if(ENET->EIR & ENET_EIR_EBERR_MASK)
+   {
+      //Disable EBERR interrupt
+      ENET->EIMR &= ~ENET_EIMR_EBERR_MASK;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief Kinetis KV5x Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void mkv5xEthEventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint32_t status;
+
+   //Read interrupt event register
+   status = ENET->EIR;
+
+   //Packet received?
+   if(status & ENET_EIR_RXF_MASK)
+   {
+      //Clear RXF interrupt flag
+      ENET->EIR = ENET_EIR_RXF_MASK;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = mkv5xEthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //System bus error?
+   if(status & ENET_EIR_EBERR_MASK)
+   {
+      //Clear EBERR interrupt flag
+      ENET->EIR = ENET_EIR_EBERR_MASK;
+
+      //Disable Ethernet MAC
+      ENET->ECR &= ~ENET_ECR_ETHEREN_MASK;
+      //Reset buffer descriptors
+      mkv5xEthInitBufferDesc(interface);
+      //Resume normal operation
+      ENET->ECR |= ENET_ECR_ETHEREN_MASK;
+      //Instruct the DMA to poll the receive descriptor list
+      ENET->RDAR = ENET_RDAR_RDAR_MASK;
+   }
+
+   //Re-enable Ethernet MAC interrupts
+   ENET->EIMR = ENET_EIMR_TXF_MASK | ENET_EIMR_RXF_MASK | ENET_EIMR_EBERR_MASK;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t mkv5xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > MKV5X_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txBufferDesc[txBufferIndex][0] & HTOBE16(ENET_TBD0_R))
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(txBuffer[txBufferIndex], buffer, offset, length);
+
+   //Set frame length
+   txBufferDesc[txBufferIndex][1] = HTOBE16(length);
+   //Clear BDU flag
+   txBufferDesc[txBufferIndex][8] = 0;
+
+   //Check current index
+   if(txBufferIndex < (MKV5X_ETH_TX_BUFFER_COUNT - 1))
+   {
+      //Give the ownership of the descriptor to the DMA engine
+      txBufferDesc[txBufferIndex][0] = HTOBE16(ENET_TBD0_R |
+         ENET_TBD0_L | ENET_TBD0_TC);
+
+      //Point to the next buffer
+      txBufferIndex++;
+   }
+   else
+   {
+      //Give the ownership of the descriptor to the DMA engine
+      txBufferDesc[txBufferIndex][0] = HTOBE16(ENET_TBD0_R |
+         ENET_TBD0_W | ENET_TBD0_L | ENET_TBD0_TC);
+
+      //Wrap around
+      txBufferIndex = 0;
+   }
+
+   //Data synchronization barrier
+   __DSB();
+
+   //Instruct the DMA to poll the transmit descriptor list
+   ENET->TDAR = ENET_TDAR_TDAR_MASK;
+
+   //Check whether the next buffer is available for writing
+   if(!(txBufferDesc[txBufferIndex][0] & HTOBE16(ENET_TBD0_R)))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mkv5xEthReceivePacket(NetInterface *interface)
+{
+   static uint8_t temp[MKV5X_ETH_RX_BUFFER_SIZE];
+   error_t error;
+   size_t n;
+
+   //Make sure the current buffer is available for reading
+   if(!(rxBufferDesc[rxBufferIndex][0] & HTOBE16(ENET_RBD0_E)))
+   {
+      //The frame should not span multiple buffers
+      if(rxBufferDesc[rxBufferIndex][0] & HTOBE16(ENET_RBD0_L))
+      {
+         //Check whether an error occurred
+         if(!(rxBufferDesc[rxBufferIndex][0] & HTOBE16(ENET_RBD0_LG |
+            ENET_RBD0_NO | ENET_RBD0_CR | ENET_RBD0_OV | ENET_RBD0_TR)))
+         {
+            //Retrieve the length of the frame
+            n = betoh16(rxBufferDesc[rxBufferIndex][1]);
+            //Limit the number of data to read
+            n = MIN(n, MKV5X_ETH_RX_BUFFER_SIZE);
+
+            //Copy data from the receive buffer
+            memcpy(temp, rxBuffer[rxBufferIndex], n);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, temp, n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Clear BDU flag
+      rxBufferDesc[rxBufferIndex][8] = 0;
+
+      //Check current index
+      if(rxBufferIndex < (MKV5X_ETH_RX_BUFFER_COUNT - 1))
+      {
+         //Give the ownership of the descriptor back to the DMA engine
+         rxBufferDesc[rxBufferIndex][0] = HTOBE16(ENET_RBD0_E);
+         //Point to the next buffer
+         rxBufferIndex++;
+      }
+      else
+      {
+         //Give the ownership of the descriptor back to the DMA engine
+         rxBufferDesc[rxBufferIndex][0] = HTOBE16(ENET_RBD0_E | ENET_RBD0_W);
+         //Wrap around
+         rxBufferIndex = 0;
+      }
+
+      //Instruct the DMA to poll the receive descriptor list
+      ENET->RDAR = ENET_RDAR_RDAR_MASK;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mkv5xEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating Kinetis KV5x hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = mkv5xEthCalcCrc(&entry->addr, sizeof(MacAddr));
+
+         //The upper 6 bits in the CRC register are used to index the
+         //contents of the hash table
+         k = (crc >> 26) & 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   ENET->GALR = hashTable[0];
+   ENET->GAUR = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  GALR = %08" PRIX32 "\r\n", ENET->GALR);
+   TRACE_DEBUG("  GAUR = %08" PRIX32 "\r\n", ENET->GAUR);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mkv5xEthUpdateMacConfig(NetInterface *interface)
+{
+   //Disable Ethernet MAC while modifying configuration registers
+   ENET->ECR &= ~ENET_ECR_ETHEREN_MASK;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+   {
+      //100 Mbps operation
+      ENET->RCR &= ~ENET_RCR_RMII_10T_MASK;
+   }
+   else
+   {
+      //10 Mbps operation
+      ENET->RCR |= ENET_RCR_RMII_10T_MASK;
+   }
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+   {
+      //Full-duplex mode
+      ENET->TCR |= ENET_TCR_FDEN_MASK;
+      //Receive path operates independently of transmit
+      ENET->RCR &= ~ENET_RCR_DRT_MASK;
+   }
+   else
+   {
+      //Half-duplex mode
+      ENET->TCR &= ~ENET_TCR_FDEN_MASK;
+      //Disable reception of frames while transmitting
+      ENET->RCR |= ENET_RCR_DRT_MASK;
+   }
+
+   //Reset buffer descriptors
+   mkv5xEthInitBufferDesc(interface);
+
+   //Re-enable Ethernet MAC
+   ENET->ECR |= ENET_ECR_ETHEREN_MASK;
+   //Instruct the DMA to poll the receive descriptor list
+   ENET->RDAR = ENET_RDAR_RDAR_MASK;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void mkv5xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Set up a write operation
+   value = ENET_MMFR_ST(1) | ENET_MMFR_OP(1) | ENET_MMFR_TA(2);
+   //PHY address
+   value |= ENET_MMFR_PA(phyAddr);
+   //Register address
+   value |= ENET_MMFR_RA(regAddr);
+   //Register value
+   value |= ENET_MMFR_DATA(data);
+
+   //Clear MII interrupt flag
+   ENET->EIR = ENET_EIR_MII_MASK;
+   //Start a write operation
+   ENET->MMFR = value;
+   //Wait for the write to complete
+   while(!(ENET->EIR & ENET_EIR_MII_MASK));
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t mkv5xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Set up a read operation
+   value = ENET_MMFR_ST(1) | ENET_MMFR_OP(2) | ENET_MMFR_TA(2);
+   //PHY address
+   value |= ENET_MMFR_PA(phyAddr);
+   //Register address
+   value |= ENET_MMFR_RA(regAddr);
+
+   //Clear MII interrupt flag
+   ENET->EIR = ENET_EIR_MII_MASK;
+   //Start a read operation
+   ENET->MMFR = value;
+   //Wait for the read to complete
+   while(!(ENET->EIR & ENET_EIR_MII_MASK));
+
+   //Return PHY register contents
+   return ENET->MMFR & ENET_MMFR_DATA_MASK;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t mkv5xEthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //Update CRC value
+      crc ^= p[i];
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         if(crc & 0x00000001)
+            crc = (crc >> 1) ^ 0xEDB88320;
+         else
+            crc = crc >> 1;
+      }
+   }
+
+   //Return CRC value
+   return crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/mkv5x_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,169 @@
+/**
+ * @file mkv5x_eth.h
+ * @brief Freescale Kinetis KV5x Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MKV5X_ETH_H
+#define _MKV5X_ETH_H
+
+//Number of TX buffers
+#ifndef MKV5X_ETH_TX_BUFFER_COUNT
+   #define MKV5X_ETH_TX_BUFFER_COUNT 2
+#elif (MKV5X_ETH_TX_BUFFER_COUNT < 1)
+   #error MKV5X_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef MKV5X_ETH_TX_BUFFER_SIZE
+   #define MKV5X_ETH_TX_BUFFER_SIZE 1536
+#elif (MKV5X_ETH_TX_BUFFER_SIZE != 1536)
+   #error MKV5X_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef MKV5X_ETH_RX_BUFFER_COUNT
+   #define MKV5X_ETH_RX_BUFFER_COUNT 4
+#elif (MKV5X_ETH_RX_BUFFER_COUNT < 1)
+   #error MKV5X_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef MKV5X_ETH_RX_BUFFER_SIZE
+   #define MKV5X_ETH_RX_BUFFER_SIZE 1536
+#elif (MKV5X_ETH_RX_BUFFER_SIZE != 1536)
+   #error MKV5X_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef MKV5X_ETH_IRQ_PRIORITY_GROUPING
+   #define MKV5X_ETH_IRQ_PRIORITY_GROUPING 3
+#elif (MKV5X_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error MKV5X_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef MKV5X_ETH_IRQ_GROUP_PRIORITY
+   #define MKV5X_ETH_IRQ_GROUP_PRIORITY 12
+#elif (MKV5X_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error MKV5X_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef MKV5X_ETH_IRQ_SUB_PRIORITY
+   #define MKV5X_ETH_IRQ_SUB_PRIORITY 0
+#elif (MKV5X_ETH_IRQ_SUB_PRIORITY < 0)
+   #error MKV5X_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//Enhanced transmit buffer descriptor
+#define ENET_TBD0_R                0x8000
+#define ENET_TBD0_TO1              0x4000
+#define ENET_TBD0_W                0x2000
+#define ENET_TBD0_TO2              0x1000
+#define ENET_TBD0_L                0x0800
+#define ENET_TBD0_TC               0x0400
+#define ENET_TBD1_DATA_LENGTH      0xFFFF
+#define ENET_TBD2_DATA_POINTER_H   0xFFFF
+#define ENET_TBD3_DATA_POINTER_L   0xFFFF
+#define ENET_TBD4_INT              0x4000
+#define ENET_TBD4_TS               0x2000
+#define ENET_TBD4_PINS             0x1000
+#define ENET_TBD4_IINS             0x0800
+#define ENET_TBD5_TXE              0x8000
+#define ENET_TBD5_UE               0x2000
+#define ENET_TBD5_EE               0x1000
+#define ENET_TBD5_FE               0x0800
+#define ENET_TBD5_LCE              0x0400
+#define ENET_TBD5_OE               0x0200
+#define ENET_TBD5_TSE              0x0100
+#define ENET_TBD8_BDU              0x8000
+#define ENET_TBD10_TIMESTAMP_H     0xFFFF
+#define ENET_TBD11_TIMESTAMP_L     0xFFFF
+
+//Enhanced receive buffer descriptor
+#define ENET_RBD0_E                0x8000
+#define ENET_RBD0_RO1              0x4000
+#define ENET_RBD0_W                0x2000
+#define ENET_RBD0_RO2              0x1000
+#define ENET_RBD0_L                0x0800
+#define ENET_RBD0_M                0x0100
+#define ENET_RBD0_BC               0x0080
+#define ENET_RBD0_MC               0x0040
+#define ENET_RBD0_LG               0x0020
+#define ENET_RBD0_NO               0x0010
+#define ENET_RBD0_CR               0x0004
+#define ENET_RBD0_OV               0x0002
+#define ENET_RBD0_TR               0x0001
+#define ENET_RBD1_DATA_LENGTH      0xFFFF
+#define ENET_RBD2_DATA_POINTER_H   0xFFFF
+#define ENET_RBD3_DATA_POINTER_L   0xFFFF
+#define ENET_RBD4_ME               0x8000
+#define ENET_RBD4_PE               0x0400
+#define ENET_RBD4_CE               0x0200
+#define ENET_RBD4_UC               0x0100
+#define ENET_RBD4_INT              0x0080
+#define ENET_RBD5_VPCP             0xE000
+#define ENET_RBD5_ICE              0x0020
+#define ENET_RBD5_PCR              0x0010
+#define ENET_RBD5_VLAN             0x0004
+#define ENET_RBD5_IPV6             0x0002
+#define ENET_RBD5_FRAG             0x0001
+#define ENET_RBD6_HEADER_LENGTH    0xF800
+#define ENET_RBD6_PROTOCOL_TYPE    0x00FF
+#define ENET_RBD7_PAYLOAD_CHECKSUM 0xFFFF
+#define ENET_RBD8_BDU              0x8000
+#define ENET_RBD10_TIMESTAMP_H     0xFFFF
+#define ENET_RBD11_TIMESTAMP_L     0xFFFF
+
+//Kinetis KV5x Ethernet MAC driver
+extern const NicDriver mkv5xEthDriver;
+
+//Kinetis KV5x Ethernet MAC related functions
+error_t mkv5xEthInit(NetInterface *interface);
+void mkv5xEthInitGpio(NetInterface *interface);
+void mkv5xEthInitBufferDesc(NetInterface *interface);
+
+void mkv5xEthTick(NetInterface *interface);
+
+void mkv5xEthEnableIrq(NetInterface *interface);
+void mkv5xEthDisableIrq(NetInterface *interface);
+void mkv5xEthEventHandler(NetInterface *interface);
+
+error_t mkv5xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t mkv5xEthReceivePacket(NetInterface *interface);
+
+error_t mkv5xEthSetMulticastFilter(NetInterface *interface);
+error_t mkv5xEthUpdateMacConfig(NetInterface *interface);
+
+void mkv5xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t mkv5xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t mkv5xEthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/mrf24wg_driver.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,355 @@
+/**
+ * @file mrf24wg_driver.c
+ * @brief MRF24WG Wi-Fi controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "mrf24wg_driver.h"
+#include "debug.h"
+
+//MRF24WG universal driver
+#include "wf_universal_driver.h"
+#include "wf_debug_output.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//Transmit buffer
+static Mrf24wgBuffer txBuffer[WF_TX_QUEUE_SIZE];
+//Receive buffer
+static uint8_t rxBuffer[MRF24WG_RX_BUFFER_SIZE];
+
+
+/**
+ * @brief MRF24WG driver
+ **/
+
+const NicDriver mrf24wgDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   mrf24wgInit,
+   mrf24wgTick,
+   mrf24wgEnableIrq,
+   mrf24wgDisableIrq,
+   mrf24wgEventHandler,
+   mrf24wgSendPacket,
+   mrf24wgSetMulticastFilter,
+   NULL,
+   NULL,
+   NULL,
+   TRUE,
+   TRUE,
+   TRUE,
+   TRUE
+};
+
+
+/**
+ * @brief MRF24WG initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mrf24wgInit(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing MRF24WG...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Clear TX buffers
+   memset(txBuffer, 0, sizeof(txBuffer));
+
+   //Initialize MRF24WG controller
+   WF_Init();
+
+   //MRF24WG is now ready to send
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief MRF24WG timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void mrf24wgTick(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void mrf24wgEnableIrq(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void mrf24wgDisableIrq(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief MRF24WG event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void mrf24wgEventHandler(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t mrf24wgSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   bool_t status;
+   uint_t i;
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > MRF24WG_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Loop through TX buffers
+   for(i = 0; i < WF_TX_QUEUE_SIZE; i++)
+   {
+      //Check whether the current buffer is available
+      if(!txBuffer[i].used)
+         break;
+   }
+
+   //Any buffer available?
+   if(i < WF_TX_QUEUE_SIZE)
+   {
+      //Save packet length
+      txBuffer[i].length = length;
+
+      //Copy user data to the transmit buffer
+      netBufferRead(txBuffer[i].data, buffer, offset, length);
+
+      //Enqueue packet
+      status = WF_QueueTxPacket(txBuffer[i].data, length);
+
+      //Check status code
+      if(status)
+         txBuffer[i].used = TRUE;
+   }
+   else
+   {
+      //No buffer available
+      status = FALSE;
+   }
+
+   //The transmitter can accept another packet
+   osSetEvent(&nicDriverInterface->nicTxEvent);
+
+   //Return status code
+   if(status)
+      return NO_ERROR;
+   else
+      return ERROR_FAILURE;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mrf24wgSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_INFO("Updating MRF24WG multicast filter...\r\n");
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Check whether the MAC filter table should be updated for the
+      //current multicast address
+      if(!macCompAddr(&entry->addr, &MAC_UNSPECIFIED_ADDR))
+      {
+         if(entry->addFlag)
+         {
+            //Add a new entry to the MAC filter table
+         }
+         else if(entry->deleteFlag)
+         {
+            //Remove the current entry from the MAC filter table
+         }
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Callback function that handles Wi-Fi events
+ * @param[in] eventType Type of notification
+ * @param[in] eventData Event data
+ **/
+
+void WF_ProcessEvent(uint8_t eventType, uint32_t eventData)
+{
+#if defined(WF_USE_DEBUG_OUTPUT)
+   //Debug message
+   DumpEventInfo(eventType, eventData);
+#endif
+
+   //Check event type
+   switch(eventType)
+   {
+   //Initialization complete?
+   case WF_EVENT_INITIALIZATION:
+      //Use the factory preprogrammed station address
+      WF_MacAddressGet(nicDriverInterface->macAddr.b);
+      //Generate the 64-bit interface identifier
+      macAddrToEui64(&nicDriverInterface->macAddr, &nicDriverInterface->eui64);
+      break;
+
+   //Connection established?
+   case WF_EVENT_CONNECTION_SUCCESSFUL:
+   case WF_EVENT_CONNECTION_REESTABLISHED:
+   case WF_EVENT_SOFTAP_NETWORK_STARTED:
+      //Link is up
+      nicDriverInterface->linkState = TRUE;
+      //Process link state change event
+      nicNotifyLinkChange(nicDriverInterface);
+      break;
+
+   //Connection lost?
+   case WF_EVENT_CONNECTION_TEMPORARILY_LOST:
+   case WF_EVENT_CONNECTION_PERMANENTLY_LOST:
+   case WF_EVENT_CONNECTION_FAILED:
+   case WF_EVENT_DISCONNECT_COMPLETE:
+      //Link is down
+      nicDriverInterface->linkState = FALSE;
+      //Process link state change event
+      nicNotifyLinkChange(nicDriverInterface);
+      break;
+
+   //Any other event?
+   default:
+      break;
+   }
+
+#if defined(MRF24WG_EVENT_HOOK)
+   //Invoke user callback function
+   MRF24WG_EVENT_HOOK(eventType, eventData);
+#endif
+}
+
+
+/**
+ * @brief Callback function (packet received)
+ **/
+
+void WF_RxPacketReady(void)
+{
+   size_t n;
+
+   //Retrieve the length of the packet
+   n = WF_RxPacketLengthGet();
+   //Copy the packet to the receive buffer
+   WF_RxPacketCopy(rxBuffer, n);
+
+   //Pass the packet to the upper layer
+   nicProcessPacket(nicDriverInterface, rxBuffer, n);
+
+   //Release the packet
+   WF_RxPacketDeallocate();
+}
+
+
+/**
+ * @brief Callback function (packet transmitted)
+ **/
+
+void WF_TxComplete(uint8_t *p)
+{
+   uint_t i;
+
+   //Loop through TX buffers
+   for(i = 0; i < WF_TX_QUEUE_SIZE; i++)
+   {
+      if(txBuffer[i].data == p)
+      {
+         //Release current buffer
+         txBuffer[i].used = FALSE;
+      }
+   }
+
+   //The transmitter can accept another packet
+   osSetEvent(&nicDriverInterface->nicTxEvent);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/mrf24wg_driver.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,83 @@
+/**
+ * @file mrf24wg_driver.h
+ * @brief MRF24WG Wi-Fi controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MRF24WG_DRIVER_H
+#define _MRF24WG_DRIVER_H
+
+//Dependencies
+#include "core/nic.h"
+
+//TX buffer size
+#ifndef MRF24WG_TX_BUFFER_SIZE
+   #define MRF24WG_TX_BUFFER_SIZE 1536
+#elif (MRF24WG_TX_BUFFER_SIZE != 1536)
+   #error MRF24WG_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef MRF24WG_RX_BUFFER_SIZE
+   #define MRF24WG_RX_BUFFER_SIZE 1536
+#elif (MRF24WG_RX_BUFFER_SIZE != 1536)
+   #error MRF24WG_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+
+/**
+ * @brief TX buffer
+ **/
+
+typedef struct
+{
+   bool_t used;
+   size_t length;
+   uint8_t data[MRF24WG_TX_BUFFER_SIZE];
+} Mrf24wgBuffer;
+
+
+//MRF24WG driver
+extern const NicDriver mrf24wgDriver;
+
+//MRF24WG related functions
+error_t mrf24wgInit(NetInterface *interface);
+
+void mrf24wgTick(NetInterface *interface);
+
+void mrf24wgEnableIrq(NetInterface *interface);
+void mrf24wgDisableIrq(NetInterface *interface);
+void mrf24wgEventHandler(NetInterface *interface);
+
+error_t mrf24wgSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t mrf24wgSetMulticastFilter(NetInterface *interface);
+
+void mrf24wgAppWifiEvent(uint8_t msgType, void *msg);
+void mrf24wgAppEthEvent(uint8_t msgType, void *msg, void *ctrlBuf);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/omapl138_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,933 @@
+/**
+ * @file omapl138_eth.c
+ * @brief OMAP-L138 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "soc_omapl138.h"
+#include "hw_types.h"
+#include "hw_syscfg0_omapl138.h"
+#include "hw_emac.h"
+#include "hw_emac_ctrl.h"
+#include "hw_mdio.h"
+#include "cache.h"
+#include "interrupt.h"
+#include "psc.h"
+#include "core/net.h"
+#include "drivers/omapl138_eth.h"
+#include "debug.h"
+
+//MDIO input clock frequency
+#define MDIO_INPUT_CLK 75000000
+//MDIO output clock frequency
+#define MDIO_OUTPUT_CLK 1000000
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static uint8_t txBuffer[OMAPL138_ETH_TX_BUFFER_COUNT][OMAPL138_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static uint8_t rxBuffer[OMAPL138_ETH_RX_BUFFER_COUNT][OMAPL138_ETH_RX_BUFFER_SIZE];
+//Transmit buffer descriptors
+#pragma data_alignment = 4
+#pragma location = ".ram_cppi"
+static Omapl138TxBufferDesc txBufferDesc[OMAPL138_ETH_TX_BUFFER_COUNT];
+//Receive buffer descriptors
+#pragma data_alignment = 4
+#pragma location = ".ram_cppi"
+static Omapl138RxBufferDesc rxBufferDesc[OMAPL138_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[OMAPL138_ETH_TX_BUFFER_COUNT][OMAPL138_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+//Receive buffer
+static uint8_t rxBuffer[OMAPL138_ETH_RX_BUFFER_COUNT][OMAPL138_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+//Transmit buffer descriptors
+static Omapl138TxBufferDesc txBufferDesc[OMAPL138_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".ram_cppi")));
+//Receive buffer descriptors
+static Omapl138RxBufferDesc rxBufferDesc[OMAPL138_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".ram_cppi")));
+
+#endif
+
+//Pointer to the current TX buffer descriptor
+static Omapl138TxBufferDesc *txCurBufferDesc;
+//Pointer to the current RX buffer descriptor
+static Omapl138RxBufferDesc *rxCurBufferDesc;
+
+
+/**
+ * @brief OMAP-L138 Ethernet MAC driver
+ **/
+
+const NicDriver omapl138EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   omapl138EthInit,
+   omapl138EthTick,
+   omapl138EthEnableIrq,
+   omapl138EthDisableIrq,
+   omapl138EthEventHandler,
+   omapl138EthSendPacket,
+   omapl138EthSetMulticastFilter,
+   omapl138EthUpdateMacConfig,
+   omapl138EthWritePhyReg,
+   omapl138EthReadPhyReg,
+   FALSE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief OMAP-L138 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t omapl138EthInit(NetInterface *interface)
+{
+   error_t error;
+   uint_t channel;
+   uint32_t temp;
+
+   //Debug message
+   TRACE_INFO("Initializing OMAP-L138 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Enable EMAC module
+   PSCModuleControl(SOC_PSC_1_REGS, HW_PSC_EMAC,
+      PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_ENABLE);
+
+   //Select the interface mode (MII/RMII) and configure pin muxing
+   omapl138EthInitGpio(interface);
+
+   //Reset the EMAC control module
+   EMAC_CTRL_SOFTRESET_R = EMAC_SOFTRESET_SOFTRESET;
+   //Wait for the reset to complete
+   while(EMAC_CTRL_SOFTRESET_R & EMAC_SOFTRESET_SOFTRESET);
+
+   //Reset the EMAC module
+   EMAC_SOFTRESET_R = EMAC_SOFTRESET_SOFTRESET;
+   //Wait for the reset to complete
+   while(EMAC_SOFTRESET_R & EMAC_SOFTRESET_SOFTRESET);
+
+   //Calculate the MDC clock divider to be used
+   temp = (MDIO_INPUT_CLK / MDIO_OUTPUT_CLK) - 1;
+
+   //Initialize MDIO interface
+   MDIO_CONTROL_R = MDIO_CONTROL_ENABLE |
+      MDIO_CONTROL_FAULTENB | (temp & MDIO_CONTROL_CLKDIV);
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Clear the control registers
+   EMAC_MACCONTROL_R = 0;
+   EMAC_RXCONTROL_R = 0;
+   EMAC_TXCONTROL_R = 0;
+
+   //Initialize all 16 header descriptor pointer registers to 0
+   for(channel = EMAC_CH0; channel <= EMAC_CH7; channel++)
+   {
+      //TX head descriptor pointer
+      EMAC_TXHDP_R(channel) = 0;
+      //TX completion pointer
+      EMAC_TXCP_R(channel) = 0;
+      //RX head descriptor pointer
+      EMAC_RXHDP_R(channel) = 0;
+      //RX completion pointer
+      EMAC_RXCP_R(channel) = 0;
+   }
+
+   //Set the upper 32 bits of the source MAC address
+   EMAC_MACSRCADDRHI_R = interface->macAddr.b[0] |
+      (interface->macAddr.b[1] << 8) |
+      (interface->macAddr.b[2] << 16) |
+      (interface->macAddr.b[3] << 24);
+
+   //Set the lower 16 bits of the source MAC address
+   EMAC_MACSRCADDRLO_R = interface->macAddr.b[4] |
+      (interface->macAddr.b[5] << 8);
+
+   //Write the channel number to the MAC index register
+   EMAC_MACINDEX_R = EMAC_CH0;
+
+   //Set the upper 32 bits of the source MAC address
+   EMAC_MACADDRHI_R = interface->macAddr.b[0] |
+      (interface->macAddr.b[1] << 8) |
+      (interface->macAddr.b[2] << 16) |
+      (interface->macAddr.b[3] << 24);
+
+   //Set the lower 16 bits of the source MAC address
+   temp = interface->macAddr.b[4] |
+      (interface->macAddr.b[5] << 8);
+
+   //Use the current MAC address to match incoming packet addresses
+   EMAC_MACADDRLO_R = EMAC_MACADDRLO_VALID | EMAC_MACADDRLO_MATCHFILT |
+         (EMAC_CH0 << EMAC_MACADDRLO_CHANNEL_SHIFT) | temp;
+
+   //Be sure to program all eight MAC address registers, whether the
+   //receive channel is to be enabled or not
+   for(channel = EMAC_CH1; channel <= EMAC_CH7; channel++)
+   {
+      //Write the channel number to the MAC index register
+      EMAC_MACINDEX_R = channel;
+      //The MAC address is not valid
+      EMAC_MACADDRLO_R = (channel << EMAC_MACADDRLO_CHANNEL_SHIFT);
+   }
+
+   //Clear the MAC address hash registers
+   EMAC_MACHASH1_R = 0;
+   EMAC_MACHASH2_R = 0;
+
+   //The RX buffer offset must be initialized to zero
+   EMAC_RXBUFFEROFFSET_R = 0;
+
+   //Clear all unicast channels
+   EMAC_RXUNICASTCLEAR_R = 0xFF;
+
+   //Accept unicast frames
+   EMAC_RXUNICASTSET_R |= (1 << EMAC_CH0);
+
+   //Received CRC is transferred to memory for all channels
+   EMAC_RXMBPENABLE_R = EMAC_RXMBPENABLE_RXPASSCRC;
+
+   //Accept broadcast frames
+   EMAC_RXMBPENABLE_R |= EMAC_RXMBPENABLE_RXBROADEN |
+      (EMAC_CH0 << EMAC_RXMBPENABLE_RXBROADCH_SHIFT);
+
+   //Accept hash matching multicast frames
+   EMAC_RXMBPENABLE_R |= EMAC_RXMBPENABLE_RXMULTEN |
+      (EMAC_CH0 << EMAC_RXMBPENABLE_RXMULTCH_SHIFT);
+
+   //Register interrupt handlers
+   IntRegister(SYS_INT_C0_TX, omapl138EthTxIrqHandler);
+   IntRegister(SYS_INT_C0_RX, omapl138EthRxIrqHandler);
+
+   //Set the channel number for the TX interrupt
+   IntChannelSet(SYS_INT_C0_TX, OMAPL138_ETH_TX_IRQ_CHANNEL);
+   //Set the channel number for the RX interrupt
+   IntChannelSet(SYS_INT_C0_RX, OMAPL138_ETH_RX_IRQ_CHANNEL);
+
+   //Clear all unused channel interrupt bits
+   EMAC_TXINTMASKCLEAR_R = 0xFF;
+   EMAC_RXINTMASKCLEAR_R = 0xFF;
+
+   //Enable the receive and transmit channel interrupt bits
+   EMAC_TXINTMASKSET_R = (1 << EMAC_CH0);
+   EMAC_RXINTMASKSET_R = (1 << EMAC_CH0);
+
+   //Configure TX and RX buffer descriptors
+   omapl138EthInitBufferDesc(interface);
+
+   //Write the RX DMA head descriptor pointer
+   EMAC_RXHDP_R(EMAC_CH0) = (uint32_t) rxCurBufferDesc;
+
+   //Enable the receive and transmit DMA controllers
+   EMAC_TXCONTROL_R = EMAC_TXCONTROL_TXEN;
+   EMAC_RXCONTROL_R = EMAC_RXCONTROL_RXEN;
+
+   //Enable TX and RX
+   EMAC_MACCONTROL_R = EMAC_MACCONTROL_GMIIEN;
+
+   //Enable TX and RX completion interrupts
+   EMAC_CTRL_CnTXEN_R(EMAC_CORE0) |= (1 << EMAC_CH0);
+   EMAC_CTRL_CnRXEN_R(EMAC_CORE0) |= (1 << EMAC_CH0);
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//TMDSLCDK138 board?
+#if defined(USE_TMDSLCDK138)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void omapl138EthInitGpio(NetInterface *interface)
+{
+   uint32_t temp;
+
+   //Enable GPIO module
+   PSCModuleControl(SOC_PSC_1_REGS, HW_PSC_GPIO,
+      PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_ENABLE);
+
+   //Configure MII_TXD0, MII_TXD1, MII_TXD2, MII_TXD3, MII_COL, MII_TXCLK and MII_TXEN
+   temp =  SYSCFG0_PINMUX_R(2) & ~(SYSCFG_PINMUX2_PINMUX2_31_28 |
+      SYSCFG_PINMUX2_PINMUX2_27_24 | SYSCFG_PINMUX2_PINMUX2_23_20 |
+      SYSCFG_PINMUX2_PINMUX2_19_16 | SYSCFG_PINMUX2_PINMUX2_15_12 |
+      SYSCFG_PINMUX2_PINMUX2_11_8 | SYSCFG_PINMUX2_PINMUX2_7_4);
+
+    SYSCFG0_PINMUX_R(2) = temp |
+       (SYSCFG_PINMUX2_PINMUX2_31_28_MII_TXD0 << SYSCFG_PINMUX2_PINMUX2_31_28_SHIFT) |
+      (SYSCFG_PINMUX2_PINMUX2_27_24_MII_TXD1 << SYSCFG_PINMUX2_PINMUX2_27_24_SHIFT) |
+      (SYSCFG_PINMUX2_PINMUX2_23_20_MII_TXD2 << SYSCFG_PINMUX2_PINMUX2_23_20_SHIFT) |
+      (SYSCFG_PINMUX2_PINMUX2_19_16_MII_TXD3 << SYSCFG_PINMUX2_PINMUX2_19_16_SHIFT) |
+      (SYSCFG_PINMUX2_PINMUX2_15_12_MII_COL << SYSCFG_PINMUX2_PINMUX2_15_12_SHIFT) |
+      (SYSCFG_PINMUX2_PINMUX2_11_8_MII_TXCLK << SYSCFG_PINMUX2_PINMUX2_11_8_SHIFT) |
+      (SYSCFG_PINMUX2_PINMUX2_7_4_MII_TXEN << SYSCFG_PINMUX2_PINMUX2_7_4_SHIFT);
+
+   //Configure MII_RXD0, MII_RXD1, MII_RXD2, MII_RXD3, MII_CRS, MII_RXER, MII_RXDV and RXCLK
+   temp = SYSCFG0_PINMUX_R(3) & ~(SYSCFG_PINMUX3_PINMUX3_31_28 |
+      SYSCFG_PINMUX3_PINMUX3_27_24 | SYSCFG_PINMUX3_PINMUX3_23_20 |
+      SYSCFG_PINMUX3_PINMUX3_19_16 | SYSCFG_PINMUX3_PINMUX3_15_12 |
+      SYSCFG_PINMUX3_PINMUX3_11_8 | SYSCFG_PINMUX3_PINMUX3_7_4 |
+      SYSCFG_PINMUX3_PINMUX3_3_0);
+
+   SYSCFG0_PINMUX_R(3) = temp |
+      (SYSCFG_PINMUX3_PINMUX3_31_28_MII_RXD0 << SYSCFG_PINMUX3_PINMUX3_31_28_SHIFT) |
+      (SYSCFG_PINMUX3_PINMUX3_27_24_MII_RXD1 << SYSCFG_PINMUX3_PINMUX3_27_24_SHIFT) |
+      (SYSCFG_PINMUX3_PINMUX3_23_20_MII_RXD2 << SYSCFG_PINMUX3_PINMUX3_23_20_SHIFT) |
+      (SYSCFG_PINMUX3_PINMUX3_19_16_MII_RXD3 << SYSCFG_PINMUX3_PINMUX3_19_16_SHIFT) |
+      (SYSCFG_PINMUX3_PINMUX3_15_12_MII_CRS << SYSCFG_PINMUX3_PINMUX3_15_12_SHIFT) |
+      (SYSCFG_PINMUX3_PINMUX3_11_8_MII_RXER << SYSCFG_PINMUX3_PINMUX3_11_8_SHIFT) |
+      (SYSCFG_PINMUX3_PINMUX3_7_4_MII_RXDV << SYSCFG_PINMUX3_PINMUX3_7_4_SHIFT) |
+      (SYSCFG_PINMUX3_PINMUX3_3_0_MII_RXCLK << SYSCFG_PINMUX3_PINMUX3_3_0_SHIFT);
+
+    //Configure MDIO and MDCLK
+    temp = SYSCFG0_PINMUX_R(4) &  ~(SYSCFG_PINMUX4_PINMUX4_3_0 |
+       SYSCFG_PINMUX4_PINMUX4_7_4);
+
+    SYSCFG0_PINMUX_R(4) = temp |
+       (SYSCFG_PINMUX4_PINMUX4_7_4_MDIO_D << SYSCFG_PINMUX4_PINMUX4_7_4_SHIFT) |
+       (SYSCFG_PINMUX4_PINMUX4_3_0_MDIO_CLK << SYSCFG_PINMUX4_PINMUX4_3_0_SHIFT);
+
+   //Select MII interface mode
+   SYSCFG0_CFGCHIP3_R &= ~SYSCFG_CFGCHIP3_RMII_SEL;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize buffer descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void omapl138EthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+   uint_t nextIndex;
+   uint_t prevIndex;
+
+   //Initialize TX buffer descriptor list
+   for(i = 0; i < OMAPL138_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Index of the next buffer
+      nextIndex = (i + 1) % OMAPL138_ETH_TX_BUFFER_COUNT;
+      //Index of the previous buffer
+      prevIndex = (i + OMAPL138_ETH_TX_BUFFER_COUNT - 1) % OMAPL138_ETH_TX_BUFFER_COUNT;
+
+      //Next descriptor pointer
+      txBufferDesc[i].word0 = (uint32_t) NULL;
+      //Buffer pointer
+      txBufferDesc[i].word1 = (uint32_t) txBuffer[i];
+      //Buffer offset and buffer length
+      txBufferDesc[i].word2 = 0;
+      //Status flags and packet length
+      txBufferDesc[i].word3 = 0;
+
+      //Form a doubly linked list
+      txBufferDesc[i].next = &txBufferDesc[nextIndex];
+      txBufferDesc[i].prev = &txBufferDesc[prevIndex];
+   }
+
+   //Point to the very first descriptor
+   txCurBufferDesc = &txBufferDesc[0];
+
+   //Mark the end of the queue
+   txCurBufferDesc->prev->word3 = EMAC_TX_WORD3_SOP |
+      EMAC_TX_WORD3_EOP | EMAC_TX_WORD3_EOQ;
+
+   //Initialize RX buffer descriptor list
+   for(i = 0; i < OMAPL138_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Index of the next buffer
+      nextIndex = (i + 1) % OMAPL138_ETH_RX_BUFFER_COUNT;
+      //Index of the previous buffer
+      prevIndex = (i + OMAPL138_ETH_RX_BUFFER_COUNT - 1) % OMAPL138_ETH_RX_BUFFER_COUNT;
+
+      //Next descriptor pointer
+      rxBufferDesc[i].word0 = (uint32_t) &rxBufferDesc[nextIndex];
+      //Buffer pointer
+      rxBufferDesc[i].word1 = (uint32_t) rxBuffer[i];
+      //Buffer offset and buffer length
+      rxBufferDesc[i].word2 = OMAPL138_ETH_RX_BUFFER_SIZE;
+      //Status flags and packet length
+      rxBufferDesc[i].word3 = EMAC_RX_WORD3_OWNER;
+
+      //Form a doubly linked list
+      rxBufferDesc[i].next = &rxBufferDesc[nextIndex];
+      rxBufferDesc[i].prev = &rxBufferDesc[prevIndex];
+   }
+
+   //Point to the very first descriptor
+   rxCurBufferDesc = &rxBufferDesc[0];
+
+   //Mark the end of the queue
+   rxCurBufferDesc->prev->word0 = (uint32_t) NULL;
+}
+
+
+/**
+ * @brief OMAP-L138 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void omapl138EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+
+   //Misqueued buffer condition?
+   if(rxCurBufferDesc->word3 & EMAC_RX_WORD3_OWNER)
+   {
+      if(EMAC_RXHDP_R(EMAC_CH0) == 0)
+      {
+         //The host acts on the misqueued buffer condition by writing the added
+         //buffer descriptor address to the appropriate RX DMA head descriptor
+         //pointer
+         EMAC_RXHDP_R(EMAC_CH0) = (uint32_t) rxCurBufferDesc;
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void omapl138EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   IntSystemEnable(SYS_INT_C0_TX);
+   IntSystemEnable(SYS_INT_C0_RX);
+
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void omapl138EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   IntSystemDisable(SYS_INT_C0_TX);
+   IntSystemDisable(SYS_INT_C0_RX);
+
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief Ethernet MAC transmit interrupt
+ **/
+
+void omapl138EthTxIrqHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+   uint32_t temp;
+   Omapl138TxBufferDesc *p;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Clear system interrupt status
+   IntSystemStatusClear(SYS_INT_C0_TX);
+
+   //Read the C0TXSTAT register to determine which channels caused the interrupt
+   status = EMAC_CTRL_C0TXSTAT_R;
+
+   //Packet transmitted on channel 0?
+   if(status & (1 << EMAC_CH0))
+   {
+      //Point to the buffer descriptor
+      p = (Omapl138TxBufferDesc *) EMAC_TXCP_R(EMAC_CH0);
+
+      //Read the status flags
+      temp = p->word3 & (EMAC_TX_WORD3_SOP | EMAC_TX_WORD3_EOP |
+         EMAC_TX_WORD3_OWNER | EMAC_TX_WORD3_EOQ);
+
+      //Misqueued buffer condition?
+      if(temp == (EMAC_TX_WORD3_SOP | EMAC_TX_WORD3_EOP | EMAC_TX_WORD3_EOQ))
+      {
+         //Check whether the next descriptor pointer is non-zero
+         if(p->word0 != 0)
+         {
+            //The host corrects the misqueued buffer condition by writing the
+            //misqueued packet’s buffer descriptor address to the appropriate
+            //TX DMA head descriptor pointer
+            EMAC_TXHDP_R(EMAC_CH0) = (uint32_t) p->word0;
+         }
+      }
+
+      //Write the TX completion pointer
+      EMAC_TXCP_R(EMAC_CH0) = (uint32_t) p;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurBufferDesc->word3 & EMAC_TX_WORD3_OWNER))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //Writes the DMA end of interrupt vector
+   EMAC_MACEOIVECTOR_R = EMAC_MACEOIVECTOR_C0TX;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief Ethernet MAC receive interrupt
+ **/
+
+void omapl138EthRxIrqHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Clear system interrupt status
+   IntSystemStatusClear(SYS_INT_C0_RX);
+
+   //Read the C0RXSTAT register to determine which channels caused the interrupt
+   status = EMAC_CTRL_C0RXSTAT_R;
+
+   //Packet received on channel 0?
+   if(status & (1 << EMAC_CH0))
+   {
+      //Disable RX interrupts
+      EMAC_CTRL_CnRXEN_R(EMAC_CORE0) &= ~(1 << EMAC_CH0);
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Writes the DMA end of interrupt vector
+   EMAC_MACEOIVECTOR_R = EMAC_MACEOIVECTOR_C0RX;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief OMAP-L138 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void omapl138EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Process all pending packets
+   do
+   {
+      //Read incoming packet
+      error = omapl138EthReceivePacket(interface);
+
+      //No more data in the receive buffer?
+   } while(error != ERROR_BUFFER_EMPTY);
+
+   //Re-enable RX interrupts
+   EMAC_CTRL_CnRXEN_R(EMAC_CORE0) |= (1 << EMAC_CH0);
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t omapl138EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+   uint32_t temp;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > OMAPL138_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurBufferDesc->word3 & EMAC_TX_WORD3_OWNER)
+      return ERROR_FAILURE;
+
+   //Mark the end of the queue with a NULL pointer
+   txCurBufferDesc->word0 = (uint32_t) NULL;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txCurBufferDesc->word1, buffer, offset, length);
+
+   //Set the length of the buffer
+   txCurBufferDesc->word2 = length & EMAC_TX_WORD2_BUFFER_LENGTH;
+
+   //Give the ownership of the descriptor to the DMA
+   txCurBufferDesc->word3 = EMAC_TX_WORD3_SOP | EMAC_TX_WORD3_EOP |
+      EMAC_TX_WORD3_OWNER | (length & EMAC_TX_WORD3_PACKET_LENGTH);
+
+   //Link the current descriptor to the previous descriptor
+   txCurBufferDesc->prev->word0 = (uint32_t) txCurBufferDesc;
+
+   //Read the status flags of the previous descriptor
+   temp = txCurBufferDesc->prev->word3 & (EMAC_TX_WORD3_SOP |
+      EMAC_TX_WORD3_EOP | EMAC_TX_WORD3_OWNER | EMAC_TX_WORD3_EOQ);
+
+   //Misqueued buffer condition?
+   if(temp == (EMAC_TX_WORD3_SOP | EMAC_TX_WORD3_EOP | EMAC_TX_WORD3_EOQ))
+   {
+      //Clear the misqueued buffer condition
+      txCurBufferDesc->prev->word3 = 0;
+
+      //The host corrects the misqueued buffer condition by writing the
+      //misqueued packet’s buffer descriptor address to the appropriate
+      //TX DMA head descriptor pointer
+      EMAC_TXHDP_R(EMAC_CH0) = (uint32_t) txCurBufferDesc;
+   }
+
+   //Point to the next descriptor in the list
+   txCurBufferDesc = txCurBufferDesc->next;
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurBufferDesc->word3 & EMAC_TX_WORD3_OWNER))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t omapl138EthReceivePacket(NetInterface *interface)
+{
+   static uint8_t buffer[OMAPL138_ETH_RX_BUFFER_SIZE];
+   error_t error;
+   size_t n;
+   uint32_t temp;
+
+   //The current buffer is available for reading?
+   if(!(rxCurBufferDesc->word3 & EMAC_RX_WORD3_OWNER))
+   {
+      //SOP and EOP flags should be set
+      if((rxCurBufferDesc->word3 & EMAC_RX_WORD3_SOP) &&
+         (rxCurBufferDesc->word3 & EMAC_RX_WORD3_EOP))
+      {
+         //Make sure no error occurred
+         if(!(rxCurBufferDesc->word3 & EMAC_RX_WORD3_ERROR_MASK))
+         {
+            //Retrieve the length of the frame
+            n = rxCurBufferDesc->word3 & EMAC_RX_WORD3_PACKET_LENGTH;
+            //Limit the number of data to read
+            n = MIN(n, OMAPL138_ETH_RX_BUFFER_SIZE);
+
+            //Copy data from the receive buffer
+            memcpy(buffer, (uint8_t *) rxCurBufferDesc->word1, n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Mark the end of the queue with a NULL pointer
+      rxCurBufferDesc->word0 = (uint32_t) NULL;
+      //Restore the length of the buffer
+      rxCurBufferDesc->word2 = OMAPL138_ETH_RX_BUFFER_SIZE;
+      //Give the ownership of the descriptor back to the DMA
+      rxCurBufferDesc->word3 = EMAC_RX_WORD3_OWNER;
+
+      //Link the current descriptor to the previous descriptor
+      rxCurBufferDesc->prev->word0 = (uint32_t) rxCurBufferDesc;
+
+      //Read the status flags of the previous descriptor
+      temp = rxCurBufferDesc->prev->word3 & (EMAC_RX_WORD3_SOP |
+         EMAC_RX_WORD3_EOP | EMAC_RX_WORD3_OWNER | EMAC_RX_WORD3_EOQ);
+
+      //Misqueued buffer condition?
+      if(temp == (EMAC_RX_WORD3_SOP | EMAC_RX_WORD3_EOP | EMAC_RX_WORD3_EOQ))
+      {
+         //The host acts on the misqueued buffer condition by writing the added
+         //buffer descriptor address to the appropriate RX DMA head descriptor
+         //pointer
+         EMAC_RXHDP_R(EMAC_CH0) = (uint32_t) rxCurBufferDesc;
+      }
+
+      //Write the RX completion pointer
+      EMAC_RXCP_R(EMAC_CH0) = (uint32_t) rxCurBufferDesc;
+
+      //Point to the next descriptor in the list
+      rxCurBufferDesc = rxCurBufferDesc->next;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Check whether a valid packet has been received
+   if(!error)
+   {
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, buffer, n);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t omapl138EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint8_t *p;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating OMAP-L138 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Point to the MAC address
+         p = entry->addr.b;
+
+         //Apply the hash function
+         k = (p[0] >> 2) ^ (p[0] << 4);
+         k ^= (p[1] >> 4) ^ (p[1] << 2);
+         k ^= (p[2] >> 6) ^ p[2];
+         k ^= (p[3] >> 2) ^ (p[3] << 4);
+         k ^= (p[4] >> 4) ^ (p[4] << 2);
+         k ^= (p[5] >> 6) ^ p[5];
+
+         //The hash value is reduced to a 6-bit index
+         k &= 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   EMAC_MACHASH1_R = hashTable[0];
+   EMAC_MACHASH2_R = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  MACHASH1 = %08" PRIX32 "\r\n", EMAC_MACHASH1_R);
+   TRACE_DEBUG("  MACHASH2 = %08" PRIX32 "\r\n", EMAC_MACHASH2_R);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t omapl138EthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read MAC control register
+   config = EMAC_MACCONTROL_R;
+
+   //100BASE-TX or 10BASE-T operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= EMAC_MACCONTROL_RMIISPEED;
+   else
+      config &= ~EMAC_MACCONTROL_RMIISPEED;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= EMAC_MACCONTROL_FULLDUPLEX;
+   else
+      config &= ~EMAC_MACCONTROL_FULLDUPLEX;
+
+   //Update MAC control register
+   EMAC_MACCONTROL_R = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void omapl138EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Set up a write operation
+   value = MDIO_USERACCESS0_GO | MDIO_USERACCESS0_WRITE;
+   //PHY address
+   value |= (phyAddr << MDIO_USERACCESS0_PHYADR_SHIFT) & MDIO_USERACCESS0_PHYADR;
+   //Register address
+   value |= (regAddr << MDIO_USERACCESS0_REGADR_SHIFT) & MDIO_USERACCESS0_REGADR;
+   //Register value
+   value |= data & MDIO_USERACCESS0_DATA;
+
+   //Start a write operation
+   MDIO_USERACCESS0_R = value;
+   //Wait for the write to complete
+   while(MDIO_USERACCESS0_R & MDIO_USERACCESS0_GO);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t omapl138EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Set up a read operation
+   value = MDIO_USERACCESS0_GO | MDIO_USERACCESS0_READ;
+   //PHY address
+   value |= (phyAddr << MDIO_USERACCESS0_PHYADR_SHIFT) & MDIO_USERACCESS0_PHYADR;
+   //Register address
+   value |= (regAddr << MDIO_USERACCESS0_REGADR_SHIFT) & MDIO_USERACCESS0_REGADR;
+
+   //Start a read operation
+   MDIO_USERACCESS0_R = value;
+   //Wait for the read to complete
+   while(MDIO_USERACCESS0_R & MDIO_USERACCESS0_GO);
+
+   //Return PHY register contents
+   return MDIO_USERACCESS0_R & MDIO_USERACCESS0_DATA;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/omapl138_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,336 @@
+/**
+ * @file omapl138_eth.h
+ * @brief OMAP-L138 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _OMAPL138_ETH_H
+#define _OMAPL138_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef OMAPL138_ETH_TX_BUFFER_COUNT
+   #define OMAPL138_ETH_TX_BUFFER_COUNT 8
+#elif (OMAPL138_ETH_TX_BUFFER_COUNT < 1)
+   #error OMAPL138_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef OMAPL138_ETH_TX_BUFFER_SIZE
+   #define OMAPL138_ETH_TX_BUFFER_SIZE 1536
+#elif (OMAPL138_ETH_TX_BUFFER_SIZE != 1536)
+   #error OMAPL138_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef OMAPL138_ETH_RX_BUFFER_COUNT
+   #define OMAPL138_ETH_RX_BUFFER_COUNT 8
+#elif (OMAPL138_ETH_RX_BUFFER_COUNT < 1)
+   #error OMAPL138_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef OMAPL138_ETH_RX_BUFFER_SIZE
+   #define OMAPL138_ETH_RX_BUFFER_SIZE 1536
+#elif (OMAPL138_ETH_RX_BUFFER_SIZE != 1536)
+   #error OMAPL138_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Channel number for the TX interrupt
+#ifndef OMAPL138_ETH_TX_IRQ_CHANNEL
+   #define OMAPL138_ETH_TX_IRQ_CHANNEL 3
+#elif (OMAPL138_ETH_TX_IRQ_CHANNEL < 0 || OMAPL138_ETH_TX_IRQ_CHANNEL > 31)
+   #error OMAPL138_ETH_TX_IRQ_CHANNEL parameter is not valid
+#endif
+
+//Channel number for the RX interrupt
+#ifndef OMAPL138_ETH_RX_IRQ_CHANNEL
+   #define OMAPL138_ETH_RX_IRQ_CHANNEL 3
+#elif (OMAPL138_ETH_RX_IRQ_CHANNEL < 0 || OMAPL138_ETH_RX_IRQ_CHANNEL > 31)
+   #error OMAPL138_ETH_RX_IRQ_CHANNEL parameter is not valid
+#endif
+
+//EMAC cores
+#define EMAC_CORE0 0
+#define EMAC_CORE1 1
+#define EMAC_CORE2 2
+
+//EMAC channels
+#define EMAC_CH0 0
+#define EMAC_CH1 1
+#define EMAC_CH2 2
+#define EMAC_CH3 3
+#define EMAC_CH4 4
+#define EMAC_CH5 5
+#define EMAC_CH6 6
+#define EMAC_CH7 7
+
+//SYSCFG0 registers
+#define SYSCFG0_PINMUX_R(n)         HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(n))
+#define SYSCFG0_CFGCHIP3_R          HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_CFGCHIP3)
+
+//EMAC registers
+#define EMAC_TXREVID_R              HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXREVID)
+#define EMAC_TXCONTROL_R            HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXCONTROL)
+#define EMAC_TXTEARDOWN_R           HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXTEARDOWN)
+#define EMAC_RXREVID_R              HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXREVID)
+#define EMAC_RXCONTROL_R            HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXCONTROL)
+#define EMAC_RXTEARDOWN_R           HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXTEARDOWN)
+#define EMAC_TXINTSTATRAW_R         HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXINTSTATRAW)
+#define EMAC_TXINTSTATMASKED_R      HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXINTSTATMASKED)
+#define EMAC_TXINTMASKSET_R         HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXINTMASKSET)
+#define EMAC_TXINTMASKCLEAR_R       HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXINTMASKCLEAR)
+#define EMAC_MACINVECTOR_R          HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_MACINVECTOR)
+#define EMAC_MACEOIVECTOR_R         HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_MACEOIVECTOR)
+#define EMAC_RXINTSTATRAW_R         HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXINTSTATRAW)
+#define EMAC_RXINTSTATMASKED_R      HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXINTSTATMASKED)
+#define EMAC_RXINTMASKSET_R         HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXINTMASKSET)
+#define EMAC_RXINTMASKCLEAR_R       HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXINTMASKCLEAR)
+#define EMAC_MACINTSTATRAW_R        HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_MACINTSTATRAW)
+#define EMAC_MACINTSTATMASKED_R     HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_MACINTSTATMASKED)
+#define EMAC_MACINTMASKSET_R        HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_MACINTMASKSET)
+#define EMAC_MACINTMASKCLEAR_R      HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_MACINTMASKCLEAR)
+#define EMAC_RXMBPENABLE_R          HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXMBPENABLE)
+#define EMAC_RXUNICASTSET_R         HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXUNICASTSET)
+#define EMAC_RXUNICASTCLEAR_R       HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXUNICASTCLEAR)
+#define EMAC_RXMAXLEN_R             HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXMAXLEN)
+#define EMAC_RXBUFFEROFFSET_R       HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXBUFFEROFFSET)
+#define EMAC_RXFILTERLOWTHRESH_R    HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXFILTERLOWTHRESH)
+#define EMAC_RXFLOWTHRESH_R(n)      HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXFLOWTHRESH(n))
+#define EMAC_RXFREEBUFFER_R(n)      HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXFREEBUFFER(n))
+#define EMAC_MACCONTROL_R           HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_MACCONTROL)
+#define EMAC_MACSTATUS_R            HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_MACSTATUS)
+#define EMAC_EMCONTROL_R            HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_EMCONTROL)
+#define EMAC_FIFOCONTROL_R          HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_FIFOCONTROL)
+#define EMAC_MACCONFIG_R            HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_MACCONFIG)
+#define EMAC_SOFTRESET_R            HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_SOFTRESET)
+#define EMAC_MACSRCADDRLO_R         HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_MACSRCADDRLO)
+#define EMAC_MACSRCADDRHI_R         HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_MACSRCADDRHI)
+#define EMAC_MACHASH1_R             HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_MACHASH1)
+#define EMAC_MACHASH2_R             HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_MACHASH2)
+#define EMAC_BOFFTEST_R             HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_BOFFTEST)
+#define EMAC_TPACETEST_R            HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TPACETEST)
+#define EMAC_RXPAUSE_R              HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXPAUSE)
+#define EMAC_TXPAUSE_R              HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXPAUSE)
+#define EMAC_RXGOODFRAMES_R         HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXGOODFRAMES)
+#define EMAC_RXBCASTFRAMES_R        HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXBCASTFRAMES)
+#define EMAC_RXMCASTFRAMES_R        HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXMCASTFRAMES)
+#define EMAC_RXPAUSEFRAMES_R        HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXPAUSEFRAMES)
+#define EMAC_RXCRCERRORS_R          HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXCRCERRORS)
+#define EMAC_RXALIGNCODEERRORS_R    HWREG(SOC_EMAC_DSC_CONTROL_REG + EMACEMAC_RXOVERSIZED)
+#define EMAC_RXJABBER_R             HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXJABBER)
+#define EMAC_RXUNDERSIZED_R         HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXUNDERSIZED)
+#define EMAC_RXFRAGMENTS_R          HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXFRAGMENTS)
+#define EMAC_RXFILTERED_R           HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXFILTERED)
+#define EMAC_RXQOSFILTERED_R        HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXQOSFILTERED)
+#define EMAC_RXOCTETS_R             HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXOCTETS)
+#define EMAC_TXGOODFRAMES_R         HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXGOODFRAMES)
+#define EMAC_TXBCASTFRAMES_R        HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXBCASTFRAMES)
+#define EMAC_TXMCASTFRAMES_R        HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXMCASTFRAMES)
+#define EMAC_TXPAUSEFRAMES_R        HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXPAUSEFRAMES)
+#define EMAC_TXDEFERRED_R           HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXDEFERRED)
+#define EMAC_TXCOLLISION_R          HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXCOLLISION)
+#define EMAC_TXSINGLECOLL_R         HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXSINGLECOLL)
+#define EMAC_TXMULTICOLL_R          HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXMULTICOLL)
+#define EMAC_TXEXCESSIVECOLL_R      HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXEXCESSIVECOLL)
+#define EMAC_TXLATECOLL_R           HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXLATECOLL)
+#define EMAC_TXUNDERRUN_R           HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXUNDERRUN)
+#define EMAC_TXCARRIERSENSE_R       HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXCARRIERSENSE)
+#define EMAC_TXOCTETS_R             HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXOCTETS)
+#define EMAC_FRAME64_R              HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_FRAME64)
+#define EMAC_FRAME65T127_R          HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_FRAME65T127)
+#define EMAC_FRAME128T255_R         HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_FRAME128T255)
+#define EMAC_FRAME256T511_R         HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_FRAME256T511)
+#define EMAC_FRAME512T1023_R        HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_FRAME512T1023)
+#define EMAC_FRAME1024TUP_R         HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_FRAME1024TUP)
+#define EMAC_NETOCTETS_R            HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_NETOCTETS)
+#define EMAC_RXSOFOVERRUNS_R        HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXSOFOVERRUNS)
+#define EMAC_RXMOFOVERRUNS_R        HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXMOFOVERRUNS)
+#define EMAC_RXDMAOVERRUNS_R        HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXDMAOVERRUNS)
+#define EMAC_MACADDRLO_R            HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_MACADDRLO)
+#define EMAC_MACADDRHI_R            HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_MACADDRHI)
+#define EMAC_MACINDEX_R             HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_MACINDEX)
+#define EMAC_TXHDP_R(n)             HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXHDP(n))
+#define EMAC_RXHDP_R(n)             HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXHDP(n))
+#define EMAC_TXCP_R(n)              HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_TXCP(n))
+#define EMAC_RXCP_R(n)              HWREG(SOC_EMAC_DSC_CONTROL_REG + EMAC_RXCP(n))
+
+//EMAC control registers
+#define EMAC_CTRL_REVID_R           HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_REVID)
+#define EMAC_CTRL_SOFTRESET_R       HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_SOFTRESET)
+#define EMAC_CTRL_INTCONTRO_R       HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_INTCONTROL)
+#define EMAC_CTRL_C0RXTHRESHEN_R    HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C0RXTHRESHEN)
+#define EMAC_CTRL_CnRXEN_R(n)       HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_CnRXEN(n))
+#define EMAC_CTRL_CnTXEN_R(n)       HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_CnTXEN(n))
+#define EMAC_CTRL_CnMISCEN_R(n)     HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_CnMISCEN(n))
+#define EMAC_CTRL_CnRXTHRESHEN_R(n) HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_CnRXTHRESHEN(n))
+#define EMAC_CTRL_C0RXTHRESHSTAT_R  HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C0RXTHRESHSTAT)
+#define EMAC_CTRL_C0RXSTAT_R        HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C0RXSTAT)
+#define EMAC_CTRL_C0TXSTAT_R        HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C0TXSTAT)
+#define EMAC_CTRL_C0MISCSTAT_R      HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C0MISCSTAT)
+#define EMAC_CTRL_C1RXTHRESHSTAT_R  HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C1RXTHRESHSTAT)
+#define EMAC_CTRL_C1RXSTAT_R        HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C1RXTHRESHSTAT)
+#define EMAC_CTRL_C1TXSTAT_R        HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C1TXSTAT)
+#define EMAC_CTRL_C1MISCSTAT_R      HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C1MISCSTAT)
+#define EMAC_CTRL_C2RXTHRESHSTAT_R  HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C2RXTHRESHSTAT)
+#define EMAC_CTRL_C2RXSTAT_R        HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C2RXSTAT)
+#define EMAC_CTRL_C2TXSTAT_R        HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C2TXSTAT)
+#define EMAC_CTRL_C2MISCSTAT_R      HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C2MISCSTAT)
+#define EMAC_CTRL_C0RXIMAX_R        HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C0RXIMAX)
+#define EMAC_CTRL_C0TXIMAX_R        HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C0TXIMAX)
+#define EMAC_CTRL_C1RXIMAX_R        HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C1RXIMAX)
+#define EMAC_CTRL_C1TXIMAX_R        HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C1TXIMAX)
+#define EMAC_CTRL_C2RXIMAX_R        HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C2RXIMAX)
+#define EMAC_CTRL_C2TXIMAX_R        HWREG(SOC_EMAC_DSC_CTRL_MOD_REG + EMAC_CTRL_C2TXIMAX)
+
+//MDIO registers
+#define MDIO_REVID_R                HWREG(SOC_MDIO_0_REGS + MDIO_REVID)
+#define MDIO_CONTROL_R              HWREG(SOC_MDIO_0_REGS + MDIO_CONTROL)
+#define MDIO_ALIVE_R                HWREG(SOC_MDIO_0_REGS + MDIO_ALIVE)
+#define MDIO_LINK_R                 HWREG(SOC_MDIO_0_REGS + MDIO_LINK)
+#define MDIO_LINKINTRAW_R           HWREG(SOC_MDIO_0_REGS + MDIO_LINKINTRAW)
+#define MDIO_LINKINTMASKED_R        HWREG(SOC_MDIO_0_REGS + MDIO_LINKINTMASKED)
+#define MDIO_USERINTRAW_R           HWREG(SOC_MDIO_0_REGS + MDIO_USERINTRAW)
+#define MDIO_USERINTMASKED_R        HWREG(SOC_MDIO_0_REGS + MDIO_USERINTMASKED)
+#define MDIO_USERINTMASKSET_R       HWREG(SOC_MDIO_0_REGS + MDIO_USERINTMASKSET)
+#define MDIO_USERINTMASKCLEAR_R     HWREG(SOC_MDIO_0_REGS + MDIO_USERINTMASKCLEAR)
+#define MDIO_USERACCESS0_R          HWREG(SOC_MDIO_0_REGS + MDIO_USERACCESS0)
+#define MDIO_USERPHYSEL0_R          HWREG(SOC_MDIO_0_REGS + MDIO_USERPHYSEL0)
+#define MDIO_USERACCESS1_R          HWREG(SOC_MDIO_0_REGS + MDIO_USERACCESS1)
+#define MDIO_USERPHYSEL1_R          HWREG(SOC_MDIO_0_REGS + MDIO_USERPHYSEL1)
+
+//MACEOIVECTOR register
+#define EMAC_MACEOIVECTOR_C0RXTHRESH    0x00000000
+#define EMAC_MACEOIVECTOR_C0RX          0x00000001
+#define EMAC_MACEOIVECTOR_C0TX          0x00000002
+#define EMAC_MACEOIVECTOR_C0MISC        0x00000003
+#define EMAC_MACEOIVECTOR_C1RXTHRESH    0x00000004
+#define EMAC_MACEOIVECTOR_C1RX          0x00000005
+#define EMAC_MACEOIVECTOR_C1TX          0x00000006
+#define EMAC_MACEOIVECTOR_C1MISC        0x00000007
+#define EMAC_MACEOIVECTOR_C2RXTHRESH    0x00000008
+#define EMAC_MACEOIVECTOR_C2RX          0x00000009
+#define EMAC_MACEOIVECTOR_C2TX          0x0000000A
+#define EMAC_MACEOIVECTOR_C2MISC        0x0000000B
+
+//TX buffer descriptor flags
+#define EMAC_TX_WORD0_NEXT_DESC_POINTER 0xFFFFFFFF
+#define EMAC_TX_WORD1_BUFFER_POINTER    0xFFFFFFFF
+#define EMAC_TX_WORD2_BUFFER_OFFSET     0xFFFF0000
+#define EMAC_TX_WORD2_BUFFER_LENGTH     0x0000FFFF
+#define EMAC_TX_WORD3_SOP               0x80000000
+#define EMAC_TX_WORD3_EOP               0x40000000
+#define EMAC_TX_WORD3_OWNER             0x20000000
+#define EMAC_TX_WORD3_EOQ               0x10000000
+#define EMAC_TX_WORD3_TDOWNCMPLT        0x08000000
+#define EMAC_TX_WORD3_PASSCRC           0x04000000
+#define EMAC_TX_WORD3_PACKET_LENGTH     0x0000FFFF
+
+//RX buffer descriptor flags
+#define EMAC_RX_WORD0_NEXT_DESC_POINTER 0xFFFFFFFF
+#define EMAC_RX_WORD1_BUFFER_POINTER    0xFFFFFFFF
+#define EMAC_RX_WORD2_BUFFER_OFFSET     0x07FF0000
+#define EMAC_RX_WORD2_BUFFER_LENGTH     0x000007FF
+#define EMAC_RX_WORD3_SOP               0x80000000
+#define EMAC_RX_WORD3_EOP               0x40000000
+#define EMAC_RX_WORD3_OWNER             0x20000000
+#define EMAC_RX_WORD3_EOQ               0x10000000
+#define EMAC_RX_WORD3_TDOWNCMPLT        0x08000000
+#define EMAC_RX_WORD3_PASSCRC           0x04000000
+#define EMAC_RX_WORD3_ERROR_MASK        0x03FF0000
+#define EMAC_RX_WORD3_JABBER            0x02000000
+#define EMAC_RX_WORD3_OVERSIZE          0x01000000
+#define EMAC_RX_WORD3_FRAGMENT          0x00800000
+#define EMAC_RX_WORD3_UNDERSIZED        0x00400000
+#define EMAC_RX_WORD3_CONTROL           0x00200000
+#define EMAC_RX_WORD3_OVERRUN           0x00100000
+#define EMAC_RX_WORD3_CODEERROR         0x00080000
+#define EMAC_RX_WORD3_ALIGNERROR        0x00040000
+#define EMAC_RX_WORD3_CRCERROR          0x00020000
+#define EMAC_RX_WORD3_NOMATCH           0x00010000
+#define EMAC_RX_WORD3_PACKET_LENGTH     0x0000FFFF
+
+
+/**
+ * @brief TX buffer descriptor
+ **/
+
+typedef struct _Omapl138TxBufferDesc
+{
+   uint32_t word0;
+   uint32_t word1;
+   uint32_t word2;
+   uint32_t word3;
+   struct _Omapl138TxBufferDesc *next;
+   struct _Omapl138TxBufferDesc *prev;
+} Omapl138TxBufferDesc;
+
+
+/**
+ * @brief RX buffer descriptor
+ **/
+
+typedef struct _Omapl138RxBufferDesc
+{
+   uint32_t word0;
+   uint32_t word1;
+   uint32_t word2;
+   uint32_t word3;
+   struct _Omapl138RxBufferDesc *next;
+   struct _Omapl138RxBufferDesc *prev;
+} Omapl138RxBufferDesc;
+
+
+//AM335x Ethernet MAC driver
+extern const NicDriver omapl138EthDriver;
+
+//AM335x Ethernet MAC related functions
+error_t omapl138EthInit(NetInterface *interface);
+void omapl138EthInitGpio(NetInterface *interface);
+void omapl138EthInitBufferDesc(NetInterface *interface);
+
+void omapl138EthTick(NetInterface *interface);
+
+void omapl138EthEnableIrq(NetInterface *interface);
+void omapl138EthDisableIrq(NetInterface *interface);
+void omapl138EthTxIrqHandler(void);
+void omapl138EthRxIrqHandler(void);
+void omapl138EthEventHandler(NetInterface *interface);
+
+error_t omapl138EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t omapl138EthReceivePacket(NetInterface *interface);
+
+error_t omapl138EthSetMulticastFilter(NetInterface *interface);
+error_t omapl138EthUpdateMacConfig(NetInterface *interface);
+
+void omapl138EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t omapl138EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/pcap_driver.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,505 @@
+/**
+ * @file pcap_driver.c
+ * @brief PCAP driver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/pcap_driver.h"
+#include "debug.h"
+
+//Undefine conflicting definitions
+#undef Socket
+#undef htons
+#undef htonl
+#undef ntohs
+#undef ntohl
+
+//PCAP dependencies
+#include <pcap.h>
+
+//Undefine conflicting definitions
+#undef interface
+
+
+/**
+ * @brief Packet descriptor
+ **/
+
+typedef struct
+{
+   size_t length;
+   uint8_t data[PCAP_DRIVER_MAX_PACKET_SIZE];
+} PcapDriverPacket;
+
+
+/**
+ * @brief PCAP driver context
+ **/
+
+typedef struct
+{
+   pcap_t *handle;
+   uint_t writeIndex;
+   uint_t readIndex;
+   PcapDriverPacket queue[PCAP_DRIVER_QUEUE_SIZE];
+} PcapDriverContext;
+
+
+/**
+ * @brief PCAP driver
+ **/
+
+const NicDriver pcapDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   pcapDriverInit,
+   pcapDriverTick,
+   pcapDriverEnableIrq,
+   pcapDriverDisableIrq,
+   pcapDriverEventHandler,
+   pcapDriverSendPacket,
+   pcapDriverSetMulticastFilter,
+   NULL,
+   NULL,
+   NULL,
+   TRUE,
+   TRUE,
+   TRUE,
+   TRUE
+};
+
+
+/**
+ * @brief PCAP driver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t pcapDriverInit(NetInterface *interface)
+{
+   int_t ret;
+   uint_t i;
+   uint_t j;
+   pcap_if_t *device;
+   pcap_if_t *deviceList;
+   struct bpf_program filerCode;
+   char_t filterExpr[256];
+   char_t errorBuffer[PCAP_ERRBUF_SIZE];
+   PcapDriverContext *context;
+#if (NET_RTOS_SUPPORT == ENABLED)
+   OsTask *task;
+#endif
+
+   //Debug message
+   TRACE_INFO("Initializing PCAP driver...\r\n");
+
+   //Allocate PCAP driver context
+   context = (PcapDriverContext *) malloc(sizeof(PcapDriverContext));
+
+   //Failed to allocate memory?
+   if(context == NULL)
+   {
+      //Debug message
+      printf("Failed to allocate context!\r\n");
+
+      //Report an error
+      return ERROR_FAILURE;
+   }
+
+   //Attach the PCAP driver context to the network interface
+   *((PcapDriverContext **) interface->nicContext) = context;
+   //Clear PCAP driver context
+   memset(context, 0, sizeof(PcapDriverContext));
+
+   //Find all the devices
+   ret = pcap_findalldevs(&deviceList, errorBuffer);
+
+   //Any error to report?
+   if(ret != 0)
+   {
+      //Debug message
+      printf("Failed to list devices!\r\n");
+
+      //Clean up side effects
+      free(context);
+
+      //Report an error
+      return ERROR_FAILURE;
+   }
+
+   //No network adapter found?
+   if(deviceList == NULL)
+   {
+      //Debug message
+      printf("No network adapter found!\r\n");
+
+      //Clean up side effects
+      free(context);
+
+      //Exit immediately
+      return ERROR_FAILURE;
+   }
+
+   //Network adapter selection
+   while(1)
+   {
+      //Debug message
+      printf("Network adapters:\r\n");
+
+      //Point to the first device
+      device = deviceList;
+      i = 0;
+
+      //Loop through the list of devices
+      while(device != NULL)
+      {
+         //Index of the current network adapter
+         printf("  %-2u", i + 1);
+
+#if !defined(_WIN32)
+         //Display the name of the device
+         if(device->name != NULL)
+            printf(" %-8s", device->name);
+#endif
+         //Description of the device
+         if(device->description != NULL)
+            printf(" %s\r\n", device->description);
+         else
+            printf(" -\r\n");
+
+         //Next device
+         device = device->next;
+         i++;
+      }
+
+      //Display message
+      printf("Select network adapter for %s interface (1-%u):", interface->name, i);
+      //Get user choice
+      scanf("%d", &j);
+
+      //Valid selection?
+      if(j >= 1 && j <= i)
+         break;
+   }
+
+   //Point to the first device
+   device = deviceList;
+
+   //Point to the desired network adapter
+   for(i = 1; i < j; i++)
+      device = device->next;
+
+   //Open the device
+   context->handle = pcap_open_live(device->name, 65535,
+      TRUE, PCAP_DRIVER_TIMEOUT, errorBuffer);
+
+   //Failed to open device?
+   if(context->handle == NULL)
+   {
+      //Debug message
+      printf("Failed to open device!\r\n");
+
+      //Clean up side effects
+      pcap_freealldevs(deviceList);
+      free(context);
+
+      //Report an error
+      return ERROR_FAILURE;
+   }
+
+   //Free the device list
+   pcap_freealldevs(deviceList);
+
+   //Filter expression
+   sprintf(filterExpr, "!(ether src %02x:%02x:%02x:%02x:%02x:%02x) && "
+      "((ether dst %02x:%02x:%02x:%02x:%02x:%02x) || (ether broadcast) || (ether multicast))",
+      interface->macAddr.b[0], interface->macAddr.b[1], interface->macAddr.b[2],
+      interface->macAddr.b[3], interface->macAddr.b[4], interface->macAddr.b[5],
+      interface->macAddr.b[0], interface->macAddr.b[1], interface->macAddr.b[2],
+      interface->macAddr.b[3], interface->macAddr.b[4], interface->macAddr.b[5]);
+
+   //Compile the filter
+   ret = pcap_compile(context->handle, &filerCode, filterExpr, 1, 0);
+
+   //Failed to open device?
+   if(ret != 0)
+   {
+      //Debug message
+      printf("Failed to compile filter!\r\n");
+
+      //Clean up side effects
+      pcap_close(context->handle);
+      free(context);
+
+      //Report an error
+      return ERROR_FAILURE;
+   }
+
+   //Set the filter
+   ret = pcap_setfilter(context->handle, &filerCode);
+
+   //Failed to open device?
+   if(ret != 0)
+   {
+      //Debug message
+      printf("Failed to set filter!\r\n");
+
+      //Clean up side effects
+      pcap_close(context->handle);
+      free(context);
+
+      //Report an error
+      return ERROR_FAILURE;
+   }
+
+#if (NET_RTOS_SUPPORT == ENABLED)
+   //Create the receive task
+   task = osCreateTask("PCAP", pcapDriverTask, interface, 0, 0);
+
+   //Failed to create the task?
+   if(task == OS_INVALID_HANDLE)
+   {
+      //Debug message
+      printf("Failed to create task!\r\n");
+
+      //Clean up side effects
+      pcap_close(context->handle);
+      free(context);
+
+      //Report an error
+      return ERROR_FAILURE;
+   }
+#endif
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Return status code
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief PCAP timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void pcapDriverTick(NetInterface *interface)
+{
+   //Not implemented
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void pcapDriverEnableIrq(NetInterface *interface)
+{
+   //Not implemented
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void pcapDriverDisableIrq(NetInterface *interface)
+{
+   //Not implemented
+}
+
+
+/**
+ * @brief PCAP event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void pcapDriverEventHandler(NetInterface *interface)
+{
+   uint_t n;
+   PcapDriverContext *context;
+
+   //Point to the PCAP driver context
+   context = *((PcapDriverContext **) interface->nicContext);
+
+   //Process all pending packets
+   while(context->queue[context->readIndex].length > 0)
+   {
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, context->queue[context->readIndex].data,
+         context->queue[context->readIndex].length);
+
+      //Compute the index of the next packet descriptor
+      n = (context->readIndex + 1) % PCAP_DRIVER_QUEUE_SIZE;
+
+      //Release the current packet
+      context->queue[context->readIndex].length = 0;
+      //Point to the next packet descriptor
+      context->readIndex = n;
+   }
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t pcapDriverSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   int_t ret;
+   size_t length;
+   PcapDriverContext *context;
+   uint8_t temp[PCAP_DRIVER_MAX_PACKET_SIZE];
+
+   //Point to the PCAP driver context
+   context = *((PcapDriverContext **) interface->nicContext);
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > PCAP_DRIVER_MAX_PACKET_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Copy the packet to the transmit buffer
+   netBufferRead(temp, buffer, offset, length);
+
+   //Send packet
+   ret = pcap_sendpacket(context->handle, temp, length);
+
+   //The transmitter can accept another packet
+   osSetEvent(&interface->nicTxEvent);
+
+   //Return status code
+   if(ret < 0)
+      return ERROR_FAILURE;
+   else
+     return NO_ERROR;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t pcapDriverSetMulticastFilter(NetInterface *interface)
+{
+   //Not implemented
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief PCAP receive task
+ * @param[in] interface Underlying network interface
+ **/
+
+void pcapDriverTask(NetInterface *interface)
+{
+   int_t ret;
+   uint_t n;
+   uint_t length;
+   const uint8_t *data;
+   struct pcap_pkthdr *header;
+   PcapDriverContext *context;
+
+   //Point to the PCAP driver context
+   context = *((PcapDriverContext **) interface->nicContext);
+
+   //Process events
+   while(1)
+   {
+      //Wait for an incoming packet
+      ret = pcap_next_ex(context->handle, &header, &data);
+
+      //Any packet received?
+      if(ret > 0)
+      {
+         //Retrieve the length of the packet
+         length = header->caplen;
+
+         //Check the length of the received packet
+         if(length > 0 && length < PCAP_DRIVER_MAX_PACKET_SIZE)
+         {
+            //Check whether the link is up
+            if(interface->linkState)
+            {
+               //Compute the index of the next packet descriptor
+               n = (context->writeIndex + 1) % PCAP_DRIVER_QUEUE_SIZE;
+
+               //Ensure the receive queue is not full
+               if(n != context->readIndex)
+               {
+                  //Copy the incoming packet
+                  memcpy(context->queue[context->writeIndex].data, data, length);
+                  //Save the length of the packet
+                  context->queue[context->writeIndex].length = length;
+
+                  //Point to the next packet descriptor
+                  context->writeIndex = n;
+
+                  //Set event flag
+                  interface->nicEvent = TRUE;
+                  //Notify the TCP/IP stack of the event
+                  osSetEvent(&netEvent);
+               }
+            }
+         }
+      }
+      else
+      {
+#if (NET_RTOS_SUPPORT == DISABLED)
+         //No packet has been received
+         break;
+#endif
+      }
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/pcap_driver.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,77 @@
+/**
+ * @file pcap_driver.h
+ * @brief PCAP driver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _PCAP_DRIVER_H
+#define _PCAP_DRIVER_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Maximum packet size
+#ifndef PCAP_DRIVER_MAX_PACKET_SIZE
+   #define PCAP_DRIVER_MAX_PACKET_SIZE 1536
+#elif (PCAP_DRIVER_MAX_PACKET_SIZE < 1)
+   #error PCAP_DRIVER_MAX_PACKET_SIZE parameter is not valid
+#endif
+
+//Maximum number of packets in the receive queue
+#ifndef PCAP_DRIVER_QUEUE_SIZE
+   #define PCAP_DRIVER_QUEUE_SIZE 64
+#elif (PCAP_DRIVER_QUEUE_SIZE < 1)
+   #error PCAP_DRIVER_QUEUE_SIZE parameter is not valid
+#endif
+
+//Receive timeout in milliseconds
+#ifndef PCAP_DRIVER_TIMEOUT
+   #define PCAP_DRIVER_TIMEOUT 1
+#elif (PCAP_DRIVER_TIMEOUT < 1)
+   #error PCAP_DRIVER_TIMEOUT parameter is not valid
+#endif
+
+//PCAP driver
+extern const NicDriver pcapDriver;
+
+//PCAP related functions
+error_t pcapDriverInit(NetInterface *interface);
+
+void pcapDriverTick(NetInterface *interface);
+
+void pcapDriverEnableIrq(NetInterface *interface);
+void pcapDriverDisableIrq(NetInterface *interface);
+
+void pcapDriverEventHandler(NetInterface *interface);
+
+error_t pcapDriverSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t pcapDriverSetMulticastFilter(NetInterface *interface);
+
+void pcapDriverTask(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/pic32mx_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,712 @@
+/**
+ * @file pic32mx_eth.c
+ * @brief PIC32MX Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include <p32xxxx.h>
+#include <sys/kmem.h>
+#include "core/net.h"
+#include "drivers/pic32mx_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//Transmit buffer
+static uint8_t txBuffer[PIC32MX_ETH_TX_BUFFER_COUNT][PIC32MX_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[PIC32MX_ETH_RX_BUFFER_COUNT][PIC32MX_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Transmit buffer descriptors
+static Pic32mxTxBufferDesc txBufferDesc[PIC32MX_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive buffer descriptors
+static Pic32mxRxBufferDesc rxBufferDesc[PIC32MX_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+
+//Pointer to the current TX buffer descriptor
+static Pic32mxTxBufferDesc *txCurBufferDesc;
+//Pointer to the current RX buffer descriptor
+static Pic32mxRxBufferDesc *rxCurBufferDesc;
+
+
+/**
+ * @brief PIC32MX Ethernet MAC driver
+ **/
+
+const NicDriver pic32mxEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   pic32mxEthInit,
+   pic32mxEthTick,
+   pic32mxEthEnableIrq,
+   pic32mxEthDisableIrq,
+   pic32mxEthEventHandler,
+   pic32mxEthSendPacket,
+   pic32mxEthSetMulticastFilter,
+   pic32mxEthUpdateMacConfig,
+   pic32mxEthWritePhyReg,
+   pic32mxEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief PIC32MX Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t pic32mxEthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing PIC32MX Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //GPIO configuration
+   pic32mxEthInitGpio(interface);
+
+   //Disable Ethernet interrupts
+   IEC1CLR = _IEC1_ETHIE_MASK;
+   //Turn the Ethernet controller off
+   ETHCON1CLR = _ETHCON1_ON_MASK | _ETHCON1_TXRTS_POSITION | _ETHCON1_RXEN_MASK;
+
+   //Wait activity abort by polling the ETHBUSY bit
+   while(ETHSTAT & _ETHSTAT_ETHBUSY_MASK);
+
+   //Enable the Ethernet controller by setting the ON bit
+   ETHCON1SET = _ETHCON1_ON_MASK;
+
+   //Clear Ethernet interrupt flag
+   IFS1CLR = _IFS1_ETHIF_MASK;
+   //Disable any Ethernet controller interrupt generation
+   ETHIEN = 0;
+   ETHIRQ = 0;
+   //Clear the TX and RX start addresses
+   ETHTXST = 0;
+   ETHRXST = 0;
+
+   //Reset the MAC using SOFTRESET
+   EMAC1CFG1SET = _EMAC1CFG1_SOFTRESET_MASK;
+   EMAC1CFG1CLR = _EMAC1CFG1_SOFTRESET_MASK;
+
+   //Reset the RMII module
+   EMAC1SUPPSET = _EMAC1SUPP_RESETRMII_MASK;
+   EMAC1SUPPCLR = _EMAC1SUPP_RESETRMII_MASK;
+
+   //Issue an MIIM block reset by setting the RESETMGMT bit
+   EMAC1MCFGSET = _EMAC1MCFG_RESETMGMT_MASK;
+   EMAC1MCFGCLR = _EMAC1MCFG_RESETMGMT_MASK;
+
+   //Select the proper divider for the MDC clock
+   EMAC1MCFG = _EMAC1MCFG_CLKSEL_DIV40;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Optionally set the station MAC address
+   if(macCompAddr(&interface->macAddr, &MAC_UNSPECIFIED_ADDR))
+   {
+      //Use the factory preprogrammed station address
+      interface->macAddr.w[0] = EMAC1SA2;
+      interface->macAddr.w[1] = EMAC1SA1;
+      interface->macAddr.w[2] = EMAC1SA0;
+
+      //Generate the 64-bit interface identifier
+      macAddrToEui64(&interface->macAddr, &interface->eui64);
+   }
+   else
+   {
+      //Override the factory preprogrammed address
+      EMAC1SA0 = interface->macAddr.w[2];
+      EMAC1SA1 = interface->macAddr.w[1];
+      EMAC1SA2 = interface->macAddr.w[0];
+   }
+
+   //Initialize hash table
+   ETHHT0 = 0;
+   ETHHT1 = 0;
+
+   //Configure the receive filter
+   ETHRXFC = _ETHRXFC_HTEN_MASK | _ETHRXFC_CRCOKEN_MASK |
+      _ETHRXFC_RUNTEN_MASK | _ETHRXFC_UCEN_MASK | _ETHRXFC_BCEN_MASK;
+
+   //Disable flow control
+   EMAC1CFG1 = _EMAC1CFG1_RXENABLE_MASK;
+   //Automatic padding and CRC generation
+   EMAC1CFG2 = _EMAC1CFG2_PADENABLE_MASK | _EMAC1CFG2_CRCENABLE_MASK;
+   //Set the maximum frame length
+   EMAC1MAXF = 1518;
+
+   //Initialize DMA descriptor lists
+   pic32mxEthInitBufferDesc(interface);
+
+   //Enable desired interrupts
+   ETHIENSET = _ETHIEN_PKTPENDIE_MASK | _ETHIEN_TXDONEIE_MASK;
+
+   //Set interrupt priority
+   IPC12CLR = _IPC12_ETHIP_MASK;
+   IPC12SET = (PIC32MX_ETH_IRQ_PRIORITY << _IPC12_ETHIP_POSITION);
+   //Set interrupt subpriority
+   IPC12CLR = _IPC12_ETHIS_MASK;
+   IPC12SET = (PIC32MX_ETH_IRQ_SUB_PRIORITY << _IPC12_ETHIS_POSITION);
+
+   //Enable the reception by setting the RXEN bit
+   ETHCON1SET = _ETHCON1_RXEN_MASK;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//PIC32 Ethernet Starter Kit?
+#if defined(USE_PIC32_ETH_STARTER_KIT) || defined(USE_PIC32_ETH_STARTER_KIT_2)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void pic32mxEthInitGpio(NetInterface *interface)
+{
+   //No analog pins are shared with the alternate RMII interface
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void pic32mxEthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX descriptor list
+   for(i = 0; i < PIC32MX_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Point to the current descriptor
+      txCurBufferDesc = KVA0_TO_KVA1(&txBufferDesc[i]);
+
+      //Use linked list rather than linear list
+      txCurBufferDesc->control = ETH_TX_CTRL_NPV;
+      //Transmit buffer address
+      txCurBufferDesc->address = (uint32_t) KVA_TO_PA(txBuffer[i]);
+      //Transmit status vector
+      txCurBufferDesc->status1 = 0;
+      txCurBufferDesc->status2 = 0;
+      //Next descriptor address
+      txCurBufferDesc->next = (uint32_t) KVA_TO_PA(&txBufferDesc[i + 1]);
+   }
+
+   //The last descriptor is chained to the first entry
+   txCurBufferDesc->next = (uint32_t) KVA_TO_PA(&txBufferDesc[0]);
+   //Point to the very first descriptor
+   txCurBufferDesc = KVA0_TO_KVA1(&txBufferDesc[0]);
+
+   //Initialize RX descriptor list
+   for(i = 0; i < PIC32MX_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Point to the current descriptor
+      rxCurBufferDesc = KVA0_TO_KVA1(&rxBufferDesc[i]);
+
+      //The descriptor is initially owned by the DMA
+      rxCurBufferDesc->control = ETH_RX_CTRL_NPV | ETH_RX_CTRL_EOWN;
+      //Receive buffer address
+      rxCurBufferDesc->address = (uint32_t) KVA_TO_PA(rxBuffer[i]);
+      //Receive status vector
+      rxCurBufferDesc->status1 = 0;
+      rxCurBufferDesc->status2 = 0;
+      //Next descriptor address
+      rxCurBufferDesc->next = (uint32_t) KVA_TO_PA(&rxBufferDesc[i + 1]);
+   }
+
+   //The last descriptor is chained to the first entry
+   rxCurBufferDesc->next = (uint32_t) KVA_TO_PA(&rxBufferDesc[0]);
+   //Point to the very first descriptor
+   rxCurBufferDesc = KVA0_TO_KVA1(&rxBufferDesc[0]);
+
+   //Starting address of TX descriptor table
+   ETHTXST = (uint32_t) KVA_TO_PA(&txBufferDesc[0]);
+   //Starting address of RX descriptor table
+   ETHRXST = (uint32_t) KVA_TO_PA(&rxBufferDesc[0]);
+   //Set receive buffer size
+   ETHCON2 = PIC32MX_ETH_RX_BUFFER_SIZE;
+}
+
+
+/**
+ * @brief PIC32MX Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void pic32mxEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void pic32mxEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   IEC1SET = _IEC1_ETHIE_MASK;
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void pic32mxEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   IEC1CLR = _IEC1_ETHIE_MASK;
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief PIC32MX Ethernet MAC interrupt service routine
+ **/
+
+void pic32mxEthIrqHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read interrupt status register
+   status = ETHIRQ;
+
+   //A packet has been transmitted?
+   if(status & _ETHIRQ_TXDONE_MASK)
+   {
+      //Clear TXDONE interrupt flag
+      ETHIRQCLR = _ETHIRQ_TXDONE_MASK;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurBufferDesc->control & ETH_TX_CTRL_EOWN))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & _ETHIRQ_PKTPEND_MASK)
+   {
+      //Disable PKTPEND interrupt
+      ETHIENCLR = _ETHIEN_PKTPENDIE_MASK;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Clear ETHIF interrupt flag before exiting the service routine
+   IFS1CLR = _IFS1_ETHIF_MASK;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief PIC32MX Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void pic32mxEthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(ETHIRQ & _ETHIRQ_PKTPEND_MASK)
+   {
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = pic32mxEthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable PKTPEND interrupt
+   ETHIENSET = _ETHIEN_PKTPENDIE_MASK;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t pic32mxEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+   uint32_t value;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > PIC32MX_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurBufferDesc->control & ETH_TX_CTRL_EOWN)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(PA_TO_KVA1(txCurBufferDesc->address), buffer, offset, length);
+
+   //Write the number of bytes to send
+   value = (length << 16) & ETH_TX_CTRL_BYTE_COUNT;
+   //Set SOP and EOP flags since the data fits in a single buffer
+   value |= ETH_TX_CTRL_SOP | ETH_TX_CTRL_EOP | ETH_TX_CTRL_NPV;
+   //Give the ownership of the descriptor to the DMA
+   txCurBufferDesc->control = value | ETH_TX_CTRL_EOWN;
+
+   //Set TXRTS bit to start the transmission
+   ETHCON1SET = _ETHCON1_TXRTS_MASK;
+
+   //Point to the next descriptor in the list
+   txCurBufferDesc = PA_TO_KVA1(txCurBufferDesc->next);
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurBufferDesc->control & ETH_TX_CTRL_EOWN))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t pic32mxEthReceivePacket(NetInterface *interface)
+{
+   static uint8_t temp[PIC32MX_ETH_RX_BUFFER_SIZE];
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxCurBufferDesc->control & ETH_RX_CTRL_EOWN))
+   {
+      //SOP and EOP flags should be set
+      if((rxCurBufferDesc->control & ETH_RX_CTRL_SOP) &&
+         (rxCurBufferDesc->control & ETH_RX_CTRL_EOP))
+      {
+         //Make sure no error occurred
+         if(rxCurBufferDesc->status2 & ETH_RX_STATUS2_OK)
+         {
+            //Retrieve the length of the frame
+            n = (rxCurBufferDesc->control & ETH_RX_CTRL_BYTE_COUNT) >> 16;
+            //Limit the number of data to read
+            n = MIN(n, PIC32MX_ETH_RX_BUFFER_SIZE);
+
+            //Copy data from the receive buffer
+            memcpy(temp, PA_TO_KVA1(rxCurBufferDesc->address), n);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, temp, n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Give the ownership of the descriptor back to the DMA
+      rxCurBufferDesc->control = ETH_RX_CTRL_NPV | ETH_RX_CTRL_EOWN;
+
+      //Point to the next descriptor in the list
+      rxCurBufferDesc = PA_TO_KVA1(rxCurBufferDesc->next);
+
+      //Decrement BUFCNT counter
+      ETHCON1SET = _ETHCON1_BUFCDEC_MASK;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t pic32mxEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating PIC32MX hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = pic32mxEthCalcCrc(&entry->addr, sizeof(MacAddr));
+         //Calculate the corresponding index in the table
+         k = (crc >> 23) & 0x3F;
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   ETHHT0 = hashTable[0];
+   ETHHT1 = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  ETHHT0 = %08" PRIX32 "\r\n", ETHHT0);
+   TRACE_DEBUG("  ETHHT1 = %08" PRIX32 "\r\n", ETHHT1);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t pic32mxEthUpdateMacConfig(NetInterface *interface)
+{
+   //Check current operating speed
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+   {
+      //100BASE-TX operation mode
+      EMAC1SUPPSET = _EMAC1SUPP_SPEEDRMII_MASK;
+   }
+   else
+   {
+      //10BASE-T operation mode
+      EMAC1SUPPCLR = _EMAC1SUPP_SPEEDRMII_MASK;
+   }
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+   {
+      //Configure FULLDPLX bit to match the current duplex mode
+      EMAC1CFG2SET = _EMAC1CFG2_FULLDPLX_MASK;
+      //Configure the Back-to-Back Inter-Packet Gap register
+      EMAC1IPGT = 0x15;
+   }
+   else
+   {
+      //Configure FULLDPLX bit to match the current duplex mode
+      EMAC1CFG2CLR = _EMAC1CFG2_FULLDPLX_MASK;
+      //Configure the Back-to-Back Inter-Packet Gap register
+      EMAC1IPGT = 0x12;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void pic32mxEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   //Set PHY address and register address
+   EMAC1MADR = (phyAddr << _EMAC1MADR_PHYADDR_POSITION) | regAddr;
+   //Start a write operation
+   EMAC1MWTD = data & _EMAC1MWTD_MWTD_MASK;
+
+   //Wait for busy bit to be set
+   __asm__ __volatile__ ("nop;");
+   __asm__ __volatile__ ("nop;");
+   __asm__ __volatile__ ("nop;");
+
+   //Wait for the write to complete
+   while(EMAC1MIND & _EMAC1MIND_MIIMBUSY_MASK);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t pic32mxEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   //Set PHY address and register address
+   EMAC1MADR = (phyAddr << _EMAC1MADR_PHYADDR_POSITION) | regAddr;
+   //Start a read operation
+   EMAC1MCMD = _EMAC1MCMD_READ_MASK;
+
+   //Wait for busy bit to be set
+   __asm__ __volatile__ ("nop;");
+   __asm__ __volatile__ ("nop;");
+   __asm__ __volatile__ ("nop;");
+
+   //Wait for the read to complete
+   while(EMAC1MIND & _EMAC1MIND_MIIMBUSY_MASK);
+
+   //Clear command register
+   EMAC1MCMD = 0;
+   //Return PHY register contents
+   return EMAC1MRDD & _EMAC1MRDD_MRDD_MASK;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t pic32mxEthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/pic32mx_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,203 @@
+/**
+ * @file pic32mx_eth.h
+ * @brief PIC32MX Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _PIC32MX_ETH_H
+#define _PIC32MX_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef PIC32MX_ETH_TX_BUFFER_COUNT
+   #define PIC32MX_ETH_TX_BUFFER_COUNT 2
+#elif (PIC32MX_ETH_TX_BUFFER_COUNT < 1)
+   #error PIC32MX_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef PIC32MX_ETH_TX_BUFFER_SIZE
+   #define PIC32MX_ETH_TX_BUFFER_SIZE 1536
+#elif (PIC32MX_ETH_TX_BUFFER_SIZE != 1536)
+   #error PIC32MX_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef PIC32MX_ETH_RX_BUFFER_COUNT
+   #define PIC32MX_ETH_RX_BUFFER_COUNT 4
+#elif (PIC32MX_ETH_RX_BUFFER_COUNT < 1)
+   #error PIC32MX_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef PIC32MX_ETH_RX_BUFFER_SIZE
+   #define PIC32MX_ETH_RX_BUFFER_SIZE 1536
+#elif (PIC32MX_ETH_RX_BUFFER_SIZE != 1536)
+   #error PIC32MX_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Ethernet interrupt priority
+#ifndef PIC32MX_ETH_IRQ_PRIORITY
+   #define PIC32MX_ETH_IRQ_PRIORITY 2
+#elif (PIC32MX_ETH_IRQ_PRIORITY < 0)
+   #error PIC32MX_ETH_IRQ_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef PIC32MX_ETH_IRQ_SUB_PRIORITY
+   #define PIC32MX_ETH_IRQ_SUB_PRIORITY 0
+#elif (PIC32MX_ETH_IRQ_SUB_PRIORITY < 0)
+   #error PIC32MX_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//EMAC1MCFG register
+#define _EMAC1MCFG_CLKSEL_DIV4  (0 << _EMAC1MCFG_CLKSEL_POSITION)
+#define _EMAC1MCFG_CLKSEL_DIV6  (2 << _EMAC1MCFG_CLKSEL_POSITION)
+#define _EMAC1MCFG_CLKSEL_DIV8  (3 << _EMAC1MCFG_CLKSEL_POSITION)
+#define _EMAC1MCFG_CLKSEL_DIV10 (4 << _EMAC1MCFG_CLKSEL_POSITION)
+#define _EMAC1MCFG_CLKSEL_DIV14 (5 << _EMAC1MCFG_CLKSEL_POSITION)
+#define _EMAC1MCFG_CLKSEL_DIV20 (6 << _EMAC1MCFG_CLKSEL_POSITION)
+#define _EMAC1MCFG_CLKSEL_DIV28 (7 << _EMAC1MCFG_CLKSEL_POSITION)
+#define _EMAC1MCFG_CLKSEL_DIV40 (8 << _EMAC1MCFG_CLKSEL_POSITION)
+
+//Transmit buffer descriptor flags
+#define ETH_TX_CTRL_SOP                   0x80000000
+#define ETH_TX_CTRL_EOP                   0x40000000
+#define ETH_TX_CTRL_BYTE_COUNT            0x07FF0000
+#define ETH_TX_CTRL_NPV                   0x00000100
+#define ETH_TX_CTRL_EOWN                  0x00000080
+#define ETH_TX_STATUS1_VLAN               0x00080000
+#define ETH_TX_STATUS1_BACKPRESSURE       0x00040000
+#define ETH_TX_STATUS1_PAUSE              0x00020000
+#define ETH_TX_STATUS1_CONTROL            0x00010000
+#define ETH_TX_STATUS1_TOTAL_BYTES        0x0000FFFF
+#define ETH_TX_STATUS2_UNDERRUN           0x80000000
+#define ETH_TX_STATUS2_GIANT              0x40000000
+#define ETH_TX_STATUS2_LATE_COL           0x20000000
+#define ETH_TX_STATUS2_MAX_COL            0x10000000
+#define ETH_TX_STATUS2_EXCESSIVE_DEFER    0x08000000
+#define ETH_TX_STATUS2_PACKET_DEFER       0x04000000
+#define ETH_TX_STATUS2_BROADCAST          0x02000000
+#define ETH_TX_STATUS2_MULTICAST          0x01000000
+#define ETH_TX_STATUS2_DONE               0x00800000
+#define ETH_TX_STATUS2_LEN_OUT_OF_RANGE   0x00400000
+#define ETH_TX_STATUS2_LEN_CHECK_ERROR    0x00200000
+#define ETH_TX_STATUS2_CRC_ERROR          0x00100000
+#define ETH_TX_STATUS2_COL_COUNT          0x000F0000
+#define ETH_TX_STATUS2_BYTE_COUNT         0x0000FFFF
+
+//Receive buffer descriptor flags
+#define ETH_RX_CTRL_SOP                   0x80000000
+#define ETH_RX_CTRL_EOP                   0x40000000
+#define ETH_RX_CTRL_BYTE_COUNT            0x07FF0000
+#define ETH_RX_CTRL_NPV                   0x00000100
+#define ETH_RX_CTRL_EOWN                  0x00000080
+#define ETH_RX_STATUS1_MULTICAST_MATCH    0x80000000
+#define ETH_RX_STATUS1_BROADCAST_MATCH    0x40000000
+#define ETH_RX_STATUS1_UNICAST_MATCH      0x20000000
+#define ETH_RX_STATUS1_PATTERN_MATCH      0x10000000
+#define ETH_RX_STATUS1_MAGIC_PACKET_MATCH 0x08000000
+#define ETH_RX_STATUS1_HASH_TABLE_MATCH   0x04000000
+#define ETH_RX_STATUS1_NOT_MATCH          0x02000000
+#define ETH_RX_STATUS1_RUNT_PACKET        0x01000000
+#define ETH_RX_STATUS1_PACKET_CHECKSUM    0x0000FFFF
+#define ETH_RX_STATUS2_VLAN               0x40000000
+#define ETH_RX_STATUS2_UNKNOWN_OP_CODE    0x20000000
+#define ETH_RX_STATUS2_PAUSE              0x10000000
+#define ETH_RX_STATUS2_CONTROL            0x08000000
+#define ETH_RX_STATUS2_DRIBBLE_NIBBLE     0x04000000
+#define ETH_RX_STATUS2_BROADCAST          0x02000000
+#define ETH_RX_STATUS2_MULTICAST          0x01000000
+#define ETH_RX_STATUS2_OK                 0x00800000
+#define ETH_RX_STATUS2_LEN_OUT_OF_RANGE   0x00400000
+#define ETH_RX_STATUS2_LEN_CHECK_ERROR    0x00200000
+#define ETH_RX_STATUS2_CRC_ERROR          0x00100000
+#define ETH_RX_STATUS2_CODE_VIOLATION     0x00080000
+#define ETH_RX_STATUS2_CARRIER_EVENT      0x00040000
+#define ETH_RX_STATUS2_RXDV_EVENT         0x00020000
+#define ETH_RX_STATUS2_LONG_EVENT         0x00010000
+#define ETH_RX_STATUS2_BYTE_COUNT         0x0000FFFF
+
+
+/**
+ * @brief TX buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t control;
+   uint32_t address;
+   uint32_t status1;
+   uint32_t status2;
+   uint32_t next;
+} Pic32mxTxBufferDesc;
+
+
+/**
+ * @brief RX buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t control;
+   uint32_t address;
+   uint32_t status1;
+   uint32_t status2;
+   uint32_t next;
+} Pic32mxRxBufferDesc;
+
+
+//PIC32MX Ethernet MAC driver
+extern const NicDriver pic32mxEthDriver;
+
+//PIC32MX Ethernet MAC related functions
+error_t pic32mxEthInit(NetInterface *interface);
+void pic32mxEthInitGpio(NetInterface *interface);
+void pic32mxEthInitBufferDesc(NetInterface *interface);
+
+void pic32mxEthTick(NetInterface *interface);
+
+void pic32mxEthEnableIrq(NetInterface *interface);
+void pic32mxEthDisableIrq(NetInterface *interface);
+void pic32mxEthIrqHandler(void);
+void pic32mxEthEventHandler(NetInterface *interface);
+
+error_t pic32mxEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t pic32mxEthReceivePacket(NetInterface *interface);
+
+error_t pic32mxEthSetMulticastFilter(NetInterface *interface);
+error_t pic32mxEthUpdateMacConfig(NetInterface *interface);
+
+void pic32mxEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t pic32mxEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t pic32mxEthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/pic32mz_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,738 @@
+/**
+ * @file pic32mz_eth.c
+ * @brief PIC32MZ Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include <p32xxxx.h>
+#include <sys/kmem.h>
+#include "core/net.h"
+#include "drivers/pic32mz_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//Transmit buffer
+static uint8_t txBuffer[PIC32MZ_ETH_TX_BUFFER_COUNT][PIC32MZ_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[PIC32MZ_ETH_RX_BUFFER_COUNT][PIC32MZ_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Transmit buffer descriptors
+static Pic32mzTxBufferDesc txBufferDesc[PIC32MZ_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive buffer descriptors
+static Pic32mzRxBufferDesc rxBufferDesc[PIC32MZ_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+
+//Pointer to the current TX buffer descriptor
+static Pic32mzTxBufferDesc *txCurBufferDesc;
+//Pointer to the current RX buffer descriptor
+static Pic32mzRxBufferDesc *rxCurBufferDesc;
+
+
+/**
+ * @brief PIC32MZ Ethernet MAC driver
+ **/
+
+const NicDriver pic32mzEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   pic32mzEthInit,
+   pic32mzEthTick,
+   pic32mzEthEnableIrq,
+   pic32mzEthDisableIrq,
+   pic32mzEthEventHandler,
+   pic32mzEthSendPacket,
+   pic32mzEthSetMulticastFilter,
+   pic32mzEthUpdateMacConfig,
+   pic32mzEthWritePhyReg,
+   pic32mzEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief PIC32MZ Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t pic32mzEthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing PIC32MZ Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //GPIO configuration
+   pic32mzEthInitGpio(interface);
+
+   //Disable Ethernet interrupts
+   IEC4CLR = _IEC4_ETHIE_MASK;
+   //Turn the Ethernet controller off
+   ETHCON1CLR = _ETHCON1_ON_MASK | _ETHCON1_TXRTS_POSITION | _ETHCON1_RXEN_MASK;
+
+   //Wait activity abort by polling the ETHBUSY bit
+   while(ETHSTAT & _ETHSTAT_ETHBUSY_MASK);
+
+   //Enable the Ethernet controller by setting the ON bit
+   ETHCON1SET = _ETHCON1_ON_MASK;
+
+   //Clear Ethernet interrupt flag
+   IFS4CLR = _IFS4_ETHIF_MASK;
+   //Disable any Ethernet controller interrupt generation
+   ETHIEN = 0;
+   ETHIRQ = 0;
+   //Clear the TX and RX start addresses
+   ETHTXST = 0;
+   ETHRXST = 0;
+
+   //Reset the MAC using SOFTRESET
+   EMAC1CFG1SET = _EMAC1CFG1_SOFTRESET_MASK;
+   EMAC1CFG1CLR = _EMAC1CFG1_SOFTRESET_MASK;
+
+   //Reset the RMII module
+   EMAC1SUPPSET = _EMAC1SUPP_RESETRMII_MASK;
+   EMAC1SUPPCLR = _EMAC1SUPP_RESETRMII_MASK;
+
+   //Issue an MIIM block reset by setting the RESETMGMT bit
+   EMAC1MCFGSET = _EMAC1MCFG_RESETMGMT_MASK;
+   EMAC1MCFGCLR = _EMAC1MCFG_RESETMGMT_MASK;
+
+   //Select the proper divider for the MDC clock
+   EMAC1MCFG = _EMAC1MCFG_CLKSEL_DIV50;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Optionally set the station MAC address
+   if(macCompAddr(&interface->macAddr, &MAC_UNSPECIFIED_ADDR))
+   {
+      //Use the factory preprogrammed station address
+      interface->macAddr.w[0] = EMAC1SA2;
+      interface->macAddr.w[1] = EMAC1SA1;
+      interface->macAddr.w[2] = EMAC1SA0;
+
+      //Generate the 64-bit interface identifier
+      macAddrToEui64(&interface->macAddr, &interface->eui64);
+   }
+   else
+   {
+      //Override the factory preprogrammed address
+      EMAC1SA0 = interface->macAddr.w[2];
+      EMAC1SA1 = interface->macAddr.w[1];
+      EMAC1SA2 = interface->macAddr.w[0];
+   }
+
+   //Initialize hash table
+   ETHHT0 = 0;
+   ETHHT1 = 0;
+
+   //Configure the receive filter
+   ETHRXFC = _ETHRXFC_HTEN_MASK | _ETHRXFC_CRCOKEN_MASK |
+      _ETHRXFC_RUNTEN_MASK | _ETHRXFC_UCEN_MASK | _ETHRXFC_BCEN_MASK;
+
+   //Disable flow control
+   EMAC1CFG1 = _EMAC1CFG1_RXENABLE_MASK;
+   //Automatic padding and CRC generation
+   EMAC1CFG2 = _EMAC1CFG2_PADENABLE_MASK | _EMAC1CFG2_CRCENABLE_MASK;
+   //Set the maximum frame length
+   EMAC1MAXF = 1518;
+
+   //Initialize DMA descriptor lists
+   pic32mzEthInitBufferDesc(interface);
+
+   //Enable desired interrupts
+   ETHIENSET = _ETHIEN_PKTPENDIE_MASK | _ETHIEN_TXDONEIE_MASK;
+
+   //Set interrupt priority
+   IPC38CLR = _IPC38_ETHIP_MASK;
+   IPC38SET = (PIC32MZ_ETH_IRQ_PRIORITY << _IPC38_ETHIP_POSITION);
+   //Set interrupt subpriority
+   IPC38CLR = _IPC38_ETHIS_MASK;
+   IPC38SET = (PIC32MZ_ETH_IRQ_SUB_PRIORITY << _IPC38_ETHIS_POSITION);
+
+   //Enable the reception by setting the RXEN bit
+   ETHCON1SET = _ETHCON1_RXEN_MASK;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//PIC32MZ EC Starter Kit, PIC32MZ EF Starter Kit or IoT Ethernet Kit?
+#if defined(USE_PIC32MZ_EC_STARTER_KIT) || defined(USE_PIC32MZ_EF_STARTER_KIT) || \
+   defined(USE_IOT_ETHERNET_KIT)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void pic32mzEthInitGpio(NetInterface *interface)
+{
+//PIC32MZ EC Starter Kit or PIC32MZ EF Starter Kit?
+#if defined(USE_PIC32MZ_EC_STARTER_KIT) || defined(USE_PIC32MZ_EF_STARTER_KIT)
+   //Disable analog pad on ETXD0 (AN35/RJ8)
+   ANSELJCLR = _ANSELJ_ANSJ8_MASK;
+   //Disable analog pad on ETXD1 (AN36/RJ9)
+   ANSELJCLR = _ANSELJ_ANSJ9_MASK;
+   //Disable analog pad on EREFCLK (AN37/RJ11)
+   ANSELJCLR = _ANSELJ_ANSJ11_MASK;
+   //Disable analog pad on ERXERR (AN40/RH4)
+   ANSELHCLR = _ANSELH_ANSH4_MASK;
+   //Disable analog pad on ERXD1 (AN41/RH5)
+   ANSELHCLR = _ANSELH_ANSH5_MASK;
+
+//IoT Ethernet Kit?
+#elif defined(USE_IOT_ETHERNET_KIT)
+   //Disable analog pad on ERXERR (AN18/RE4)
+   ANSELECLR = _ANSELE_ANSE4_MASK;
+   //Disable analog pad on ETXEN (AN17/RE5)
+   ANSELECLR = _ANSELE_ANSE5_MASK;
+   //Disable analog pad on ETXD0 (AN16/RE6)
+   ANSELECLR = _ANSELE_ANSE6_MASK;
+   //Disable analog pad on ETXD1 (AN15/RE7)
+   ANSELECLR = _ANSELE_ANSE7_MASK;
+#endif
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void pic32mzEthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX descriptor list
+   for(i = 0; i < PIC32MZ_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Point to the current descriptor
+      txCurBufferDesc = KVA0_TO_KVA1(&txBufferDesc[i]);
+
+      //Use linked list rather than linear list
+      txCurBufferDesc->control = ETH_TX_CTRL_NPV;
+      //Transmit buffer address
+      txCurBufferDesc->address = (uint32_t) KVA_TO_PA(txBuffer[i]);
+      //Transmit status vector
+      txCurBufferDesc->status1 = 0;
+      txCurBufferDesc->status2 = 0;
+      //Next descriptor address
+      txCurBufferDesc->next = (uint32_t) KVA_TO_PA(&txBufferDesc[i + 1]);
+   }
+
+   //The last descriptor is chained to the first entry
+   txCurBufferDesc->next = (uint32_t) KVA_TO_PA(&txBufferDesc[0]);
+   //Point to the very first descriptor
+   txCurBufferDesc = KVA0_TO_KVA1(&txBufferDesc[0]);
+
+   //Initialize RX descriptor list
+   for(i = 0; i < PIC32MZ_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Point to the current descriptor
+      rxCurBufferDesc = KVA0_TO_KVA1(&rxBufferDesc[i]);
+
+      //The descriptor is initially owned by the DMA
+      rxCurBufferDesc->control = ETH_RX_CTRL_NPV | ETH_RX_CTRL_EOWN;
+      //Receive buffer address
+      rxCurBufferDesc->address = (uint32_t) KVA_TO_PA(rxBuffer[i]);
+      //Receive status vector
+      rxCurBufferDesc->status1 = 0;
+      rxCurBufferDesc->status2 = 0;
+      //Next descriptor address
+      rxCurBufferDesc->next = (uint32_t) KVA_TO_PA(&rxBufferDesc[i + 1]);
+   }
+
+   //The last descriptor is chained to the first entry
+   rxCurBufferDesc->next = (uint32_t) KVA_TO_PA(&rxBufferDesc[0]);
+   //Point to the very first descriptor
+   rxCurBufferDesc = KVA0_TO_KVA1(&rxBufferDesc[0]);
+
+   //Starting address of TX descriptor table
+   ETHTXST = (uint32_t) KVA_TO_PA(&txBufferDesc[0]);
+   //Starting address of RX descriptor table
+   ETHRXST = (uint32_t) KVA_TO_PA(&rxBufferDesc[0]);
+   //Set receive buffer size
+   ETHCON2 = PIC32MZ_ETH_RX_BUFFER_SIZE;
+}
+
+
+/**
+ * @brief PIC32MZ Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void pic32mzEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void pic32mzEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   IEC4SET = _IEC4_ETHIE_MASK;
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void pic32mzEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   IEC4CLR = _IEC4_ETHIE_MASK;
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief PIC32MZ Ethernet MAC interrupt service routine
+ **/
+
+void pic32mzEthIrqHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read interrupt status register
+   status = ETHIRQ;
+
+   //A packet has been transmitted?
+   if(status & _ETHIRQ_TXDONE_MASK)
+   {
+      //Clear TXDONE interrupt flag
+      ETHIRQCLR = _ETHIRQ_TXDONE_MASK;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurBufferDesc->control & ETH_TX_CTRL_EOWN))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & _ETHIRQ_PKTPEND_MASK)
+   {
+      //Disable PKTPEND interrupt
+      ETHIENCLR = _ETHIEN_PKTPENDIE_MASK;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Clear ETHIF interrupt flag before exiting the service routine
+   IFS4CLR = _IFS4_ETHIF_MASK;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief PIC32MZ Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void pic32mzEthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(ETHIRQ & _ETHIRQ_PKTPEND_MASK)
+   {
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = pic32mzEthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable PKTPEND interrupt
+   ETHIENSET = _ETHIEN_PKTPENDIE_MASK;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t pic32mzEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+   uint32_t value;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > PIC32MZ_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurBufferDesc->control & ETH_TX_CTRL_EOWN)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(PA_TO_KVA1(txCurBufferDesc->address), buffer, offset, length);
+
+   //Write the number of bytes to send
+   value = (length << 16) & ETH_TX_CTRL_BYTE_COUNT;
+   //Set SOP and EOP flags since the data fits in a single buffer
+   value |= ETH_TX_CTRL_SOP | ETH_TX_CTRL_EOP | ETH_TX_CTRL_NPV;
+   //Give the ownership of the descriptor to the DMA
+   txCurBufferDesc->control = value | ETH_TX_CTRL_EOWN;
+
+   //Set TXRTS bit to start the transmission
+   ETHCON1SET = _ETHCON1_TXRTS_MASK;
+
+   //Point to the next descriptor in the list
+   txCurBufferDesc = PA_TO_KVA1(txCurBufferDesc->next);
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurBufferDesc->control & ETH_TX_CTRL_EOWN))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t pic32mzEthReceivePacket(NetInterface *interface)
+{
+   static uint8_t temp[PIC32MZ_ETH_RX_BUFFER_SIZE];
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxCurBufferDesc->control & ETH_RX_CTRL_EOWN))
+   {
+      //SOP and EOP flags should be set
+      if((rxCurBufferDesc->control & ETH_RX_CTRL_SOP) &&
+         (rxCurBufferDesc->control & ETH_RX_CTRL_EOP))
+      {
+         //Make sure no error occurred
+         if(rxCurBufferDesc->status2 & ETH_RX_STATUS2_OK)
+         {
+            //Retrieve the length of the frame
+            n = (rxCurBufferDesc->control & ETH_RX_CTRL_BYTE_COUNT) >> 16;
+            //Limit the number of data to read
+            n = MIN(n, PIC32MZ_ETH_RX_BUFFER_SIZE);
+
+            //Copy data from the receive buffer
+            memcpy(temp, PA_TO_KVA1(rxCurBufferDesc->address), n);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, temp, n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Give the ownership of the descriptor back to the DMA
+      rxCurBufferDesc->control = ETH_RX_CTRL_NPV | ETH_RX_CTRL_EOWN;
+
+      //Point to the next descriptor in the list
+      rxCurBufferDesc = PA_TO_KVA1(rxCurBufferDesc->next);
+
+      //Decrement BUFCNT counter
+      ETHCON1SET = _ETHCON1_BUFCDEC_MASK;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t pic32mzEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating PIC32MZ hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = pic32mzEthCalcCrc(&entry->addr, sizeof(MacAddr));
+         //Calculate the corresponding index in the table
+         k = (crc >> 23) & 0x3F;
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   ETHHT0 = hashTable[0];
+   ETHHT1 = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  ETHHT0 = %08" PRIX32 "\r\n", ETHHT0);
+   TRACE_DEBUG("  ETHHT1 = %08" PRIX32 "\r\n", ETHHT1);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t pic32mzEthUpdateMacConfig(NetInterface *interface)
+{
+   //Check current operating speed
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+   {
+      //100BASE-TX operation mode
+      EMAC1SUPPSET = _EMAC1SUPP_SPEEDRMII_MASK;
+   }
+   else
+   {
+      //10BASE-T operation mode
+      EMAC1SUPPCLR = _EMAC1SUPP_SPEEDRMII_MASK;
+   }
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+   {
+      //Configure FULLDPLX bit to match the current duplex mode
+      EMAC1CFG2SET = _EMAC1CFG2_FULLDPLX_MASK;
+      //Configure the Back-to-Back Inter-Packet Gap register
+      EMAC1IPGT = 0x15;
+   }
+   else
+   {
+      //Configure FULLDPLX bit to match the current duplex mode
+      EMAC1CFG2CLR = _EMAC1CFG2_FULLDPLX_MASK;
+      //Configure the Back-to-Back Inter-Packet Gap register
+      EMAC1IPGT = 0x12;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void pic32mzEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint_t i;
+
+   //Set PHY address and register address
+   EMAC1MADR = (phyAddr << _EMAC1MADR_PHYADDR_POSITION) | regAddr;
+   //Start a write operation
+   EMAC1MWTD = data & _EMAC1MWTD_MWTD_MASK;
+
+   //Wait for busy bit to be set
+   for(i = 0; i < 16; i++)
+      __asm__ __volatile__ ("nop;");
+
+   //Wait for the write to complete
+   while(EMAC1MIND & _EMAC1MIND_MIIMBUSY_MASK);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t pic32mzEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint_t i;
+
+   //Set PHY address and register address
+   EMAC1MADR = (phyAddr << _EMAC1MADR_PHYADDR_POSITION) | regAddr;
+   //Start a read operation
+   EMAC1MCMD = _EMAC1MCMD_READ_MASK;
+
+   //Wait for busy bit to be set
+   for(i = 0; i < 16; i++)
+      __asm__ __volatile__ ("nop;");
+
+   //Wait for the read to complete
+   while(EMAC1MIND & _EMAC1MIND_MIIMBUSY_MASK);
+
+   //Clear command register
+   EMAC1MCMD = 0;
+   //Return PHY register contents
+   return EMAC1MRDD & _EMAC1MRDD_MRDD_MASK;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t pic32mzEthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/pic32mz_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,205 @@
+/**
+ * @file pic32mz_eth.h
+ * @brief PIC32MZ Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _PIC32MZ_ETH_H
+#define _PIC32MZ_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef PIC32MZ_ETH_TX_BUFFER_COUNT
+   #define PIC32MZ_ETH_TX_BUFFER_COUNT 3
+#elif (PIC32MZ_ETH_TX_BUFFER_COUNT < 1)
+   #error PIC32MZ_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef PIC32MZ_ETH_TX_BUFFER_SIZE
+   #define PIC32MZ_ETH_TX_BUFFER_SIZE 1536
+#elif (PIC32MZ_ETH_TX_BUFFER_SIZE != 1536)
+   #error PIC32MZ_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef PIC32MZ_ETH_RX_BUFFER_COUNT
+   #define PIC32MZ_ETH_RX_BUFFER_COUNT 6
+#elif (PIC32MZ_ETH_RX_BUFFER_COUNT < 1)
+   #error PIC32MZ_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef PIC32MZ_ETH_RX_BUFFER_SIZE
+   #define PIC32MZ_ETH_RX_BUFFER_SIZE 1536
+#elif (PIC32MZ_ETH_RX_BUFFER_SIZE != 1536)
+   #error PIC32MZ_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Ethernet interrupt priority
+#ifndef PIC32MZ_ETH_IRQ_PRIORITY
+   #define PIC32MZ_ETH_IRQ_PRIORITY 2
+#elif (PIC32MZ_ETH_IRQ_PRIORITY < 0)
+   #error PIC32MZ_ETH_IRQ_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef PIC32MZ_ETH_IRQ_SUB_PRIORITY
+   #define PIC32MZ_ETH_IRQ_SUB_PRIORITY 0
+#elif (PIC32MZ_ETH_IRQ_SUB_PRIORITY < 0)
+   #error PIC32MZ_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//EMAC1MCFG register
+#define _EMAC1MCFG_CLKSEL_DIV4  (0 << _EMAC1MCFG_CLKSEL_POSITION)
+#define _EMAC1MCFG_CLKSEL_DIV6  (2 << _EMAC1MCFG_CLKSEL_POSITION)
+#define _EMAC1MCFG_CLKSEL_DIV8  (3 << _EMAC1MCFG_CLKSEL_POSITION)
+#define _EMAC1MCFG_CLKSEL_DIV10 (4 << _EMAC1MCFG_CLKSEL_POSITION)
+#define _EMAC1MCFG_CLKSEL_DIV14 (5 << _EMAC1MCFG_CLKSEL_POSITION)
+#define _EMAC1MCFG_CLKSEL_DIV20 (6 << _EMAC1MCFG_CLKSEL_POSITION)
+#define _EMAC1MCFG_CLKSEL_DIV28 (7 << _EMAC1MCFG_CLKSEL_POSITION)
+#define _EMAC1MCFG_CLKSEL_DIV40 (8 << _EMAC1MCFG_CLKSEL_POSITION)
+#define _EMAC1MCFG_CLKSEL_DIV48 (9 << _EMAC1MCFG_CLKSEL_POSITION)
+#define _EMAC1MCFG_CLKSEL_DIV50 (10 << _EMAC1MCFG_CLKSEL_POSITION)
+
+//Transmit buffer descriptor flags
+#define ETH_TX_CTRL_SOP                   0x80000000
+#define ETH_TX_CTRL_EOP                   0x40000000
+#define ETH_TX_CTRL_BYTE_COUNT            0x07FF0000
+#define ETH_TX_CTRL_NPV                   0x00000100
+#define ETH_TX_CTRL_EOWN                  0x00000080
+#define ETH_TX_STATUS1_VLAN               0x00080000
+#define ETH_TX_STATUS1_BACKPRESSURE       0x00040000
+#define ETH_TX_STATUS1_PAUSE              0x00020000
+#define ETH_TX_STATUS1_CONTROL            0x00010000
+#define ETH_TX_STATUS1_TOTAL_BYTES        0x0000FFFF
+#define ETH_TX_STATUS2_UNDERRUN           0x80000000
+#define ETH_TX_STATUS2_GIANT              0x40000000
+#define ETH_TX_STATUS2_LATE_COL           0x20000000
+#define ETH_TX_STATUS2_MAX_COL            0x10000000
+#define ETH_TX_STATUS2_EXCESSIVE_DEFER    0x08000000
+#define ETH_TX_STATUS2_PACKET_DEFER       0x04000000
+#define ETH_TX_STATUS2_BROADCAST          0x02000000
+#define ETH_TX_STATUS2_MULTICAST          0x01000000
+#define ETH_TX_STATUS2_DONE               0x00800000
+#define ETH_TX_STATUS2_LEN_OUT_OF_RANGE   0x00400000
+#define ETH_TX_STATUS2_LEN_CHECK_ERROR    0x00200000
+#define ETH_TX_STATUS2_CRC_ERROR          0x00100000
+#define ETH_TX_STATUS2_COL_COUNT          0x000F0000
+#define ETH_TX_STATUS2_BYTE_COUNT         0x0000FFFF
+
+//Receive buffer descriptor flags
+#define ETH_RX_CTRL_SOP                   0x80000000
+#define ETH_RX_CTRL_EOP                   0x40000000
+#define ETH_RX_CTRL_BYTE_COUNT            0x07FF0000
+#define ETH_RX_CTRL_NPV                   0x00000100
+#define ETH_RX_CTRL_EOWN                  0x00000080
+#define ETH_RX_STATUS1_MULTICAST_MATCH    0x80000000
+#define ETH_RX_STATUS1_BROADCAST_MATCH    0x40000000
+#define ETH_RX_STATUS1_UNICAST_MATCH      0x20000000
+#define ETH_RX_STATUS1_PATTERN_MATCH      0x10000000
+#define ETH_RX_STATUS1_MAGIC_PACKET_MATCH 0x08000000
+#define ETH_RX_STATUS1_HASH_TABLE_MATCH   0x04000000
+#define ETH_RX_STATUS1_NOT_MATCH          0x02000000
+#define ETH_RX_STATUS1_RUNT_PACKET        0x01000000
+#define ETH_RX_STATUS1_PACKET_CHECKSUM    0x0000FFFF
+#define ETH_RX_STATUS2_VLAN               0x40000000
+#define ETH_RX_STATUS2_UNKNOWN_OP_CODE    0x20000000
+#define ETH_RX_STATUS2_PAUSE              0x10000000
+#define ETH_RX_STATUS2_CONTROL            0x08000000
+#define ETH_RX_STATUS2_DRIBBLE_NIBBLE     0x04000000
+#define ETH_RX_STATUS2_BROADCAST          0x02000000
+#define ETH_RX_STATUS2_MULTICAST          0x01000000
+#define ETH_RX_STATUS2_OK                 0x00800000
+#define ETH_RX_STATUS2_LEN_OUT_OF_RANGE   0x00400000
+#define ETH_RX_STATUS2_LEN_CHECK_ERROR    0x00200000
+#define ETH_RX_STATUS2_CRC_ERROR          0x00100000
+#define ETH_RX_STATUS2_CODE_VIOLATION     0x00080000
+#define ETH_RX_STATUS2_CARRIER_EVENT      0x00040000
+#define ETH_RX_STATUS2_RXDV_EVENT         0x00020000
+#define ETH_RX_STATUS2_LONG_EVENT         0x00010000
+#define ETH_RX_STATUS2_BYTE_COUNT         0x0000FFFF
+
+
+/**
+ * @brief TX buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t control;
+   uint32_t address;
+   uint32_t status1;
+   uint32_t status2;
+   uint32_t next;
+} Pic32mzTxBufferDesc;
+
+
+/**
+ * @brief RX buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t control;
+   uint32_t address;
+   uint32_t status1;
+   uint32_t status2;
+   uint32_t next;
+} Pic32mzRxBufferDesc;
+
+
+//PIC32MZ Ethernet MAC driver
+extern const NicDriver pic32mzEthDriver;
+
+//PIC32MZ Ethernet MAC related functions
+error_t pic32mzEthInit(NetInterface *interface);
+void pic32mzEthInitGpio(NetInterface *interface);
+void pic32mzEthInitBufferDesc(NetInterface *interface);
+
+void pic32mzEthTick(NetInterface *interface);
+
+void pic32mzEthEnableIrq(NetInterface *interface);
+void pic32mzEthDisableIrq(NetInterface *interface);
+void pic32mzEthIrqHandler(void);
+void pic32mzEthEventHandler(NetInterface *interface);
+
+error_t pic32mzEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t pic32mzEthReceivePacket(NetInterface *interface);
+
+error_t pic32mzEthSetMulticastFilter(NetInterface *interface);
+error_t pic32mzEthUpdateMacConfig(NetInterface *interface);
+
+void pic32mzEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t pic32mzEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t pic32mzEthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/rtl8211.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,287 @@
+/**
+ * @file rtl8211.c
+ * @brief RTL8211 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/rtl8211.h"
+#include "debug.h"
+
+
+/**
+ * @brief RTL8211 Ethernet PHY driver
+ **/
+
+const PhyDriver rtl8211PhyDriver =
+{
+   rtl8211Init,
+   rtl8211Tick,
+   rtl8211EnableIrq,
+   rtl8211DisableIrq,
+   rtl8211EventHandler,
+};
+
+
+/**
+ * @brief RTL8211 PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t rtl8211Init(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing RTL8211...\r\n");
+
+   //Initialize external interrupt line driver
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->init();
+
+   //Reset PHY transceiver
+   rtl8211WritePhyReg(interface, RTL8211_PHY_REG_BMCR, BMCR_RESET);
+   //Wait for the reset to complete
+   while(rtl8211ReadPhyReg(interface, RTL8211_PHY_REG_BMCR) & BMCR_RESET);
+
+   //Dump PHY registers for debugging purpose
+   rtl8211DumpPhyReg(interface);
+
+   //The PHY will generate interrupts when link status changes are detected
+   rtl8211WritePhyReg(interface, RTL8211_PHY_REG_INER, INER_AN_COMPLETE | INER_LINK_STATUS);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief RTL8211 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void rtl8211Tick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //No external interrupt line driver?
+   if(interface->extIntDriver == NULL)
+   {
+      //Read basic status register
+      value = rtl8211ReadPhyReg(interface, RTL8211_PHY_REG_BMSR);
+      //Retrieve current link state
+      linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+      //Link up event?
+      if(linkState && !interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+      //Link down event?
+      else if(!linkState && interface->linkState)
+      {
+         //Set event flag
+         interface->phyEvent = TRUE;
+         //Notify the TCP/IP stack of the event
+         osSetEvent(&netEvent);
+      }
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void rtl8211EnableIrq(NetInterface *interface)
+{
+   //Enable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void rtl8211DisableIrq(NetInterface *interface)
+{
+   //Disable PHY transceiver interrupts
+   if(interface->extIntDriver != NULL)
+      interface->extIntDriver->disableIrq();
+}
+
+
+/**
+ * @brief RTL8211 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void rtl8211EventHandler(NetInterface *interface)
+{
+   uint16_t status;
+
+   //Read status register to acknowledge the interrupt
+   status = rtl8211ReadPhyReg(interface, RTL8211_PHY_REG_INSR);
+
+   //Link status change?
+   if(status & (INSR_AN_COMPLETE | INSR_LINK_STATUS))
+   {
+      //Any link failure condition is latched in the BMSR register... Reading
+      //the register twice will always return the actual link status
+      status = rtl8211ReadPhyReg(interface, RTL8211_PHY_REG_BMSR);
+      status = rtl8211ReadPhyReg(interface, RTL8211_PHY_REG_BMSR);
+
+      //Link is up?
+      if(status & BMSR_LINK_STATUS)
+      {
+         //Read PHY status register
+         status = rtl8211ReadPhyReg(interface, RTL8211_PHY_REG_PHYSR);
+
+         //Check current speed
+         switch(status & PHYSR_SPEED_MASK)
+         {
+         //10BASE-T
+         case PHYSR_SPEED_10:
+            interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+            break;
+         //100BASE-TX
+         case PHYSR_SPEED_100:
+            interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+            break;
+         //1000BASE-T
+         case PHYSR_SPEED_1000:
+            interface->linkSpeed = NIC_LINK_SPEED_1GBPS;
+            break;
+         //Unknown speed
+         default:
+            //Debug message
+            TRACE_WARNING("Invalid speed\r\n");
+            break;
+         }
+
+         //Check current duplex mode
+         if(status & PHYSR_DUPLEX)
+            interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+         else
+            interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+
+         //Update link state
+         interface->linkState = TRUE;
+
+         //Adjust MAC configuration parameters for proper operation
+         interface->nicDriver->updateMacConfig(interface);
+      }
+      else
+      {
+         //Update link state
+         interface->linkState = FALSE;
+      }
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void rtl8211WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = RTL8211_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t rtl8211ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = RTL8211_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void rtl8211DumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, rtl8211ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/rtl8211.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,291 @@
+/**
+ * @file rtl8211.h
+ * @brief RTL8211 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _RTL8211_H
+#define _RTL8211_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#ifndef RTL8211_PHY_ADDR
+   #define RTL8211_PHY_ADDR 1
+#elif (RTL8211_PHY_ADDR < 0 || RTL8211_PHY_ADDR > 31)
+   #error RTL8211_PHY_ADDR parameter is not valid
+#endif
+
+//RTL8211 registers
+#define RTL8211_PHY_REG_BMCR        0x00
+#define RTL8211_PHY_REG_BMSR        0x01
+#define RTL8211_PHY_REG_PHYIDR1     0x02
+#define RTL8211_PHY_REG_PHYIDR2     0x03
+#define RTL8211_PHY_REG_ANAR        0x04
+#define RTL8211_PHY_REG_ANLPAR      0x05
+#define RTL8211_PHY_REG_ANER        0x06
+#define RTL8211_PHY_REG_ANNPRR      0x07
+#define RTL8211_PHY_REG_LPNPAR      0x08
+#define RTL8211_PHY_REG_GBCR        0x09
+#define RTL8211_PHY_REG_GBSR        0x0A
+#define RTL8211_PHY_REG_MACR        0x0D
+#define RTL8211_PHY_REG_MAADR       0x0E
+#define RTL8211_PHY_REG_GBESR       0x0F
+#define RTL8211_PHY_REG_PHYCR       0x10
+#define RTL8211_PHY_REG_PHYSR       0x11
+#define RTL8211_PHY_REG_INER        0x12
+#define RTL8211_PHY_REG_INSR        0x13
+#define RTL8211_PHY_REG_RXERC       0x18
+#define RTL8211_PHY_REG_LDPSR       0x1B
+#define RTL8211_PHY_REG_EPAGSR      0x1E
+#define RTL8211_PHY_REG_PAGSEL      0x1F
+
+//BMCR register
+#define BMCR_RESET                  (1 << 15)
+#define BMCR_LOOPBACK               (1 << 14)
+#define BMCR_SPEED_SEL              (1 << 13)
+#define BMCR_AN_EN                  (1 << 12)
+#define BMCR_POWER_DOWN             (1 << 11)
+#define BMCR_ISOLATE                (1 << 10)
+#define BMCR_RESTART_AN             (1 << 9)
+#define BMCR_DUPLEX_MODE            (1 << 8)
+#define BMCR_COL_TEST               (1 << 7)
+
+//BMSR register
+#define BMSR_100BT4                 (1 << 15)
+#define BMSR_100BTX_FD              (1 << 14)
+#define BMSR_100BTX                 (1 << 13)
+#define BMSR_10BT_FD                (1 << 12)
+#define BMSR_10BT                   (1 << 11)
+#define BMSR_NO_PREAMBLE            (1 << 6)
+#define BMSR_AN_COMPLETE            (1 << 5)
+#define BMSR_REMOTE_FAULT           (1 << 4)
+#define BMSR_AN_ABLE                (1 << 3)
+#define BMSR_LINK_STATUS            (1 << 2)
+#define BMSR_JABBER_DETECT          (1 << 1)
+#define BMSR_EXTENDED_CAP           (1 << 0)
+
+//ANAR register
+#define ANAR_NEXT_PAGE              (1 << 15)
+#define ANAR_REMOTE_FAULT           (1 << 13)
+#define ANAR_PAUSE1                 (1 << 11)
+#define ANAR_PAUSE0                 (1 << 10)
+#define ANAR_100BT4                 (1 << 9)
+#define ANAR_100BTX_FD              (1 << 8)
+#define ANAR_100BTX                 (1 << 7)
+#define ANAR_10BT_FD                (1 << 6)
+#define ANAR_10BT                   (1 << 5)
+#define ANAR_SELECTOR4              (1 << 4)
+#define ANAR_SELECTOR3              (1 << 3)
+#define ANAR_SELECTOR2              (1 << 2)
+#define ANAR_SELECTOR1              (1 << 1)
+#define ANAR_SELECTOR0              (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NEXT_PAGE            (1 << 15)
+#define ANLPAR_LP_ACK               (1 << 14)
+#define ANLPAR_REMOTE_FAULT         (1 << 13)
+#define ANLPAR_PAUSE1               (1 << 11)
+#define ANLPAR_PAUSE0               (1 << 10)
+#define ANLPAR_100BT4               (1 << 9)
+#define ANLPAR_100BTX_FD            (1 << 8)
+#define ANLPAR_100BTX               (1 << 7)
+#define ANLPAR_10BT_FD              (1 << 6)
+#define ANLPAR_10BT                 (1 << 5)
+#define ANLPAR_SELECTOR4            (1 << 4)
+#define ANLPAR_SELECTOR3            (1 << 3)
+#define ANLPAR_SELECTOR2            (1 << 2)
+#define ANLPAR_SELECTOR1            (1 << 1)
+#define ANLPAR_SELECTOR0            (1 << 0)
+
+//ANER register
+#define ANER_PAR_DET_FAULT          (1 << 4)
+#define ANER_LP_NEXT_PAGE_ABLE      (1 << 3)
+#define ANER_NEXT_PAGE_ABLE         (1 << 2)
+#define ANER_PAGE_RECEIVED          (1 << 1)
+#define ANER_LP_AN_ABLE             (1 << 0)
+
+//ANNPTR register
+#define ANNPTR_NEXT_PAGE            (1 << 15)
+#define ANNPTR_MSG_PAGE             (1 << 13)
+#define ANNPTR_ACK2                 (1 << 12)
+#define ANNPTR_TOGGLE               (1 << 11)
+#define ANNPTR_MESSAGE10            (1 << 10)
+#define ANNPTR_MESSAGE9             (1 << 9)
+#define ANNPTR_MESSAGE8             (1 << 8)
+#define ANNPTR_MESSAGE7             (1 << 7)
+#define ANNPTR_MESSAGE6             (1 << 6)
+#define ANNPTR_MESSAGE5             (1 << 5)
+#define ANNPTR_MESSAGE4             (1 << 4)
+#define ANNPTR_MESSAGE3             (1 << 3)
+#define ANNPTR_MESSAGE2             (1 << 2)
+#define ANNPTR_MESSAGE1             (1 << 1)
+#define ANNPTR_MESSAGE0             (1 << 0)
+
+//ANNPRR register
+#define ANNPRR_NEXT_PAGE            (1 << 15)
+#define ANNPRR_ACK                  (1 << 14)
+#define ANNPRR_MSG_PAGE             (1 << 13)
+#define ANNPRR_ACK2                 (1 << 12)
+#define ANNPRR_TOGGLE               (1 << 11)
+#define ANNPRR_MESSAGE10            (1 << 10)
+#define ANNPRR_MESSAGE9             (1 << 9)
+#define ANNPRR_MESSAGE8             (1 << 8)
+#define ANNPRR_MESSAGE7             (1 << 7)
+#define ANNPRR_MESSAGE6             (1 << 6)
+#define ANNPRR_MESSAGE5             (1 << 5)
+#define ANNPRR_MESSAGE4             (1 << 4)
+#define ANNPRR_MESSAGE3             (1 << 3)
+#define ANNPRR_MESSAGE2             (1 << 2)
+#define ANNPRR_MESSAGE1             (1 << 1)
+#define ANNPRR_MESSAGE0             (1 << 0)
+
+//GBCR register
+#define GBCR_TEST_MODE2             (1 << 15)
+#define GBCR_TEST_MODE1             (1 << 14)
+#define GBCR_TEST_MODE0             (1 << 13)
+#define GBCR_MS_MAN_CONF_EN         (1 << 12)
+#define GBCR_MS_MAN_CONF_VAL        (1 << 11)
+#define GBCR_PORT_TYPE              (1 << 10)
+#define GBCR_1000BT_FD              (1 << 9)
+#define GBCR_1000BT_HD              (1 << 8)
+
+//GBSR register
+#define GBSR_MS_CONF_FAULT          (1 << 15)
+#define GBSR_MS_CONF_RES            (1 << 14)
+#define GBSR_LOC_REC_STATUS         (1 << 13)
+#define GBSR_REM_REC_STATUS         (1 << 12)
+#define GBSR_LP_1000BT_FD           (1 << 11)
+#define GBSR_LP_1000BT_HD           (1 << 10)
+#define GBSR_IDLE_ERR_CTR7          (1 << 7)
+#define GBSR_IDLE_ERR_CTR6          (1 << 6)
+#define GBSR_IDLE_ERR_CTR5          (1 << 5)
+#define GBSR_IDLE_ERR_CTR4          (1 << 4)
+#define GBSR_IDLE_ERR_CTR3          (1 << 3)
+#define GBSR_IDLE_ERR_CTR2          (1 << 2)
+#define GBSR_IDLE_ERR_CTR1          (1 << 1)
+#define GBSR_IDLE_ERR_CTR0          (1 << 0)
+
+//MACR register
+#define MACR_FUNCTION1              (1 << 15)
+#define MACR_FUNCTION0              (1 << 14)
+#define MACR_DEVAD4                 (1 << 4)
+#define MACR_DEVAD3                 (1 << 3)
+#define MACR_DEVAD2                 (1 << 2)
+#define MACR_DEVAD1                 (1 << 1)
+#define MACR_DEVAD0                 (1 << 0)
+
+//GBESR register
+#define GBESR_1000BX_FD             (1 << 15)
+#define GBESR_1000BX_HD             (1 << 14)
+#define GBESR_1000BT_FD             (1 << 13)
+#define GBESR_1000BT_HD             (1 << 12)
+
+//PHYCR register
+#define PHYCR_DISABLE_RXC           (1 << 15)
+#define PHYCR_FPR_FAIL_SEL2         (1 << 14)
+#define PHYCR_FPR_FAIL_SEL1         (1 << 13)
+#define PHYCR_FPR_FAIL_SEL0         (1 << 12)
+#define PHYCR_ASSERT_CRS_ON_TX      (1 << 11)
+#define PHYCR_FORCE_LINK_GOOD       (1 << 10)
+#define PHYCR_ENABLE_CROSSOVER      (1 << 6)
+#define PHYCR_MDI_MODE              (1 << 5)
+#define PHYCR_DISABLE CLK125        (1 << 4)
+#define PHYCR_DISABLE_JABBER        (1 << 0)
+
+//PHYSR register
+#define PHYSR_SPEED1                (1 << 15)
+#define PHYSR_SPEED0                (1 << 14)
+#define PHYSR_DUPLEX                (1 << 13)
+#define PHYSR_PAGE_RECEIVED         (1 << 12)
+#define PHYSR_SPEED_DUPLEX_RESOLVED (1 << 11)
+#define PHYSR_LINK                  (1 << 10)
+#define PHYSR_MDI_CROSSOVER_STATUS  (1 << 6)
+#define PHYSR_RE_LINK_OK            (1 << 1)
+#define PHYSR_JABBER                (1 << 0)
+
+//Speed
+#define PHYSR_SPEED_MASK            (3 << 14)
+#define PHYSR_SPEED_10              (0 << 14)
+#define PHYSR_SPEED_100             (1 << 14)
+#define PHYSR_SPEED_1000            (2 << 14)
+
+//INER register
+#define INER_AN_ERROR               (1 << 15)
+#define INER_PAGE_RECEIVED          (1 << 12)
+#define INER_AN_COMPLETE            (1 << 11)
+#define INER_LINK_STATUS            (1 << 10)
+#define INER_SYMBOL_ERROR           (1 << 9)
+#define INER_FALSE_CARRIER          (1 << 8)
+#define INER_JABBER                 (1 << 0)
+
+//INSR register
+#define INSR_AN_ERROR               (1 << 15)
+#define INSR_PAGE_RECEIVED          (1 << 12)
+#define INSR_AN_COMPLETE            (1 << 11)
+#define INSR_LINK_STATUS            (1 << 10)
+#define INSR_SYMBOL_ERROR           (1 << 9)
+#define INSR_FALSE_CARRIER          (1 << 8)
+#define INSR_JABBER                 (1 << 0)
+
+//LDPSR register
+#define LDPSR_POWER_SAVE_MODE       (1 << 0)
+
+//EPAGSR register
+#define EPAGSR_EXT_PAGE_SEL7        (1 << 7)
+#define EPAGSR_EXT_PAGE_SEL6        (1 << 6)
+#define EPAGSR_EXT_PAGE_SEL5        (1 << 5)
+#define EPAGSR_EXT_PAGE_SEL4        (1 << 4)
+#define EPAGSR_EXT_PAGE_SEL3        (1 << 3)
+#define EPAGSR_EXT_PAGE_SEL2        (1 << 2)
+#define EPAGSR_EXT_PAGE_SEL1        (1 << 1)
+#define EPAGSR_EXT_PAGE_SEL0        (1 << 0)
+
+//PAGSEL register
+#define PAGSEL_PAGE_SEL2            (1 << 2)
+#define PAGSEL_PAGE_SEL1            (1 << 1)
+#define PAGSEL_PAGE_SEL0            (1 << 0)
+
+//RTL8211 Ethernet PHY driver
+extern const PhyDriver rtl8211PhyDriver;
+
+//RTL8211 related functions
+error_t rtl8211Init(NetInterface *interface);
+
+void rtl8211Tick(NetInterface *interface);
+
+void rtl8211EnableIrq(NetInterface *interface);
+void rtl8211DisableIrq(NetInterface *interface);
+
+void rtl8211EventHandler(NetInterface *interface);
+
+void rtl8211WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t rtl8211ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void rtl8211DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/rx63n_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,784 @@
+/**
+ * @file rx63n_eth.c
+ * @brief Renesas RX63N Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include <iorx63n.h>
+#include <intrinsics.h>
+#include "core/net.h"
+#include "drivers/rx63n_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWRX compiler?
+#if defined(__ICCRX__)
+
+//Transmit buffer
+#pragma data_alignment = 32
+static uint8_t txBuffer[RX63N_ETH_TX_BUFFER_COUNT][RX63N_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 32
+static uint8_t rxBuffer[RX63N_ETH_RX_BUFFER_COUNT][RX63N_ETH_RX_BUFFER_SIZE];
+//Transmit DMA descriptors
+#pragma data_alignment = 32
+static Rx63nTxDmaDesc txDmaDesc[RX63N_ETH_TX_BUFFER_COUNT];
+//Receive DMA descriptors
+#pragma data_alignment = 32
+static Rx63nRxDmaDesc rxDmaDesc[RX63N_ETH_RX_BUFFER_COUNT];
+
+//GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[RX63N_ETH_TX_BUFFER_COUNT][RX63N_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(32)));
+//Receive buffer
+static uint8_t rxBuffer[RX63N_ETH_RX_BUFFER_COUNT][RX63N_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(32)));
+//Transmit DMA descriptors
+static Rx63nTxDmaDesc txDmaDesc[RX63N_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(32)));
+//Receive DMA descriptors
+static Rx63nRxDmaDesc rxDmaDesc[RX63N_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(32)));
+
+#endif
+
+//Current transmit descriptor
+static uint_t txIndex;
+//Current receive descriptor
+static uint_t rxIndex;
+
+
+/**
+ * @brief RX63N Ethernet MAC driver
+ **/
+
+const NicDriver rx63nEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   rx63nEthInit,
+   rx63nEthTick,
+   rx63nEthEnableIrq,
+   rx63nEthDisableIrq,
+   rx63nEthEventHandler,
+   rx63nEthSendPacket,
+   rx63nEthSetMulticastFilter,
+   rx63nEthUpdateMacConfig,
+   rx63nEthWritePhyReg,
+   rx63nEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   TRUE
+};
+
+
+/**
+ * @brief RX63N Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t rx63nEthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing RX63N Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Disable protection
+   SYSTEM.PRCR.WORD = 0xA50B;
+   //Cancel EDMAC module stop state
+   MSTP(EDMAC) = 0;
+   //Enable protection
+   SYSTEM.PRCR.WORD = 0xA500;
+
+   //GPIO configuration
+   rx63nEthInitGpio(interface);
+
+   //Reset EDMAC module
+   EDMAC.EDMR.BIT.SWR = 1;
+   sleep(10);
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Initialize DMA descriptor lists
+   rx63nEthInitDmaDesc(interface);
+
+   //Maximum frame length that can be accepted
+   ETHERC.RFLR.LONG = 1518;
+   //Set default inter packet gap (96-bit time)
+   ETHERC.IPGR.LONG = 0x14;
+
+   //Set the upper 32 bits of the MAC address
+   ETHERC.MAHR = (interface->macAddr.b[0] << 24) | (interface->macAddr.b[1] << 16) |
+      (interface->macAddr.b[2] << 8) | interface->macAddr.b[3];
+
+   //Set the lower 16 bits of the MAC address
+   ETHERC.MALR.BIT.MA = (interface->macAddr.b[4] << 8) | interface->macAddr.b[5];
+
+   //Set descriptor length (16 bytes)
+   EDMAC.EDMR.BIT.DL = 0;
+   //Select little endian mode
+   EDMAC.EDMR.BIT.DE = 1;
+   //Use store and forward mode
+   EDMAC.TFTR.BIT.TFT = 0;
+
+   //Set transmit FIFO size (2048 bytes)
+   EDMAC.FDR.BIT.TFD = 7;
+   //Set receive FIFO size (2048 bytes)
+   EDMAC.FDR.BIT.RFD = 7;
+
+   //Enable continuous reception of multiple frames
+   EDMAC.RMCR.BIT.RNR = 1;
+
+   //Accept transmit interrupt notifications
+   EDMAC.TRIMD.BIT.TIM = 0;
+   EDMAC.TRIMD.BIT.TIS = 1;
+
+   //Disable all EDMAC interrupts
+   EDMAC.EESIPR.LONG = 0;
+   //Enable only the desired EDMAC interrupts
+   EDMAC.EESIPR.BIT.TWBIP = 1;
+   EDMAC.EESIPR.BIT.FRIP = 1;
+
+   //Configure EDMAC interrupt priority
+   IPR(ETHER, EINT) = RX63N_ETH_IRQ_PRIORITY;
+
+   //Enable transmission and reception
+   ETHERC.ECMR.BIT.TE = 1;
+   ETHERC.ECMR.BIT.RE = 1;
+
+   //Instruct the DMA to poll the receive descriptor list
+   EDMAC.EDRRR.BIT.RR = 1;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//RX63N Demonstration Kit?
+#if defined(USE_RDK_RX63N)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void rx63nEthInitGpio(NetInterface *interface)
+{
+   //Unlock MPC registers
+   MPC.PWPR.BIT.B0WI = 0;
+   MPC.PWPR.BIT.PFSWE = 1;
+
+   //Select RMII interface mode
+   MPC.PFENET.BIT.PHYMODE = 0;
+
+   //Configure ET_MDIO (PA3)
+   PORTA.PMR.BIT.B3 = 1;
+   MPC.PA3PFS.BYTE = 0x11;
+
+   //Configure ET_MDC (PA4)
+   PORTA.PMR.BIT.B4 = 1;
+   MPC.PA4PFS.BYTE = 0x11;
+
+   //Configure ET_LINKSTA (PA5)
+   PORTA.PMR.BIT.B5 = 1;
+   MPC.PA5PFS.BYTE = 0x11;
+
+   //Configure RMII_RXD1 (PB0)
+   PORTB.PMR.BIT.B0 = 1;
+   MPC.PB0PFS.BYTE = 0x12;
+
+   //Configure RMII_RXD0 (PB1)
+   PORTB.PMR.BIT.B1 = 1;
+   MPC.PB1PFS.BYTE = 0x12;
+
+   //Configure REF50CK (PB2)
+   PORTB.PMR.BIT.B2 = 1;
+   MPC.PB2PFS.BYTE = 0x12;
+
+   //Configure RMII_RX_ER (PB3)
+   PORTB.PMR.BIT.B3 = 1;
+   MPC.PB3PFS.BYTE = 0x12;
+
+   //Configure RMII_TXD_EN (PB4)
+   PORTB.PMR.BIT.B4 = 1;
+   MPC.PB4PFS.BYTE = 0x12;
+
+   //Configure RMII_TXD0 (PB5)
+   PORTB.PMR.BIT.B5 = 1;
+   MPC.PB5PFS.BYTE = 0x12;
+
+   //Configure RMII_TXD1 (PB6)
+   PORTB.PMR.BIT.B6 = 1;
+   MPC.PB6PFS.BYTE = 0x12;
+
+   //Configure RMII_CRS_DV (PB7)
+   PORTB.PMR.BIT.B7 = 1;
+   MPC.PB7PFS.BYTE = 0x12;
+
+   //Lock MPC registers
+   MPC.PWPR.BIT.PFSWE = 0;
+   MPC.PWPR.BIT.B0WI = 0;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void rx63nEthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX descriptors
+   for(i = 0; i < RX63N_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the application
+      txDmaDesc[i].td0 = 0;
+      //Transmit buffer length
+      txDmaDesc[i].td1 = 0;
+      //Transmit buffer address
+      txDmaDesc[i].td2 = (uint32_t) txBuffer[i];
+      //Clear padding field
+      txDmaDesc[i].padding = 0;
+   }
+
+   //Mark the last descriptor entry with the TDLE flag
+   txDmaDesc[i - 1].td0 |= EDMAC_TD0_TDLE;
+   //Initialize TX descriptor index
+   txIndex = 0;
+
+   //Initialize RX descriptors
+   for(i = 0; i < RX63N_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the DMA
+      rxDmaDesc[i].rd0 = EDMAC_RD0_RACT;
+      //Receive buffer length
+      rxDmaDesc[i].rd1 = (RX63N_ETH_RX_BUFFER_SIZE << 16) & EDMAC_RD1_RBL;
+      //Receive buffer address
+      rxDmaDesc[i].rd2 = (uint32_t) rxBuffer[i];
+      //Clear padding field
+      rxDmaDesc[i].padding = 0;
+   }
+
+   //Mark the last descriptor entry with the RDLE flag
+   rxDmaDesc[i - 1].rd0 |= EDMAC_RD0_RDLE;
+   //Initialize RX descriptor index
+   rxIndex = 0;
+
+   //Start address of the TX descriptor list
+   EDMAC.TDLAR = txDmaDesc;
+   //Start address of the RX descriptor list
+   EDMAC.RDLAR = rxDmaDesc;
+}
+
+
+/**
+ * @brief RX63N Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void rx63nEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void rx63nEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   IEN(ETHER, EINT) = 1;
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void rx63nEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   IEN(ETHER, EINT) = 0;
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief RX63N Ethernet MAC interrupt service routine
+ **/
+
+#pragma vector = VECT_ETHER_EINT
+__interrupt void rx63nEthIrqHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Allow nested interrupts
+   __enable_interrupt();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read interrupt status register
+   status = EDMAC.EESR.LONG;
+
+   //A packet has been transmitted?
+   if(status & EDMAC_EESR_TWB)
+   {
+      //Clear TWB interrupt flag
+      EDMAC.EESR.LONG = EDMAC_EESR_TWB;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txDmaDesc[txIndex].td0 & EDMAC_TD0_TACT))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & EDMAC_EESR_FR)
+   {
+      //Disable FR interrupts
+      EDMAC.EESIPR.BIT.FRIP = 0;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief RX63N Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void rx63nEthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(EDMAC.EESR.LONG & EDMAC_EESR_FR)
+   {
+      //Clear FR interrupt flag
+      EDMAC.EESR.LONG = EDMAC_EESR_FR;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = rx63nEthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable EDMAC interrupts
+   EDMAC.EESIPR.BIT.TWBIP = 1;
+   EDMAC.EESIPR.BIT.FRIP = 1;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t rx63nEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   //Retrieve the length of the packet
+   size_t length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > RX63N_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txDmaDesc[txIndex].td0 & EDMAC_TD0_TACT)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(txBuffer[txIndex], buffer, offset, length);
+
+   //Write the number of bytes to send
+   txDmaDesc[txIndex].td1 = (length << 16) & EDMAC_TD1_TBL;
+
+   //Check current index
+   if(txIndex < (RX63N_ETH_TX_BUFFER_COUNT - 1))
+   {
+      //Give the ownership of the descriptor to the DMA engine
+      txDmaDesc[txIndex].td0 = EDMAC_TD0_TACT | EDMAC_TD0_TFP_SOF |
+         EDMAC_TD0_TFP_EOF | EDMAC_TD0_TWBI;
+
+      //Point to the next descriptor
+      txIndex++;
+   }
+   else
+   {
+      //Give the ownership of the descriptor to the DMA engine
+      txDmaDesc[txIndex].td0 = EDMAC_TD0_TACT | EDMAC_TD0_TDLE |
+         EDMAC_TD0_TFP_SOF | EDMAC_TD0_TFP_EOF | EDMAC_TD0_TWBI;
+
+      //Wrap around
+      txIndex = 0;
+   }
+
+   //Instruct the DMA to poll the transmit descriptor list
+   EDMAC.EDTRR.BIT.TR = 1;
+
+   //Check whether the next buffer is available for writing
+   if(!(txDmaDesc[txIndex].td0 & EDMAC_TD0_TACT))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful write operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t rx63nEthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxDmaDesc[rxIndex].rd0 & EDMAC_RD0_RACT))
+   {
+      //SOF and EOF flags should be set
+      if((rxDmaDesc[rxIndex].rd0 & EDMAC_RD0_RFP_SOF) &&
+         (rxDmaDesc[rxIndex].rd0 & EDMAC_RD0_RFP_EOF))
+      {
+         //Make sure no error occurred
+         if(!(rxDmaDesc[rxIndex].rd0 & (EDMAC_RD0_RFS_MASK & ~EDMAC_RD0_RFS_RMAF)))
+         {
+            //Retrieve the length of the frame
+            n = rxDmaDesc[rxIndex].rd1 & EDMAC_RD1_RFL;
+            //Limit the number of data to read
+            n = MIN(n, RX63N_ETH_RX_BUFFER_SIZE);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, rxBuffer[rxIndex], n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Check current index
+      if(rxIndex < (RX63N_ETH_RX_BUFFER_COUNT - 1))
+      {
+         //Give the ownership of the descriptor back to the DMA
+         rxDmaDesc[rxIndex].rd0 = EDMAC_RD0_RACT;
+         //Point to the next descriptor
+         rxIndex++;
+      }
+      else
+      {
+         //Give the ownership of the descriptor back to the DMA
+         rxDmaDesc[rxIndex].rd0 = EDMAC_RD0_RACT | EDMAC_RD0_RDLE;
+         //Wrap around
+         rxIndex = 0;
+      }
+
+      //Instruct the DMA to poll the receive descriptor list
+      EDMAC.EDRRR.BIT.RR = 1;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t rx63nEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   bool_t acceptMulticast;
+
+   //This flag will be set if multicast addresses should be accepted
+   acceptMulticast = FALSE;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Valid entry?
+      if(interface->macMulticastFilter[i].refCount > 0)
+      {
+         //Accept multicast addresses
+         acceptMulticast = TRUE;
+         //We are done
+         break;
+      }
+   }
+
+   //Enable the reception of multicast frames if necessary
+   if(acceptMulticast)
+      EDMAC.EESR.BIT.RMAF = 1;
+   else
+      EDMAC.EESR.BIT.RMAF = 0;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t rx63nEthUpdateMacConfig(NetInterface *interface)
+{
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      ETHERC.ECMR.BIT.RTM = 1;
+   else
+      ETHERC.ECMR.BIT.RTM = 0;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      ETHERC.ECMR.BIT.DM = 1;
+   else
+      ETHERC.ECMR.BIT.DM = 0;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void rx63nEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   //Synchronization pattern
+   rx63nEthWriteSmi(SMI_SYNC, 32);
+   //Start of frame
+   rx63nEthWriteSmi(SMI_START, 2);
+   //Set up a write operation
+   rx63nEthWriteSmi(SMI_WRITE, 2);
+   //Write PHY address
+   rx63nEthWriteSmi(phyAddr, 5);
+   //Write register address
+   rx63nEthWriteSmi(regAddr, 5);
+   //Turnaround
+   rx63nEthWriteSmi(SMI_TA, 2);
+   //Write register value
+   rx63nEthWriteSmi(data, 16);
+   //Release MDIO
+   rx63nEthReadSmi(1);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t rx63nEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint16_t data;
+
+   //Synchronization pattern
+   rx63nEthWriteSmi(SMI_SYNC, 32);
+   //Start of frame
+   rx63nEthWriteSmi(SMI_START, 2);
+   //Set up a read operation
+   rx63nEthWriteSmi(SMI_READ, 2);
+   //Write PHY address
+   rx63nEthWriteSmi(phyAddr, 5);
+   //Write register address
+   rx63nEthWriteSmi(regAddr, 5);
+   //Turnaround to avoid contention
+   rx63nEthReadSmi(1);
+   //Read register value
+   data = rx63nEthReadSmi(16);
+   //Force the PHY to release the MDIO pin
+   rx63nEthReadSmi(1);
+
+   //Return PHY register contents
+   return data;
+}
+
+
+/**
+ * @brief SMI write operation
+ * @param[in] data Raw data to be written
+ * @param[in] length Number of bits to be written
+ **/
+
+void rx63nEthWriteSmi(uint32_t data, uint_t length)
+{
+   //Skip the most significant bits since they are meaningless
+   data <<= 32 - length;
+
+   //Configure MDIO as an output
+   ETHERC.PIR.BIT.MMD = 1;
+
+   //Write the specified number of bits
+   while(length--)
+   {
+      //Write MDIO
+      if(data & 0x80000000)
+         ETHERC.PIR.BIT.MDO = 1;
+      else
+         ETHERC.PIR.BIT.MDO = 0;
+
+      //Assert MDC
+      usleep(1);
+      ETHERC.PIR.BIT.MDC = 1;
+      //Deassert MDC
+      usleep(1);
+      ETHERC.PIR.BIT.MDC = 0;
+
+      //Rotate data
+      data <<= 1;
+   }
+}
+
+
+/**
+ * @brief SMI read operation
+ * @param[in] length Number of bits to be read
+ * @return Data resulting from the MDIO read operation
+ **/
+
+uint32_t rx63nEthReadSmi(uint_t length)
+{
+   uint32_t data = 0;
+
+   //Configure MDIO as an input
+   ETHERC.PIR.BIT.MMD = 0;
+
+   //Read the specified number of bits
+   while(length--)
+   {
+      //Rotate data
+      data <<= 1;
+
+      //Assert MDC
+      ETHERC.PIR.BIT.MDC = 1;
+      usleep(1);
+      //Deassert MDC
+      ETHERC.PIR.BIT.MDC = 0;
+      usleep(1);
+
+      //Check MDIO state
+      if(ETHERC.PIR.BIT.MDI)
+         data |= 0x00000001;
+   }
+
+   //Return the received data
+   return data;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/rx63n_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,192 @@
+/**
+ * @file rx63n_eth.h
+ * @brief Renesas RX63N Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _RX63N_ETH_H
+#define _RX63N_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef RX63N_ETH_TX_BUFFER_COUNT
+   #define RX63N_ETH_TX_BUFFER_COUNT 3
+#elif (RX63N_ETH_TX_BUFFER_COUNT < 1)
+   #error RX63N_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef RX63N_ETH_TX_BUFFER_SIZE
+   #define RX63N_ETH_TX_BUFFER_SIZE 1536
+#elif (RX63N_ETH_TX_BUFFER_SIZE != 1536)
+   #error RX63N_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef RX63N_ETH_RX_BUFFER_COUNT
+   #define RX63N_ETH_RX_BUFFER_COUNT 6
+#elif (RX63N_ETH_RX_BUFFER_COUNT < 1)
+   #error RX63N_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef RX63N_ETH_RX_BUFFER_SIZE
+   #define RX63N_ETH_RX_BUFFER_SIZE 1536
+#elif (RX63N_ETH_RX_BUFFER_SIZE != 1536)
+   #error RX63N_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Ethernet interrupt priority
+#ifndef RX63N_ETH_IRQ_PRIORITY
+   #define RX63N_ETH_IRQ_PRIORITY 2
+#elif (RX63N_ETH_IRQ_PRIORITY < 0)
+   #error RX63N_ETH_IRQ_PRIORITY parameter is not valid
+#endif
+
+//EESR register
+#define EDMAC_EESR_TWB     0x40000000
+#define EDMAC_EESR_TABT    0x04000000
+#define EDMAC_EESR_RABT    0x02000000
+#define EDMAC_EESR_RFCOF   0x01000000
+#define EDMAC_EESR_ADE     0x00800000
+#define EDMAC_EESR_ECI     0x00400000
+#define EDMAC_EESR_TC      0x00200000
+#define EDMAC_EESR_TDE     0x00100000
+#define EDMAC_EESR_TFUF    0x00080000
+#define EDMAC_EESR_FR      0x00040000
+#define EDMAC_EESR_RDE     0x00020000
+#define EDMAC_EESR_RFOF    0x00010000
+#define EDMAC_EESR_CND     0x00000800
+#define EDMAC_EESR_DLC     0x00000400
+#define EDMAC_EESR_CD      0x00000200
+#define EDMAC_EESR_TRO     0x00000100
+#define EDMAC_EESR_RMAF    0x00000080
+#define EDMAC_EESR_RRF     0x00000010
+#define EDMAC_EESR_RTLF    0x00000008
+#define EDMAC_EESR_RTSF    0x00000004
+#define EDMAC_EESR_PRE     0x00000002
+#define EDMAC_EESR_CERF    0x00000001
+
+//Transmit DMA descriptor flags
+#define EDMAC_TD0_TACT     0x80000000
+#define EDMAC_TD0_TDLE     0x40000000
+#define EDMAC_TD0_TFP_SOF  0x20000000
+#define EDMAC_TD0_TFP_EOF  0x10000000
+#define EDMAC_TD0_TFE      0x08000000
+#define EDMAC_TD0_TWBI     0x04000000
+#define EDMAC_TD0_TFS_MASK 0x0000010F
+#define EDMAC_TD0_TFS_TABT 0x00000100
+#define EDMAC_TD0_TFS_CND  0x00000008
+#define EDMAC_TD0_TFS_DLC  0x00000004
+#define EDMAC_TD0_TFS_CD   0x00000002
+#define EDMAC_TD0_TFS_TRO  0x00000001
+#define EDMAC_TD1_TBL      0xFFFF0000
+#define EDMAC_TD2_TBA      0xFFFFFFFF
+
+//Receive DMA descriptor flags
+#define EDMAC_RD0_RACT     0x80000000
+#define EDMAC_RD0_RDLE     0x40000000
+#define EDMAC_RD0_RFP_SOF  0x20000000
+#define EDMAC_RD0_RFP_EOF  0x10000000
+#define EDMAC_RD0_RFE      0x08000000
+#define EDMAC_RD0_RFS_MASK 0x0000039F
+#define EDMAC_RD0_RFS_RFOF 0x00000200
+#define EDMAC_RD0_RFS_RABT 0x00000100
+#define EDMAC_RD0_RFS_RMAF 0x00000080
+#define EDMAC_RD0_RFS_RRF  0x00000010
+#define EDMAC_RD0_RFS_RTLF 0x00000008
+#define EDMAC_RD0_RFS_RTSF 0x00000004
+#define EDMAC_RD0_RFS_PRE  0x00000002
+#define EDMAC_RD0_RFS_CERF 0x00000001
+#define EDMAC_RD1_RBL      0xFFFF0000
+#define EDMAC_RD1_RFL      0x0000FFFF
+#define EDMAC_RD2_RBA      0xFFFFFFFF
+
+//Serial Management Interface
+#define SMI_SYNC           0xFFFFFFFF
+#define SMI_START          0x00000001
+#define SMI_WRITE          0x00000001
+#define SMI_READ           0x00000002
+#define SMI_TA             0x00000002
+
+
+/**
+ * @brief Transmit DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t td0;
+   uint32_t td1;
+   uint32_t td2;
+   uint32_t padding;
+} Rx63nTxDmaDesc;
+
+
+/**
+ * @brief Receive DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t rd0;
+   uint32_t rd1;
+   uint32_t rd2;
+   uint32_t padding;
+} Rx63nRxDmaDesc;
+
+
+//RX63N Ethernet MAC driver
+extern const NicDriver rx63nEthDriver;
+
+//RX63N Ethernet MAC related functions
+error_t rx63nEthInit(NetInterface *interface);
+void rx63nEthInitGpio(NetInterface *interface);
+void rx63nEthInitDmaDesc(NetInterface *interface);
+
+void rx63nEthTick(NetInterface *interface);
+
+void rx63nEthEnableIrq(NetInterface *interface);
+void rx63nEthDisableIrq(NetInterface *interface);
+void rx63nEthEventHandler(NetInterface *interface);
+
+error_t rx63nEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t rx63nEthReceivePacket(NetInterface *interface);
+
+error_t rx63nEthSetMulticastFilter(NetInterface *interface);
+error_t rx63nEthUpdateMacConfig(NetInterface *interface);
+
+void rx63nEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t rx63nEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+void rx63nEthWriteSmi(uint32_t data, uint_t length);
+uint32_t rx63nEthReadSmi(uint_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/rza1_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1179 @@
+/**
+ * @file rza1_eth.c
+ * @brief Renesas RZ/A1 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "iodefine.h"
+#include "cpg_iobitmask.h"
+#include "intc.h"
+#include "core/net.h"
+#include "drivers/rza1_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 32
+static uint8_t txBuffer[RZA1_ETH_TX_BUFFER_COUNT][RZA1_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 32
+static uint8_t rxBuffer[RZA1_ETH_RX_BUFFER_COUNT][RZA1_ETH_RX_BUFFER_SIZE];
+//Transmit DMA descriptors
+#pragma data_alignment = 32
+static Rza1TxDmaDesc txDmaDesc[RZA1_ETH_TX_BUFFER_COUNT];
+//Receive DMA descriptors
+#pragma data_alignment = 32
+static Rza1RxDmaDesc rxDmaDesc[RZA1_ETH_RX_BUFFER_COUNT];
+
+//ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[RZA1_ETH_TX_BUFFER_COUNT][RZA1_ETH_TX_BUFFER_SIZE]
+   __attribute__((section(".BSS_DMAC_SAMPLE_INTERNAL_RAM"), aligned(32)));
+//Receive buffer
+static uint8_t rxBuffer[RZA1_ETH_RX_BUFFER_COUNT][RZA1_ETH_RX_BUFFER_SIZE]
+   __attribute__((section(".BSS_DMAC_SAMPLE_INTERNAL_RAM"), aligned(32)));
+//Transmit DMA descriptors
+static Rza1TxDmaDesc txDmaDesc[RZA1_ETH_TX_BUFFER_COUNT]
+   __attribute__((section(".BSS_DMAC_SAMPLE_INTERNAL_RAM"), aligned(32)));
+//Receive DMA descriptors
+static Rza1RxDmaDesc rxDmaDesc[RZA1_ETH_RX_BUFFER_COUNT]
+   __attribute__((section(".BSS_DMAC_SAMPLE_INTERNAL_RAM"), aligned(32)));
+
+#endif
+
+//Current transmit descriptor
+static uint_t txIndex;
+//Current receive descriptor
+static uint_t rxIndex;
+
+
+/**
+ * @brief RZ/A1 Ethernet MAC driver
+ **/
+
+const NicDriver rza1EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   rza1EthInit,
+   rza1EthTick,
+   rza1EthEnableIrq,
+   rza1EthDisableIrq,
+   rza1EthEventHandler,
+   rza1EthSendPacket,
+   rza1EthSetMulticastFilter,
+   rza1EthUpdateMacConfig,
+   rza1EthWritePhyReg,
+   rza1EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   TRUE
+};
+
+
+/**
+ * @brief RZ/A1 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t rza1EthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing RZ/A1 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Enable Ethernet peripheral clock
+   CPG.STBCR7 &= ~CPG_STBCR7_MSTP74;
+
+   //GPIO configuration
+   rza1EthInitGpio(interface);
+
+   //Perform software reset
+   ETHER.ARSTR = ETHER_ARSTR_ARST;
+   //Wait for the reset to complete
+   sleep(10);
+
+   //Start EDMAC transmitting and receiving units
+   ETHER.EDSR0 = ETHER_EDSR0_ENT | ETHER_EDSR0_ENR;
+
+   //To execute a software reset with this register, 1 must be
+   //written to both the SWRT and SWRR bits simultaneously
+   ETHER.EDMR0 = ETHER_EDMR0_SWRT | ETHER_EDMR0_SWRR;
+   //Wait for the reset to complete
+   while(ETHER.EDMR0 & (ETHER_EDMR0_SWRT | ETHER_EDMR0_SWRR));
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Initialize DMA descriptor lists
+   rza1EthInitDmaDesc(interface);
+
+   //Select little endian mode and set descriptor length (16 bytes)
+   ETHER.EDMR0 = ETHER_EDMR0_DE | ETHER_EDMR0_DL_16;
+
+   //Error masks
+   ETHER.TRSCER0 = 0;
+   //Use store and forward mode
+   ETHER.TFTR0 = 0;
+
+   //Set transmit FIFO size and receive FIFO size (2048 bytes)
+   ETHER.FDR0 = ETHER_FDR0_TFD_2048 | ETHER_FDR0_RFD_2048;
+
+   //Enable continuous reception of multiple frames
+   ETHER.RMCR0 = ETHER_RMCR0_RNC;
+   //No padding insertion into receive data
+   ETHER.RPADIR0 = 0;
+
+   //Receive FIFO threshold (8 frames or 2048-64 bytes)
+   ETHER.FCFTR0 = ETHER_FCFTR0_RFF_8 | ETHER_FCFTR0_RFD_2048;
+
+   //Intelligent checksum operation mode
+   ETHER.CSMR = 0;
+
+   //Enable multicast address filtering
+   ETHER.ECMR0 |= ETH_ECMR0_MCT;
+
+   //Set the upper 32 bits of the MAC address
+   ETHER.MAHR0 = (interface->macAddr.b[0] << 24) | (interface->macAddr.b[1] << 16) |
+      (interface->macAddr.b[2] << 8) | interface->macAddr.b[3];
+
+   //Set the lower 16 bits of the MAC address
+   ETHER.MALR0 = (interface->macAddr.b[4] << 8) | interface->macAddr.b[5];
+
+   //Disable all CAM entries
+   ETHER.TSU_TEN = 0;
+
+   //Maximum frame length that can be accepted
+   ETHER.RFLR0 = 1518;
+   //Automatic pause frame
+   ETHER.APR0 = 0;
+   //Manual pause frame
+   ETHER.MPR0 = 0;
+   //Automatic pause frame retransmit count
+   ETHER.TPAUSER0 = 0;
+
+   //Disable all EMAC interrupts
+   ETHER.ECSIPR0 = 0;
+
+   //Enable the desired EDMAC interrupts
+   ETHER.EESIPR0 =  ETHER_EESIPR0_TWBIP | ETHER_EESIPR0_FRIP;
+
+   //Register interrupt handler
+   R_INTC_Regist_Int_Func(INTC_ID_ETHERI, rza1EthIrqHandler);
+   //Configure interrupt priority
+   R_INTC_Set_Priority(INTC_ID_ETHERI, RZA1_ETH_IRQ_PRIORITY);
+
+   //Enable EDMAC transmission and reception
+   ETHER.ECMR0 |= ETH_ECMR0_RE | ETH_ECMR0_TE;
+
+   //Instruct the DMA to poll the receive descriptor list
+   ETHER.EDRRR0 = ETHER_EDRRR0_RR;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//RSK RZ/A1H, Stream it! RZ, Hachiko or VK-RZ/A1H evaluation board?
+#if defined(USE_RSK_RZA1H) || defined(USE_STREAM_IT_RZ) || \
+   defined(USE_HACHIKO) || defined(USE_VK_RZA1H)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void rza1EthInitGpio(NetInterface *interface)
+{
+//RSK RZ/A1H or Hachiko evaluation board?
+#if defined(USE_RSK_RZA1H) || defined(USE_HACHIKO)
+   //Configure ET_COL (P1_14)
+   PORT1.PMCn.BIT.PMCn14 = 1;
+   PORT1.PFCn.BIT.PFCn14 = 1;
+   PORT1.PFCEn.BIT.PFCEn14 = 1;
+   PORT1.PFCAEn.BIT.PFCAEn14 = 0;
+   PORT1.PIPCn.BIT.PIPCn14 = 1;
+
+   //Configure ET_TXCLK (P2_0)
+   PORT2.PMCn.BIT.PMCn0 = 1;
+   PORT2.PFCn.BIT.PFCn0 = 1;
+   PORT2.PFCEn.BIT.PFCEn0 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn0 = 0;
+   PORT2.PIPCn.BIT.PIPCn0 = 1;
+
+   //Configure ET_TXER (P2_1)
+   PORT2.PMCn.BIT.PMCn1 = 1;
+   PORT2.PFCn.BIT.PFCn1 = 1;
+   PORT2.PFCEn.BIT.PFCEn1 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn1 = 0;
+   PORT2.PIPCn.BIT.PIPCn1 = 1;
+
+   //Configure ET_TXEN (P2_2)
+   PORT2.PMCn.BIT.PMCn2 = 1;
+   PORT2.PFCn.BIT.PFCn2 = 1;
+   PORT2.PFCEn.BIT.PFCEn2 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn2 = 0;
+   PORT2.PIPCn.BIT.PIPCn2 = 1;
+
+   //Configure ET_CRS (P2_3)
+   PORT2.PMCn.BIT.PMCn3 = 1;
+   PORT2.PFCn.BIT.PFCn3 = 1;
+   PORT2.PFCEn.BIT.PFCEn3 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn3 = 0;
+   PORT2.PIPCn.BIT.PIPCn3 = 1;
+
+   //Configure ET_TXD0 (P2_4)
+   PORT2.PMCn.BIT.PMCn4 = 1;
+   PORT2.PFCn.BIT.PFCn4 = 1;
+   PORT2.PFCEn.BIT.PFCEn4 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn4 = 0;
+   PORT2.PIPCn.BIT.PIPCn4 = 1;
+
+   //Configure ET_TXD1 (P2_5)
+   PORT2.PMCn.BIT.PMCn5 = 1;
+   PORT2.PFCn.BIT.PFCn5 = 1;
+   PORT2.PFCEn.BIT.PFCEn5 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn5 = 0;
+   PORT2.PIPCn.BIT.PIPCn5 = 1;
+
+   //Configure ET_TXD2 (P2_6)
+   PORT2.PMCn.BIT.PMCn6 = 1;
+   PORT2.PFCn.BIT.PFCn6 = 1;
+   PORT2.PFCEn.BIT.PFCEn6 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn6 = 0;
+   PORT2.PIPCn.BIT.PIPCn6 = 1;
+
+   //Configure ET_TXD3 (P2_7)
+   PORT2.PMCn.BIT.PMCn7 = 1;
+   PORT2.PFCn.BIT.PFCn7 = 1;
+   PORT2.PFCEn.BIT.PFCEn7 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn7 = 0;
+   PORT2.PIPCn.BIT.PIPCn7 = 1;
+
+   //Configure ET_RXD0 (P2_8)
+   PORT2.PMCn.BIT.PMCn8 = 1;
+   PORT2.PFCn.BIT.PFCn8 = 1;
+   PORT2.PFCEn.BIT.PFCEn8 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn8 = 0;
+   PORT2.PIPCn.BIT.PIPCn8 = 1;
+
+   //Configure ET_RXD1 (P2_9)
+   PORT2.PMCn.BIT.PMCn9 = 1;
+   PORT2.PFCn.BIT.PFCn9 = 1;
+   PORT2.PFCEn.BIT.PFCEn9 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn9 = 0;
+   PORT2.PIPCn.BIT.PIPCn9 = 1;
+
+   //Configure ET_RXD2 (P2_10)
+   PORT2.PMCn.BIT.PMCn10 = 1;
+   PORT2.PFCn.BIT.PFCn10 = 1;
+   PORT2.PFCEn.BIT.PFCEn10 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn10 = 0;
+   PORT2.PIPCn.BIT.PIPCn10 = 1;
+
+   //Configure ET_RXD3 (P2_11)
+   PORT2.PMCn.BIT.PMCn11 = 1;
+   PORT2.PFCn.BIT.PFCn11 = 1;
+   PORT2.PFCEn.BIT.PFCEn11 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn11 = 0;
+   PORT2.PIPCn.BIT.PIPCn11 = 1;
+
+   //Configure ET_MDIO (P3_3)
+   PORT3.PMCn.BIT.PMCn3 = 1;
+   PORT3.PFCn.BIT.PFCn3 = 1;
+   PORT3.PFCEn.BIT.PFCEn3 = 0;
+   PORT3.PFCAEn.BIT.PFCAEn3 = 0;
+   PORT3.PIPCn.BIT.PIPCn3 = 1;
+
+   //Configure ET_RXCLK (P3_4)
+   PORT3.PMCn.BIT.PMCn4 = 1;
+   PORT3.PFCn.BIT.PFCn4 = 1;
+   PORT3.PFCEn.BIT.PFCEn4 = 0;
+   PORT3.PFCAEn.BIT.PFCAEn4 = 0;
+   PORT3.PIPCn.BIT.PIPCn4 = 1;
+
+   //Configure ET_RXER (P3_5)
+   PORT3.PMCn.BIT.PMCn5 = 1;
+   PORT3.PFCn.BIT.PFCn5 = 1;
+   PORT3.PFCEn.BIT.PFCEn5 = 0;
+   PORT3.PFCAEn.BIT.PFCAEn5 = 0;
+   PORT3.PIPCn.BIT.PIPCn5 = 1;
+
+   //Configure ET_RXDV (P3_6)
+   PORT3.PMCn.BIT.PMCn6 = 1;
+   PORT3.PFCn.BIT.PFCn6 = 1;
+   PORT3.PFCEn.BIT.PFCEn6 = 0;
+   PORT3.PFCAEn.BIT.PFCAEn6 = 0;
+   PORT3.PIPCn.BIT.PIPCn6 = 1;
+
+   //Configure ET_MDC (P5_9)
+   PORT5.PMCn.BIT.PMCn9 = 1;
+   PORT5.PFCn.BIT.PFCn9 = 1;
+   PORT5.PFCEn.BIT.PFCEn9 = 0;
+   PORT5.PFCAEn.BIT.PFCAEn9 = 0;
+   PORT5.PIPCn.BIT.PIPCn9 = 1;
+
+//VK-RZ/A1H evaluation board?
+#elif defined(USE_VK_RZA1H)
+   //Configure ET_COL (P1_14)
+   PORT1.PMCn.BIT.PMCn14 = 1;
+   PORT1.PFCn.BIT.PFCn14 = 1;
+   PORT1.PFCEn.BIT.PFCEn14 = 1;
+   PORT1.PFCAEn.BIT.PFCAEn14 = 0;
+   PORT1.PIPCn.BIT.PIPCn14 = 1;
+
+   //Configure ET_TXCLK (P2_0)
+   PORT2.PMCn.BIT.PMCn0 = 1;
+   PORT2.PFCn.BIT.PFCn0 = 1;
+   PORT2.PFCEn.BIT.PFCEn0 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn0 = 0;
+   PORT2.PIPCn.BIT.PIPCn0 = 1;
+
+   //Configure ET_TXER (P2_1)
+   PORT2.PMCn.BIT.PMCn1 = 1;
+   PORT2.PFCn.BIT.PFCn1 = 1;
+   PORT2.PFCEn.BIT.PFCEn1 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn1 = 0;
+   PORT2.PIPCn.BIT.PIPCn1 = 1;
+
+   //Configure ET_TXEN (P2_2)
+   PORT2.PMCn.BIT.PMCn2 = 1;
+   PORT2.PFCn.BIT.PFCn2 = 1;
+   PORT2.PFCEn.BIT.PFCEn2 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn2 = 0;
+   PORT2.PIPCn.BIT.PIPCn2 = 1;
+
+   //Configure ET_CRS (P2_3)
+   PORT2.PMCn.BIT.PMCn3 = 1;
+   PORT2.PFCn.BIT.PFCn3 = 1;
+   PORT2.PFCEn.BIT.PFCEn3 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn3 = 0;
+   PORT2.PIPCn.BIT.PIPCn3 = 1;
+
+   //Configure ET_TXD0 (P2_4)
+   PORT2.PMCn.BIT.PMCn4 = 1;
+   PORT2.PFCn.BIT.PFCn4 = 1;
+   PORT2.PFCEn.BIT.PFCEn4 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn4 = 0;
+   PORT2.PIPCn.BIT.PIPCn4 = 1;
+
+   //Configure ET_TXD1 (P2_5)
+   PORT2.PMCn.BIT.PMCn5 = 1;
+   PORT2.PFCn.BIT.PFCn5 = 1;
+   PORT2.PFCEn.BIT.PFCEn5 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn5 = 0;
+   PORT2.PIPCn.BIT.PIPCn5 = 1;
+
+   //Configure ET_TXD2 (P2_6)
+   PORT2.PMCn.BIT.PMCn6 = 1;
+   PORT2.PFCn.BIT.PFCn6 = 1;
+   PORT2.PFCEn.BIT.PFCEn6 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn6 = 0;
+   PORT2.PIPCn.BIT.PIPCn6 = 1;
+
+   //Configure ET_TXD3 (P2_7)
+   PORT2.PMCn.BIT.PMCn7 = 1;
+   PORT2.PFCn.BIT.PFCn7 = 1;
+   PORT2.PFCEn.BIT.PFCEn7 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn7 = 0;
+   PORT2.PIPCn.BIT.PIPCn7 = 1;
+
+   //Configure ET_RXD0 (P2_8)
+   PORT2.PMCn.BIT.PMCn8 = 1;
+   PORT2.PFCn.BIT.PFCn8 = 1;
+   PORT2.PFCEn.BIT.PFCEn8 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn8 = 0;
+   PORT2.PIPCn.BIT.PIPCn8 = 1;
+
+   //Configure ET_RXD1 (P2_9)
+   PORT2.PMCn.BIT.PMCn9 = 1;
+   PORT2.PFCn.BIT.PFCn9 = 1;
+   PORT2.PFCEn.BIT.PFCEn9 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn9 = 0;
+   PORT2.PIPCn.BIT.PIPCn9 = 1;
+
+   //Configure ET_RXD2 (P2_10)
+   PORT2.PMCn.BIT.PMCn10 = 1;
+   PORT2.PFCn.BIT.PFCn10 = 1;
+   PORT2.PFCEn.BIT.PFCEn10 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn10 = 0;
+   PORT2.PIPCn.BIT.PIPCn10 = 1;
+
+   //Configure ET_RXD3 (P2_11)
+   PORT2.PMCn.BIT.PMCn11 = 1;
+   PORT2.PFCn.BIT.PFCn11 = 1;
+   PORT2.PFCEn.BIT.PFCEn11 = 0;
+   PORT2.PFCAEn.BIT.PFCAEn11 = 0;
+   PORT2.PIPCn.BIT.PIPCn11 = 1;
+
+   //Configure ET_MDIO (P3_3)
+   PORT3.PMCn.BIT.PMCn3 = 1;
+   PORT3.PFCn.BIT.PFCn3 = 1;
+   PORT3.PFCEn.BIT.PFCEn3 = 0;
+   PORT3.PFCAEn.BIT.PFCAEn3 = 0;
+   PORT3.PIPCn.BIT.PIPCn3 = 1;
+
+   //Configure ET_RXCLK (P3_4)
+   PORT3.PMCn.BIT.PMCn4 = 1;
+   PORT3.PFCn.BIT.PFCn4 = 1;
+   PORT3.PFCEn.BIT.PFCEn4 = 0;
+   PORT3.PFCAEn.BIT.PFCAEn4 = 0;
+   PORT3.PIPCn.BIT.PIPCn4 = 1;
+
+   //Configure ET_RXER (P3_5)
+   PORT3.PMCn.BIT.PMCn5 = 1;
+   PORT3.PFCn.BIT.PFCn5 = 1;
+   PORT3.PFCEn.BIT.PFCEn5 = 0;
+   PORT3.PFCAEn.BIT.PFCAEn5 = 0;
+   PORT3.PIPCn.BIT.PIPCn5 = 1;
+
+   //Configure ET_RXDV (P3_6)
+   PORT3.PMCn.BIT.PMCn6 = 1;
+   PORT3.PFCn.BIT.PFCn6 = 1;
+   PORT3.PFCEn.BIT.PFCEn6 = 0;
+   PORT3.PFCAEn.BIT.PFCAEn6 = 0;
+   PORT3.PIPCn.BIT.PIPCn6 = 1;
+
+   //Configure ET_MDC (P7_0)
+   PORT7.PMCn.BIT.PMCn0 = 1;
+   PORT7.PFCn.BIT.PFCn0 = 0;
+   PORT7.PFCEn.BIT.PFCEn0 = 1;
+   PORT7.PFCAEn.BIT.PFCAEn0 = 0;
+   PORT7.PIPCn.BIT.PIPCn0 = 1;
+
+//Stream it! RZ evaluation board?
+#elif defined(USE_STREAM_IT_RZ)
+   //Configure ET_TXD0 (P8_0)
+   PORT8.PMCn.BIT.PMCn0 = 1;
+   PORT8.PFCn.BIT.PFCn0 = 1;
+   PORT8.PFCEn.BIT.PFCEn0 = 0;
+   PORT8.PFCAEn.BIT.PFCAEn0 = 0;
+   PORT8.PIPCn.BIT.PIPCn0 = 1;
+
+   //Configure ET_TXD1 (P8_1)
+   PORT8.PMCn.BIT.PMCn1 = 1;
+   PORT8.PFCn.BIT.PFCn1 = 1;
+   PORT8.PFCEn.BIT.PFCEn1 = 0;
+   PORT8.PFCAEn.BIT.PFCAEn1 = 0;
+   PORT8.PIPCn.BIT.PIPCn1 = 1;
+
+   //Configure ET_TXD2 (P8_2)
+   PORT8.PMCn.BIT.PMCn2 = 1;
+   PORT8.PFCn.BIT.PFCn2 = 1;
+   PORT8.PFCEn.BIT.PFCEn2 = 0;
+   PORT8.PFCAEn.BIT.PFCAEn2 = 0;
+   PORT8.PIPCn.BIT.PIPCn2 = 1;
+
+   //Configure ET_TXD3 (P8_3)
+   PORT8.PMCn.BIT.PMCn3 = 1;
+   PORT8.PFCn.BIT.PFCn3 = 1;
+   PORT8.PFCEn.BIT.PFCEn3 = 0;
+   PORT8.PFCAEn.BIT.PFCAEn3 = 0;
+   PORT8.PIPCn.BIT.PIPCn3 = 1;
+
+   //Configure ET_TXCLK (P8_4)
+   PORT8.PMCn.BIT.PMCn4 = 1;
+   PORT8.PFCn.BIT.PFCn4 = 1;
+   PORT8.PFCEn.BIT.PFCEn4 = 0;
+   PORT8.PFCAEn.BIT.PFCAEn4 = 0;
+   PORT8.PIPCn.BIT.PIPCn4 = 1;
+
+   //Configure ET_TXER (P8_5)
+   PORT8.PMCn.BIT.PMCn5 = 1;
+   PORT8.PFCn.BIT.PFCn5 = 1;
+   PORT8.PFCEn.BIT.PFCEn5 = 0;
+   PORT8.PFCAEn.BIT.PFCAEn5 = 0;
+   PORT8.PIPCn.BIT.PIPCn5 = 1;
+
+   //Configure ET_TXEN (P8_6)
+   PORT8.PMCn.BIT.PMCn6 = 1;
+   PORT8.PFCn.BIT.PFCn6 = 1;
+   PORT8.PFCEn.BIT.PFCEn6 = 0;
+   PORT8.PFCAEn.BIT.PFCAEn6 = 0;
+   PORT8.PIPCn.BIT.PIPCn6 = 1;
+
+   //Configure ET_RXD0 (P8_7)
+   PORT8.PMCn.BIT.PMCn7 = 1;
+   PORT8.PFCn.BIT.PFCn7 = 1;
+   PORT8.PFCEn.BIT.PFCEn7 = 0;
+   PORT8.PFCAEn.BIT.PFCAEn7 = 0;
+   PORT8.PIPCn.BIT.PIPCn7 = 1;
+
+   //Configure ET_RXD1 (P8_8)
+   PORT8.PMCn.BIT.PMCn8 = 1;
+   PORT8.PFCn.BIT.PFCn8 = 1;
+   PORT8.PFCEn.BIT.PFCEn8 = 0;
+   PORT8.PFCAEn.BIT.PFCAEn8 = 0;
+   PORT8.PIPCn.BIT.PIPCn8 = 1;
+
+   //Configure ET_RXD2 (P8_9)
+   PORT8.PMCn.BIT.PMCn9 = 1;
+   PORT8.PFCn.BIT.PFCn9 = 1;
+   PORT8.PFCEn.BIT.PFCEn9 = 0;
+   PORT8.PFCAEn.BIT.PFCAEn9 = 0;
+   PORT8.PIPCn.BIT.PIPCn9 = 1;
+
+   //Configure ET_RXD3 (P8_10)
+   PORT8.PMCn.BIT.PMCn10 = 1;
+   PORT8.PFCn.BIT.PFCn10 = 1;
+   PORT8.PFCEn.BIT.PFCEn10 = 0;
+   PORT8.PFCAEn.BIT.PFCAEn10 = 0;
+   PORT8.PIPCn.BIT.PIPCn10 = 1;
+
+   //Configure ET_COL (P8_14)
+   PORT8.PMCn.BIT.PMCn14 = 1;
+   PORT8.PFCn.BIT.PFCn14 = 1;
+   PORT8.PFCEn.BIT.PFCEn14 = 0;
+   PORT8.PFCAEn.BIT.PFCAEn14 = 0;
+   PORT8.PIPCn.BIT.PIPCn14 = 1;
+
+   //Configure ET_CRS (P8_15)
+   PORT8.PMCn.BIT.PMCn15 = 1;
+   PORT8.PFCn.BIT.PFCn15 = 1;
+   PORT8.PFCEn.BIT.PFCEn15 = 0;
+   PORT8.PFCAEn.BIT.PFCAEn15 = 0;
+   PORT8.PIPCn.BIT.PIPCn15 = 1;
+
+   //Configure ET_MDC (P9_0)
+   PORT9.PMCn.BIT.PMCn0 = 1;
+   PORT9.PFCn.BIT.PFCn0 = 1;
+   PORT9.PFCEn.BIT.PFCEn0 = 0;
+   PORT9.PFCAEn.BIT.PFCAEn0 = 0;
+   PORT9.PIPCn.BIT.PIPCn0 = 1;
+
+   //Configure ET_MDIO (P9_1)
+   PORT9.PMCn.BIT.PMCn1 = 1;
+   PORT9.PFCn.BIT.PFCn1 = 1;
+   PORT9.PFCEn.BIT.PFCEn1 = 0;
+   PORT9.PFCAEn.BIT.PFCAEn1 = 0;
+   PORT9.PIPCn.BIT.PIPCn1 = 1;
+
+   //Configure ET_RXCLK (P9_2)
+   PORT9.PMCn.BIT.PMCn2 = 1;
+   PORT9.PFCn.BIT.PFCn2 = 1;
+   PORT9.PFCEn.BIT.PFCEn2 = 0;
+   PORT9.PFCAEn.BIT.PFCAEn2 = 0;
+   PORT9.PIPCn.BIT.PIPCn2 = 1;
+
+   //Configure ET_RXER (P9_3)
+   PORT9.PMCn.BIT.PMCn3 = 1;
+   PORT9.PFCn.BIT.PFCn3 = 1;
+   PORT9.PFCEn.BIT.PFCEn3 = 0;
+   PORT9.PFCAEn.BIT.PFCAEn3 = 0;
+   PORT9.PIPCn.BIT.PIPCn3 = 1;
+
+   //Configure ET_RXDV (P9_4)
+   PORT9.PMCn.BIT.PMCn4 = 1;
+   PORT9.PFCn.BIT.PFCn4 = 1;
+   PORT9.PFCEn.BIT.PFCEn4 = 0;
+   PORT9.PFCAEn.BIT.PFCAEn4 = 0;
+   PORT9.PIPCn.BIT.PIPCn4 = 1;
+
+   //Configure PHY_RST (P2_7)
+   PORT2.PMCn.BIT.PMCn7 = 0;
+   PORT2.PIPCn.BIT.PIPCn7 = 0;
+   PORT2.PMn.BIT.PMn7 = 0;
+
+   //Reset the PHY transceiver
+   PORT2.Pn.BIT.Pn7 = 0;
+   sleep(10);
+
+   //Take the PHY transceiver out of reset
+   PORT2.Pn.BIT.Pn7 = 1;
+   sleep(10);
+#endif
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void rza1EthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX descriptors
+   for(i = 0; i < RZA1_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the application
+      txDmaDesc[i].td0 = 0;
+      //Transmit buffer length
+      txDmaDesc[i].td1 = 0;
+      //Transmit buffer address
+      txDmaDesc[i].td2 = (uint32_t) txBuffer[i];
+      //Clear padding field
+      txDmaDesc[i].padding = 0;
+   }
+
+   //Mark the last descriptor entry with the TDLE flag
+   txDmaDesc[i - 1].td0 |= ETHER_TD0_TDLE;
+   //Initialize TX descriptor index
+   txIndex = 0;
+
+   //Initialize RX descriptors
+   for(i = 0; i < RZA1_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the DMA
+      rxDmaDesc[i].rd0 = ETHER_RD0_RACT;
+      //Receive buffer length
+      rxDmaDesc[i].rd1 = (RZA1_ETH_RX_BUFFER_SIZE << 16) & ETHER_RD1_RBL;
+      //Receive buffer address
+      rxDmaDesc[i].rd2 = (uint32_t) rxBuffer[i];
+      //Clear padding field
+      rxDmaDesc[i].padding = 0;
+   }
+
+   //Mark the last descriptor entry with the RDLE flag
+   rxDmaDesc[i - 1].rd0 |= ETHER_RD0_RDLE;
+   //Initialize RX descriptor index
+   rxIndex = 0;
+
+   //Address of the first TX descriptor
+   ETHER.TDLAR0 = (uint32_t) &txDmaDesc[0];
+   ETHER.TDFAR0 = (uint32_t) &txDmaDesc[0];
+   //Address of the last TX descriptor
+   ETHER.TDFXR0 = (uint32_t) &txDmaDesc[RZA1_ETH_TX_BUFFER_COUNT - 1];
+   //Set TDLF flag
+   ETHER.TDFFR0 = ETHER_TDFFR_TDLF;
+
+   //Address of the first RX descriptor
+   ETHER.RDLAR0 = (uint32_t) &rxDmaDesc[0];
+   ETHER.RDFAR0 = (uint32_t) &rxDmaDesc[0];
+   //Address of the last RX descriptor
+   ETHER.RDFXR0 = (uint32_t) &rxDmaDesc[RZA1_ETH_RX_BUFFER_COUNT - 1];
+   //Set RDLF flag
+   ETHER.RDFFR0 = ETHER_RDFFR0_RDLF;
+}
+
+
+/**
+ * @brief RZ/A1 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void rza1EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void rza1EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   R_INTC_Enable(INTC_ID_ETHERI);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void rza1EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   R_INTC_Disable(INTC_ID_ETHERI);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief RZ/A1 Ethernet MAC interrupt service routine
+ * @param[in] intSense Unused parameter
+ **/
+
+void rza1EthIrqHandler(uint32_t intSense)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read interrupt status register
+   status = ETHER.EESR0;
+
+   //A packet has been transmitted?
+   if(status & ETHER_EESR0_TWB)
+   {
+      //Clear TWB interrupt flag
+      ETHER.EESR0 = ETHER_EESR0_TWB;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txDmaDesc[txIndex].td0 & ETHER_TD0_TACT))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & ETHER_EESR0_FR)
+   {
+      //Disable FR interrupts
+      ETHER.EESIPR0 &= ~ETHER_EESIPR0_FRIP;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief RZ/A1 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void rza1EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(ETHER.EESR0 & ETHER_EESR0_FR)
+   {
+      //Clear FR interrupt flag
+      ETHER.EESR0 = ETHER_EESR0_FR;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = rza1EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable EDMAC interrupts
+   ETHER.EESIPR0 =  ETHER_EESIPR0_TWBIP | ETHER_EESIPR0_FRIP;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t rza1EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   //Retrieve the length of the packet
+   size_t length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > RZA1_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txDmaDesc[txIndex].td0 & ETHER_TD0_TACT)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(txBuffer[txIndex], buffer, offset, length);
+
+   //Write the number of bytes to send
+   txDmaDesc[txIndex].td1 = (length << 16) & ETHER_TD1_TDL;
+
+   //Check current index
+   if(txIndex < (RZA1_ETH_TX_BUFFER_COUNT - 1))
+   {
+      //Give the ownership of the descriptor to the DMA engine
+      txDmaDesc[txIndex].td0 = ETHER_TD0_TACT | ETHER_TD0_TFP_SOF |
+         ETHER_TD0_TFP_EOF | ETHER_TD0_TWBI;
+
+      //Point to the next descriptor
+      txIndex++;
+   }
+   else
+   {
+      //Give the ownership of the descriptor to the DMA engine
+      txDmaDesc[txIndex].td0 = ETHER_TD0_TACT | ETHER_TD0_TDLE |
+         ETHER_TD0_TFP_SOF | ETHER_TD0_TFP_EOF | ETHER_TD0_TWBI;
+
+      //Wrap around
+      txIndex = 0;
+   }
+
+   //Instruct the DMA to poll the transmit descriptor list
+   ETHER.EDTRR0 = ETHER_EDTRR0_TR;
+
+   //Check whether the next buffer is available for writing
+   if(!(txDmaDesc[txIndex].td0 & ETHER_TD0_TACT))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful write operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+error_t rza1EthReceivePacket(NetInterface *interface)
+{
+   static uint8_t temp[RZA1_ETH_RX_BUFFER_SIZE];
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxDmaDesc[rxIndex].rd0 & ETHER_RD0_RACT))
+   {
+      //SOF and EOF flags should be set
+      if((rxDmaDesc[rxIndex].rd0 & ETHER_RD0_RFP_SOF) &&
+         (rxDmaDesc[rxIndex].rd0 & ETHER_RD0_RFP_EOF))
+      {
+         //Make sure no error occurred
+         if(!(rxDmaDesc[rxIndex].rd0 & (ETHER_RD0_RFS_MASK & ~ETHER_RD0_RFS_RMAF)))
+         {
+            //Retrieve the length of the frame
+            n = rxDmaDesc[rxIndex].rd1 & ETHER_RD1_RDL;
+            //Limit the number of data to read
+            n = MIN(n, RZA1_ETH_RX_BUFFER_SIZE);
+
+            //Copy data from the receive buffer
+            memcpy(temp, rxBuffer[rxIndex], n);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, temp, n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Check current index
+      if(rxIndex < (RZA1_ETH_RX_BUFFER_COUNT - 1))
+      {
+         //Give the ownership of the descriptor back to the DMA
+         rxDmaDesc[rxIndex].rd0 = ETHER_RD0_RACT;
+         //Point to the next descriptor
+         rxIndex++;
+      }
+      else
+      {
+         //Give the ownership of the descriptor back to the DMA
+         rxDmaDesc[rxIndex].rd0 = ETHER_RD0_RACT | ETHER_RD0_RDLE;
+         //Wrap around
+         rxIndex = 0;
+      }
+
+      //Instruct the DMA to poll the receive descriptor list
+      ETHER.EDRRR0 = ETHER_EDRRR0_RR;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t rza1EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   volatile uint32_t *addrHigh;
+   volatile uint32_t *addrLow;
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating RZ/A1 multicast filter...\r\n");
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE && i < 32; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Debug message
+         TRACE_DEBUG("  %s\r\n", macAddrToString(&entry->addr, NULL));
+
+         //Point to the CAM entry registers
+         addrHigh = &ETHER.TSU_ADRH0 + 2 * i;
+         addrLow = &ETHER.TSU_ADRL0 + 2 * i;
+
+         //The contents of the CAM entry table registers cannot be
+         //modified while the ADSBSY flag is set
+         while(ETHER.TSU_ADSBSY & ETHER_TSU_ADSBSY_ADSBSY);
+
+         //Set the upper 32 bits of the MAC address
+         *addrHigh = (entry->addr.b[0] << 24) | (entry->addr.b[1] << 16) |
+            (entry->addr.b[2] << 8) |entry->addr.b[3];
+
+         //Wait for the ADSBSY flag to be cleared
+         while(ETHER.TSU_ADSBSY & ETHER_TSU_ADSBSY_ADSBSY);
+
+         //Set the lower 16 bits of the MAC address
+         *addrLow = (entry->addr.b[4] << 8) | entry->addr.b[5];
+
+         //Enable the CAM entry
+         ETHER.TSU_TEN |= 1 << (31 - i);
+      }
+      else
+      {
+         //Disable the CAM entry
+         ETHER.TSU_TEN &= ~(1 << (31 - i));
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t rza1EthUpdateMacConfig(NetInterface *interface)
+{
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      ETHER.ECMR0 |= ETH_ECMR0_DM;
+   else
+      ETHER.ECMR0 &= ~ETH_ECMR0_DM;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void rza1EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   //Synchronization pattern
+   rza1EthWriteSmi(SMI_SYNC, 32);
+   //Start of frame
+   rza1EthWriteSmi(SMI_START, 2);
+   //Set up a write operation
+   rza1EthWriteSmi(SMI_WRITE, 2);
+   //Write PHY address
+   rza1EthWriteSmi(phyAddr, 5);
+   //Write register address
+   rza1EthWriteSmi(regAddr, 5);
+   //Turnaround
+   rza1EthWriteSmi(SMI_TA, 2);
+   //Write register value
+   rza1EthWriteSmi(data, 16);
+   //Release MDIO
+   rza1EthReadSmi(1);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t rza1EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint16_t data;
+
+   //Synchronization pattern
+   rza1EthWriteSmi(SMI_SYNC, 32);
+   //Start of frame
+   rza1EthWriteSmi(SMI_START, 2);
+   //Set up a read operation
+   rza1EthWriteSmi(SMI_READ, 2);
+   //Write PHY address
+   rza1EthWriteSmi(phyAddr, 5);
+   //Write register address
+   rza1EthWriteSmi(regAddr, 5);
+   //Turnaround to avoid contention
+   rza1EthReadSmi(1);
+   //Read register value
+   data = rza1EthReadSmi(16);
+   //Force the PHY to release the MDIO pin
+   rza1EthReadSmi(1);
+
+   //Return PHY register contents
+   return data;
+}
+
+
+/**
+ * @brief SMI write operation
+ * @param[in] data Raw data to be written
+ * @param[in] length Number of bits to be written
+ **/
+
+void rza1EthWriteSmi(uint32_t data, uint_t length)
+{
+   //Skip the most significant bits since they are meaningless
+   data <<= 32 - length;
+
+   //Configure MDIO as an output
+   ETHER.PIR0 |= ETHER_PIR0_MMD;
+
+   //Write the specified number of bits
+   while(length--)
+   {
+      //Write MDIO
+      if(data & 0x80000000)
+         ETHER.PIR0 |= ETHER_PIR0_MDO;
+      else
+         ETHER.PIR0 &= ~ETHER_PIR0_MDO;
+
+      //Assert MDC
+      usleep(1);
+      ETHER.PIR0 |= ETHER_PIR0_MDC;
+      //Deassert MDC
+      usleep(1);
+      ETHER.PIR0 &= ~ETHER_PIR0_MDC;
+
+      //Rotate data
+      data <<= 1;
+   }
+}
+
+
+/**
+ * @brief SMI read operation
+ * @param[in] length Number of bits to be read
+ * @return Data resulting from the MDIO read operation
+ **/
+
+uint32_t rza1EthReadSmi(uint_t length)
+{
+   uint32_t data = 0;
+
+   //Configure MDIO as an input
+   ETHER.PIR0 &= ~ETHER_PIR0_MMD;
+
+   //Read the specified number of bits
+   while(length--)
+   {
+      //Rotate data
+      data <<= 1;
+
+      //Assert MDC
+      ETHER.PIR0 |= ETHER_PIR0_MDC;
+      usleep(1);
+      //Deassert MDC
+      ETHER.PIR0 &= ~ETHER_PIR0_MDC;
+      usleep(1);
+
+      //Check MDIO state
+      if(ETHER.PIR0 & ETHER_PIR0_MDI)
+         data |= 0x00000001;
+   }
+
+   //Return the received data
+   return data;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/rza1_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,282 @@
+/**
+ * @file rza1_eth.h
+ * @brief Renesas RZ/A1 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _RZA1_ETH_H
+#define _RZA1_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef RZA1_ETH_TX_BUFFER_COUNT
+   #define RZA1_ETH_TX_BUFFER_COUNT 8
+#elif (RZA1_ETH_TX_BUFFER_COUNT < 1)
+   #error RZA1_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef RZA1_ETH_TX_BUFFER_SIZE
+   #define RZA1_ETH_TX_BUFFER_SIZE 1536
+#elif (RZA1_ETH_TX_BUFFER_SIZE != 1536)
+   #error RZA1_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef RZA1_ETH_RX_BUFFER_COUNT
+   #define RZA1_ETH_RX_BUFFER_COUNT 8
+#elif (RZA1_ETH_RX_BUFFER_COUNT < 1)
+   #error RZA1_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef RZA1_ETH_RX_BUFFER_SIZE
+   #define RZA1_ETH_RX_BUFFER_SIZE 1536
+#elif (RZA1_ETH_RX_BUFFER_SIZE != 1536)
+   #error RZA1_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Ethernet interrupt priority
+#ifndef RZA1_ETH_IRQ_PRIORITY
+   #define RZA1_ETH_IRQ_PRIORITY 25
+#elif (RZA1_ETH_IRQ_PRIORITY < 0)
+   #error RZA1_ETH_IRQ_PRIORITY parameter is not valid
+#endif
+
+//ARSTR register
+#define ETHER_ARSTR_ARST         0x00000001
+
+//ECMR0 register
+#define ETH_ECMR0_TRCCM          0x04000000
+#define ETH_ECMR0_RCSC           0x00800000
+#define ETH_ECMR0_DPAD           0x00200000
+#define ETH_ECMR0_RZPF           0x00100000
+#define ETH_ECMR0_ZPF            0x00080000
+#define ETH_ECMR0_PFR            0x00040000
+#define ETH_ECMR0_RXF            0x00020000
+#define ETH_ECMR0_TXF            0x00010000
+#define ETH_ECMR0_MCT            0x00002000
+#define ETH_ECMR0_RE             0x00000040
+#define ETH_ECMR0_TE             0x00000020
+#define ETH_ECMR0_DM             0x00000002
+#define ETH_ECMR0_PRM            0x00000001
+
+//PIR0 register
+#define ETHER_PIR0_MDI           0x00000008
+#define ETHER_PIR0_MDO           0x00000004
+#define ETHER_PIR0_MMD           0x00000002
+#define ETHER_PIR0_MDC           0x00000001
+
+//TSU_ADSBSY register
+#define ETHER_TSU_ADSBSY_ADSBSY  0x00000001
+
+//EDSR0 register
+#define ETHER_EDSR0_ENT          0x00000002
+#define ETHER_EDSR0_ENR          0x00000001
+
+//EDMR0 register
+#define ETHER_EDMR0_DE           0x00000040
+#define ETHER_EDMR0_DL           0x00000030
+#define ETHER_EDMR0_SWRT         0x00000002
+#define ETHER_EDMR0_SWRR         0x00000001
+
+#define ETHER_EDMR0_DL_16        0x00000000
+#define ETHER_EDMR0_DL_32        0x00000010
+#define ETHER_EDMR0_DL_64        0x00000020
+
+//EDTRR0 register
+#define ETHER_EDTRR0_TR          0x00000003
+
+//EDRRR0 register
+#define ETHER_EDRRR0_RR          0x00000001
+
+//EESR0 register
+#define ETHER_EESR0_TWB          0xC0000000
+#define ETHER_EESR0_TC1          0x20000000
+#define ETHER_EESR0_TUC          0x10000000
+#define ETHER_EESR0_ROC          0x08000000
+#define ETHER_EESR0_TABT         0x04000000
+#define ETHER_EESR0_RABT         0x02000000
+#define ETHER_EESR0_RFCOF        0x01000000
+#define ETHER_EESR0_ECI          0x00400000
+#define ETHER_EESR0_TC0          0x00200000
+#define ETHER_EESR0_TDE          0x00100000
+#define ETHER_EESR0_TFUF         0x00080000
+#define ETHER_EESR0_FR           0x00040000
+#define ETHER_EESR0_RDE          0x00020000
+#define ETHER_EESR0_RFOF         0x00010000
+#define ETHER_EESR0_RMAF         0x00000080
+#define ETHER_EESR0_RRF          0x00000010
+#define ETHER_EESR0_RTLF         0x00000008
+#define ETHER_EESR0_RTSF         0x00000004
+#define ETHER_EESR0_PRE          0x00000002
+#define ETHER_EESR0_CERF         0x00000001
+
+//EESIPR0 register
+#define ETHER_EESIPR0_TWBIP      0xC0000000
+#define ETHER_EESIPR0_TC1IP      0x20000000
+#define ETHER_EESIPR0_TUCIP      0x10000000
+#define ETHER_EESIPR0_ROCIP      0x08000000
+#define ETHER_EESIPR0_TABTIP     0x04000000
+#define ETHER_EESIPR0_RABTIP     0x02000000
+#define ETHER_EESIPR0_RFCOFIP    0x01000000
+#define ETHER_EESIPR0_ECIIP      0x00400000
+#define ETHER_EESIPR0_TC0IP      0x00200000
+#define ETHER_EESIPR0_TDEIP      0x00100000
+#define ETHER_EESIPR0_TFUFIP     0x00080000
+#define ETHER_EESIPR0_FRIP       0x00040000
+#define ETHER_EESIPR0_RDEIP      0x00020000
+#define ETHER_EESIPR0_RFOFIP     0x00010000
+#define ETHER_EESIPR0_RMAFIP     0x00000080
+#define ETHER_EESIPR0_RRFIP      0x00000010
+#define ETHER_EESIPR0_RTLFIP     0x00000008
+#define ETHER_EESIPR0_RTSFIP     0x00000004
+#define ETHER_EESIPR0_PREIP      0x00000002
+#define ETHER_EESIPR0_CERFIP     0x00000001
+
+//TDFFR0 register
+#define ETHER_TDFFR_TDLF         0x00000001
+
+//RDFFR0 register
+#define ETHER_RDFFR0_RDLF        0x00000001
+
+//FDR0 register
+#define ETHER_FDR0_TFD           0x00000700
+#define ETHER_FDR0_RFD           0X0000001F
+
+#define ETHER_FDR0_TFD_2048      0x00000700
+#define ETHER_FDR0_RFD_2048      0x00000007
+
+//RMCR0 register
+#define ETHER_RMCR0_RNC          0x00000001
+
+//FCFTR register
+#define ETHER_FCFTR0_RFF         0x001F0000
+#define ETHER_FCFTR0_RFD         0x000000FF
+
+#define ETHER_FCFTR0_RFF_8       0x00070000
+#define ETHER_FCFTR0_RFD_2048    0x00000007
+
+//Transmit DMA descriptor flags
+#define ETHER_TD0_TACT           0x80000000
+#define ETHER_TD0_TDLE           0x40000000
+#define ETHER_TD0_TFP_SOF        0x20000000
+#define ETHER_TD0_TFP_EOF        0x10000000
+#define ETHER_TD0_TFE            0x08000000
+#define ETHER_TD0_TWBI           0x04000000
+#define ETHER_TD0_TFS_MASK       0x00000300
+#define ETHER_TD0_TFS_TUC        0x00000200
+#define ETHER_TD0_TFS_TABT       0x00000100
+#define ETHER_TD1_TDL            0xFFFF0000
+#define ETHER_TD2_TBA            0xFFFFFFFF
+
+//Receive DMA descriptor flags
+#define ETHER_RD0_RACT           0x80000000
+#define ETHER_RD0_RDLE           0x40000000
+#define ETHER_RD0_RFP_SOF        0x20000000
+#define ETHER_RD0_RFP_EOF        0x10000000
+#define ETHER_RD0_RFE            0x08000000
+#define ETHER_RD0_RCSE           0x04000000
+#define ETHER_RD0_RFS_MASK       0x02DF0000
+#define ETHER_RD0_RFS_RFOF       0x02000000
+#define ETHER_RD0_RFS_RMAF       0x00800000
+#define ETHER_RD0_RFS_RUAF       0x00400000
+#define ETHER_RD0_RFS_RRF        0x00100000
+#define ETHER_RD0_RFS_RTLF       0x00080000
+#define ETHER_RD0_RFS_RTSF       0x00040000
+#define ETHER_RD0_RFS_PRE        0x00020000
+#define ETHER_RD0_RFS_CERF       0x00010000
+#define ETHER_RD0_RCS            0x0000FFFF
+#define ETHER_RD1_RBL            0xFFFF0000
+#define ETHER_RD1_RDL            0x0000FFFF
+#define ETHER_RD2_RBA            0xFFFFFFFF
+
+//Serial Management Interface
+#define SMI_SYNC                 0xFFFFFFFF
+#define SMI_START                0x00000001
+#define SMI_WRITE                0x00000001
+#define SMI_READ                 0x00000002
+#define SMI_TA                   0x00000002
+
+
+/**
+ * @brief Transmit DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t td0;
+   uint32_t td1;
+   uint32_t td2;
+   uint32_t padding;
+} Rza1TxDmaDesc;
+
+
+/**
+ * @brief Receive DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t rd0;
+   uint32_t rd1;
+   uint32_t rd2;
+   uint32_t padding;
+} Rza1RxDmaDesc;
+
+
+//RZ/A1 Ethernet MAC driver
+extern const NicDriver rza1EthDriver;
+
+//RZ/A1 Ethernet MAC related functions
+error_t rza1EthInit(NetInterface *interface);
+void rza1EthInitGpio(NetInterface *interface);
+void rza1EthInitDmaDesc(NetInterface *interface);
+
+void rza1EthTick(NetInterface *interface);
+
+void rza1EthEnableIrq(NetInterface *interface);
+void rza1EthDisableIrq(NetInterface *interface);
+void rza1EthIrqHandler(uint32_t intSense);
+void rza1EthEventHandler(NetInterface *interface);
+
+error_t rza1EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t rza1EthReceivePacket(NetInterface *interface);
+
+error_t rza1EthSetMulticastFilter(NetInterface *interface);
+error_t rza1EthUpdateMacConfig(NetInterface *interface);
+
+void rza1EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t rza1EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+void rza1EthWriteSmi(uint32_t data, uint_t length);
+uint32_t rza1EthReadSmi(uint_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/s7g2_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,786 @@
+/**
+ * @file s7g2_eth.c
+ * @brief Renesas Synergy S7G2 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "bsp_irq_cfg.h"
+#include "r7fs7g2x.h"
+#include "core/net.h"
+#include "drivers/s7g2_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 32
+static uint8_t txBuffer[S7G2_ETH_TX_BUFFER_COUNT][S7G2_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 32
+static uint8_t rxBuffer[S7G2_ETH_RX_BUFFER_COUNT][S7G2_ETH_RX_BUFFER_SIZE];
+//Transmit DMA descriptors
+#pragma data_alignment = 32
+static S7g2TxDmaDesc txDmaDesc[S7G2_ETH_TX_BUFFER_COUNT];
+//Receive DMA descriptors
+#pragma data_alignment = 32
+static S7g2RxDmaDesc rxDmaDesc[S7G2_ETH_RX_BUFFER_COUNT];
+
+//ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[S7G2_ETH_TX_BUFFER_COUNT][S7G2_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(32)));
+//Receive buffer
+static uint8_t rxBuffer[S7G2_ETH_RX_BUFFER_COUNT][S7G2_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(32)));
+//Transmit DMA descriptors
+static S7g2TxDmaDesc txDmaDesc[S7G2_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(32)));
+//Receive DMA descriptors
+static S7g2RxDmaDesc rxDmaDesc[S7G2_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(32)));
+
+#endif
+
+//Current transmit descriptor
+static uint_t txIndex;
+//Current receive descriptor
+static uint_t rxIndex;
+
+
+/**
+ * @brief S7G2 Ethernet MAC driver
+ **/
+
+const NicDriver s7g2EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   s7g2EthInit,
+   s7g2EthTick,
+   s7g2EthEnableIrq,
+   s7g2EthDisableIrq,
+   s7g2EthEventHandler,
+   s7g2EthSendPacket,
+   s7g2EthSetMulticastFilter,
+   s7g2EthUpdateMacConfig,
+   s7g2EthWritePhyReg,
+   s7g2EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   TRUE
+};
+
+
+/**
+ * @brief S7G2 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t s7g2EthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing S7G2 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Disable protection
+   R_SYSTEM->PRCR = 0xA50B;
+   //Cancel EDMAC1 module stop state
+   R_MSTP->MSTPCRB_b.MSTPB14 = 0;
+   //Enable protection
+   R_SYSTEM->PRCR = 0xA500;
+
+   //GPIO configuration
+   s7g2EthInitGpio(interface);
+
+   //Reset EDMAC1 module
+   R_EDMAC1->EDMR_b.SWR = 1;
+   sleep(10);
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Initialize DMA descriptor lists
+   s7g2EthInitDmaDesc(interface);
+
+   //Maximum frame length that can be accepted
+   R_ETHERC1->RFLR = 1518;
+   //Set default inter packet gap (96-bit time)
+   R_ETHERC1->IPGR = 0x14;
+
+   //Set the upper 32 bits of the MAC address
+   R_ETHERC1->MAHR = (interface->macAddr.b[0] << 24) | (interface->macAddr.b[1] << 16) |
+      (interface->macAddr.b[2] << 8) | interface->macAddr.b[3];
+
+   //Set the lower 16 bits of the MAC address
+   R_ETHERC1->MALR_b.MALR = (interface->macAddr.b[4] << 8) | interface->macAddr.b[5];
+
+   //Set descriptor length (16 bytes)
+   R_EDMAC1->EDMR_b.DL = 0;
+   //Select little endian mode
+   R_EDMAC1->EDMR_b.DE = 1;
+   //Use store and forward mode
+   R_EDMAC1->TFTR_b.TFT = 0;
+
+   //Set transmit FIFO size (2048 bytes)
+   R_EDMAC1->FDR_b.TFD = 7;
+   //Set receive FIFO size (2048 bytes)
+   R_EDMAC1->FDR_b.RFD = 7;
+
+   //Enable continuous reception of multiple frames
+   R_EDMAC1->RMCR_b.RNR = 1;
+
+   //Accept transmit interrupt notifications
+   R_EDMAC1->TRIMD_b.TIM = 0;
+   R_EDMAC1->TRIMD_b.TIS = 1;
+
+   //Disable all EDMAC interrupts
+   R_EDMAC1->EESIPR = 0;
+   //Enable only the desired EDMAC interrupts
+   R_EDMAC1->EESIPR_b.TWBIP = 1;
+   R_EDMAC1->EESIPR_b.FRIP = 1;
+
+   //Set priority grouping (4 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(S7G2_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure EDMAC interrupt priority
+   NVIC_SetPriority(ETHER_EINT1_IRQn, NVIC_EncodePriority(S7G2_ETH_IRQ_PRIORITY_GROUPING,
+      S7G2_ETH_IRQ_GROUP_PRIORITY, S7G2_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable transmission and reception
+   R_ETHERC1->ECMR_b.TE = 1;
+   R_ETHERC1->ECMR_b.RE = 1;
+
+   //Instruct the DMA to poll the receive descriptor list
+   R_EDMAC1->EDRRR_b.RR = 1;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//SK-S7G2 evaluation board?
+#if defined(USE_SK_S7G2)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void s7g2EthInitGpio(NetInterface *interface)
+{
+   //Unlock PFS registers
+   R_PMISC->PWPR_b.BOWI = 0;
+   R_PMISC->PWPR_b.PSFWE = 1;
+
+   //Select RMII interface mode
+   R_PMISC->PFENET_b.PHYMODE1 = 0;
+
+   //Configure ET1_MDC (P4_3)
+   R_PFS->P403PFS_b.PMR = 1;
+   R_PFS->P403PFS_b.PSEL = 23;
+
+   //Configure ET1_MDIO (P4_4)
+   R_PFS->P404PFS_b.PMR = 1;
+   R_PFS->P404PFS_b.PSEL = 23;
+
+   //Configure RMII1_TXD_EN (P4_5)
+   R_PFS->P405PFS_b.PMR = 1;
+   R_PFS->P405PFS_b.PSEL = 23;
+
+   //Configure RMII1_TXD1 (P4_6)
+   R_PFS->P406PFS_b.PMR = 1;
+   R_PFS->P406PFS_b.PSEL = 23;
+
+   //Configure RMII1_TXD0 (P7_0)
+   R_PFS->P700PFS_b.PMR = 1;
+   R_PFS->P700PFS_b.PSEL = 23;
+
+   //Configure REF50CK1 (P7_1)
+   R_PFS->P701PFS_b.PMR = 1;
+   R_PFS->P701PFS_b.PSEL = 23;
+
+   //Configure RMII1_RXD0 (P7_2)
+   R_PFS->P702PFS_b.PMR = 1;
+   R_PFS->P702PFS_b.PSEL = 23;
+
+   //Configure RMII1_RXD1 (P7_3)
+   R_PFS->P703PFS_b.PMR = 1;
+   R_PFS->P703PFS_b.PSEL = 23;
+
+   //Configure RMII1_RX_ER (P7_4)
+   R_PFS->P704PFS_b.PMR = 1;
+   R_PFS->P704PFS_b.PSEL = 23;
+
+   //Configure RMII1_CRS_DV (P7_5)
+   R_PFS->P705PFS_b.PMR = 1;
+   R_PFS->P705PFS_b.PSEL = 23;
+
+   //Lock PFS registers
+   R_PMISC->PWPR_b.PSFWE = 0;
+   R_PMISC->PWPR_b.BOWI = 1;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void s7g2EthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX descriptors
+   for(i = 0; i < S7G2_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the application
+      txDmaDesc[i].td0 = 0;
+      //Transmit buffer length
+      txDmaDesc[i].td1 = 0;
+      //Transmit buffer address
+      txDmaDesc[i].td2 = (uint32_t) txBuffer[i];
+      //Clear padding field
+      txDmaDesc[i].padding = 0;
+   }
+
+   //Mark the last descriptor entry with the TDLE flag
+   txDmaDesc[i - 1].td0 |= EDMAC_TD0_TDLE;
+   //Initialize TX descriptor index
+   txIndex = 0;
+
+   //Initialize RX descriptors
+   for(i = 0; i < S7G2_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the DMA
+      rxDmaDesc[i].rd0 = EDMAC_RD0_RACT;
+      //Receive buffer length
+      rxDmaDesc[i].rd1 = (S7G2_ETH_RX_BUFFER_SIZE << 16) & EDMAC_RD1_RBL;
+      //Receive buffer address
+      rxDmaDesc[i].rd2 = (uint32_t) rxBuffer[i];
+      //Clear padding field
+      rxDmaDesc[i].padding = 0;
+   }
+
+   //Mark the last descriptor entry with the RDLE flag
+   rxDmaDesc[i - 1].rd0 |= EDMAC_RD0_RDLE;
+   //Initialize RX descriptor index
+   rxIndex = 0;
+
+   //Start address of the TX descriptor list
+   R_EDMAC1->TDLAR = (uint32_t) txDmaDesc;
+   //Start address of the RX descriptor list
+   R_EDMAC1->RDLAR = (uint32_t) rxDmaDesc;
+}
+
+
+/**
+ * @brief S7G2 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void s7g2EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void s7g2EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(ETHER_EINT1_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void s7g2EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(ETHER_EINT1_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief S7G2 Ethernet MAC interrupt service routine
+ **/
+
+void ETHER_EINT1_IRQHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read interrupt status register
+   status = R_EDMAC1->EESR;
+
+   //A packet has been transmitted?
+   if(status & EDMAC_EESR_TWB)
+   {
+      //Clear TWB interrupt flag
+      R_EDMAC1->EESR = EDMAC_EESR_TWB;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txDmaDesc[txIndex].td0 & EDMAC_TD0_TACT))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & EDMAC_EESR_FR)
+   {
+      //Disable FR interrupts
+      R_EDMAC1->EESIPR_b.FRIP = 0;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Clear IR flag
+   R_ICU->IELSRn_b[ETHER_EINT1_IRQn].IR = 0;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief S7G2 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void s7g2EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(R_EDMAC1->EESR & EDMAC_EESR_FR)
+   {
+      //Clear FR interrupt flag
+      R_EDMAC1->EESR = EDMAC_EESR_FR;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = s7g2EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable EDMAC interrupts
+   R_EDMAC1->EESIPR_b.TWBIP = 1;
+   R_EDMAC1->EESIPR_b.FRIP = 1;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t s7g2EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   //Retrieve the length of the packet
+   size_t length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > S7G2_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txDmaDesc[txIndex].td0 & EDMAC_TD0_TACT)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(txBuffer[txIndex], buffer, offset, length);
+
+   //Write the number of bytes to send
+   txDmaDesc[txIndex].td1 = (length << 16) & EDMAC_TD1_TBL;
+
+   //Check current index
+   if(txIndex < (S7G2_ETH_TX_BUFFER_COUNT - 1))
+   {
+      //Give the ownership of the descriptor to the DMA engine
+      txDmaDesc[txIndex].td0 = EDMAC_TD0_TACT | EDMAC_TD0_TFP_SOF |
+         EDMAC_TD0_TFP_EOF | EDMAC_TD0_TWBI;
+
+      //Point to the next descriptor
+      txIndex++;
+   }
+   else
+   {
+      //Give the ownership of the descriptor to the DMA engine
+      txDmaDesc[txIndex].td0 = EDMAC_TD0_TACT | EDMAC_TD0_TDLE |
+         EDMAC_TD0_TFP_SOF | EDMAC_TD0_TFP_EOF | EDMAC_TD0_TWBI;
+
+      //Wrap around
+      txIndex = 0;
+   }
+
+   //Instruct the DMA to poll the transmit descriptor list
+   R_EDMAC1->EDTRR_b.TR = 1;
+
+   //Check whether the next buffer is available for writing
+   if(!(txDmaDesc[txIndex].td0 & EDMAC_TD0_TACT))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful write operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t s7g2EthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxDmaDesc[rxIndex].rd0 & EDMAC_RD0_RACT))
+   {
+      //SOF and EOF flags should be set
+      if((rxDmaDesc[rxIndex].rd0 & EDMAC_RD0_RFP_SOF) &&
+         (rxDmaDesc[rxIndex].rd0 & EDMAC_RD0_RFP_EOF))
+      {
+         //Make sure no error occurred
+         if(!(rxDmaDesc[rxIndex].rd0 & (EDMAC_RD0_RFS_MASK & ~EDMAC_RD0_RFS_RMAF)))
+         {
+            //Retrieve the length of the frame
+            n = rxDmaDesc[rxIndex].rd1 & EDMAC_RD1_RFL;
+            //Limit the number of data to read
+            n = MIN(n, S7G2_ETH_RX_BUFFER_SIZE);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, rxBuffer[rxIndex], n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Check current index
+      if(rxIndex < (S7G2_ETH_RX_BUFFER_COUNT - 1))
+      {
+         //Give the ownership of the descriptor back to the DMA
+         rxDmaDesc[rxIndex].rd0 = EDMAC_RD0_RACT;
+         //Point to the next descriptor
+         rxIndex++;
+      }
+      else
+      {
+         //Give the ownership of the descriptor back to the DMA
+         rxDmaDesc[rxIndex].rd0 = EDMAC_RD0_RACT | EDMAC_RD0_RDLE;
+         //Wrap around
+         rxIndex = 0;
+      }
+
+      //Instruct the DMA to poll the receive descriptor list
+      R_EDMAC1->EDRRR_b.RR = 1;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t s7g2EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   bool_t acceptMulticast;
+
+   //This flag will be set if multicast addresses should be accepted
+   acceptMulticast = FALSE;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Valid entry?
+      if(interface->macMulticastFilter[i].refCount > 0)
+      {
+         //Accept multicast addresses
+         acceptMulticast = TRUE;
+         //We are done
+         break;
+      }
+   }
+
+   //Enable the reception of multicast frames if necessary
+   if(acceptMulticast)
+      R_EDMAC1->EESR_b.RMAF = 1;
+   else
+      R_EDMAC1->EESR_b.RMAF = 0;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t s7g2EthUpdateMacConfig(NetInterface *interface)
+{
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      R_ETHERC1->ECMR_b.RTM = 1;
+   else
+      R_ETHERC1->ECMR_b.RTM = 0;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      R_ETHERC1->ECMR_b.DM = 1;
+   else
+      R_ETHERC1->ECMR_b.DM = 0;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void s7g2EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   //Synchronization pattern
+   s7g2EthWriteSmi(SMI_SYNC, 32);
+   //Start of frame
+   s7g2EthWriteSmi(SMI_START, 2);
+   //Set up a write operation
+   s7g2EthWriteSmi(SMI_WRITE, 2);
+   //Write PHY address
+   s7g2EthWriteSmi(phyAddr, 5);
+   //Write register address
+   s7g2EthWriteSmi(regAddr, 5);
+   //Turnaround
+   s7g2EthWriteSmi(SMI_TA, 2);
+   //Write register value
+   s7g2EthWriteSmi(data, 16);
+   //Release MDIO
+   s7g2EthReadSmi(1);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t s7g2EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint16_t data;
+
+   //Synchronization pattern
+   s7g2EthWriteSmi(SMI_SYNC, 32);
+   //Start of frame
+   s7g2EthWriteSmi(SMI_START, 2);
+   //Set up a read operation
+   s7g2EthWriteSmi(SMI_READ, 2);
+   //Write PHY address
+   s7g2EthWriteSmi(phyAddr, 5);
+   //Write register address
+   s7g2EthWriteSmi(regAddr, 5);
+   //Turnaround to avoid contention
+   s7g2EthReadSmi(1);
+   //Read register value
+   data = s7g2EthReadSmi(16);
+   //Force the PHY to release the MDIO pin
+   s7g2EthReadSmi(1);
+
+   //Return PHY register contents
+   return data;
+}
+
+
+/**
+ * @brief SMI write operation
+ * @param[in] data Raw data to be written
+ * @param[in] length Number of bits to be written
+ **/
+
+void s7g2EthWriteSmi(uint32_t data, uint_t length)
+{
+   //Skip the most significant bits since they are meaningless
+   data <<= 32 - length;
+
+   //Configure MDIO as an output
+   R_ETHERC1->PIR_b.MMD = 1;
+
+   //Write the specified number of bits
+   while(length--)
+   {
+      //Write MDIO
+      if(data & 0x80000000)
+         R_ETHERC1->PIR_b.MDO = 1;
+      else
+         R_ETHERC1->PIR_b.MDO = 0;
+
+      //Assert MDC
+      usleep(1);
+      R_ETHERC1->PIR_b.MDC = 1;
+      //Deassert MDC
+      usleep(1);
+      R_ETHERC1->PIR_b.MDC = 0;
+
+      //Rotate data
+      data <<= 1;
+   }
+}
+
+
+/**
+ * @brief SMI read operation
+ * @param[in] length Number of bits to be read
+ * @return Data resulting from the MDIO read operation
+ **/
+
+uint32_t s7g2EthReadSmi(uint_t length)
+{
+   uint32_t data = 0;
+
+   //Configure MDIO as an input
+   R_ETHERC1->PIR_b.MMD = 0;
+
+   //Read the specified number of bits
+   while(length--)
+   {
+      //Rotate data
+      data <<= 1;
+
+      //Assert MDC
+      R_ETHERC1->PIR_b.MDC = 1;
+      usleep(1);
+      //Deassert MDC
+      R_ETHERC1->PIR_b.MDC = 0;
+      usleep(1);
+
+      //Check MDIO state
+      if(R_ETHERC1->PIR_b.MDI)
+         data |= 0x00000001;
+   }
+
+   //Return the received data
+   return data;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/s7g2_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,206 @@
+/**
+ * @file s7g2_eth.h
+ * @brief Renesas Synergy S7G2 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _S7G2_ETH_H
+#define _S7G2_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef S7G2_ETH_TX_BUFFER_COUNT
+   #define S7G2_ETH_TX_BUFFER_COUNT 3
+#elif (S7G2_ETH_TX_BUFFER_COUNT < 1)
+   #error S7G2_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef S7G2_ETH_TX_BUFFER_SIZE
+   #define S7G2_ETH_TX_BUFFER_SIZE 1536
+#elif (S7G2_ETH_TX_BUFFER_SIZE != 1536)
+   #error S7G2_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef S7G2_ETH_RX_BUFFER_COUNT
+   #define S7G2_ETH_RX_BUFFER_COUNT 6
+#elif (S7G2_ETH_RX_BUFFER_COUNT < 1)
+   #error S7G2_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef S7G2_ETH_RX_BUFFER_SIZE
+   #define S7G2_ETH_RX_BUFFER_SIZE 1536
+#elif (S7G2_ETH_RX_BUFFER_SIZE != 1536)
+   #error S7G2_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef S7G2_ETH_IRQ_PRIORITY_GROUPING
+   #define S7G2_ETH_IRQ_PRIORITY_GROUPING 3
+#elif (S7G2_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error S7G2_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef S7G2_ETH_IRQ_GROUP_PRIORITY
+   #define S7G2_ETH_IRQ_GROUP_PRIORITY 12
+#elif (S7G2_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error S7G2_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef S7G2_ETH_IRQ_SUB_PRIORITY
+   #define S7G2_ETH_IRQ_SUB_PRIORITY 0
+#elif (S7G2_ETH_IRQ_SUB_PRIORITY < 0)
+   #error S7G2_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//EESR register
+#define EDMAC_EESR_TWB     0x40000000
+#define EDMAC_EESR_TABT    0x04000000
+#define EDMAC_EESR_RABT    0x02000000
+#define EDMAC_EESR_RFCOF   0x01000000
+#define EDMAC_EESR_ADE     0x00800000
+#define EDMAC_EESR_ECI     0x00400000
+#define EDMAC_EESR_TC      0x00200000
+#define EDMAC_EESR_TDE     0x00100000
+#define EDMAC_EESR_TFUF    0x00080000
+#define EDMAC_EESR_FR      0x00040000
+#define EDMAC_EESR_RDE     0x00020000
+#define EDMAC_EESR_RFOF    0x00010000
+#define EDMAC_EESR_CND     0x00000800
+#define EDMAC_EESR_DLC     0x00000400
+#define EDMAC_EESR_CD      0x00000200
+#define EDMAC_EESR_TRO     0x00000100
+#define EDMAC_EESR_RMAF    0x00000080
+#define EDMAC_EESR_RRF     0x00000010
+#define EDMAC_EESR_RTLF    0x00000008
+#define EDMAC_EESR_RTSF    0x00000004
+#define EDMAC_EESR_PRE     0x00000002
+#define EDMAC_EESR_CERF    0x00000001
+
+//Transmit DMA descriptor flags
+#define EDMAC_TD0_TACT     0x80000000
+#define EDMAC_TD0_TDLE     0x40000000
+#define EDMAC_TD0_TFP_SOF  0x20000000
+#define EDMAC_TD0_TFP_EOF  0x10000000
+#define EDMAC_TD0_TFE      0x08000000
+#define EDMAC_TD0_TWBI     0x04000000
+#define EDMAC_TD0_TFS_MASK 0x0000010F
+#define EDMAC_TD0_TFS_TABT 0x00000100
+#define EDMAC_TD0_TFS_CND  0x00000008
+#define EDMAC_TD0_TFS_DLC  0x00000004
+#define EDMAC_TD0_TFS_CD   0x00000002
+#define EDMAC_TD0_TFS_TRO  0x00000001
+#define EDMAC_TD1_TBL      0xFFFF0000
+#define EDMAC_TD2_TBA      0xFFFFFFFF
+
+//Receive DMA descriptor flags
+#define EDMAC_RD0_RACT     0x80000000
+#define EDMAC_RD0_RDLE     0x40000000
+#define EDMAC_RD0_RFP_SOF  0x20000000
+#define EDMAC_RD0_RFP_EOF  0x10000000
+#define EDMAC_RD0_RFE      0x08000000
+#define EDMAC_RD0_RFS_MASK 0x0000039F
+#define EDMAC_RD0_RFS_RFOF 0x00000200
+#define EDMAC_RD0_RFS_RABT 0x00000100
+#define EDMAC_RD0_RFS_RMAF 0x00000080
+#define EDMAC_RD0_RFS_RRF  0x00000010
+#define EDMAC_RD0_RFS_RTLF 0x00000008
+#define EDMAC_RD0_RFS_RTSF 0x00000004
+#define EDMAC_RD0_RFS_PRE  0x00000002
+#define EDMAC_RD0_RFS_CERF 0x00000001
+#define EDMAC_RD1_RBL      0xFFFF0000
+#define EDMAC_RD1_RFL      0x0000FFFF
+#define EDMAC_RD2_RBA      0xFFFFFFFF
+
+//Serial Management Interface
+#define SMI_SYNC           0xFFFFFFFF
+#define SMI_START          0x00000001
+#define SMI_WRITE          0x00000001
+#define SMI_READ           0x00000002
+#define SMI_TA             0x00000002
+
+
+/**
+ * @brief Transmit DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t td0;
+   uint32_t td1;
+   uint32_t td2;
+   uint32_t padding;
+} S7g2TxDmaDesc;
+
+
+/**
+ * @brief Receive DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t rd0;
+   uint32_t rd1;
+   uint32_t rd2;
+   uint32_t padding;
+} S7g2RxDmaDesc;
+
+
+//S7G2 Ethernet MAC driver
+extern const NicDriver s7g2EthDriver;
+
+//S7G2 Ethernet MAC related functions
+error_t s7g2EthInit(NetInterface *interface);
+void s7g2EthInitGpio(NetInterface *interface);
+void s7g2EthInitDmaDesc(NetInterface *interface);
+
+void s7g2EthTick(NetInterface *interface);
+
+void s7g2EthEnableIrq(NetInterface *interface);
+void s7g2EthDisableIrq(NetInterface *interface);
+void s7g2EthEventHandler(NetInterface *interface);
+
+error_t s7g2EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t s7g2EthReceivePacket(NetInterface *interface);
+
+error_t s7g2EthSetMulticastFilter(NetInterface *interface);
+error_t s7g2EthUpdateMacConfig(NetInterface *interface);
+
+void s7g2EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t s7g2EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+void s7g2EthWriteSmi(uint32_t data, uint_t length);
+uint32_t s7g2EthReadSmi(uint_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/sam3x_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,723 @@
+/**
+ * @file sam3x_eth.c
+ * @brief SAM3X Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include <limits.h>
+#include "sam3xa.h"
+#include "core/net.h"
+#include "drivers/sam3x_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//TX buffer
+#pragma data_alignment = 8
+static uint8_t txBuffer[SAM3X_ETH_TX_BUFFER_COUNT][SAM3X_ETH_TX_BUFFER_SIZE];
+//RX buffer
+#pragma data_alignment = 8
+static uint8_t rxBuffer[SAM3X_ETH_RX_BUFFER_COUNT][SAM3X_ETH_RX_BUFFER_SIZE];
+//TX buffer descriptors
+#pragma data_alignment = 4
+static Sam3xTxBufferDesc txBufferDesc[SAM3X_ETH_TX_BUFFER_COUNT];
+//RX buffer descriptors
+#pragma data_alignment = 4
+static Sam3xRxBufferDesc rxBufferDesc[SAM3X_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//TX buffer
+static uint8_t txBuffer[SAM3X_ETH_TX_BUFFER_COUNT][SAM3X_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(8)));
+//RX buffer
+static uint8_t rxBuffer[SAM3X_ETH_RX_BUFFER_COUNT][SAM3X_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(8)));
+//TX buffer descriptors
+static Sam3xTxBufferDesc txBufferDesc[SAM3X_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//RX buffer descriptors
+static Sam3xRxBufferDesc rxBufferDesc[SAM3X_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+
+#endif
+
+//TX buffer index
+static uint_t txBufferIndex;
+//RX buffer index
+static uint_t rxBufferIndex;
+
+
+/**
+ * @brief SAM3X Ethernet MAC driver
+ **/
+
+const NicDriver sam3xEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   sam3xEthInit,
+   sam3xEthTick,
+   sam3xEthEnableIrq,
+   sam3xEthDisableIrq,
+   sam3xEthEventHandler,
+   sam3xEthSendPacket,
+   sam3xEthSetMulticastFilter,
+   sam3xEthUpdateMacConfig,
+   sam3xEthWritePhyReg,
+   sam3xEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief SAM3X Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sam3xEthInit(NetInterface *interface)
+{
+   error_t error;
+   volatile uint32_t status;
+
+   //Debug message
+   TRACE_INFO("Initializing SAM3X Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Enable EMAC peripheral clock
+   PMC->PMC_PCER1 = (1 << (ID_EMAC - 32));
+
+   //GPIO configuration
+   sam3xEthInitGpio(interface);
+
+   //Configure MDC clock speed
+   EMAC->EMAC_NCFGR = EMAC_NCFGR_CLK_MCK_64;
+   //Enable management port (MDC and MDIO)
+   EMAC->EMAC_NCR |= EMAC_NCR_MPE;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Set the MAC address
+   EMAC->EMAC_SA[0].EMAC_SAxB = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   EMAC->EMAC_SA[0].EMAC_SAxT = interface->macAddr.w[2];
+
+   //Configure the receive filter
+   EMAC->EMAC_NCFGR |= EMAC_NCFGR_UNI | EMAC_NCFGR_MTI;
+
+   //Initialize hash table
+   EMAC->EMAC_HRB = 0;
+   EMAC->EMAC_HRT = 0;
+
+   //Initialize buffer descriptors
+   sam3xEthInitBufferDesc(interface);
+
+   //Clear transmit status register
+   EMAC->EMAC_TSR = EMAC_TSR_UND | EMAC_TSR_COMP | EMAC_TSR_BEX |
+      EMAC_TSR_TGO | EMAC_TSR_RLES | EMAC_TSR_COL | EMAC_TSR_UBR;
+   //Clear receive status register
+   EMAC->EMAC_RSR = EMAC_RSR_OVR | EMAC_RSR_REC | EMAC_RSR_BNA;
+
+   //First disable all EMAC interrupts
+   EMAC->EMAC_IDR = 0xFFFFFFFF;
+   //Only the desired ones are enabled
+   EMAC->EMAC_IER = EMAC_IER_ROVR | EMAC_IER_TCOMP | EMAC_IER_TXERR |
+      EMAC_IER_RLE | EMAC_IER_TUND | EMAC_IER_RXUBR | EMAC_IER_RCOMP;
+
+   //Read EMAC ISR register to clear any pending interrupt
+   status = EMAC->EMAC_ISR;
+
+   //Set priority grouping (4 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(SAM3X_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure EMAC interrupt priority
+   NVIC_SetPriority(EMAC_IRQn, NVIC_EncodePriority(SAM3X_ETH_IRQ_PRIORITY_GROUPING,
+      SAM3X_ETH_IRQ_GROUP_PRIORITY, SAM3X_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable the EMAC to transmit and receive data
+   EMAC->EMAC_NCR |= EMAC_NCR_TE | EMAC_NCR_RE;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//SAM3X-EK evaluation board?
+#if defined(USE_SAM3X_EK)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam3xEthInitGpio(NetInterface *interface)
+{
+   //Enable PIO peripheral clock
+   PMC->PMC_PCER0 = (1 << ID_PIOB);
+
+   //Disable pull-up resistors on RMII pins
+   PIOB->PIO_PUDR = EMAC_RMII_MASK;
+   //Disable interrupts-on-change
+   PIOB->PIO_IDR = EMAC_RMII_MASK;
+   //Assign RMII pins to peripheral A function
+   PIOB->PIO_ABSR &= ~EMAC_RMII_MASK;
+   //Disable the PIO from controlling the corresponding pins
+   PIOB->PIO_PDR = EMAC_RMII_MASK;
+
+   //Select RMII operation mode and enable transceiver clock
+   EMAC->EMAC_USRIO = EMAC_USRIO_CLKEN | EMAC_USRIO_RMII;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize buffer descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam3xEthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+   uint32_t address;
+
+   //Initialize TX buffer descriptors
+   for(i = 0; i < SAM3X_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current TX buffer
+      address = (uint32_t) txBuffer[i];
+      //Write the address to the descriptor entry
+      txBufferDesc[i].address = address;
+      //Initialize status field
+      txBufferDesc[i].status = EMAC_TX_USED;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   txBufferDesc[i - 1].status |= EMAC_TX_WRAP;
+   //Initialize TX buffer index
+   txBufferIndex = 0;
+
+   //Initialize RX buffer descriptors
+   for(i = 0; i < SAM3X_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current RX buffer
+      address = (uint32_t) rxBuffer[i];
+      //Write the address to the descriptor entry
+      rxBufferDesc[i].address = address & EMAC_RX_ADDRESS;
+      //Clear status field
+      rxBufferDesc[i].status = 0;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   rxBufferDesc[i - 1].address |= EMAC_RX_WRAP;
+   //Initialize RX buffer index
+   rxBufferIndex = 0;
+
+   //Start location of the TX descriptor list
+   EMAC->EMAC_TBQP = (uint32_t) txBufferDesc;
+   //Start location of the RX descriptor list
+   EMAC->EMAC_RBQP = (uint32_t) rxBufferDesc;
+}
+
+
+/**
+ * @brief SAM3X Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam3xEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam3xEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(EMAC_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam3xEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(EMAC_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief SAM3X Ethernet MAC interrupt service routine
+ **/
+
+void EMAC_Handler(void)
+{
+   bool_t flag;
+   volatile uint32_t isr;
+   volatile uint32_t tsr;
+   volatile uint32_t rsr;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Each time the software reads EMAC_ISR, it has to check the
+   //contents of EMAC_TSR, EMAC_RSR and EMAC_NSR
+   isr = EMAC->EMAC_ISR;
+   tsr = EMAC->EMAC_TSR;
+   rsr = EMAC->EMAC_RSR;
+
+   //A packet has been transmitted?
+   if(tsr & (EMAC_TSR_UND | EMAC_TSR_COMP | EMAC_TSR_BEX |
+      EMAC_TSR_TGO | EMAC_TSR_RLES | EMAC_TSR_COL | EMAC_TSR_UBR))
+   {
+      //Only clear TSR flags that are currently set
+      EMAC->EMAC_TSR = tsr;
+
+      //Check whether the TX buffer is available for writing
+      if(txBufferDesc[txBufferIndex].status & EMAC_TX_USED)
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(rsr & (EMAC_RSR_OVR | EMAC_RSR_REC | EMAC_RSR_BNA))
+   {
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief SAM3X Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam3xEthEventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint32_t rsr;
+
+   //Read receive status
+   rsr = EMAC->EMAC_RSR;
+
+   //Packet received?
+   if(rsr & (EMAC_RSR_OVR | EMAC_RSR_REC | EMAC_RSR_BNA))
+   {
+      //Only clear RSR flags that are currently set
+      EMAC->EMAC_RSR = rsr;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = sam3xEthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t sam3xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > SAM3X_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(!(txBufferDesc[txBufferIndex].status & EMAC_TX_USED))
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(txBuffer[txBufferIndex], buffer, offset, length);
+
+   //Set the necessary flags in the descriptor entry
+   if(txBufferIndex < (SAM3X_ETH_TX_BUFFER_COUNT - 1))
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status =
+         EMAC_TX_LAST | (length & EMAC_TX_LENGTH);
+
+      //Point to the next buffer
+      txBufferIndex++;
+   }
+   else
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status = EMAC_TX_WRAP |
+         EMAC_TX_LAST | (length & EMAC_TX_LENGTH);
+
+      //Wrap around
+      txBufferIndex = 0;
+   }
+
+   //Set the TSTART bit to initiate transmission
+   EMAC->EMAC_NCR |= EMAC_NCR_TSTART;
+
+   //Check whether the next buffer is available for writing
+   if(txBufferDesc[txBufferIndex].status & EMAC_TX_USED)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sam3xEthReceivePacket(NetInterface *interface)
+{
+   static uint8_t temp[ETH_MAX_FRAME_SIZE];
+   error_t error;
+   uint_t i;
+   uint_t j;
+   uint_t sofIndex;
+   uint_t eofIndex;
+   size_t n;
+   size_t size;
+   size_t length;
+
+   //Initialize SOF and EOF indices
+   sofIndex = UINT_MAX;
+   eofIndex = UINT_MAX;
+
+   //Search for SOF and EOF flags
+   for(i = 0; i < SAM3X_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Point to the current entry
+      j = rxBufferIndex + i;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(j >= SAM3X_ETH_RX_BUFFER_COUNT)
+         j -= SAM3X_ETH_RX_BUFFER_COUNT;
+
+      //No more entries to process?
+      if(!(rxBufferDesc[j].address & EMAC_RX_OWNERSHIP))
+      {
+         //Stop processing
+         break;
+      }
+      //A valid SOF has been found?
+      if(rxBufferDesc[j].status & EMAC_RX_SOF)
+      {
+         //Save the position of the SOF
+         sofIndex = i;
+      }
+      //A valid EOF has been found?
+      if((rxBufferDesc[j].status & EMAC_RX_EOF) && sofIndex != UINT_MAX)
+      {
+         //Save the position of the EOF
+         eofIndex = i;
+         //Retrieve the length of the frame
+         size = rxBufferDesc[j].status & EMAC_RX_LENGTH;
+         //Limit the number of data to read
+         size = MIN(size, ETH_MAX_FRAME_SIZE);
+         //Stop processing since we have reached the end of the frame
+         break;
+      }
+   }
+
+   //Determine the number of entries to process
+   if(eofIndex != UINT_MAX)
+      j = eofIndex + 1;
+   else if(sofIndex != UINT_MAX)
+      j = sofIndex;
+   else
+      j = i;
+
+   //Total number of bytes that have been copied from the receive buffer
+   length = 0;
+
+   //Process incoming frame
+   for(i = 0; i < j; i++)
+   {
+      //Any data to copy from current buffer?
+      if(eofIndex != UINT_MAX && i >= sofIndex && i <= eofIndex)
+      {
+         //Calculate the number of bytes to read at a time
+         n = MIN(size, SAM3X_ETH_RX_BUFFER_SIZE);
+         //Copy data from receive buffer
+         memcpy(temp + length, rxBuffer[rxBufferIndex], n);
+         //Update byte counters
+         length += n;
+         size -= n;
+      }
+
+      //Mark the current buffer as free
+      rxBufferDesc[rxBufferIndex].address &= ~EMAC_RX_OWNERSHIP;
+
+      //Point to the following entry
+      rxBufferIndex++;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(rxBufferIndex >= SAM3X_ETH_RX_BUFFER_COUNT)
+         rxBufferIndex = 0;
+   }
+
+   //Any packet to process?
+   if(length > 0)
+   {
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, temp, length);
+      //Valid packet received
+      error = NO_ERROR;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sam3xEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint8_t *p;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating SAM3X hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Point to the MAC address
+         p = entry->addr.b;
+
+         //Apply the hash function
+         k = (p[0] >> 6) ^ p[0];
+         k ^= (p[1] >> 4) ^ (p[1] << 2);
+         k ^= (p[2] >> 2) ^ (p[2] << 4);
+         k ^= (p[3] >> 6) ^ p[3];
+         k ^= (p[4] >> 4) ^ (p[4] << 2);
+         k ^= (p[5] >> 2) ^ (p[5] << 4);
+
+         //The hash value is reduced to a 6-bit index
+         k &= 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   EMAC->EMAC_HRB = hashTable[0];
+   EMAC->EMAC_HRT = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HRB = %08" PRIX32 "\r\n", EMAC->EMAC_HRB);
+   TRACE_DEBUG("  HRT = %08" PRIX32 "\r\n", EMAC->EMAC_HRT);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sam3xEthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read network configuration register
+   config = EMAC->EMAC_NCFGR;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= EMAC_NCFGR_SPD;
+   else
+      config &= ~EMAC_NCFGR_SPD;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= EMAC_NCFGR_FD;
+   else
+      config &= ~EMAC_NCFGR_FD;
+
+   //Write configuration value back to NCFGR register
+   EMAC->EMAC_NCFGR = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void sam3xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Set up a write operation
+   value = EMAC_MAN_SOF(1) | EMAC_MAN_RW(1) | EMAC_MAN_CODE(2);
+   //PHY address
+   value |= EMAC_MAN_PHYA(phyAddr);
+   //Register address
+   value |= EMAC_MAN_REGA(regAddr);
+   //Register value
+   value |= EMAC_MAN_DATA(data);
+
+   //Start a write operation
+   EMAC->EMAC_MAN = value;
+   //Wait for the write to complete
+   while(!(EMAC->EMAC_NSR & EMAC_NSR_IDLE));
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t sam3xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Set up a read operation
+   value = EMAC_MAN_SOF(1) | EMAC_MAN_RW(2) | EMAC_MAN_CODE(2);
+   //PHY address
+   value |= EMAC_MAN_PHYA(phyAddr);
+   //Register address
+   value |= EMAC_MAN_REGA(regAddr);
+
+   //Start a read operation
+   EMAC->EMAC_MAN = value;
+   //Wait for the read to complete
+   while(!(EMAC->EMAC_NSR & EMAC_NSR_IDLE));
+
+   //Return PHY register contents
+   return EMAC->EMAC_MAN & EMAC_MAN_DATA_Msk;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/sam3x_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,167 @@
+/**
+ * @file sam3x_eth.h
+ * @brief SAM3X Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SAM3X_ETH_H
+#define _SAM3X_ETH_H
+
+//Number of TX buffers
+#ifndef SAM3X_ETH_TX_BUFFER_COUNT
+   #define SAM3X_ETH_TX_BUFFER_COUNT 2
+#elif (SAM3X_ETH_TX_BUFFER_COUNT < 1)
+   #error SAM3X_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef SAM3X_ETH_TX_BUFFER_SIZE
+   #define SAM3X_ETH_TX_BUFFER_SIZE 1536
+#elif (SAM3X_ETH_TX_BUFFER_SIZE != 1536)
+   #error SAM3X_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef SAM3X_ETH_RX_BUFFER_COUNT
+   #define SAM3X_ETH_RX_BUFFER_COUNT 48
+#elif (SAM3X_ETH_RX_BUFFER_COUNT < 12)
+   #error SAM3X_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef SAM3X_ETH_RX_BUFFER_SIZE
+   #define SAM3X_ETH_RX_BUFFER_SIZE 128
+#elif (SAM3X_ETH_RX_BUFFER_SIZE != 128)
+   #error SAM3X_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef SAM3X_ETH_IRQ_PRIORITY_GROUPING
+   #define SAM3X_ETH_IRQ_PRIORITY_GROUPING 3
+#elif (SAM3X_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error SAM3X_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef SAM3X_ETH_IRQ_GROUP_PRIORITY
+   #define SAM3X_ETH_IRQ_GROUP_PRIORITY 12
+#elif (SAM3X_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error SAM3X_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef SAM3X_ETH_IRQ_SUB_PRIORITY
+   #define SAM3X_ETH_IRQ_SUB_PRIORITY 0
+#elif (SAM3X_ETH_IRQ_SUB_PRIORITY < 0)
+   #error SAM3X_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//RMII signals
+#define EMAC_RMII_MASK (PIO_PB9A_EMDIO | PIO_PB8A_EMDC | \
+   PIO_PB7A_ERXER | PIO_PB6A_ERX1 | PIO_PB5A_ERX0 | PIO_PB4A_ERXDV | \
+   PIO_PB3A_ETX1 | PIO_PB2A_ETX0 | PIO_PB1A_ETXEN | PIO_PB0A_ETXCK)
+
+//TX buffer descriptor flags
+#define EMAC_TX_USED           0x80000000
+#define EMAC_TX_WRAP           0x40000000
+#define EMAC_TX_ERROR          0x20000000
+#define EMAC_TX_UNDERRUN       0x10000000
+#define EMAC_TX_EXHAUSTED      0x08000000
+#define EMAC_TX_NO_CRC         0x00010000
+#define EMAC_TX_LAST           0x00008000
+#define EMAC_TX_LENGTH         0x000007FF
+
+//RX buffer descriptor flags
+#define EMAC_RX_ADDRESS        0xFFFFFFFC
+#define EMAC_RX_WRAP           0x00000002
+#define EMAC_RX_OWNERSHIP      0x00000001
+#define EMAC_RX_BROADCAST      0x80000000
+#define EMAC_RX_MULTICAST_HASH 0x40000000
+#define EMAC_RX_UNICAST_HASH   0x20000000
+#define EMAC_RX_EXT_ADDR       0x10000000
+#define EMAC_RX_SAR1           0x04000000
+#define EMAC_RX_SAR2           0x02000000
+#define EMAC_RX_SAR3           0x01000000
+#define EMAC_RX_SAR4           0x00800000
+#define EMAC_RX_TYPE_ID        0x00400000
+#define EMAC_RX_VLAN_TAG       0x00200000
+#define EMAC_RX_PRIORITY_TAG   0x00100000
+#define EMAC_RX_VLAN_PRIORITY  0x000E0000
+#define EMAC_RX_CFI            0x00010000
+#define EMAC_RX_EOF            0x00008000
+#define EMAC_RX_SOF            0x00004000
+#define EMAC_RX_OFFSET         0x00003000
+#define EMAC_RX_LENGTH         0x00000FFF
+
+
+/**
+ * @brief Transmit buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Sam3xTxBufferDesc;
+
+
+/**
+ * @brief Receive buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Sam3xRxBufferDesc;
+
+
+//SAM3X Ethernet MAC driver
+extern const NicDriver sam3xEthDriver;
+
+//SAM3X Ethernet MAC related functions
+error_t sam3xEthInit(NetInterface *interface);
+void sam3xEthInitGpio(NetInterface *interface);
+void sam3xEthInitBufferDesc(NetInterface *interface);
+
+void sam3xEthTick(NetInterface *interface);
+
+void sam3xEthEnableIrq(NetInterface *interface);
+void sam3xEthDisableIrq(NetInterface *interface);
+void sam3xEthEventHandler(NetInterface *interface);
+
+error_t sam3xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t sam3xEthReceivePacket(NetInterface *interface);
+
+error_t sam3xEthSetMulticastFilter(NetInterface *interface);
+error_t sam3xEthUpdateMacConfig(NetInterface *interface);
+
+void sam3xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t sam3xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/sam4e_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,724 @@
+/**
+ * @file sam4e_eth.c
+ * @brief SAM4E Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include <limits.h>
+#include "sam4e.h"
+#include "core/net.h"
+#include "drivers/sam4e_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//TX buffer
+#pragma data_alignment = 8
+static uint8_t txBuffer[SAM4E_ETH_TX_BUFFER_COUNT][SAM4E_ETH_TX_BUFFER_SIZE];
+//RX buffer
+#pragma data_alignment = 8
+static uint8_t rxBuffer[SAM4E_ETH_RX_BUFFER_COUNT][SAM4E_ETH_RX_BUFFER_SIZE];
+//TX buffer descriptors
+#pragma data_alignment = 4
+static Sam4eTxBufferDesc txBufferDesc[SAM4E_ETH_TX_BUFFER_COUNT];
+//RX buffer descriptors
+#pragma data_alignment = 4
+static Sam4eRxBufferDesc rxBufferDesc[SAM4E_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//TX buffer
+static uint8_t txBuffer[SAM4E_ETH_TX_BUFFER_COUNT][SAM4E_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(8)));
+//RX buffer
+static uint8_t rxBuffer[SAM4E_ETH_RX_BUFFER_COUNT][SAM4E_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(8)));
+//TX buffer descriptors
+static Sam4eTxBufferDesc txBufferDesc[SAM4E_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//RX buffer descriptors
+static Sam4eRxBufferDesc rxBufferDesc[SAM4E_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+
+#endif
+
+//TX buffer index
+static uint_t txBufferIndex;
+//RX buffer index
+static uint_t rxBufferIndex;
+
+
+/**
+ * @brief SAM4E Ethernet MAC driver
+ **/
+
+const NicDriver sam4eEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   sam4eEthInit,
+   sam4eEthTick,
+   sam4eEthEnableIrq,
+   sam4eEthDisableIrq,
+   sam4eEthEventHandler,
+   sam4eEthSendPacket,
+   sam4eEthSetMulticastFilter,
+   sam4eEthUpdateMacConfig,
+   sam4eEthWritePhyReg,
+   sam4eEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief SAM4E Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sam4eEthInit(NetInterface *interface)
+{
+   error_t error;
+   volatile uint32_t status;
+
+   //Debug message
+   TRACE_INFO("Initializing SAM4E Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Enable GMAC peripheral clock
+   PMC->PMC_PCER1 = (1 << (ID_GMAC - 32));
+
+   //GPIO configuration
+   sam4eEthInitGpio(interface);
+
+   //Configure MDC clock speed
+   GMAC->GMAC_NCFGR = GMAC_NCFGR_CLK_MCK_96;
+   //Enable management port (MDC and MDIO)
+   GMAC->GMAC_NCR |= GMAC_NCR_MPE;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Set the MAC address
+   GMAC->GMAC_SA[0].GMAC_SAB = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   GMAC->GMAC_SA[0].GMAC_SAT = interface->macAddr.w[2];
+
+   //Configure the receive filter
+   GMAC->GMAC_NCFGR |= GMAC_NCFGR_UNIHEN | GMAC_NCFGR_MTIHEN;
+
+   //Initialize hash table
+   GMAC->GMAC_HRB = 0;
+   GMAC->GMAC_HRT = 0;
+
+   //Initialize buffer descriptors
+   sam4eEthInitBufferDesc(interface);
+
+   //Clear transmit status register
+   GMAC->GMAC_TSR = GMAC_TSR_HRESP | GMAC_TSR_UND | GMAC_TSR_TXCOMP | GMAC_TSR_TFC |
+      GMAC_TSR_TXGO | GMAC_TSR_RLE | GMAC_TSR_COL | GMAC_TSR_UBR;
+   //Clear receive status register
+   GMAC->GMAC_RSR = GMAC_RSR_HNO | GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA;
+
+   //First disable all GMAC interrupts
+   GMAC->GMAC_IDR = 0xFFFFFFFF;
+   //Only the desired ones are enabled
+   GMAC->GMAC_IER = GMAC_IER_HRESP | GMAC_IER_ROVR | GMAC_IER_TCOMP | GMAC_IER_TFC |
+      GMAC_IER_RLEX | GMAC_IER_TUR | GMAC_IER_RXUBR | GMAC_IER_RCOMP;
+
+   //Read GMAC ISR register to clear any pending interrupt
+   status = GMAC->GMAC_ISR;
+
+   //Set priority grouping (4 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(SAM4E_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure GMAC interrupt priority
+   NVIC_SetPriority(GMAC_IRQn, NVIC_EncodePriority(SAM4E_ETH_IRQ_PRIORITY_GROUPING,
+      SAM4E_ETH_IRQ_GROUP_PRIORITY, SAM4E_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable the GMAC to transmit and receive data
+   GMAC->GMAC_NCR |= GMAC_NCR_TXEN | GMAC_NCR_RXEN;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//SAM4E-EK or SAM4E-Xplained-Pro evaluation board?
+#if defined(USE_SAM4E_EK) || defined(USE_SAM4E_XPLAINED_PRO)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam4eEthInitGpio(NetInterface *interface)
+{
+   //Enable PIO peripheral clock
+   PMC->PMC_PCER0 = (1 << ID_PIOD);
+
+   //Disable pull-up resistors on MII pins
+   PIOD->PIO_PUDR = GMAC_MII_MASK;
+   //Disable interrupts-on-change
+   PIOD->PIO_IDR = GMAC_MII_MASK;
+   //Assign MII pins to peripheral A function
+   PIOD->PIO_ABCDSR[0] &= ~GMAC_MII_MASK;
+   PIOD->PIO_ABCDSR[1] &= ~GMAC_MII_MASK;
+   //Disable the PIO from controlling the corresponding pins
+   PIOD->PIO_PDR = GMAC_MII_MASK;
+
+   //Select MII operation mode
+   GMAC->GMAC_UR = GMAC_UR_RMIIMII;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize buffer descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam4eEthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+   uint32_t address;
+
+   //Initialize TX buffer descriptors
+   for(i = 0; i < SAM4E_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current TX buffer
+      address = (uint32_t) txBuffer[i];
+      //Write the address to the descriptor entry
+      txBufferDesc[i].address = address;
+      //Initialize status field
+      txBufferDesc[i].status = GMAC_TX_USED;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   txBufferDesc[i - 1].status |= GMAC_TX_WRAP;
+   //Initialize TX buffer index
+   txBufferIndex = 0;
+
+   //Initialize RX buffer descriptors
+   for(i = 0; i < SAM4E_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current RX buffer
+      address = (uint32_t) rxBuffer[i];
+      //Write the address to the descriptor entry
+      rxBufferDesc[i].address = address & GMAC_RX_ADDRESS;
+      //Clear status field
+      rxBufferDesc[i].status = 0;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   rxBufferDesc[i - 1].address |= GMAC_RX_WRAP;
+   //Initialize RX buffer index
+   rxBufferIndex = 0;
+
+   //Start location of the TX descriptor list
+   GMAC->GMAC_TBQB = (uint32_t) txBufferDesc;
+   //Start location of the RX descriptor list
+   GMAC->GMAC_RBQB = (uint32_t) rxBufferDesc;
+}
+
+
+/**
+ * @brief SAM4E Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam4eEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam4eEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(GMAC_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam4eEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(GMAC_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief SAM4E Ethernet MAC interrupt service routine
+ **/
+
+void GMAC_Handler(void)
+{
+   bool_t flag;
+   volatile uint32_t isr;
+   volatile uint32_t tsr;
+   volatile uint32_t rsr;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Each time the software reads GMAC_ISR, it has to check the
+   //contents of GMAC_TSR, GMAC_RSR and GMAC_NSR
+   isr = GMAC->GMAC_ISR;
+   tsr = GMAC->GMAC_TSR;
+   rsr = GMAC->GMAC_RSR;
+
+   //A packet has been transmitted?
+   if(tsr & (GMAC_TSR_HRESP | GMAC_TSR_UND | GMAC_TSR_TXCOMP | GMAC_TSR_TFC |
+      GMAC_TSR_TXGO | GMAC_TSR_RLE | GMAC_TSR_COL | GMAC_TSR_UBR))
+   {
+      //Only clear TSR flags that are currently set
+      GMAC->GMAC_TSR = tsr;
+
+      //Check whether the TX buffer is available for writing
+      if(txBufferDesc[txBufferIndex].status & GMAC_TX_USED)
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(rsr & (GMAC_RSR_HNO | GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA))
+   {
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief SAM4E Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam4eEthEventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint32_t rsr;
+
+   //Read receive status
+   rsr = GMAC->GMAC_RSR;
+
+   //Packet received?
+   if(rsr & (GMAC_RSR_HNO | GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA))
+   {
+      //Only clear RSR flags that are currently set
+      GMAC->GMAC_RSR = rsr;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = sam4eEthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t sam4eEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > SAM4E_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(!(txBufferDesc[txBufferIndex].status & GMAC_TX_USED))
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(txBuffer[txBufferIndex], buffer, offset, length);
+
+   //Set the necessary flags in the descriptor entry
+   if(txBufferIndex < (SAM4E_ETH_TX_BUFFER_COUNT - 1))
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status =
+         GMAC_TX_LAST | (length & GMAC_TX_LENGTH);
+
+      //Point to the next buffer
+      txBufferIndex++;
+   }
+   else
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status = GMAC_TX_WRAP |
+         GMAC_TX_LAST | (length & GMAC_TX_LENGTH);
+
+      //Wrap around
+      txBufferIndex = 0;
+   }
+
+   //Set the TSTART bit to initiate transmission
+   GMAC->GMAC_NCR |= GMAC_NCR_TSTART;
+
+   //Check whether the next buffer is available for writing
+   if(txBufferDesc[txBufferIndex].status & GMAC_TX_USED)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sam4eEthReceivePacket(NetInterface *interface)
+{
+   static uint8_t temp[ETH_MAX_FRAME_SIZE];
+   error_t error;
+   uint_t i;
+   uint_t j;
+   uint_t sofIndex;
+   uint_t eofIndex;
+   size_t n;
+   size_t size;
+   size_t length;
+
+   //Initialize SOF and EOF indices
+   sofIndex = UINT_MAX;
+   eofIndex = UINT_MAX;
+
+   //Search for SOF and EOF flags
+   for(i = 0; i < SAM4E_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Point to the current entry
+      j = rxBufferIndex + i;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(j >= SAM4E_ETH_RX_BUFFER_COUNT)
+         j -= SAM4E_ETH_RX_BUFFER_COUNT;
+
+      //No more entries to process?
+      if(!(rxBufferDesc[j].address & GMAC_RX_OWNERSHIP))
+      {
+         //Stop processing
+         break;
+      }
+      //A valid SOF has been found?
+      if(rxBufferDesc[j].status & GMAC_RX_SOF)
+      {
+         //Save the position of the SOF
+         sofIndex = i;
+      }
+      //A valid EOF has been found?
+      if((rxBufferDesc[j].status & GMAC_RX_EOF) && sofIndex != UINT_MAX)
+      {
+         //Save the position of the EOF
+         eofIndex = i;
+         //Retrieve the length of the frame
+         size = rxBufferDesc[j].status & GMAC_RX_LENGTH;
+         //Limit the number of data to read
+         size = MIN(size, ETH_MAX_FRAME_SIZE);
+         //Stop processing since we have reached the end of the frame
+         break;
+      }
+   }
+
+   //Determine the number of entries to process
+   if(eofIndex != UINT_MAX)
+      j = eofIndex + 1;
+   else if(sofIndex != UINT_MAX)
+      j = sofIndex;
+   else
+      j = i;
+
+   //Total number of bytes that have been copied from the receive buffer
+   length = 0;
+
+   //Process incoming frame
+   for(i = 0; i < j; i++)
+   {
+      //Any data to copy from current buffer?
+      if(eofIndex != UINT_MAX && i >= sofIndex && i <= eofIndex)
+      {
+         //Calculate the number of bytes to read at a time
+         n = MIN(size, SAM4E_ETH_RX_BUFFER_SIZE);
+         //Copy data from receive buffer
+         memcpy(temp + length, rxBuffer[rxBufferIndex], n);
+         //Update byte counters
+         length += n;
+         size -= n;
+      }
+
+      //Mark the current buffer as free
+      rxBufferDesc[rxBufferIndex].address &= ~GMAC_RX_OWNERSHIP;
+
+      //Point to the following entry
+      rxBufferIndex++;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(rxBufferIndex >= SAM4E_ETH_RX_BUFFER_COUNT)
+         rxBufferIndex = 0;
+   }
+
+   //Any packet to process?
+   if(length > 0)
+   {
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, temp, length);
+      //Valid packet received
+      error = NO_ERROR;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sam4eEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint8_t *p;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating SAM4E hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Point to the MAC address
+         p = entry->addr.b;
+
+         //Apply the hash function
+         k = (p[0] >> 6) ^ p[0];
+         k ^= (p[1] >> 4) ^ (p[1] << 2);
+         k ^= (p[2] >> 2) ^ (p[2] << 4);
+         k ^= (p[3] >> 6) ^ p[3];
+         k ^= (p[4] >> 4) ^ (p[4] << 2);
+         k ^= (p[5] >> 2) ^ (p[5] << 4);
+
+         //The hash value is reduced to a 6-bit index
+         k &= 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   GMAC->GMAC_HRB = hashTable[0];
+   GMAC->GMAC_HRT = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HRB = %08" PRIX32 "\r\n", GMAC->GMAC_HRB);
+   TRACE_DEBUG("  HRT = %08" PRIX32 "\r\n", GMAC->GMAC_HRT);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sam4eEthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read network configuration register
+   config = GMAC->GMAC_NCFGR;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= GMAC_NCFGR_SPD;
+   else
+      config &= ~GMAC_NCFGR_SPD;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= GMAC_NCFGR_FD;
+   else
+      config &= ~GMAC_NCFGR_FD;
+
+   //Write configuration value back to NCFGR register
+   GMAC->GMAC_NCFGR = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void sam4eEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Set up a write operation
+   value = GMAC_MAN_CLTTO | GMAC_MAN_OP(1) | GMAC_MAN_WTN(2);
+   //PHY address
+   value |= GMAC_MAN_PHYA(phyAddr);
+   //Register address
+   value |= GMAC_MAN_REGA(regAddr);
+   //Register value
+   value |= GMAC_MAN_DATA(data);
+
+   //Start a write operation
+   GMAC->GMAC_MAN = value;
+   //Wait for the write to complete
+   while(!(GMAC->GMAC_NSR & GMAC_NSR_IDLE));
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t sam4eEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Set up a read operation
+   value = GMAC_MAN_CLTTO | GMAC_MAN_OP(2) | GMAC_MAN_WTN(2);
+   //PHY address
+   value |= GMAC_MAN_PHYA(phyAddr);
+   //Register address
+   value |= GMAC_MAN_REGA(regAddr);
+
+   //Start a read operation
+   GMAC->GMAC_MAN = value;
+   //Wait for the read to complete
+   while(!(GMAC->GMAC_NSR & GMAC_NSR_IDLE));
+
+   //Return PHY register contents
+   return GMAC->GMAC_MAN & GMAC_MAN_DATA_Msk;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/sam4e_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,177 @@
+/**
+ * @file sam4e_eth.h
+ * @brief SAM4E Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SAM4E_ETH_H
+#define _SAM4E_ETH_H
+
+//Number of TX buffers
+#ifndef SAM4E_ETH_TX_BUFFER_COUNT
+   #define SAM4E_ETH_TX_BUFFER_COUNT 2
+#elif (SAM4E_ETH_TX_BUFFER_COUNT < 1)
+   #error SAM4E_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef SAM4E_ETH_TX_BUFFER_SIZE
+   #define SAM4E_ETH_TX_BUFFER_SIZE 1536
+#elif (SAM4E_ETH_TX_BUFFER_SIZE != 1536)
+   #error SAM4E_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef SAM4E_ETH_RX_BUFFER_COUNT
+   #define SAM4E_ETH_RX_BUFFER_COUNT 48
+#elif (SAM4E_ETH_RX_BUFFER_COUNT < 12)
+   #error SAM4E_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef SAM4E_ETH_RX_BUFFER_SIZE
+   #define SAM4E_ETH_RX_BUFFER_SIZE 128
+#elif (SAM4E_ETH_RX_BUFFER_SIZE != 128)
+   #error SAM4E_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef SAM4E_ETH_IRQ_PRIORITY_GROUPING
+   #define SAM4E_ETH_IRQ_PRIORITY_GROUPING 3
+#elif (SAM4E_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error SAM4E_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef SAM4E_ETH_IRQ_GROUP_PRIORITY
+   #define SAM4E_ETH_IRQ_GROUP_PRIORITY 12
+#elif (SAM4E_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error SAM4E_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef SAM4E_ETH_IRQ_SUB_PRIORITY
+   #define SAM4E_ETH_IRQ_SUB_PRIORITY 0
+#elif (SAM4E_ETH_IRQ_SUB_PRIORITY < 0)
+   #error SAM4E_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//Legacy definitions
+#ifndef PIO_PD6A_GRX1
+   #define PIO_PD6A_GRX1 PIO_PD6A_GRX0
+#endif
+
+//MII signals
+#define GMAC_MII_MASK (PIO_PD16A_GTX3 | \
+   PIO_PD15A_GTX2 | PIO_PD14A_GRXCK | PIO_PD13A_GCOL | PIO_PD12A_GRX3 | \
+   PIO_PD11A_GRX2 | PIO_PD10A_GCRS | PIO_PD9A_GMDIO | PIO_PD8A_GMDC | \
+   PIO_PD7A_GRXER | PIO_PD6A_GRX1 | PIO_PD5A_GRX0 | PIO_PD4A_GRXDV | \
+   PIO_PD3A_GTX1 | PIO_PD2A_GTX0 | PIO_PD1A_GTXEN | PIO_PD0A_GTXCK)
+
+//TX buffer descriptor flags
+#define GMAC_TX_USED           0x80000000
+#define GMAC_TX_WRAP           0x40000000
+#define GMAC_TX_RLE_ERROR      0x20000000
+#define GMAC_TX_UNDERRUN_ERROR 0x10000000
+#define GMAC_TX_AHB_ERROR      0x08000000
+#define GMAC_TX_LATE_COL_ERROR 0x04000000
+#define GMAC_TX_CHECKSUM_ERROR 0x00700000
+#define GMAC_TX_NO_CRC         0x00010000
+#define GMAC_TX_LAST           0x00008000
+#define GMAC_TX_LENGTH         0x00003FFF
+
+//RX buffer descriptor flags
+#define GMAC_RX_ADDRESS        0xFFFFFFFC
+#define GMAC_RX_WRAP           0x00000002
+#define GMAC_RX_OWNERSHIP      0x00000001
+#define GMAC_RX_BROADCAST      0x80000000
+#define GMAC_RX_MULTICAST_HASH 0x40000000
+#define GMAC_RX_UNICAST_HASH   0x20000000
+#define GMAC_RX_SAR            0x08000000
+#define GMAC_RX_SAR_MASK       0x06000000
+#define GMAC_RX_TYPE_ID        0x01000000
+#define GMAC_RX_SNAP           0x01000000
+#define GMAC_RX_TYPE_ID_MASK   0x00C00000
+#define GMAC_RX_CHECKSUM_VALID 0x00C00000
+#define GMAC_RX_VLAN_TAG       0x00200000
+#define GMAC_RX_PRIORITY_TAG   0x00100000
+#define GMAC_RX_VLAN_PRIORITY  0x000E0000
+#define GMAC_RX_CFI            0x00010000
+#define GMAC_RX_EOF            0x00008000
+#define GMAC_RX_SOF            0x00004000
+#define GMAC_RX_LENGTH_MSB     0x00002000
+#define GMAC_RX_BAD_FCS        0x00002000
+#define GMAC_RX_LENGTH         0x00001FFF
+
+
+/**
+ * @brief Transmit buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Sam4eTxBufferDesc;
+
+
+/**
+ * @brief Receive buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Sam4eRxBufferDesc;
+
+
+//SAM4E Ethernet MAC driver
+extern const NicDriver sam4eEthDriver;
+
+//SAM4E Ethernet MAC related functions
+error_t sam4eEthInit(NetInterface *interface);
+void sam4eEthInitGpio(NetInterface *interface);
+void sam4eEthInitBufferDesc(NetInterface *interface);
+
+void sam4eEthTick(NetInterface *interface);
+
+void sam4eEthEnableIrq(NetInterface *interface);
+void sam4eEthDisableIrq(NetInterface *interface);
+void sam4eEthEventHandler(NetInterface *interface);
+
+error_t sam4eEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t sam4eEthReceivePacket(NetInterface *interface);
+
+error_t sam4eEthSetMulticastFilter(NetInterface *interface);
+error_t sam4eEthUpdateMacConfig(NetInterface *interface);
+
+void sam4eEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t sam4eEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/sam7x_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,726 @@
+/**
+ * @file sam7x_eth.c
+ * @brief AT91SAM7X Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include <limits.h>
+#include "at91sam7x256.h"
+#include "core/net.h"
+#include "drivers/sam7x_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//TX buffer
+#pragma data_alignment = 8
+static uint8_t txBuffer[SAM7X_ETH_TX_BUFFER_COUNT][SAM7X_ETH_TX_BUFFER_SIZE];
+//RX buffer
+#pragma data_alignment = 8
+static uint8_t rxBuffer[SAM7X_ETH_RX_BUFFER_COUNT][SAM7X_ETH_RX_BUFFER_SIZE];
+//TX buffer descriptors
+#pragma data_alignment = 4
+static Sam7xTxBufferDesc txBufferDesc[SAM7X_ETH_TX_BUFFER_COUNT];
+//RX buffer descriptors
+#pragma data_alignment = 4
+static Sam7xRxBufferDesc rxBufferDesc[SAM7X_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//TX buffer
+static uint8_t txBuffer[SAM7X_ETH_TX_BUFFER_COUNT][SAM7X_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(8)));
+//RX buffer
+static uint8_t rxBuffer[SAM7X_ETH_RX_BUFFER_COUNT][SAM7X_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(8)));
+//TX buffer descriptors
+static Sam7xTxBufferDesc txBufferDesc[SAM7X_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//RX buffer descriptors
+static Sam7xRxBufferDesc rxBufferDesc[SAM7X_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+
+#endif
+
+//TX buffer index
+static uint_t txBufferIndex;
+//RX buffer index
+static uint_t rxBufferIndex;
+
+
+/**
+ * @brief SAM7X Ethernet MAC driver
+ **/
+
+const NicDriver sam7xEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   sam7xEthInit,
+   sam7xEthTick,
+   sam7xEthEnableIrq,
+   sam7xEthDisableIrq,
+   sam7xEthEventHandler,
+   sam7xEthSendPacket,
+   sam7xEthSetMulticastFilter,
+   sam7xEthUpdateMacConfig,
+   sam7xEthWritePhyReg,
+   sam7xEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief SAM7X Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sam7xEthInit(NetInterface *interface)
+{
+   error_t error;
+   volatile uint32_t status;
+
+   //Debug message
+   TRACE_INFO("Initializing SAM7X Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Enable EMAC peripheral clock
+   AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_EMAC);
+
+   //GPIO configuration
+   sam7xEthInitGpio(interface);
+
+   //Configure MDC clock speed
+   AT91C_BASE_EMAC->EMAC_NCFGR = AT91C_EMAC_CLK_HCLK_32;
+   //Enable management port (MDC and MDIO)
+   AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_MPE;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Set the MAC address
+   AT91C_BASE_EMAC->EMAC_SA1L = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   AT91C_BASE_EMAC->EMAC_SA1H = interface->macAddr.w[2];
+
+   //Configure the receive filter
+   AT91C_BASE_EMAC->EMAC_NCFGR |= AT91C_EMAC_UNI | AT91C_EMAC_MTI;
+
+   //Initialize hash table
+   AT91C_BASE_EMAC->EMAC_HRB = 0;
+   AT91C_BASE_EMAC->EMAC_HRT = 0;
+
+   //Initialize buffer descriptors
+   sam7xEthInitBufferDesc(interface);
+
+   //Clear transmit status register
+   AT91C_BASE_EMAC->EMAC_TSR = AT91C_EMAC_UND | AT91C_EMAC_COMP | AT91C_EMAC_BEX |
+      AT91C_EMAC_TGO | AT91C_EMAC_RLES | AT91C_EMAC_COL | AT91C_EMAC_UBR;
+   //Clear receive status register
+   AT91C_BASE_EMAC->EMAC_RSR = AT91C_EMAC_OVR | AT91C_EMAC_REC | AT91C_EMAC_BNA;
+
+   //First disable all EMAC interrupts
+   AT91C_BASE_EMAC->EMAC_IDR = 0xFFFFFFFF;
+   //Only the desired ones are enabled
+   AT91C_BASE_EMAC->EMAC_IER = AT91C_EMAC_ROVR | AT91C_EMAC_TCOMP | AT91C_EMAC_TXERR |
+      AT91C_EMAC_RLEX | AT91C_EMAC_TUNDR | AT91C_EMAC_RXUBR | AT91C_EMAC_RCOMP;
+
+   //Read EMAC ISR register to clear any pending interrupt
+   status = AT91C_BASE_EMAC->EMAC_ISR;
+
+   //Configure interrupt controller
+   AT91C_BASE_AIC->AIC_SMR[AT91C_ID_EMAC] = AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL | AT91C_AIC_PRIOR_LOWEST;
+   AT91C_BASE_AIC->AIC_SVR[AT91C_ID_EMAC] = (uint32_t) emacIrqWrapper;
+
+   //Clear EMAC interrupt flag
+   AT91C_BASE_AIC->AIC_ICCR = (1 << AT91C_ID_EMAC);
+
+   //Enable the EMAC to transmit and receive data
+   AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TE | AT91C_EMAC_RE;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//SAM7-EX256 evaluation board?
+#if defined(USE_SAM7_EX256)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam7xEthInitGpio(NetInterface *interface)
+{
+   //Enable PIO peripheral clock
+   AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_PIOB);
+
+   //Disable pull-up resistors on MII pins
+   AT91C_BASE_PIOB->PIO_PPUDR = AT91C_EMAC_MII_MASK;
+   //Disable interrupts-on-change
+   AT91C_BASE_PIOB->PIO_IDR = AT91C_EMAC_MII_MASK;
+   //Assign MII pins to peripheral A function
+   AT91C_BASE_PIOB->PIO_ASR = AT91C_EMAC_MII_MASK;
+   //Disable the PIO from controlling the corresponding pins
+   AT91C_BASE_PIOB->PIO_PDR = AT91C_EMAC_MII_MASK;
+
+   //Select MII operation mode and enable transceiver clock
+   AT91C_BASE_EMAC->EMAC_USRIO = AT91C_EMAC_CLKEN;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize buffer descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam7xEthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+   uint32_t address;
+
+   //Initialize TX buffer descriptors
+   for(i = 0; i < SAM7X_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current TX buffer
+      address = (uint32_t) txBuffer[i];
+      //Write the address to the descriptor entry
+      txBufferDesc[i].address = address;
+      //Initialize status field
+      txBufferDesc[i].status = AT91C_EMAC_TX_USED;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   txBufferDesc[i - 1].status |= AT91C_EMAC_TX_WRAP;
+   //Initialize TX buffer index
+   txBufferIndex = 0;
+
+   //Initialize RX buffer descriptors
+   for(i = 0; i < SAM7X_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current RX buffer
+      address = (uint32_t) rxBuffer[i];
+      //Write the address to the descriptor entry
+      rxBufferDesc[i].address = address & AT91C_EMAC_RX_ADDRESS;
+      //Clear status field
+      rxBufferDesc[i].status = 0;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   rxBufferDesc[i - 1].address |= AT91C_EMAC_RX_WRAP;
+   //Initialize RX buffer index
+   rxBufferIndex = 0;
+
+   //Start location of the TX descriptor list
+   AT91C_BASE_EMAC->EMAC_TBQP = (uint32_t) txBufferDesc;
+   //Start location of the RX descriptor list
+   AT91C_BASE_EMAC->EMAC_RBQP = (uint32_t) rxBufferDesc;
+}
+
+
+/**
+ * @brief SAM7X Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam7xEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam7xEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   AT91C_BASE_AIC->AIC_IECR = (1 << AT91C_ID_EMAC);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam7xEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   AT91C_BASE_AIC->AIC_IDCR = (1 << AT91C_ID_EMAC);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief SAM7X Ethernet MAC interrupt service routine
+ **/
+
+void sam7xEthIrqHandler(void)
+{
+   bool_t flag;
+   volatile uint32_t isr;
+   volatile uint32_t tsr;
+   volatile uint32_t rsr;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Each time the software reads EMAC_ISR, it has to check the contents of
+   //EMAC_TSR, EMAC_RSR and EMAC_NSR (see SAM7X errata 41.3.3.2)
+   isr = AT91C_BASE_EMAC->EMAC_ISR;
+   tsr = AT91C_BASE_EMAC->EMAC_TSR;
+   rsr = AT91C_BASE_EMAC->EMAC_RSR;
+
+   //A packet has been transmitted?
+   if(tsr & (AT91C_EMAC_UND | AT91C_EMAC_COMP | AT91C_EMAC_BEX |
+      AT91C_EMAC_TGO | AT91C_EMAC_RLES | AT91C_EMAC_COL | AT91C_EMAC_UBR))
+   {
+      //Only clear TSR flags that are currently set
+      AT91C_BASE_EMAC->EMAC_TSR = tsr;
+
+      //Check whether the TX buffer is available for writing
+      if(txBufferDesc[txBufferIndex].status & AT91C_EMAC_TX_USED)
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(rsr & (AT91C_EMAC_OVR | AT91C_EMAC_REC | AT91C_EMAC_BNA))
+   {
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Write AIC_EOICR register before exiting
+   AT91C_BASE_AIC->AIC_EOICR = 0;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief SAM7X Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam7xEthEventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint32_t rsr;
+
+   //Read receive status
+   rsr = AT91C_BASE_EMAC->EMAC_RSR;
+
+   //Packet received?
+   if(rsr & (AT91C_EMAC_OVR | AT91C_EMAC_REC | AT91C_EMAC_BNA))
+   {
+      //Only clear RSR flags that are currently set
+      AT91C_BASE_EMAC->EMAC_RSR = rsr;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = sam7xEthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t sam7xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > SAM7X_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(!(txBufferDesc[txBufferIndex].status & AT91C_EMAC_TX_USED))
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(txBuffer[txBufferIndex], buffer, offset, length);
+
+   //Set the necessary flags in the descriptor entry
+   if(txBufferIndex < (SAM7X_ETH_TX_BUFFER_COUNT - 1))
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status =
+         AT91C_EMAC_TX_LAST | (length & AT91C_EMAC_TX_LENGTH);
+
+      //Point to the next buffer
+      txBufferIndex++;
+   }
+   else
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status = AT91C_EMAC_TX_WRAP |
+         AT91C_EMAC_TX_LAST | (length & AT91C_EMAC_TX_LENGTH);
+
+      //Wrap around
+      txBufferIndex = 0;
+   }
+
+   //Set the TSTART bit to initiate transmission
+   AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TSTART;
+
+   //Check whether the next buffer is available for writing
+   if(txBufferDesc[txBufferIndex].status & AT91C_EMAC_TX_USED)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sam7xEthReceivePacket(NetInterface *interface)
+{
+   static uint8_t temp[ETH_MAX_FRAME_SIZE];
+   error_t error;
+   uint_t i;
+   uint_t j;
+   uint_t sofIndex;
+   uint_t eofIndex;
+   size_t n;
+   size_t size;
+   size_t length;
+
+   //Initialize SOF and EOF indices
+   sofIndex = UINT_MAX;
+   eofIndex = UINT_MAX;
+
+   //Search for SOF and EOF flags
+   for(i = 0; i < SAM7X_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Point to the current entry
+      j = rxBufferIndex + i;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(j >= SAM7X_ETH_RX_BUFFER_COUNT)
+         j -= SAM7X_ETH_RX_BUFFER_COUNT;
+
+      //No more entries to process?
+      if(!(rxBufferDesc[j].address & EMAC_RX_OWNERSHIP))
+      {
+         //Stop processing
+         break;
+      }
+      //A valid SOF has been found?
+      if(rxBufferDesc[j].status & EMAC_RX_SOF)
+      {
+         //Save the position of the SOF
+         sofIndex = i;
+      }
+      //A valid EOF has been found?
+      if((rxBufferDesc[j].status & EMAC_RX_EOF) && sofIndex != UINT_MAX)
+      {
+         //Save the position of the EOF
+         eofIndex = i;
+         //Retrieve the length of the frame
+         size = rxBufferDesc[j].status & EMAC_RX_LENGTH;
+         //Limit the number of data to read
+         size = MIN(size, ETH_MAX_FRAME_SIZE);
+         //Stop processing since we have reached the end of the frame
+         break;
+      }
+   }
+
+   //Determine the number of entries to process
+   if(eofIndex != UINT_MAX)
+      j = eofIndex + 1;
+   else if(sofIndex != UINT_MAX)
+      j = sofIndex;
+   else
+      j = i;
+
+   //Total number of bytes that have been copied from the receive buffer
+   length = 0;
+
+   //Process incoming frame
+   for(i = 0; i < j; i++)
+   {
+      //Any data to copy from current buffer?
+      if(eofIndex != UINT_MAX && i >= sofIndex && i <= eofIndex)
+      {
+         //Calculate the number of bytes to read at a time
+         n = MIN(size, SAM7X_ETH_RX_BUFFER_SIZE);
+         //Copy data from receive buffer
+         memcpy(temp + length, rxBuffer[rxBufferIndex], n);
+         //Update byte counters
+         length += n;
+         size -= n;
+      }
+
+      //Mark the current buffer as free
+      rxBufferDesc[rxBufferIndex].address &= ~EMAC_RX_OWNERSHIP;
+
+      //Point to the following entry
+      rxBufferIndex++;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(rxBufferIndex >= SAM7X_ETH_RX_BUFFER_COUNT)
+         rxBufferIndex = 0;
+   }
+
+   //Any packet to process?
+   if(length > 0)
+   {
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, temp, length);
+      //Valid packet received
+      error = NO_ERROR;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sam7xEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint8_t *p;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating SAM7X hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Point to the MAC address
+         p = entry->addr.b;
+
+         //Apply the hash function
+         k = (p[0] >> 6) ^ p[0];
+         k ^= (p[1] >> 4) ^ (p[1] << 2);
+         k ^= (p[2] >> 2) ^ (p[2] << 4);
+         k ^= (p[3] >> 6) ^ p[3];
+         k ^= (p[4] >> 4) ^ (p[4] << 2);
+         k ^= (p[5] >> 2) ^ (p[5] << 4);
+
+         //The hash value is reduced to a 6-bit index
+         k &= 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   AT91C_BASE_EMAC->EMAC_HRB = hashTable[0];
+   AT91C_BASE_EMAC->EMAC_HRT = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HRB = %08" PRIX32 "\r\n", AT91C_BASE_EMAC->EMAC_HRB);
+   TRACE_DEBUG("  HRT = %08" PRIX32 "\r\n", AT91C_BASE_EMAC->EMAC_HRT);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sam7xEthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read network configuration register
+   config = AT91C_BASE_EMAC->EMAC_NCFGR;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= AT91C_EMAC_SPD;
+   else
+      config &= ~AT91C_EMAC_SPD;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= AT91C_EMAC_FD;
+   else
+      config &= ~AT91C_EMAC_FD;
+
+   //Write configuration value back to NCFGR register
+   AT91C_BASE_EMAC->EMAC_NCFGR = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void sam7xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Set up a write operation
+   value = AT91C_EMAC_SOF_01 | AT91C_EMAC_RW_01 | AT91C_EMAC_CODE_10;
+   //PHY address
+   value |= (phyAddr << 23) & AT91C_EMAC_PHYA;
+   //Register address
+   value |= (regAddr << 18) & AT91C_EMAC_REGA;
+   //Register value
+   value |= data & AT91C_EMAC_DATA;
+
+   //Start a write operation
+   AT91C_BASE_EMAC->EMAC_MAN = value;
+   //Wait for the write to complete
+   while(!(AT91C_BASE_EMAC->EMAC_NSR & AT91C_EMAC_IDLE));
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t sam7xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Set up a read operation
+   value = AT91C_EMAC_SOF_01 | AT91C_EMAC_RW_10 | AT91C_EMAC_CODE_10;
+   //PHY address
+   value |= (phyAddr << 23) & AT91C_EMAC_PHYA;
+   //Register address
+   value |= (regAddr << 18) & AT91C_EMAC_REGA;
+
+   //Start a read operation
+   AT91C_BASE_EMAC->EMAC_MAN = value;
+   //Wait for the read to complete
+   while(!(AT91C_BASE_EMAC->EMAC_NSR & AT91C_EMAC_IDLE));
+
+   //Return PHY register contents
+   return AT91C_BASE_EMAC->EMAC_MAN & AT91C_EMAC_DATA;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/sam7x_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,158 @@
+/**
+ * @file sam7x_eth.h
+ * @brief AT91SAM7X Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SAM7X_ETH_H
+#define _SAM7X_ETH_H
+
+//Number of TX buffers
+#ifndef SAM7X_ETH_TX_BUFFER_COUNT
+   #define SAM7X_ETH_TX_BUFFER_COUNT 2
+#elif (SAM7X_ETH_TX_BUFFER_COUNT < 1)
+   #error SAM7X_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef SAM7X_ETH_TX_BUFFER_SIZE
+   #define SAM7X_ETH_TX_BUFFER_SIZE 1536
+#elif (SAM7X_ETH_TX_BUFFER_SIZE != 1536)
+   #error SAM7X_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef SAM7X_ETH_RX_BUFFER_COUNT
+   #define SAM7X_ETH_RX_BUFFER_COUNT 48
+#elif (SAM7X_ETH_RX_BUFFER_COUNT < 12)
+   #error SAM7X_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef SAM7X_ETH_RX_BUFFER_SIZE
+   #define SAM7X_ETH_RX_BUFFER_SIZE 128
+#elif (SAM7X_ETH_RX_BUFFER_SIZE != 128)
+   #error SAM7X_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//MII signals
+#define AT91C_EMAC_MII_MASK (AT91C_PB17_ERXCK | AT91C_PB16_ECOL | \
+   AT91C_PB15_ERXDV_ECRSDV | AT91C_PB14_ERX3 | AT91C_PB13_ERX2 | AT91C_PB12_ETXER | \
+   AT91C_PB11_ETX3 | AT91C_PB10_ETX2 | AT91C_PB9_EMDIO | AT91C_PB8_EMDC | \
+   AT91C_PB7_ERXER | AT91C_PB6_ERX1 | AT91C_PB5_ERX0 | AT91C_PB4_ECRS | \
+   AT91C_PB3_ETX1 | AT91C_PB2_ETX0 | AT91C_PB1_ETXEN | AT91C_PB0_ETXCK_EREFCK)
+
+//PHY maintenance register (EMAC_MAN)
+#define AT91C_EMAC_SOF_01    (1 << 30)
+#define AT91C_EMAC_RW_01     (1 << 28)
+#define AT91C_EMAC_RW_10     (2 << 28)
+#define AT91C_EMAC_CODE_10   (2 << 16)
+
+//TX buffer descriptor flags
+#define AT91C_EMAC_TX_USED           0x80000000
+#define AT91C_EMAC_TX_WRAP           0x40000000
+#define AT91C_EMAC_TX_ERROR          0x20000000
+#define AT91C_EMAC_TX_UNDERRUN       0x10000000
+#define AT91C_EMAC_TX_EXHAUSTED      0x08000000
+#define AT91C_EMAC_TX_NO_CRC         0x00010000
+#define AT91C_EMAC_TX_LAST           0x00008000
+#define AT91C_EMAC_TX_LENGTH         0x000007FF
+
+//RX buffer descriptor flags
+#define AT91C_EMAC_RX_ADDRESS        0xFFFFFFFC
+#define AT91C_EMAC_RX_WRAP           0x00000002
+#define AT91C_EMAC_RX_OWNERSHIP      0x00000001
+#define AT91C_EMAC_RX_BROADCAST      0x80000000
+#define AT91C_EMAC_RX_MULTICAST_HASH 0x40000000
+#define AT91C_EMAC_RX_UNICAST_HASH   0x20000000
+#define AT91C_EMAC_RX_EXT_ADDR       0x10000000
+#define AT91C_EMAC_RX_SAR1           0x04000000
+#define AT91C_EMAC_RX_SAR2           0x02000000
+#define AT91C_EMAC_RX_SAR3           0x01000000
+#define AT91C_EMAC_RX_SAR4           0x00800000
+#define AT91C_EMAC_RX_TYPE_ID        0x00400000
+#define AT91C_EMAC_RX_VLAN_TAG       0x00200000
+#define AT91C_EMAC_RX_PRIORITY_TAG   0x00100000
+#define AT91C_EMAC_RX_VLAN_PRIORITY  0x000E0000
+#define AT91C_EMAC_RX_CFI            0x00010000
+#define AT91C_EMAC_RX_EOF            0x00008000
+#define AT91C_EMAC_RX_SOF            0x00004000
+#define AT91C_EMAC_RX_OFFSET         0x00003000
+#define AT91C_EMAC_RX_LENGTH         0x00000FFF
+
+
+/**
+ * @brief Transmit buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Sam7xTxBufferDesc;
+
+
+/**
+ * @brief Receive buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Sam7xRxBufferDesc;
+
+
+//SAM7X Ethernet MAC driver
+extern const NicDriver sam7xEthDriver;
+
+//SAM7X Ethernet MAC related functions
+error_t sam7xEthInit(NetInterface *interface);
+void sam7xEthInitGpio(NetInterface *interface);
+void sam7xEthInitBufferDesc(NetInterface *interface);
+
+void sam7xEthTick(NetInterface *interface);
+
+void sam7xEthEnableIrq(NetInterface *interface);
+void sam7xEthDisableIrq(NetInterface *interface);
+void sam7xEthIrqHandler(void);
+void sam7xEthEventHandler(NetInterface *interface);
+
+error_t sam7xEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t sam7xEthReceivePacket(NetInterface *interface);
+
+error_t sam7xEthSetMulticastFilter(NetInterface *interface);
+error_t sam7xEthUpdateMacConfig(NetInterface *interface);
+
+void sam7xEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t sam7xEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+//Wrapper for the interrupt service routine
+void emacIrqWrapper(void);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/sam9263_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,730 @@
+/**
+ * @file sam9263_eth.c
+ * @brief AT91SAM9263 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include <limits.h>
+#include "at91sam9263.h"
+#include "core/net.h"
+#include "drivers/sam9263_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//TX buffer
+#pragma data_alignment = 8
+static uint8_t txBuffer[SAM9263_ETH_TX_BUFFER_COUNT][SAM9263_ETH_TX_BUFFER_SIZE];
+//RX buffer
+#pragma data_alignment = 8
+static uint8_t rxBuffer[SAM9263_ETH_RX_BUFFER_COUNT][SAM9263_ETH_RX_BUFFER_SIZE];
+//TX buffer descriptors
+#pragma data_alignment = 4
+static Sam9263TxBufferDesc txBufferDesc[SAM9263_ETH_TX_BUFFER_COUNT];
+//RX buffer descriptors
+#pragma data_alignment = 4
+static Sam9263RxBufferDesc rxBufferDesc[SAM9263_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//TX buffer
+static uint8_t txBuffer[SAM9263_ETH_TX_BUFFER_COUNT][SAM9263_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(8)));
+//RX buffer
+static uint8_t rxBuffer[SAM9263_ETH_RX_BUFFER_COUNT][SAM9263_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(8)));
+//TX buffer descriptors
+static Sam9263TxBufferDesc txBufferDesc[SAM9263_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//RX buffer descriptors
+static Sam9263RxBufferDesc rxBufferDesc[SAM9263_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+
+#endif
+
+//TX buffer index
+static uint_t txBufferIndex;
+//RX buffer index
+static uint_t rxBufferIndex;
+
+
+/**
+ * @brief SAM9263 Ethernet MAC driver
+ **/
+
+const NicDriver sam9263EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   sam9263EthInit,
+   sam9263EthTick,
+   sam9263EthEnableIrq,
+   sam9263EthDisableIrq,
+   sam9263EthEventHandler,
+   sam9263EthSendPacket,
+   sam9263EthSetMulticastFilter,
+   sam9263EthUpdateMacConfig,
+   sam9263EthWritePhyReg,
+   sam9263EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief SAM9263 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sam9263EthInit(NetInterface *interface)
+{
+   error_t error;
+   volatile uint32_t status;
+
+   //Debug message
+   TRACE_INFO("Initializing SAM9263 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Enable EMAC peripheral clock
+   AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_EMAC);
+
+   //GPIO configuration
+   sam9263EthInitGpio(interface);
+
+   //Configure MDC clock speed
+   AT91C_BASE_EMAC->EMAC_NCFGR = AT91C_EMAC_CLK_HCLK_64;
+   //Enable management port (MDC and MDIO)
+   AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_MPE;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Set the MAC address
+   AT91C_BASE_EMAC->EMAC_SA1L = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   AT91C_BASE_EMAC->EMAC_SA1H = interface->macAddr.w[2];
+
+   //Configure the receive filter
+   AT91C_BASE_EMAC->EMAC_NCFGR |= AT91C_EMAC_UNI | AT91C_EMAC_MTI;
+
+   //Initialize hash table
+   AT91C_BASE_EMAC->EMAC_HRB = 0;
+   AT91C_BASE_EMAC->EMAC_HRT = 0;
+
+   //Initialize buffer descriptors
+   sam9263EthInitBufferDesc(interface);
+
+   //Clear transmit status register
+   AT91C_BASE_EMAC->EMAC_TSR = AT91C_EMAC_UND | AT91C_EMAC_COMP | AT91C_EMAC_BEX |
+      AT91C_EMAC_TGO | AT91C_EMAC_RLES | AT91C_EMAC_COL | AT91C_EMAC_UBR;
+   //Clear receive status register
+   AT91C_BASE_EMAC->EMAC_RSR = AT91C_EMAC_OVR | AT91C_EMAC_REC | AT91C_EMAC_BNA;
+
+   //First disable all EMAC interrupts
+   AT91C_BASE_EMAC->EMAC_IDR = 0xFFFFFFFF;
+   //Only the desired ones are enabled
+   AT91C_BASE_EMAC->EMAC_IER = AT91C_EMAC_ROVR | AT91C_EMAC_TCOMP | AT91C_EMAC_TXERR |
+      AT91C_EMAC_RLEX | AT91C_EMAC_TUNDR | AT91C_EMAC_RXUBR | AT91C_EMAC_RCOMP;
+
+   //Read EMAC ISR register to clear any pending interrupt
+   status = AT91C_BASE_EMAC->EMAC_ISR;
+
+   //Configure interrupt controller
+   AT91C_BASE_AIC->AIC_SMR[AT91C_ID_EMAC] = AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL | AT91C_AIC_PRIOR_LOWEST;
+   AT91C_BASE_AIC->AIC_SVR[AT91C_ID_EMAC] = (uint32_t) emacIrqWrapper;
+
+   //Clear EMAC interrupt flag
+   AT91C_BASE_AIC->AIC_ICCR = (1 << AT91C_ID_EMAC);
+
+   //Enable the EMAC to transmit and receive data
+   AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TE | AT91C_EMAC_RE;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//SAM9263-EK evaluation board?
+#if defined(USE_SAM9263_EK)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam9263EthInitGpio(NetInterface *interface)
+{
+   //Enable PIO peripheral clocks
+   AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_PIOA) | (1 << AT91C_ID_PIOCDE);
+
+   //Disable pull-up resistors on RMII pins
+   AT91C_BASE_PIOC->PIO_PPUDR = AT91C_EMAC_RMII_MASK_C;
+   AT91C_BASE_PIOE->PIO_PPUDR = AT91C_EMAC_RMII_MASK_E;
+   //Disable interrupts-on-change
+   AT91C_BASE_PIOC->PIO_IDR = AT91C_EMAC_RMII_MASK_C;
+   AT91C_BASE_PIOE->PIO_IDR = AT91C_EMAC_RMII_MASK_E;
+   //Assign RMII pins to to the relevant peripheral function
+   AT91C_BASE_PIOC->PIO_BSR = AT91C_EMAC_RMII_MASK_C;
+   AT91C_BASE_PIOE->PIO_ASR = AT91C_EMAC_RMII_MASK_E;
+   //Disable the PIO from controlling the corresponding pins
+   AT91C_BASE_PIOC->PIO_PDR = AT91C_EMAC_RMII_MASK_C;
+   AT91C_BASE_PIOE->PIO_PDR = AT91C_EMAC_RMII_MASK_E;
+
+   //Select RMII operation mode and enable transceiver clock
+   AT91C_BASE_EMAC->EMAC_USRIO = AT91C_EMAC_CLKEN | AT91C_EMAC_RMII;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize buffer descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam9263EthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+   uint32_t address;
+
+   //Initialize TX buffer descriptors
+   for(i = 0; i < SAM9263_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current TX buffer
+      address = (uint32_t) txBuffer[i];
+      //Write the address to the descriptor entry
+      txBufferDesc[i].address = address;
+      //Initialize status field
+      txBufferDesc[i].status = AT91C_EMAC_TX_USED;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   txBufferDesc[i - 1].status |= AT91C_EMAC_TX_WRAP;
+   //Initialize TX buffer index
+   txBufferIndex = 0;
+
+   //Initialize RX buffer descriptors
+   for(i = 0; i < SAM9263_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current RX buffer
+      address = (uint32_t) rxBuffer[i];
+      //Write the address to the descriptor entry
+      rxBufferDesc[i].address = address & AT91C_EMAC_RX_ADDRESS;
+      //Clear status field
+      rxBufferDesc[i].status = 0;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   rxBufferDesc[i - 1].address |= AT91C_EMAC_RX_WRAP;
+   //Initialize RX buffer index
+   rxBufferIndex = 0;
+
+   //Start location of the TX descriptor list
+   AT91C_BASE_EMAC->EMAC_TBQP = (uint32_t) txBufferDesc;
+   //Start location of the RX descriptor list
+   AT91C_BASE_EMAC->EMAC_RBQP = (uint32_t) rxBufferDesc;
+}
+
+
+/**
+ * @brief SAM9263 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam9263EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam9263EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   AT91C_BASE_AIC->AIC_IECR = (1 << AT91C_ID_EMAC);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam9263EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   AT91C_BASE_AIC->AIC_IDCR = (1 << AT91C_ID_EMAC);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief SAM9263 Ethernet MAC interrupt service routine
+ **/
+
+void sam9263EthIrqHandler(void)
+{
+   bool_t flag;
+   volatile uint32_t isr;
+   volatile uint32_t tsr;
+   volatile uint32_t rsr;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Each time the software reads EMAC_ISR, it has to check the
+   //contents of EMAC_TSR, EMAC_RSR and EMAC_NSR
+   isr = AT91C_BASE_EMAC->EMAC_ISR;
+   tsr = AT91C_BASE_EMAC->EMAC_TSR;
+   rsr = AT91C_BASE_EMAC->EMAC_RSR;
+
+   //A packet has been transmitted?
+   if(tsr & (AT91C_EMAC_UND | AT91C_EMAC_COMP | AT91C_EMAC_BEX |
+      AT91C_EMAC_TGO | AT91C_EMAC_RLES | AT91C_EMAC_COL | AT91C_EMAC_UBR))
+   {
+      //Only clear TSR flags that are currently set
+      AT91C_BASE_EMAC->EMAC_TSR = tsr;
+
+      //Check whether the TX buffer is available for writing
+      if(txBufferDesc[txBufferIndex].status & AT91C_EMAC_TX_USED)
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(rsr & (AT91C_EMAC_OVR | AT91C_EMAC_REC | AT91C_EMAC_BNA))
+   {
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Write AIC_EOICR register before exiting
+   AT91C_BASE_AIC->AIC_EOICR = 0;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief SAM9263 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void sam9263EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint32_t rsr;
+
+   //Read receive status
+   rsr = AT91C_BASE_EMAC->EMAC_RSR;
+
+   //Packet received?
+   if(rsr & (AT91C_EMAC_OVR | AT91C_EMAC_REC | AT91C_EMAC_BNA))
+   {
+      //Only clear RSR flags that are currently set
+      AT91C_BASE_EMAC->EMAC_RSR = rsr;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = sam9263EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t sam9263EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > SAM9263_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(!(txBufferDesc[txBufferIndex].status & AT91C_EMAC_TX_USED))
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(txBuffer[txBufferIndex], buffer, offset, length);
+
+   //Set the necessary flags in the descriptor entry
+   if(txBufferIndex < (SAM9263_ETH_TX_BUFFER_COUNT - 1))
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status =
+         AT91C_EMAC_TX_LAST | (length & AT91C_EMAC_TX_LENGTH);
+
+      //Point to the next buffer
+      txBufferIndex++;
+   }
+   else
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status = AT91C_EMAC_TX_WRAP |
+         AT91C_EMAC_TX_LAST | (length & AT91C_EMAC_TX_LENGTH);
+
+      //Wrap around
+      txBufferIndex = 0;
+   }
+
+   //Set the TSTART bit to initiate transmission
+   AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TSTART;
+
+   //Check whether the next buffer is available for writing
+   if(txBufferDesc[txBufferIndex].status & AT91C_EMAC_TX_USED)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sam9263EthReceivePacket(NetInterface *interface)
+{
+   static uint8_t temp[ETH_MAX_FRAME_SIZE];
+   error_t error;
+   uint_t i;
+   uint_t j;
+   uint_t sofIndex;
+   uint_t eofIndex;
+   size_t n;
+   size_t size;
+   size_t length;
+
+   //Initialize SOF and EOF indices
+   sofIndex = UINT_MAX;
+   eofIndex = UINT_MAX;
+
+   //Search for SOF and EOF flags
+   for(i = 0; i < SAM9263_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Point to the current entry
+      j = rxBufferIndex + i;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(j >= SAM9263_ETH_RX_BUFFER_COUNT)
+         j -= SAM9263_ETH_RX_BUFFER_COUNT;
+
+      //No more entries to process?
+      if(!(rxBufferDesc[j].address & EMAC_RX_OWNERSHIP))
+      {
+         //Stop processing
+         break;
+      }
+      //A valid SOF has been found?
+      if(rxBufferDesc[j].status & EMAC_RX_SOF)
+      {
+         //Save the position of the SOF
+         sofIndex = i;
+      }
+      //A valid EOF has been found?
+      if((rxBufferDesc[j].status & EMAC_RX_EOF) && sofIndex != UINT_MAX)
+      {
+         //Save the position of the EOF
+         eofIndex = i;
+         //Retrieve the length of the frame
+         size = rxBufferDesc[j].status & EMAC_RX_LENGTH;
+         //Limit the number of data to read
+         size = MIN(size, ETH_MAX_FRAME_SIZE);
+         //Stop processing since we have reached the end of the frame
+         break;
+      }
+   }
+
+   //Determine the number of entries to process
+   if(eofIndex != UINT_MAX)
+      j = eofIndex + 1;
+   else if(sofIndex != UINT_MAX)
+      j = sofIndex;
+   else
+      j = i;
+
+   //Total number of bytes that have been copied from the receive buffer
+   length = 0;
+
+   //Process incoming frame
+   for(i = 0; i < j; i++)
+   {
+      //Any data to copy from current buffer?
+      if(eofIndex != UINT_MAX && i >= sofIndex && i <= eofIndex)
+      {
+         //Calculate the number of bytes to read at a time
+         n = MIN(size, SAM9263_ETH_RX_BUFFER_SIZE);
+         //Copy data from receive buffer
+         memcpy(temp + length, rxBuffer[rxBufferIndex], n);
+         //Update byte counters
+         length += n;
+         size -= n;
+      }
+
+      //Mark the current buffer as free
+      rxBufferDesc[rxBufferIndex].address &= ~EMAC_RX_OWNERSHIP;
+
+      //Point to the following entry
+      rxBufferIndex++;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(rxBufferIndex >= SAM9263_ETH_RX_BUFFER_COUNT)
+         rxBufferIndex = 0;
+   }
+
+   //Any packet to process?
+   if(length > 0)
+   {
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, temp, length);
+      //Valid packet received
+      error = NO_ERROR;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sam9263EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint8_t *p;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating SAM9263 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Point to the MAC address
+         p = entry->addr.b;
+
+         //Apply the hash function
+         k = (p[0] >> 6) ^ p[0];
+         k ^= (p[1] >> 4) ^ (p[1] << 2);
+         k ^= (p[2] >> 2) ^ (p[2] << 4);
+         k ^= (p[3] >> 6) ^ p[3];
+         k ^= (p[4] >> 4) ^ (p[4] << 2);
+         k ^= (p[5] >> 2) ^ (p[5] << 4);
+
+         //The hash value is reduced to a 6-bit index
+         k &= 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   AT91C_BASE_EMAC->EMAC_HRB = hashTable[0];
+   AT91C_BASE_EMAC->EMAC_HRT = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HRB = %08" PRIX32 "\r\n", AT91C_BASE_EMAC->EMAC_HRB);
+   TRACE_DEBUG("  HRT = %08" PRIX32 "\r\n", AT91C_BASE_EMAC->EMAC_HRT);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sam9263EthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read network configuration register
+   config = AT91C_BASE_EMAC->EMAC_NCFGR;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= AT91C_EMAC_SPD;
+   else
+      config &= ~AT91C_EMAC_SPD;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= AT91C_EMAC_FD;
+   else
+      config &= ~AT91C_EMAC_FD;
+
+   //Write configuration value back to NCFGR register
+   AT91C_BASE_EMAC->EMAC_NCFGR = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void sam9263EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Set up a write operation
+   value = AT91C_EMAC_SOF_01 | AT91C_EMAC_RW_01 | AT91C_EMAC_CODE_10;
+   //PHY address
+   value |= (phyAddr << 23) & AT91C_EMAC_PHYA;
+   //Register address
+   value |= (regAddr << 18) & AT91C_EMAC_REGA;
+   //Register value
+   value |= data & AT91C_EMAC_DATA;
+
+   //Start a write operation
+   AT91C_BASE_EMAC->EMAC_MAN = value;
+   //Wait for the write to complete
+   while(!(AT91C_BASE_EMAC->EMAC_NSR & AT91C_EMAC_IDLE));
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t sam9263EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Set up a read operation
+   value = AT91C_EMAC_SOF_01 | AT91C_EMAC_RW_10 | AT91C_EMAC_CODE_10;
+   //PHY address
+   value |= (phyAddr << 23) & AT91C_EMAC_PHYA;
+   //Register address
+   value |= (regAddr << 18) & AT91C_EMAC_REGA;
+
+   //Start a read operation
+   AT91C_BASE_EMAC->EMAC_MAN = value;
+   //Wait for the read to complete
+   while(!(AT91C_BASE_EMAC->EMAC_NSR & AT91C_EMAC_IDLE));
+
+   //Return PHY register contents
+   return AT91C_BASE_EMAC->EMAC_MAN & AT91C_EMAC_DATA;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/sam9263_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,161 @@
+/**
+ * @file sam9263_eth.h
+ * @brief AT91SAM9263 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SAM9263_ETH_H
+#define _SAM9263_ETH_H
+
+//Number of TX buffers
+#ifndef SAM9263_ETH_TX_BUFFER_COUNT
+   #define SAM9263_ETH_TX_BUFFER_COUNT 3
+#elif (SAM9263_ETH_TX_BUFFER_COUNT < 1)
+   #error SAM9263_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef SAM9263_ETH_TX_BUFFER_SIZE
+   #define SAM9263_ETH_TX_BUFFER_SIZE 1536
+#elif (SAM9263_ETH_TX_BUFFER_SIZE != 1536)
+   #error SAM9263_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef SAM9263_ETH_RX_BUFFER_COUNT
+   #define SAM9263_ETH_RX_BUFFER_COUNT 72
+#elif (SAM9263_ETH_RX_BUFFER_COUNT < 12)
+   #error SAM9263_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef SAM9263_ETH_RX_BUFFER_SIZE
+   #define SAM9263_ETH_RX_BUFFER_SIZE 128
+#elif (SAM9263_ETH_RX_BUFFER_SIZE != 128)
+   #error SAM9263_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//EMAC controller base address
+#define AT91C_BASE_EMAC AT91C_BASE_MACB
+
+//RMII signals
+#define AT91C_EMAC_RMII_MASK_C AT91C_PC25_ERXDV
+
+#define AT91C_EMAC_RMII_MASK_E (AT91C_PE30_EMDIO | \
+   AT91C_PE29_EMDC | AT91C_PE28_ETXEN | AT91C_PE27_ERXER | AT91C_PE26_ERX1 | \
+   AT91C_PE25_ERX0 | AT91C_PE24_ETX1 | AT91C_PE23_ETX0 | AT91C_PE21_ETXCK)
+
+//PHY maintenance register (EMAC_MAN)
+#define AT91C_EMAC_SOF_01    (1 << 30)
+#define AT91C_EMAC_RW_01     (1 << 28)
+#define AT91C_EMAC_RW_10     (2 << 28)
+#define AT91C_EMAC_CODE_10   (2 << 16)
+
+//TX buffer descriptor flags
+#define AT91C_EMAC_TX_USED           0x80000000
+#define AT91C_EMAC_TX_WRAP           0x40000000
+#define AT91C_EMAC_TX_ERROR          0x20000000
+#define AT91C_EMAC_TX_UNDERRUN       0x10000000
+#define AT91C_EMAC_TX_EXHAUSTED      0x08000000
+#define AT91C_EMAC_TX_NO_CRC         0x00010000
+#define AT91C_EMAC_TX_LAST           0x00008000
+#define AT91C_EMAC_TX_LENGTH         0x000007FF
+
+//RX buffer descriptor flags
+#define AT91C_EMAC_RX_ADDRESS        0xFFFFFFFC
+#define AT91C_EMAC_RX_WRAP           0x00000002
+#define AT91C_EMAC_RX_OWNERSHIP      0x00000001
+#define AT91C_EMAC_RX_BROADCAST      0x80000000
+#define AT91C_EMAC_RX_MULTICAST_HASH 0x40000000
+#define AT91C_EMAC_RX_UNICAST_HASH   0x20000000
+#define AT91C_EMAC_RX_EXT_ADDR       0x10000000
+#define AT91C_EMAC_RX_SAR1           0x04000000
+#define AT91C_EMAC_RX_SAR2           0x02000000
+#define AT91C_EMAC_RX_SAR3           0x01000000
+#define AT91C_EMAC_RX_SAR4           0x00800000
+#define AT91C_EMAC_RX_TYPE_ID        0x00400000
+#define AT91C_EMAC_RX_VLAN_TAG       0x00200000
+#define AT91C_EMAC_RX_PRIORITY_TAG   0x00100000
+#define AT91C_EMAC_RX_VLAN_PRIORITY  0x000E0000
+#define AT91C_EMAC_RX_CFI            0x00010000
+#define AT91C_EMAC_RX_EOF            0x00008000
+#define AT91C_EMAC_RX_SOF            0x00004000
+#define AT91C_EMAC_RX_OFFSET         0x00003000
+#define AT91C_EMAC_RX_LENGTH         0x00000FFF
+
+
+/**
+ * @brief Transmit buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Sam9263TxBufferDesc;
+
+
+/**
+ * @brief Receive buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Sam9263RxBufferDesc;
+
+
+//SAM9263 Ethernet MAC driver
+extern const NicDriver sam9263EthDriver;
+
+//SAM9263 Ethernet MAC related functions
+error_t sam9263EthInit(NetInterface *interface);
+void sam9263EthInitGpio(NetInterface *interface);
+void sam9263EthInitBufferDesc(NetInterface *interface);
+
+void sam9263EthTick(NetInterface *interface);
+
+void sam9263EthEnableIrq(NetInterface *interface);
+void sam9263EthDisableIrq(NetInterface *interface);
+void sam9263EthIrqHandler(void);
+void sam9263EthEventHandler(NetInterface *interface);
+
+error_t sam9263EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t sam9263EthReceivePacket(NetInterface *interface);
+
+error_t sam9263EthSetMulticastFilter(NetInterface *interface);
+error_t sam9263EthUpdateMacConfig(NetInterface *interface);
+
+void sam9263EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t sam9263EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+//Wrapper for the interrupt service routine
+void emacIrqWrapper(void);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/sama5d2_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,803 @@
+/**
+ * @file sama5d2_eth.c
+ * @brief SAMA5D2 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include <limits.h>
+#include "chip.h"
+#include "peripherals/aic.h"
+#include "peripherals/pio.h"
+#include "core/net.h"
+#include "drivers/sama5d2_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//TX buffer
+#pragma data_alignment = 8
+#pragma location = ".region_ddr_nocache"
+static uint8_t txBuffer[SAMA5D2_ETH_TX_BUFFER_COUNT][SAMA5D2_ETH_TX_BUFFER_SIZE];
+//RX buffer
+#pragma data_alignment = 8
+#pragma location = ".region_ddr_nocache"
+static uint8_t rxBuffer[SAMA5D2_ETH_RX_BUFFER_COUNT][SAMA5D2_ETH_RX_BUFFER_SIZE];
+//TX buffer descriptors
+#pragma data_alignment = 4
+#pragma location = ".region_ddr_nocache"
+static Sama5d2TxBufferDesc txBufferDesc[SAMA5D2_ETH_TX_BUFFER_COUNT];
+//RX buffer descriptors
+#pragma data_alignment = 4
+#pragma location = ".region_ddr_nocache"
+static Sama5d2RxBufferDesc rxBufferDesc[SAMA5D2_ETH_RX_BUFFER_COUNT];
+
+//Dummy TX buffer
+#pragma data_alignment = 8
+#pragma location = ".region_ddr_nocache"
+static uint8_t dummyTxBuffer[SAMA5D2_ETH_DUMMY_BUFFER_COUNT][SAMA5D2_ETH_DUMMY_BUFFER_SIZE];
+//Dummy RX buffer
+#pragma data_alignment = 8
+#pragma location = ".region_ddr_nocache"
+static uint8_t dummyRxBuffer[SAMA5D2_ETH_DUMMY_BUFFER_COUNT][SAMA5D2_ETH_DUMMY_BUFFER_SIZE];
+//Dummy TX buffer descriptors
+#pragma data_alignment = 4
+#pragma location = ".region_ddr_nocache"
+static Sama5d2TxBufferDesc dummyTxBufferDesc[SAMA5D2_ETH_DUMMY_BUFFER_COUNT];
+//Dummy RX buffer descriptors
+#pragma data_alignment = 4
+#pragma location = ".region_ddr_nocache"
+static Sama5d2RxBufferDesc dummyRxBufferDesc[SAMA5D2_ETH_DUMMY_BUFFER_COUNT];
+
+//GCC compiler?
+#else
+
+//TX buffer
+static uint8_t txBuffer[SAMA5D2_ETH_TX_BUFFER_COUNT][SAMA5D2_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(8), __section__(".region_ddr_nocache")));
+//RX buffer
+static uint8_t rxBuffer[SAMA5D2_ETH_RX_BUFFER_COUNT][SAMA5D2_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(8), __section__(".region_ddr_nocache")));
+//TX buffer descriptors
+static Sama5d2TxBufferDesc txBufferDesc[SAMA5D2_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".region_ddr_nocache")));
+//RX buffer descriptors
+static Sama5d2RxBufferDesc rxBufferDesc[SAMA5D2_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".region_ddr_nocache")));
+
+//Dummy TX buffer
+static uint8_t dummyTxBuffer[SAMA5D2_ETH_DUMMY_BUFFER_COUNT][SAMA5D2_ETH_DUMMY_BUFFER_SIZE]
+   __attribute__((aligned(8), __section__(".region_ddr_nocache")));
+//Dummy RX buffer
+static uint8_t dummyRxBuffer[SAMA5D2_ETH_DUMMY_BUFFER_COUNT][SAMA5D2_ETH_DUMMY_BUFFER_SIZE]
+   __attribute__((aligned(8), __section__(".region_ddr_nocache")));
+//Dummy TX buffer descriptors
+static Sama5d2TxBufferDesc dummyTxBufferDesc[SAMA5D2_ETH_DUMMY_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".region_ddr_nocache")));
+//Dummy RX buffer descriptors
+static Sama5d2RxBufferDesc dummyRxBufferDesc[SAMA5D2_ETH_DUMMY_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".region_ddr_nocache")));
+
+#endif
+
+//TX buffer index
+static uint_t txBufferIndex;
+//RX buffer index
+static uint_t rxBufferIndex;
+
+
+/**
+ * @brief SAMA5D2 Ethernet MAC driver
+ **/
+
+const NicDriver sama5d2EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   sama5d2EthInit,
+   sama5d2EthTick,
+   sama5d2EthEnableIrq,
+   sama5d2EthDisableIrq,
+   sama5d2EthEventHandler,
+   sama5d2EthSendPacket,
+   sama5d2EthSetMulticastFilter,
+   sama5d2EthUpdateMacConfig,
+   sama5d2EthWritePhyReg,
+   sama5d2EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief SAMA5D2 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sama5d2EthInit(NetInterface *interface)
+{
+   error_t error;
+   volatile uint32_t status;
+
+   //Debug message
+   TRACE_INFO("Initializing SAMA5D2 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Enable GMAC peripheral clock
+   PMC->PMC_PCER0 = (1 << ID_GMAC0);
+
+   //GPIO configuration
+   sama5d2EthInitGpio(interface);
+
+   //Configure MDC clock speed
+   GMAC0->GMAC_NCFGR = GMAC_NCFGR_CLK_MCK_96;
+   //Enable management port (MDC and MDIO)
+   GMAC0->GMAC_NCR |= GMAC_NCR_MPE;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Set the MAC address
+   GMAC0->GMAC_SA[0].GMAC_SAB = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   GMAC0->GMAC_SA[0].GMAC_SAT = interface->macAddr.w[2];
+
+   //Configure the receive filter
+   GMAC0->GMAC_NCFGR |= GMAC_NCFGR_UNIHEN | GMAC_NCFGR_MTIHEN;
+
+   //DMA configuration
+   GMAC0->GMAC_DCFGR = GMAC_DCFGR_DRBS(SAMA5D2_ETH_RX_BUFFER_SIZE / 64) |
+      GMAC_DCFGR_TXPBMS | GMAC_DCFGR_RXBMS_FULL | GMAC_DCFGR_FBLDO_INCR4;
+
+   GMAC0->GMAC_RBSRPQ[0] = GMAC_RBSRPQ_RBS(SAMA5D2_ETH_DUMMY_BUFFER_SIZE / 64);
+   GMAC0->GMAC_RBSRPQ[1] = GMAC_RBSRPQ_RBS(SAMA5D2_ETH_DUMMY_BUFFER_SIZE / 64);
+
+   //Initialize hash table
+   GMAC0->GMAC_HRB = 0;
+   GMAC0->GMAC_HRT = 0;
+
+   //Initialize buffer descriptors
+   sama5d2EthInitBufferDesc(interface);
+
+   //Clear transmit status register
+   GMAC0->GMAC_TSR = GMAC_TSR_HRESP | GMAC_TSR_TXCOMP | GMAC_TSR_TFC |
+      GMAC_TSR_TXGO | GMAC_TSR_RLE | GMAC_TSR_COL | GMAC_TSR_UBR;
+   //Clear receive status register
+   GMAC0->GMAC_RSR = GMAC_RSR_HNO | GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA;
+
+   //First disable all GMAC interrupts
+   GMAC0->GMAC_IDR = 0xFFFFFFFF;
+   GMAC0->GMAC_IDRPQ[0] = 0xFFFFFFFF;
+   GMAC0->GMAC_IDRPQ[1] = 0xFFFFFFFF;
+
+   //Only the desired ones are enabled
+   GMAC0->GMAC_IER = GMAC_IER_HRESP | GMAC_IER_ROVR | GMAC_IER_TCOMP | GMAC_IER_TFC |
+      GMAC_IER_RLEX | GMAC_IER_TUR | GMAC_IER_RXUBR | GMAC_IER_RCOMP;
+
+   //Read GMAC ISR register to clear any pending interrupt
+   status = GMAC0->GMAC_ISR;
+
+   //Register interrupt handler
+   aic_set_source_vector(ID_GMAC0, sama5d2EthIrqHandler);
+
+   //Configure interrupt priority
+   aic_configure(ID_GMAC0, AIC_SMR_SRCTYPE_INT_LEVEL_SENSITIVE |
+      AIC_SMR_PRIOR(SAMA5D2_ETH_IRQ_PRIORITY));
+
+   //Enable the GMAC to transmit and receive data
+   GMAC0->GMAC_NCR |= GMAC_NCR_TXEN | GMAC_NCR_RXEN;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//SAMA5D2-Xplained-Ultra evaluation board?
+#if defined(CONFIG_BOARD_SAMA5D2_XPLAINED)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void sama5d2EthInitGpio(NetInterface *interface)
+{
+   struct _pin rmiiPins[] = PINS_GMAC_RMII_IOS3;
+
+   //Configure RMII pins
+   pio_configure(rmiiPins, arraysize(rmiiPins));
+
+   //Select RMII operation mode
+   GMAC0->GMAC_UR = GMAC_UR_RMII;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize buffer descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void sama5d2EthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+   uint32_t address;
+
+   //Initialize TX buffer descriptors
+   for(i = 0; i < SAMA5D2_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current TX buffer
+      address = (uint32_t) txBuffer[i];
+      //Write the address to the descriptor entry
+      txBufferDesc[i].address = address;
+      //Initialize status field
+      txBufferDesc[i].status = GMAC_TX_USED;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   txBufferDesc[i - 1].status |= GMAC_TX_WRAP;
+   //Initialize TX buffer index
+   txBufferIndex = 0;
+
+   //Initialize RX buffer descriptors
+   for(i = 0; i < SAMA5D2_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current RX buffer
+      address = (uint32_t) rxBuffer[i];
+      //Write the address to the descriptor entry
+      rxBufferDesc[i].address = address & GMAC_RX_ADDRESS;
+      //Clear status field
+      rxBufferDesc[i].status = 0;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   rxBufferDesc[i - 1].address |= GMAC_RX_WRAP;
+   //Initialize RX buffer index
+   rxBufferIndex = 0;
+
+   //Initialize dummy TX buffer descriptors
+   for(i = 0; i < SAMA5D2_ETH_DUMMY_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current TX buffer
+      address = (uint32_t) dummyTxBuffer[i];
+      //Write the address to the descriptor entry
+      dummyTxBufferDesc[i].address = address;
+      //Initialize status field
+      dummyTxBufferDesc[i].status = GMAC_TX_USED;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   dummyTxBufferDesc[i - 1].status |= GMAC_TX_WRAP;
+
+   //Initialize dummy RX buffer descriptors
+   for(i = 0; i < SAMA5D2_ETH_DUMMY_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current RX buffer
+      address = (uint32_t) dummyRxBuffer[i];
+      //Write the address to the descriptor entry
+      dummyRxBufferDesc[i].address = (address & GMAC_RX_ADDRESS) | GMAC_RX_OWNERSHIP;
+      //Clear status field
+      dummyRxBufferDesc[i].status = 0;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   dummyRxBufferDesc[i - 1].address |= GMAC_RX_WRAP;
+
+   //Start location of the TX descriptor list
+   GMAC0->GMAC_TBQB = (uint32_t) txBufferDesc;
+   GMAC0->GMAC_TBQBAPQ[0] = (uint32_t) dummyTxBufferDesc;
+   GMAC0->GMAC_TBQBAPQ[1] = (uint32_t) dummyTxBufferDesc;
+
+   //Start location of the RX descriptor list
+   GMAC0->GMAC_RBQB = (uint32_t) rxBufferDesc;
+   GMAC0->GMAC_RBQBAPQ[0] = (uint32_t) dummyRxBufferDesc;
+   GMAC0->GMAC_RBQBAPQ[1] = (uint32_t) dummyRxBufferDesc;
+}
+
+
+/**
+ * @brief SAMA5D2 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void sama5d2EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void sama5d2EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   aic_enable(ID_GMAC0);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void sama5d2EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   aic_disable(ID_GMAC0);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief SAMA5D2 Ethernet MAC interrupt service routine
+ **/
+
+void sama5d2EthIrqHandler(void)
+{
+   bool_t flag;
+   volatile uint32_t isr;
+   volatile uint32_t tsr;
+   volatile uint32_t rsr;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Each time the software reads GMAC_ISR, it has to check the
+   //contents of GMAC_TSR, GMAC_RSR and GMAC_NSR
+   isr = GMAC0->GMAC_ISRPQ[0];
+   isr = GMAC0->GMAC_ISRPQ[1];
+   isr = GMAC0->GMAC_ISR;
+   tsr = GMAC0->GMAC_TSR;
+   rsr = GMAC0->GMAC_RSR;
+
+   //A packet has been transmitted?
+   if(tsr & (GMAC_TSR_HRESP | GMAC_TSR_TXCOMP | GMAC_TSR_TFC |
+      GMAC_TSR_TXGO | GMAC_TSR_RLE | GMAC_TSR_COL | GMAC_TSR_UBR))
+   {
+      //Only clear TSR flags that are currently set
+      GMAC0->GMAC_TSR = tsr;
+
+      //Check whether the TX buffer is available for writing
+      if(txBufferDesc[txBufferIndex].status & GMAC_TX_USED)
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(rsr & (GMAC_RSR_HNO | GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA))
+   {
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Write AIC_EOICR register before exiting
+   AIC->AIC_EOICR = 0;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief SAMA5D2 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void sama5d2EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint32_t rsr;
+
+   //Read receive status
+   rsr = GMAC0->GMAC_RSR;
+
+   //Packet received?
+   if(rsr & (GMAC_RSR_HNO | GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA))
+   {
+      //Only clear RSR flags that are currently set
+      GMAC0->GMAC_RSR = rsr;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = sama5d2EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t sama5d2EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > SAMA5D2_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(!(txBufferDesc[txBufferIndex].status & GMAC_TX_USED))
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(txBuffer[txBufferIndex], buffer, offset, length);
+
+   //Set the necessary flags in the descriptor entry
+   if(txBufferIndex < (SAMA5D2_ETH_TX_BUFFER_COUNT - 1))
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status =
+         GMAC_TX_LAST | (length & GMAC_TX_LENGTH);
+
+      //Point to the next buffer
+      txBufferIndex++;
+   }
+   else
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status = GMAC_TX_WRAP |
+         GMAC_TX_LAST | (length & GMAC_TX_LENGTH);
+
+      //Wrap around
+      txBufferIndex = 0;
+   }
+
+   //Data synchronization barrier
+   __DSB();
+
+   //Set the TSTART bit to initiate transmission
+   GMAC0->GMAC_NCR |= GMAC_NCR_TSTART;
+
+   //Check whether the next buffer is available for writing
+   if(txBufferDesc[txBufferIndex].status & GMAC_TX_USED)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sama5d2EthReceivePacket(NetInterface *interface)
+{
+   static uint8_t temp[ETH_MAX_FRAME_SIZE];
+   error_t error;
+   uint_t i;
+   uint_t j;
+   uint_t sofIndex;
+   uint_t eofIndex;
+   size_t n;
+   size_t size;
+   size_t length;
+
+   //Initialize SOF and EOF indices
+   sofIndex = UINT_MAX;
+   eofIndex = UINT_MAX;
+
+   //Search for SOF and EOF flags
+   for(i = 0; i < SAMA5D2_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Point to the current entry
+      j = rxBufferIndex + i;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(j >= SAMA5D2_ETH_RX_BUFFER_COUNT)
+         j -= SAMA5D2_ETH_RX_BUFFER_COUNT;
+
+      //No more entries to process?
+      if(!(rxBufferDesc[j].address & GMAC_RX_OWNERSHIP))
+      {
+         //Stop processing
+         break;
+      }
+      //A valid SOF has been found?
+      if(rxBufferDesc[j].status & GMAC_RX_SOF)
+      {
+         //Save the position of the SOF
+         sofIndex = i;
+      }
+      //A valid EOF has been found?
+      if((rxBufferDesc[j].status & GMAC_RX_EOF) && sofIndex != UINT_MAX)
+      {
+         //Save the position of the EOF
+         eofIndex = i;
+         //Retrieve the length of the frame
+         size = rxBufferDesc[j].status & GMAC_RX_LENGTH;
+         //Limit the number of data to read
+         size = MIN(size, ETH_MAX_FRAME_SIZE);
+         //Stop processing since we have reached the end of the frame
+         break;
+      }
+   }
+
+   //Determine the number of entries to process
+   if(eofIndex != UINT_MAX)
+      j = eofIndex + 1;
+   else if(sofIndex != UINT_MAX)
+      j = sofIndex;
+   else
+      j = i;
+
+   //Total number of bytes that have been copied from the receive buffer
+   length = 0;
+
+   //Process incoming frame
+   for(i = 0; i < j; i++)
+   {
+      //Any data to copy from current buffer?
+      if(eofIndex != UINT_MAX && i >= sofIndex && i <= eofIndex)
+      {
+         //Calculate the number of bytes to read at a time
+         n = MIN(size, SAMA5D2_ETH_RX_BUFFER_SIZE);
+         //Copy data from receive buffer
+         memcpy(temp + length, rxBuffer[rxBufferIndex], n);
+         //Update byte counters
+         length += n;
+         size -= n;
+      }
+
+      //Mark the current buffer as free
+      rxBufferDesc[rxBufferIndex].address &= ~GMAC_RX_OWNERSHIP;
+
+      //Point to the following entry
+      rxBufferIndex++;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(rxBufferIndex >= SAMA5D2_ETH_RX_BUFFER_COUNT)
+         rxBufferIndex = 0;
+   }
+
+   //Any packet to process?
+   if(length > 0)
+   {
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, temp, length);
+      //Valid packet received
+      error = NO_ERROR;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sama5d2EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint8_t *p;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating SAMA5D2 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Point to the MAC address
+         p = entry->addr.b;
+
+         //Apply the hash function
+         k = (p[0] >> 6) ^ p[0];
+         k ^= (p[1] >> 4) ^ (p[1] << 2);
+         k ^= (p[2] >> 2) ^ (p[2] << 4);
+         k ^= (p[3] >> 6) ^ p[3];
+         k ^= (p[4] >> 4) ^ (p[4] << 2);
+         k ^= (p[5] >> 2) ^ (p[5] << 4);
+
+         //The hash value is reduced to a 6-bit index
+         k &= 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   GMAC0->GMAC_HRB = hashTable[0];
+   GMAC0->GMAC_HRT = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HRB = %08" PRIX32 "\r\n", GMAC0->GMAC_HRB);
+   TRACE_DEBUG("  HRT = %08" PRIX32 "\r\n", GMAC0->GMAC_HRT);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sama5d2EthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read network configuration register
+   config = GMAC0->GMAC_NCFGR;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= GMAC_NCFGR_SPD;
+   else
+      config &= ~GMAC_NCFGR_SPD;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= GMAC_NCFGR_FD;
+   else
+      config &= ~GMAC_NCFGR_FD;
+
+   //Write configuration value back to NCFGR register
+   GMAC0->GMAC_NCFGR = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void sama5d2EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Set up a write operation
+   value = GMAC_MAN_CLTTO | GMAC_MAN_OP(1) | GMAC_MAN_WTN(2);
+   //PHY address
+   value |= GMAC_MAN_PHYA(phyAddr);
+   //Register address
+   value |= GMAC_MAN_REGA(regAddr);
+   //Register value
+   value |= GMAC_MAN_DATA(data);
+
+   //Start a write operation
+   GMAC0->GMAC_MAN = value;
+   //Wait for the write to complete
+   while(!(GMAC0->GMAC_NSR & GMAC_NSR_IDLE));
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t sama5d2EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Set up a read operation
+   value = GMAC_MAN_CLTTO | GMAC_MAN_OP(2) | GMAC_MAN_WTN(2);
+   //PHY address
+   value |= GMAC_MAN_PHYA(phyAddr);
+   //Register address
+   value |= GMAC_MAN_REGA(regAddr);
+
+   //Start a read operation
+   GMAC0->GMAC_MAN = value;
+   //Wait for the read to complete
+   while(!(GMAC0->GMAC_NSR & GMAC_NSR_IDLE));
+
+   //Return PHY register contents
+   return GMAC0->GMAC_MAN & GMAC_MAN_DATA_Msk;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/sama5d2_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,166 @@
+/**
+ * @file sama5d2_eth.h
+ * @brief SAMA5D2 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SAMA5D2_ETH_H
+#define _SAMA5D2_ETH_H
+
+//Number of TX buffers
+#ifndef SAMA5D2_ETH_TX_BUFFER_COUNT
+   #define SAMA5D2_ETH_TX_BUFFER_COUNT 4
+#elif (SAMA5D2_ETH_TX_BUFFER_COUNT < 1)
+   #error SAMA5D2_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef SAMA5D2_ETH_TX_BUFFER_SIZE
+   #define SAMA5D2_ETH_TX_BUFFER_SIZE 1536
+#elif (SAMA5D2_ETH_TX_BUFFER_SIZE != 1536)
+   #error SAMA5D2_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef SAMA5D2_ETH_RX_BUFFER_COUNT
+   #define SAMA5D2_ETH_RX_BUFFER_COUNT 96
+#elif (SAMA5D2_ETH_RX_BUFFER_COUNT < 12)
+   #error SAMA5D2_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef SAMA5D2_ETH_RX_BUFFER_SIZE
+   #define SAMA5D2_ETH_RX_BUFFER_SIZE 128
+#elif (SAMA5D2_ETH_RX_BUFFER_SIZE != 128)
+   #error SAMA5D2_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of dummy buffers
+#ifndef SAMA5D2_ETH_DUMMY_BUFFER_COUNT
+   #define SAMA5D2_ETH_DUMMY_BUFFER_COUNT 2
+#elif (SAMA5D2_ETH_DUMMY_BUFFER_COUNT < 1)
+   #error SAMA5D2_ETH_DUMMY_BUFFER_COUNT parameter is not valid
+#endif
+
+//Dummy buffer size
+#ifndef SAMA5D2_ETH_DUMMY_BUFFER_SIZE
+   #define SAMA5D2_ETH_DUMMY_BUFFER_SIZE 128
+#elif (SAMA5D2_ETH_DUMMY_BUFFER_SIZE != 128)
+   #error SAMA5D2_ETH_DUMMY_BUFFER_SIZE parameter is not valid
+#endif
+
+//Ethernet interrupt priority
+#ifndef SAMA5D2_ETH_IRQ_PRIORITY
+   #define SAMA5D2_ETH_IRQ_PRIORITY 0
+#elif (SAMA5D2_ETH_IRQ_PRIORITY < 0)
+   #error SAMA5D2_ETH_IRQ_PRIORITY parameter is not valid
+#endif
+
+//TX buffer descriptor flags
+#define GMAC_TX_USED           0x80000000
+#define GMAC_TX_WRAP           0x40000000
+#define GMAC_TX_RLE_ERROR      0x20000000
+#define GMAC_TX_UNDERRUN_ERROR 0x10000000
+#define GMAC_TX_AHB_ERROR      0x08000000
+#define GMAC_TX_LATE_COL_ERROR 0x04000000
+#define GMAC_TX_CHECKSUM_ERROR 0x00700000
+#define GMAC_TX_NO_CRC         0x00010000
+#define GMAC_TX_LAST           0x00008000
+#define GMAC_TX_LENGTH         0x00003FFF
+
+//RX buffer descriptor flags
+#define GMAC_RX_ADDRESS        0xFFFFFFFC
+#define GMAC_RX_WRAP           0x00000002
+#define GMAC_RX_OWNERSHIP      0x00000001
+#define GMAC_RX_BROADCAST      0x80000000
+#define GMAC_RX_MULTICAST_HASH 0x40000000
+#define GMAC_RX_UNICAST_HASH   0x20000000
+#define GMAC_RX_SAR            0x08000000
+#define GMAC_RX_SAR_MASK       0x06000000
+#define GMAC_RX_TYPE_ID        0x01000000
+#define GMAC_RX_SNAP           0x01000000
+#define GMAC_RX_TYPE_ID_MASK   0x00C00000
+#define GMAC_RX_CHECKSUM_VALID 0x00C00000
+#define GMAC_RX_VLAN_TAG       0x00200000
+#define GMAC_RX_PRIORITY_TAG   0x00100000
+#define GMAC_RX_VLAN_PRIORITY  0x000E0000
+#define GMAC_RX_CFI            0x00010000
+#define GMAC_RX_EOF            0x00008000
+#define GMAC_RX_SOF            0x00004000
+#define GMAC_RX_LENGTH_MSB     0x00002000
+#define GMAC_RX_BAD_FCS        0x00002000
+#define GMAC_RX_LENGTH         0x00001FFF
+
+
+/**
+ * @brief Transmit buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Sama5d2TxBufferDesc;
+
+
+/**
+ * @brief Receive buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Sama5d2RxBufferDesc;
+
+
+//SAMA5D2 Ethernet MAC driver
+extern const NicDriver sama5d2EthDriver;
+
+//SAMA5D2 Ethernet MAC related functions
+error_t sama5d2EthInit(NetInterface *interface);
+void sama5d2EthInitGpio(NetInterface *interface);
+void sama5d2EthInitBufferDesc(NetInterface *interface);
+
+void sama5d2EthTick(NetInterface *interface);
+
+void sama5d2EthEnableIrq(NetInterface *interface);
+void sama5d2EthDisableIrq(NetInterface *interface);
+void sama5d2EthIrqHandler(void);
+void sama5d2EthEventHandler(NetInterface *interface);
+
+error_t sama5d2EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t sama5d2EthReceivePacket(NetInterface *interface);
+
+error_t sama5d2EthSetMulticastFilter(NetInterface *interface);
+error_t sama5d2EthUpdateMacConfig(NetInterface *interface);
+
+void sama5d2EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t sama5d2EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/sama5d3_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,733 @@
+/**
+ * @file sama5d3_eth.c
+ * @brief SAMA5D3 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include <limits.h>
+#include "sama5d3x.h"
+#include "core/net.h"
+#include "drivers/sama5d3_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//TX buffer
+#pragma data_alignment = 8
+#pragma location = ".ram_no_cache"
+static uint8_t txBuffer[SAMA5D3_ETH_TX_BUFFER_COUNT][SAMA5D3_ETH_TX_BUFFER_SIZE];
+//RX buffer
+#pragma data_alignment = 8
+#pragma location = ".ram_no_cache"
+static uint8_t rxBuffer[SAMA5D3_ETH_RX_BUFFER_COUNT][SAMA5D3_ETH_RX_BUFFER_SIZE];
+//TX buffer descriptors
+#pragma data_alignment = 8
+#pragma location = ".ram_no_cache"
+static Sama5d3TxBufferDesc txBufferDesc[SAMA5D3_ETH_TX_BUFFER_COUNT];
+//RX buffer descriptors
+#pragma data_alignment = 8
+#pragma location = ".ram_no_cache"
+static Sama5d3RxBufferDesc rxBufferDesc[SAMA5D3_ETH_RX_BUFFER_COUNT];
+
+//GCC compiler?
+#else
+
+//TX buffer
+static uint8_t txBuffer[SAMA5D3_ETH_TX_BUFFER_COUNT][SAMA5D3_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(8), __section__(".ram_no_cache")));
+//RX buffer
+static uint8_t rxBuffer[SAMA5D3_ETH_RX_BUFFER_COUNT][SAMA5D3_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(8), __section__(".ram_no_cache")));
+//TX buffer descriptors
+static Sama5d3TxBufferDesc txBufferDesc[SAMA5D3_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(8), __section__(".ram_no_cache")));
+//RX buffer descriptors
+static Sama5d3RxBufferDesc rxBufferDesc[SAMA5D3_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(8), __section__(".ram_no_cache")));
+
+#endif
+
+//TX buffer index
+static uint_t txBufferIndex;
+//RX buffer index
+static uint_t rxBufferIndex;
+
+
+/**
+ * @brief SAMA5D3 Ethernet MAC driver
+ **/
+
+const NicDriver sama5d3EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   sama5d3EthInit,
+   sama5d3EthTick,
+   sama5d3EthEnableIrq,
+   sama5d3EthDisableIrq,
+   sama5d3EthEventHandler,
+   sama5d3EthSendPacket,
+   sama5d3EthSetMulticastFilter,
+   sama5d3EthUpdateMacConfig,
+   sama5d3EthWritePhyReg,
+   sama5d3EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief SAMA5D3 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sama5d3EthInit(NetInterface *interface)
+{
+   error_t error;
+   volatile uint32_t status;
+
+   //Debug message
+   TRACE_INFO("Initializing SAMA5D3 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Enable EMAC peripheral clock
+   PMC->PMC_PCER1 = (1 << (ID_EMAC - 32));
+   //Enable IRQ controller peripheral clock
+   PMC->PMC_PCER1 = (1 << (ID_IRQ - 32));
+
+   //GPIO configuration
+   sama5d3EthInitGpio(interface);
+
+   //Configure MDC clock speed
+   EMAC->EMAC_NCFGR = EMAC_NCFGR_CLK_MCK_64;
+   //Enable management port (MDC and MDIO)
+   EMAC->EMAC_NCR |= EMAC_NCR_MPE;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Set the MAC address
+   EMAC->EMAC_SA[0].EMAC_SAxB = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   EMAC->EMAC_SA[0].EMAC_SAxT = interface->macAddr.w[2];
+
+   //Configure the receive filter
+   EMAC->EMAC_NCFGR |= EMAC_NCFGR_UNI | EMAC_NCFGR_MTI;
+
+   //Initialize hash table
+   EMAC->EMAC_HRB = 0;
+   EMAC->EMAC_HRT = 0;
+
+   //Initialize buffer descriptors
+   sama5d3EthInitBufferDesc(interface);
+
+   //Clear transmit status register
+   EMAC->EMAC_TSR = EMAC_TSR_UND | EMAC_TSR_COMP | EMAC_TSR_BEX |
+      EMAC_TSR_TGO | EMAC_TSR_RLES | EMAC_TSR_COL | EMAC_TSR_UBR;
+   //Clear receive status register
+   EMAC->EMAC_RSR = EMAC_RSR_OVR | EMAC_RSR_REC | EMAC_RSR_BNA;
+
+   //First disable all EMAC interrupts
+   EMAC->EMAC_IDR = 0xFFFFFFFF;
+   //Only the desired ones are enabled
+   EMAC->EMAC_IER = EMAC_IER_ROVR | EMAC_IER_TCOMP | EMAC_IER_TXERR |
+      EMAC_IER_RLE | EMAC_IER_TUND | EMAC_IER_RXUBR | EMAC_IER_RCOMP;
+
+   //Read EMAC ISR register to clear any pending interrupt
+   status = EMAC->EMAC_ISR;
+
+   //Configure interrupt controller
+   AIC->AIC_SSR = ID_EMAC;
+   AIC->AIC_SMR = AIC_SMR_SRCTYPE_INT_LEVEL_SENSITIVE | AIC_SMR_PRIOR(SAMA5D3_ETH_IRQ_PRIORITY);
+   AIC->AIC_SVR = (uint32_t) sama5d3EthIrqHandler;
+
+   //Enable the EMAC to transmit and receive data
+   EMAC->EMAC_NCR |= EMAC_NCR_TE | EMAC_NCR_RE;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//SAMA5D3-Xplained evaluation board?
+#if defined(USE_SAMA5D3_XPLAINED)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void sama5d3EthInitGpio(NetInterface *interface)
+{
+   //Enable PIO peripheral clock
+   PMC->PMC_PCER0 = (1 << ID_PIOC);
+
+   //Disable pull-up resistors on RMII pins
+   PIOC->PIO_PUDR = EMAC_RMII_MASK;
+   //Disable interrupts-on-change
+   PIOC->PIO_IDR = EMAC_RMII_MASK;
+   //Assign RMII pins to peripheral A function
+   PIOC->PIO_ABCDSR[0] &= ~EMAC_RMII_MASK;
+   PIOC->PIO_ABCDSR[1] &= ~EMAC_RMII_MASK;
+   //Disable the PIO from controlling the corresponding pins
+   PIOC->PIO_PDR = EMAC_RMII_MASK;
+
+   //Select RMII operation mode and enable transceiver clock
+   EMAC->EMAC_USRIO = EMAC_USRIO_CLKEN | EMAC_USRIO_RMII;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize buffer descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void sama5d3EthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+   uint32_t address;
+
+   //Initialize TX buffer descriptors
+   for(i = 0; i < SAMA5D3_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current TX buffer
+      address = (uint32_t) txBuffer[i];
+      //Write the address to the descriptor entry
+      txBufferDesc[i].address = address;
+      //Initialize status field
+      txBufferDesc[i].status = EMAC_TX_USED;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   txBufferDesc[i - 1].status |= EMAC_TX_WRAP;
+   //Initialize TX buffer index
+   txBufferIndex = 0;
+
+   //Initialize RX buffer descriptors
+   for(i = 0; i < SAMA5D3_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current RX buffer
+      address = (uint32_t) rxBuffer[i];
+      //Write the address to the descriptor entry
+      rxBufferDesc[i].address = address & EMAC_RX_ADDRESS;
+      //Clear status field
+      rxBufferDesc[i].status = 0;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   rxBufferDesc[i - 1].address |= EMAC_RX_WRAP;
+   //Initialize RX buffer index
+   rxBufferIndex = 0;
+
+   //Start location of the TX descriptor list
+   EMAC->EMAC_TBQP = (uint32_t) txBufferDesc;
+   //Start location of the RX descriptor list
+   EMAC->EMAC_RBQP = (uint32_t) rxBufferDesc;
+}
+
+
+/**
+ * @brief SAMA5D3 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void sama5d3EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void sama5d3EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   AIC->AIC_SSR = ID_EMAC;
+   AIC->AIC_IECR = AIC_IECR_INTEN;
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void sama5d3EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   AIC->AIC_SSR = ID_EMAC;
+   AIC->AIC_IDCR = AIC_IDCR_INTD;
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief SAMA5D3 Ethernet MAC interrupt service routine
+ **/
+
+void sama5d3EthIrqHandler(void)
+{
+   bool_t flag;
+   volatile uint32_t isr;
+   volatile uint32_t tsr;
+   volatile uint32_t rsr;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Each time the software reads EMAC_ISR, it has to check the
+   //contents of EMAC_TSR, EMAC_RSR and EMAC_NSR
+   isr = EMAC->EMAC_ISR;
+   tsr = EMAC->EMAC_TSR;
+   rsr = EMAC->EMAC_RSR;
+
+   //A packet has been transmitted?
+   if(tsr & (EMAC_TSR_UND | EMAC_TSR_COMP | EMAC_TSR_BEX |
+      EMAC_TSR_TGO | EMAC_TSR_RLES | EMAC_TSR_COL | EMAC_TSR_UBR))
+   {
+      //Only clear TSR flags that are currently set
+      EMAC->EMAC_TSR = tsr;
+
+      //Check whether the TX buffer is available for writing
+      if(txBufferDesc[txBufferIndex].status & EMAC_TX_USED)
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(rsr & (EMAC_RSR_OVR | EMAC_RSR_REC | EMAC_RSR_BNA))
+   {
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Write AIC_EOICR register before exiting
+   AIC->AIC_EOICR = 0;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief SAMA5D3 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void sama5d3EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint32_t rsr;
+
+   //Read receive status
+   rsr = EMAC->EMAC_RSR;
+
+   //Packet received?
+   if(rsr & (EMAC_RSR_OVR | EMAC_RSR_REC | EMAC_RSR_BNA))
+   {
+      //Only clear RSR flags that are currently set
+      EMAC->EMAC_RSR = rsr;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = sama5d3EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t sama5d3EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > SAMA5D3_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(!(txBufferDesc[txBufferIndex].status & EMAC_TX_USED))
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(txBuffer[txBufferIndex], buffer, offset, length);
+
+   //Set the necessary flags in the descriptor entry
+   if(txBufferIndex < (SAMA5D3_ETH_TX_BUFFER_COUNT - 1))
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status =
+         EMAC_TX_LAST | (length & EMAC_TX_LENGTH);
+
+      //Point to the next buffer
+      txBufferIndex++;
+   }
+   else
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status = EMAC_TX_WRAP |
+         EMAC_TX_LAST | (length & EMAC_TX_LENGTH);
+
+      //Wrap around
+      txBufferIndex = 0;
+   }
+
+   //Set the TSTART bit to initiate transmission
+   EMAC->EMAC_NCR |= EMAC_NCR_TSTART;
+
+   //Check whether the next buffer is available for writing
+   if(txBufferDesc[txBufferIndex].status & EMAC_TX_USED)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sama5d3EthReceivePacket(NetInterface *interface)
+{
+   static uint8_t temp[ETH_MAX_FRAME_SIZE];
+   error_t error;
+   uint_t i;
+   uint_t j;
+   uint_t sofIndex;
+   uint_t eofIndex;
+   size_t n;
+   size_t size;
+   size_t length;
+
+   //Initialize SOF and EOF indices
+   sofIndex = UINT_MAX;
+   eofIndex = UINT_MAX;
+
+   //Search for SOF and EOF flags
+   for(i = 0; i < SAMA5D3_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Point to the current entry
+      j = rxBufferIndex + i;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(j >= SAMA5D3_ETH_RX_BUFFER_COUNT)
+         j -= SAMA5D3_ETH_RX_BUFFER_COUNT;
+
+      //No more entries to process?
+      if(!(rxBufferDesc[j].address & EMAC_RX_OWNERSHIP))
+      {
+         //Stop processing
+         break;
+      }
+      //A valid SOF has been found?
+      if(rxBufferDesc[j].status & EMAC_RX_SOF)
+      {
+         //Save the position of the SOF
+         sofIndex = i;
+      }
+      //A valid EOF has been found?
+      if((rxBufferDesc[j].status & EMAC_RX_EOF) && sofIndex != UINT_MAX)
+      {
+         //Save the position of the EOF
+         eofIndex = i;
+         //Retrieve the length of the frame
+         size = rxBufferDesc[j].status & EMAC_RX_LENGTH;
+         //Limit the number of data to read
+         size = MIN(size, ETH_MAX_FRAME_SIZE);
+         //Stop processing since we have reached the end of the frame
+         break;
+      }
+   }
+
+   //Determine the number of entries to process
+   if(eofIndex != UINT_MAX)
+      j = eofIndex + 1;
+   else if(sofIndex != UINT_MAX)
+      j = sofIndex;
+   else
+      j = i;
+
+   //Total number of bytes that have been copied from the receive buffer
+   length = 0;
+
+   //Process incoming frame
+   for(i = 0; i < j; i++)
+   {
+      //Any data to copy from current buffer?
+      if(eofIndex != UINT_MAX && i >= sofIndex && i <= eofIndex)
+      {
+         //Calculate the number of bytes to read at a time
+         n = MIN(size, SAMA5D3_ETH_RX_BUFFER_SIZE);
+         //Copy data from receive buffer
+         memcpy(temp + length, rxBuffer[rxBufferIndex], n);
+         //Update byte counters
+         length += n;
+         size -= n;
+      }
+
+      //Mark the current buffer as free
+      rxBufferDesc[rxBufferIndex].address &= ~EMAC_RX_OWNERSHIP;
+
+      //Point to the following entry
+      rxBufferIndex++;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(rxBufferIndex >= SAMA5D3_ETH_RX_BUFFER_COUNT)
+         rxBufferIndex = 0;
+   }
+
+   //Any packet to process?
+   if(length > 0)
+   {
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, temp, length);
+      //Valid packet received
+      error = NO_ERROR;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sama5d3EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint8_t *p;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating SAMA5D3 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Point to the MAC address
+         p = entry->addr.b;
+
+         //Apply the hash function
+         k = (p[0] >> 6) ^ p[0];
+         k ^= (p[1] >> 4) ^ (p[1] << 2);
+         k ^= (p[2] >> 2) ^ (p[2] << 4);
+         k ^= (p[3] >> 6) ^ p[3];
+         k ^= (p[4] >> 4) ^ (p[4] << 2);
+         k ^= (p[5] >> 2) ^ (p[5] << 4);
+
+         //The hash value is reduced to a 6-bit index
+         k &= 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   EMAC->EMAC_HRB = hashTable[0];
+   EMAC->EMAC_HRT = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HRB = %08" PRIX32 "\r\n", EMAC->EMAC_HRB);
+   TRACE_DEBUG("  HRT = %08" PRIX32 "\r\n", EMAC->EMAC_HRT);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sama5d3EthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read network configuration register
+   config = EMAC->EMAC_NCFGR;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= EMAC_NCFGR_SPD;
+   else
+      config &= ~EMAC_NCFGR_SPD;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= EMAC_NCFGR_FD;
+   else
+      config &= ~EMAC_NCFGR_FD;
+
+   //Write configuration value back to NCFGR register
+   EMAC->EMAC_NCFGR = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void sama5d3EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Set up a write operation
+   value = EMAC_MAN_SOF(1) | EMAC_MAN_RW(1) | EMAC_MAN_CODE(2);
+   //PHY address
+   value |= EMAC_MAN_PHYA(phyAddr);
+   //Register address
+   value |= EMAC_MAN_REGA(regAddr);
+   //Register value
+   value |= EMAC_MAN_DATA(data);
+
+   //Start a write operation
+   EMAC->EMAC_MAN = value;
+   //Wait for the write to complete
+   while(!(EMAC->EMAC_NSR & EMAC_NSR_IDLE));
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t sama5d3EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Set up a read operation
+   value = EMAC_MAN_SOF(1) | EMAC_MAN_RW(2) | EMAC_MAN_CODE(2);
+   //PHY address
+   value |= EMAC_MAN_PHYA(phyAddr);
+   //Register address
+   value |= EMAC_MAN_REGA(regAddr);
+
+   //Start a read operation
+   EMAC->EMAC_MAN = value;
+   //Wait for the read to complete
+   while(!(EMAC->EMAC_NSR & EMAC_NSR_IDLE));
+
+   //Return PHY register contents
+   return EMAC->EMAC_MAN & EMAC_MAN_DATA_Msk;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/sama5d3_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,158 @@
+/**
+ * @file sama5d3_eth.h
+ * @brief SAMA5D3 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SAMA5D3_ETH_H
+#define _SAMA5D3_ETH_H
+
+//Number of TX buffers
+#ifndef SAMA5D3_ETH_TX_BUFFER_COUNT
+   #define SAMA5D3_ETH_TX_BUFFER_COUNT 4
+#elif (SAMA5D3_ETH_TX_BUFFER_COUNT < 1)
+   #error SAMA5D3_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef SAMA5D3_ETH_TX_BUFFER_SIZE
+   #define SAMA5D3_ETH_TX_BUFFER_SIZE 1536
+#elif (SAMA5D3_ETH_TX_BUFFER_SIZE != 1536)
+   #error SAMA5D3_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef SAMA5D3_ETH_RX_BUFFER_COUNT
+   #define SAMA5D3_ETH_RX_BUFFER_COUNT 96
+#elif (SAMA5D3_ETH_RX_BUFFER_COUNT < 12)
+   #error SAMA5D3_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef SAMA5D3_ETH_RX_BUFFER_SIZE
+   #define SAMA5D3_ETH_RX_BUFFER_SIZE 128
+#elif (SAMA5D3_ETH_RX_BUFFER_SIZE != 128)
+   #error SAMA5D3_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Ethernet interrupt priority
+#ifndef SAMA5D3_ETH_IRQ_PRIORITY
+   #define SAMA5D3_ETH_IRQ_PRIORITY 0
+#elif (SAMA5D3_ETH_IRQ_PRIORITY < 0)
+   #error SAMA5D3_ETH_IRQ_PRIORITY parameter is not valid
+#endif
+
+//RMII signals
+#define EMAC_RMII_MASK (PIO_PC9A_EMDIO | PIO_PC8A_EMDC | \
+   PIO_PC7A_EREFCK | PIO_PC6A_ERXER | PIO_PC5A_ECRSDV | PIO_PC4A_ETXEN | \
+   PIO_PC3A_ERX1 | PIO_PC2A_ERX0 | PIO_PC1A_ETX1 | PIO_PC0A_ETX0)
+
+//TX buffer descriptor flags
+#define EMAC_TX_USED           0x80000000
+#define EMAC_TX_WRAP           0x40000000
+#define EMAC_TX_ERROR          0x20000000
+#define EMAC_TX_UNDERRUN       0x10000000
+#define EMAC_TX_EXHAUSTED      0x08000000
+#define EMAC_TX_NO_CRC         0x00010000
+#define EMAC_TX_LAST           0x00008000
+#define EMAC_TX_LENGTH         0x000007FF
+
+//RX buffer descriptor flags
+#define EMAC_RX_ADDRESS        0xFFFFFFFC
+#define EMAC_RX_WRAP           0x00000002
+#define EMAC_RX_OWNERSHIP      0x00000001
+#define EMAC_RX_BROADCAST      0x80000000
+#define EMAC_RX_MULTICAST_HASH 0x40000000
+#define EMAC_RX_UNICAST_HASH   0x20000000
+#define EMAC_RX_EXT_ADDR       0x10000000
+#define EMAC_RX_SAR1           0x04000000
+#define EMAC_RX_SAR2           0x02000000
+#define EMAC_RX_SAR3           0x01000000
+#define EMAC_RX_SAR4           0x00800000
+#define EMAC_RX_TYPE_ID        0x00400000
+#define EMAC_RX_VLAN_TAG       0x00200000
+#define EMAC_RX_PRIORITY_TAG   0x00100000
+#define EMAC_RX_VLAN_PRIORITY  0x000E0000
+#define EMAC_RX_CFI            0x00010000
+#define EMAC_RX_EOF            0x00008000
+#define EMAC_RX_SOF            0x00004000
+#define EMAC_RX_OFFSET         0x00003000
+#define EMAC_RX_LENGTH         0x00000FFF
+
+
+#if !defined(_SAMA5D3_GIGABIT_ETH_H)
+
+/**
+ * @brief Transmit buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Sama5d3TxBufferDesc;
+
+
+/**
+ * @brief Receive buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Sama5d3RxBufferDesc;
+
+#endif
+
+
+//SAMA5D3 Ethernet MAC driver
+extern const NicDriver sama5d3EthDriver;
+
+//SAMA5D3 Ethernet MAC related functions
+error_t sama5d3EthInit(NetInterface *interface);
+void sama5d3EthInitGpio(NetInterface *interface);
+void sama5d3EthInitBufferDesc(NetInterface *interface);
+
+void sama5d3EthTick(NetInterface *interface);
+
+void sama5d3EthEnableIrq(NetInterface *interface);
+void sama5d3EthDisableIrq(NetInterface *interface);
+void sama5d3EthIrqHandler(void);
+void sama5d3EthEventHandler(NetInterface *interface);
+
+error_t sama5d3EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t sama5d3EthReceivePacket(NetInterface *interface);
+
+error_t sama5d3EthSetMulticastFilter(NetInterface *interface);
+error_t sama5d3EthUpdateMacConfig(NetInterface *interface);
+
+void sama5d3EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t sama5d3EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/sama5d3_gigabit_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,747 @@
+/**
+ * @file sama5d3_gigabit_eth.c
+ * @brief SAMA5D3 Gigabit Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include <limits.h>
+#include "sama5d3x.h"
+#include "core/net.h"
+#include "drivers/sama5d3_gigabit_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//TX buffer
+#pragma data_alignment = 8
+#pragma location = ".ram_no_cache"
+static uint8_t txBuffer[SAMA5D3_GIGABIT_ETH_TX_BUFFER_COUNT][SAMA5D3_GIGABIT_ETH_TX_BUFFER_SIZE];
+//RX buffer
+#pragma data_alignment = 8
+#pragma location = ".ram_no_cache"
+static uint8_t rxBuffer[SAMA5D3_GIGABIT_ETH_RX_BUFFER_COUNT][SAMA5D3_GIGABIT_ETH_RX_BUFFER_SIZE];
+//TX buffer descriptors
+#pragma data_alignment = 8
+#pragma location = ".ram_no_cache"
+static Sama5d3TxBufferDesc txBufferDesc[SAMA5D3_GIGABIT_ETH_TX_BUFFER_COUNT];
+//RX buffer descriptors
+#pragma data_alignment = 8
+#pragma location = ".ram_no_cache"
+static Sama5d3RxBufferDesc rxBufferDesc[SAMA5D3_GIGABIT_ETH_RX_BUFFER_COUNT];
+
+//GCC compiler?
+#else
+
+//TX buffer
+static uint8_t txBuffer[SAMA5D3_GIGABIT_ETH_TX_BUFFER_COUNT][SAMA5D3_GIGABIT_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(8), __section__(".ram_no_cache")));
+//RX buffer
+static uint8_t rxBuffer[SAMA5D3_GIGABIT_ETH_RX_BUFFER_COUNT][SAMA5D3_GIGABIT_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(8), __section__(".ram_no_cache")));
+//TX buffer descriptors
+static Sama5d3TxBufferDesc txBufferDesc[SAMA5D3_GIGABIT_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(8), __section__(".ram_no_cache")));
+//RX buffer descriptors
+static Sama5d3RxBufferDesc rxBufferDesc[SAMA5D3_GIGABIT_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(8), __section__(".ram_no_cache")));
+
+#endif
+
+//TX buffer index
+static uint_t txBufferIndex;
+//RX buffer index
+static uint_t rxBufferIndex;
+
+
+/**
+ * @brief SAMA5D3 Gigabit Ethernet MAC driver
+ **/
+
+const NicDriver sama5d3GigabitEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   sama5d3GigabitEthInit,
+   sama5d3GigabitEthTick,
+   sama5d3GigabitEthEnableIrq,
+   sama5d3GigabitEthDisableIrq,
+   sama5d3GigabitEthEventHandler,
+   sama5d3GigabitEthSendPacket,
+   sama5d3GigabitEthSetMulticastFilter,
+   sama5d3GigabitEthUpdateMacConfig,
+   sama5d3GigabitEthWritePhyReg,
+   sama5d3GigabitEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief SAMA5D3 Gigabit Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sama5d3GigabitEthInit(NetInterface *interface)
+{
+   error_t error;
+   volatile uint32_t status;
+
+   //Debug message
+   TRACE_INFO("Initializing SAMA5D3 Gigabit Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Enable GMAC peripheral clock
+   PMC->PMC_PCER1 = (1 << (ID_GMAC - 32));
+   //Enable IRQ controller peripheral clock
+   PMC->PMC_PCER1 = (1 << (ID_IRQ - 32));
+
+   //GPIO configuration
+   sama5d3GigabitEthInitGpio(interface);
+
+   //Configure MDC clock speed
+   GMAC->GMAC_NCFGR = GMAC_NCFGR_DBW_DBW64 | GMAC_NCFGR_CLK_MCK_224;
+   //Enable management port (MDC and MDIO)
+   GMAC->GMAC_NCR |= GMAC_NCR_MPE;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Set the MAC address
+   GMAC->GMAC_SA[0].GMAC_SAB = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   GMAC->GMAC_SA[0].GMAC_SAT = interface->macAddr.w[2];
+
+   //Configure the receive filter
+   GMAC->GMAC_NCFGR |= GMAC_NCFGR_UNIHEN | GMAC_NCFGR_MTIHEN;
+
+   //Initialize hash table
+   GMAC->GMAC_HRB = 0;
+   GMAC->GMAC_HRT = 0;
+
+   //Initialize buffer descriptors
+   sama5d3GigabitEthInitBufferDesc(interface);
+
+   //Clear transmit status register
+   GMAC->GMAC_TSR = GMAC_TSR_HRESP | GMAC_TSR_UND | GMAC_TSR_TXCOMP | GMAC_TSR_TFC |
+      GMAC_TSR_TXGO | GMAC_TSR_RLE | GMAC_TSR_COL | GMAC_TSR_UBR;
+   //Clear receive status register
+   GMAC->GMAC_RSR = GMAC_RSR_HNO | GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA;
+
+   //First disable all GMAC interrupts
+   GMAC->GMAC_IDR = 0xFFFFFFFF;
+   //Only the desired ones are enabled
+   GMAC->GMAC_IER = GMAC_IER_HRESP | GMAC_IER_ROVR | GMAC_IER_TCOMP | GMAC_IER_TFC |
+      GMAC_IER_RLEX | GMAC_IER_TUR | GMAC_IER_RXUBR | GMAC_IER_RCOMP;
+
+   //Read GMAC ISR register to clear any pending interrupt
+   status = GMAC->GMAC_ISR;
+
+   //Configure interrupt controller
+   AIC->AIC_SSR = ID_GMAC;
+   AIC->AIC_SMR = AIC_SMR_SRCTYPE_INT_LEVEL_SENSITIVE | AIC_SMR_PRIOR(SAMA5D3_GIGABIT_ETH_IRQ_PRIORITY);
+   AIC->AIC_SVR = (uint32_t) sama5d3GigabitEthIrqHandler;
+
+   //Enable the GMAC to transmit and receive data
+   GMAC->GMAC_NCR |= GMAC_NCR_TXEN | GMAC_NCR_RXEN;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//SAMA5D3-Xplained evaluation board?
+#if defined(USE_SAMA5D3_XPLAINED)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void sama5d3GigabitEthInitGpio(NetInterface *interface)
+{
+   //Enable PIO peripheral clock
+   PMC->PMC_PCER0 = (1 << ID_PIOB);
+
+   //Disable pull-up resistors on RGMII pins
+   PIOB->PIO_PUDR = GMAC_RGMII_MASK;
+   //Disable interrupts-on-change
+   PIOB->PIO_IDR = GMAC_RGMII_MASK;
+   //Assign MII pins to peripheral A function
+   PIOB->PIO_ABCDSR[0] &= ~GMAC_RGMII_MASK;
+   PIOB->PIO_ABCDSR[1] &= ~GMAC_RGMII_MASK;
+   //Disable the PIO from controlling the corresponding pins
+   PIOB->PIO_PDR = GMAC_RGMII_MASK;
+
+   //Select RGMII operation mode
+   GMAC->GMAC_UR = GMAC_UR_RGMII;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize buffer descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void sama5d3GigabitEthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+   uint32_t address;
+
+   //Initialize TX buffer descriptors
+   for(i = 0; i < SAMA5D3_GIGABIT_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current TX buffer
+      address = (uint32_t) txBuffer[i];
+      //Write the address to the descriptor entry
+      txBufferDesc[i].address = address;
+      //Initialize status field
+      txBufferDesc[i].status = GMAC_TX_USED;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   txBufferDesc[i - 1].status |= GMAC_TX_WRAP;
+   //Initialize TX buffer index
+   txBufferIndex = 0;
+
+   //Initialize RX buffer descriptors
+   for(i = 0; i < SAMA5D3_GIGABIT_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current RX buffer
+      address = (uint32_t) rxBuffer[i];
+      //Write the address to the descriptor entry
+      rxBufferDesc[i].address = address & GMAC_RX_ADDRESS;
+      //Clear status field
+      rxBufferDesc[i].status = 0;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   rxBufferDesc[i - 1].address |= GMAC_RX_WRAP;
+   //Initialize RX buffer index
+   rxBufferIndex = 0;
+
+   //Start location of the TX descriptor list
+   GMAC->GMAC_TBQB = (uint32_t) txBufferDesc;
+   //Start location of the RX descriptor list
+   GMAC->GMAC_RBQB = (uint32_t) rxBufferDesc;
+}
+
+
+/**
+ * @brief SAMA5D3 Gigabit Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void sama5d3GigabitEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void sama5d3GigabitEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   AIC->AIC_SSR = ID_GMAC;
+   AIC->AIC_IECR = AIC_IECR_INTEN;
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void sama5d3GigabitEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   AIC->AIC_SSR = ID_GMAC;
+   AIC->AIC_IDCR = AIC_IDCR_INTD;
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief SAMA5D3 Gigabit Ethernet MAC interrupt service routine
+ **/
+
+void sama5d3GigabitEthIrqHandler(void)
+{
+   bool_t flag;
+   volatile uint32_t isr;
+   volatile uint32_t tsr;
+   volatile uint32_t rsr;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Each time the software reads GMAC_ISR, it has to check the
+   //contents of GMAC_TSR, GMAC_RSR and GMAC_NSR
+   isr = GMAC->GMAC_ISR;
+   tsr = GMAC->GMAC_TSR;
+   rsr = GMAC->GMAC_RSR;
+
+   //A packet has been transmitted?
+   if(tsr & (GMAC_TSR_HRESP | GMAC_TSR_UND | GMAC_TSR_TXCOMP | GMAC_TSR_TFC |
+      GMAC_TSR_TXGO | GMAC_TSR_RLE | GMAC_TSR_COL | GMAC_TSR_UBR))
+   {
+      //Only clear TSR flags that are currently set
+      GMAC->GMAC_TSR = tsr;
+
+      //Avoid DMA lockup by sending only one frame at a time (see errata 57.5.1)
+      if((txBufferDesc[0].status & GMAC_TX_USED) &&
+         (txBufferDesc[1].status & GMAC_TX_USED))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(rsr & (GMAC_RSR_HNO | GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA))
+   {
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Write AIC_EOICR register before exiting
+   AIC->AIC_EOICR = 0;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief SAMA5D3 Gigabit Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void sama5d3GigabitEthEventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint32_t rsr;
+
+   //Read receive status
+   rsr = GMAC->GMAC_RSR;
+
+   //Packet received?
+   if(rsr & (GMAC_RSR_HNO | GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA))
+   {
+      //Only clear RSR flags that are currently set
+      GMAC->GMAC_RSR = rsr;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = sama5d3GigabitEthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t sama5d3GigabitEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > SAMA5D3_GIGABIT_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(!(txBufferDesc[txBufferIndex].status & GMAC_TX_USED))
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(txBuffer[txBufferIndex], buffer, offset, length);
+
+   //Set the necessary flags in the descriptor entry
+   if(txBufferIndex < (SAMA5D3_GIGABIT_ETH_TX_BUFFER_COUNT - 1))
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status =
+         GMAC_TX_LAST | (length & GMAC_TX_LENGTH);
+
+      //Point to the next buffer
+      txBufferIndex++;
+   }
+   else
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status = GMAC_TX_WRAP |
+         GMAC_TX_LAST | (length & GMAC_TX_LENGTH);
+
+      //Wrap around
+      txBufferIndex = 0;
+   }
+
+   //Set the TSTART bit to initiate transmission
+   GMAC->GMAC_NCR |= GMAC_NCR_TSTART;
+
+   //Check whether the next buffer is available for writing
+   if(txBufferDesc[txBufferIndex].status & GMAC_TX_USED)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sama5d3GigabitEthReceivePacket(NetInterface *interface)
+{
+   static uint8_t temp[ETH_MAX_FRAME_SIZE];
+   error_t error;
+   uint_t i;
+   uint_t j;
+   uint_t sofIndex;
+   uint_t eofIndex;
+   size_t n;
+   size_t size;
+   size_t length;
+
+   //Initialize SOF and EOF indices
+   sofIndex = UINT_MAX;
+   eofIndex = UINT_MAX;
+
+   //Search for SOF and EOF flags
+   for(i = 0; i < SAMA5D3_GIGABIT_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Point to the current entry
+      j = rxBufferIndex + i;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(j >= SAMA5D3_GIGABIT_ETH_RX_BUFFER_COUNT)
+         j -= SAMA5D3_GIGABIT_ETH_RX_BUFFER_COUNT;
+
+      //No more entries to process?
+      if(!(rxBufferDesc[j].address & GMAC_RX_OWNERSHIP))
+      {
+         //Stop processing
+         break;
+      }
+      //A valid SOF has been found?
+      if(rxBufferDesc[j].status & GMAC_RX_SOF)
+      {
+         //Save the position of the SOF
+         sofIndex = i;
+      }
+      //A valid EOF has been found?
+      if((rxBufferDesc[j].status & GMAC_RX_EOF) && sofIndex != UINT_MAX)
+      {
+         //Save the position of the EOF
+         eofIndex = i;
+         //Retrieve the length of the frame
+         size = rxBufferDesc[j].status & GMAC_RX_LENGTH;
+         //Limit the number of data to read
+         size = MIN(size, ETH_MAX_FRAME_SIZE);
+         //Stop processing since we have reached the end of the frame
+         break;
+      }
+   }
+
+   //Determine the number of entries to process
+   if(eofIndex != UINT_MAX)
+      j = eofIndex + 1;
+   else if(sofIndex != UINT_MAX)
+      j = sofIndex;
+   else
+      j = i;
+
+   //Total number of bytes that have been copied from the receive buffer
+   length = 0;
+
+   //Process incoming frame
+   for(i = 0; i < j; i++)
+   {
+      //Any data to copy from current buffer?
+      if(eofIndex != UINT_MAX && i >= sofIndex && i <= eofIndex)
+      {
+         //Calculate the number of bytes to read at a time
+         n = MIN(size, SAMA5D3_GIGABIT_ETH_RX_BUFFER_SIZE);
+         //Copy data from receive buffer
+         memcpy(temp + length, rxBuffer[rxBufferIndex], n);
+         //Update byte counters
+         length += n;
+         size -= n;
+      }
+
+      //Mark the current buffer as free
+      rxBufferDesc[rxBufferIndex].address &= ~GMAC_RX_OWNERSHIP;
+
+      //Point to the following entry
+      rxBufferIndex++;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(rxBufferIndex >= SAMA5D3_GIGABIT_ETH_RX_BUFFER_COUNT)
+         rxBufferIndex = 0;
+   }
+
+   //Any packet to process?
+   if(length > 0)
+   {
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, temp, length);
+      //Valid packet received
+      error = NO_ERROR;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sama5d3GigabitEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint8_t *p;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating SAMA5D3 Gigabit hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Point to the MAC address
+         p = entry->addr.b;
+
+         //Apply the hash function
+         k = (p[0] >> 6) ^ p[0];
+         k ^= (p[1] >> 4) ^ (p[1] << 2);
+         k ^= (p[2] >> 2) ^ (p[2] << 4);
+         k ^= (p[3] >> 6) ^ p[3];
+         k ^= (p[4] >> 4) ^ (p[4] << 2);
+         k ^= (p[5] >> 2) ^ (p[5] << 4);
+
+         //The hash value is reduced to a 6-bit index
+         k &= 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   GMAC->GMAC_HRB = hashTable[0];
+   GMAC->GMAC_HRT = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HRB = %08" PRIX32 "\r\n", GMAC->GMAC_HRB);
+   TRACE_DEBUG("  HRT = %08" PRIX32 "\r\n", GMAC->GMAC_HRT);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t sama5d3GigabitEthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read network configuration register
+   config = GMAC->GMAC_NCFGR;
+
+   //1000BASE-T operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_1GBPS)
+   {
+      config |= GMAC_NCFGR_GBE;
+      config &= ~GMAC_NCFGR_SPD;
+   }
+   //100BASE-TX operation mode?
+   else if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+   {
+      config &= ~GMAC_NCFGR_GBE;
+      config |= GMAC_NCFGR_SPD;
+   }
+   //10BASE-T operation mode?
+   else
+   {
+      config &= ~GMAC_NCFGR_GBE;
+      config &= ~GMAC_NCFGR_SPD;
+   }
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= GMAC_NCFGR_FD;
+   else
+      config &= ~GMAC_NCFGR_FD;
+
+   //Write configuration value back to NCFGR register
+   GMAC->GMAC_NCFGR = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void sama5d3GigabitEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Set up a write operation
+   value = GMAC_MAN_CLTTO | GMAC_MAN_OP(1) | GMAC_MAN_WTN(2);
+   //PHY address
+   value |= GMAC_MAN_PHYA(phyAddr);
+   //Register address
+   value |= GMAC_MAN_REGA(regAddr);
+   //Register value
+   value |= GMAC_MAN_DATA(data);
+
+   //Start a write operation
+   GMAC->GMAC_MAN = value;
+   //Wait for the write to complete
+   while(!(GMAC->GMAC_NSR & GMAC_NSR_IDLE));
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t sama5d3GigabitEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Set up a read operation
+   value = GMAC_MAN_CLTTO | GMAC_MAN_OP(2) | GMAC_MAN_WTN(2);
+   //PHY address
+   value |= GMAC_MAN_PHYA(phyAddr);
+   //Register address
+   value |= GMAC_MAN_REGA(regAddr);
+
+   //Start a read operation
+   GMAC->GMAC_MAN = value;
+   //Wait for the read to complete
+   while(!(GMAC->GMAC_NSR & GMAC_NSR_IDLE));
+
+   //Return PHY register contents
+   return GMAC->GMAC_MAN & GMAC_MAN_DATA_Msk;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/sama5d3_gigabit_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,164 @@
+/**
+ * @file sama5d3_gigabit_eth.h
+ * @brief SAMA5D3 Gigabit Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SAMA5D3_GIGABIT_ETH_H
+#define _SAMA5D3_GIGABIT_ETH_H
+
+//Number of TX buffers
+#ifndef SAMA5D3_GIGABIT_ETH_TX_BUFFER_COUNT
+   #define SAMA5D3_GIGABIT_ETH_TX_BUFFER_COUNT 2
+#elif (SAMA5D3_GIGABIT_ETH_TX_BUFFER_COUNT != 2)
+   #error SAMA5D3_GIGABIT_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef SAMA5D3_GIGABIT_ETH_TX_BUFFER_SIZE
+   #define SAMA5D3_GIGABIT_ETH_TX_BUFFER_SIZE 1536
+#elif (SAMA5D3_GIGABIT_ETH_TX_BUFFER_SIZE != 1536)
+   #error SAMA5D3_GIGABIT_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef SAMA5D3_GIGABIT_ETH_RX_BUFFER_COUNT
+   #define SAMA5D3_GIGABIT_ETH_RX_BUFFER_COUNT 96
+#elif (SAMA5D3_GIGABIT_ETH_RX_BUFFER_COUNT < 12)
+   #error SAMA5D3_GIGABIT_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef SAMA5D3_GIGABIT_ETH_RX_BUFFER_SIZE
+   #define SAMA5D3_GIGABIT_ETH_RX_BUFFER_SIZE 128
+#elif (SAMA5D3_GIGABIT_ETH_RX_BUFFER_SIZE != 128)
+   #error SAMA5D3_GIGABIT_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Gigabit Ethernet interrupt priority
+#ifndef SAMA5D3_GIGABIT_ETH_IRQ_PRIORITY
+   #define SAMA5D3_GIGABIT_ETH_IRQ_PRIORITY 0
+#elif (SAMA5D3_GIGABIT_ETH_IRQ_PRIORITY < 0)
+   #error SAMA5D3_GIGABIT_ETH_IRQ_PRIORITY parameter is not valid
+#endif
+
+//RGMII signals
+#define GMAC_RGMII_MASK (PIO_PB18A_G125CK | \
+   PIO_PB17A_GMDIO | PIO_PB16A_GMDC | PIO_PB13A_GRXER | \
+   PIO_PB12A_GRXDV | PIO_PB11A_GRXCK | PIO_PB9A_GTXEN | \
+   PIO_PB8A_GTXCK | PIO_PB7A_GRX3 | PIO_PB6A_GRX2 | \
+   PIO_PB5A_GRX1 | PIO_PB4A_GRX0 | PIO_PB3A_GTX3 | \
+   PIO_PB2A_GTX2 | PIO_PB1A_GTX1 | PIO_PB0A_GTX0)
+
+//TX buffer descriptor flags
+#define GMAC_TX_USED           0x80000000
+#define GMAC_TX_WRAP           0x40000000
+#define GMAC_TX_RLE_ERROR      0x20000000
+#define GMAC_TX_UNDERRUN_ERROR 0x10000000
+#define GMAC_TX_AHB_ERROR      0x08000000
+#define GMAC_TX_LATE_COL_ERROR 0x04000000
+#define GMAC_TX_CHECKSUM_ERROR 0x00700000
+#define GMAC_TX_NO_CRC         0x00010000
+#define GMAC_TX_LAST           0x00008000
+#define GMAC_TX_LENGTH         0x00003FFF
+
+//RX buffer descriptor flags
+#define GMAC_RX_ADDRESS        0xFFFFFFFC
+#define GMAC_RX_WRAP           0x00000002
+#define GMAC_RX_OWNERSHIP      0x00000001
+#define GMAC_RX_BROADCAST      0x80000000
+#define GMAC_RX_MULTICAST_HASH 0x40000000
+#define GMAC_RX_UNICAST_HASH   0x20000000
+#define GMAC_RX_SAR            0x08000000
+#define GMAC_RX_SAR_MASK       0x06000000
+#define GMAC_RX_TYPE_ID        0x01000000
+#define GMAC_RX_SNAP           0x01000000
+#define GMAC_RX_TYPE_ID_MASK   0x00C00000
+#define GMAC_RX_CHECKSUM_VALID 0x00C00000
+#define GMAC_RX_VLAN_TAG       0x00200000
+#define GMAC_RX_PRIORITY_TAG   0x00100000
+#define GMAC_RX_VLAN_PRIORITY  0x000E0000
+#define GMAC_RX_CFI            0x00010000
+#define GMAC_RX_EOF            0x00008000
+#define GMAC_RX_SOF            0x00004000
+#define GMAC_RX_LENGTH_MSB     0x00002000
+#define GMAC_RX_BAD_FCS        0x00002000
+#define GMAC_RX_LENGTH         0x00001FFF
+
+
+#if !defined(_SAMA5D3_ETH_H)
+
+/**
+ * @brief Transmit buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Sama5d3TxBufferDesc;
+
+
+/**
+ * @brief Receive buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Sama5d3RxBufferDesc;
+
+#endif
+
+
+//SAMA5D3 Gigabit Ethernet MAC driver
+extern const NicDriver sama5d3GigabitEthDriver;
+
+//SAMA5D3 Gigabit Ethernet MAC related functions
+error_t sama5d3GigabitEthInit(NetInterface *interface);
+void sama5d3GigabitEthInitGpio(NetInterface *interface);
+void sama5d3GigabitEthInitBufferDesc(NetInterface *interface);
+
+void sama5d3GigabitEthTick(NetInterface *interface);
+
+void sama5d3GigabitEthEnableIrq(NetInterface *interface);
+void sama5d3GigabitEthDisableIrq(NetInterface *interface);
+void sama5d3GigabitEthIrqHandler(void);
+void sama5d3GigabitEthEventHandler(NetInterface *interface);
+
+error_t sama5d3GigabitEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t sama5d3GigabitEthReceivePacket(NetInterface *interface);
+
+error_t sama5d3GigabitEthSetMulticastFilter(NetInterface *interface);
+error_t sama5d3GigabitEthUpdateMacConfig(NetInterface *interface);
+
+void sama5d3GigabitEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t sama5d3GigabitEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/same70_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,820 @@
+/**
+ * @file same70_eth.c
+ * @brief SAME70 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include <limits.h>
+#include "same70.h"
+#include "core/net.h"
+#include "drivers/same70_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//TX buffer
+#pragma data_alignment = 8
+#pragma location = ".ram_no_cache"
+static uint8_t txBuffer[SAME70_ETH_TX_BUFFER_COUNT][SAME70_ETH_TX_BUFFER_SIZE];
+//RX buffer
+#pragma data_alignment = 8
+#pragma location = ".ram_no_cache"
+static uint8_t rxBuffer[SAME70_ETH_RX_BUFFER_COUNT][SAME70_ETH_RX_BUFFER_SIZE];
+//TX buffer descriptors
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static Same70TxBufferDesc txBufferDesc[SAME70_ETH_TX_BUFFER_COUNT];
+//RX buffer descriptors
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static Same70RxBufferDesc rxBufferDesc[SAME70_ETH_RX_BUFFER_COUNT];
+
+//Dummy TX buffer
+#pragma data_alignment = 8
+#pragma location = ".ram_no_cache"
+static uint8_t dummyTxBuffer[SAME70_ETH_DUMMY_BUFFER_COUNT][SAME70_ETH_DUMMY_BUFFER_SIZE];
+//Dummy RX buffer
+#pragma data_alignment = 8
+#pragma location = ".ram_no_cache"
+static uint8_t dummyRxBuffer[SAME70_ETH_DUMMY_BUFFER_COUNT][SAME70_ETH_DUMMY_BUFFER_SIZE];
+//Dummy TX buffer descriptors
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static Same70TxBufferDesc dummyTxBufferDesc[SAME70_ETH_DUMMY_BUFFER_COUNT];
+//Dummy RX buffer descriptors
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static Same70RxBufferDesc dummyRxBufferDesc[SAME70_ETH_DUMMY_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//TX buffer
+static uint8_t txBuffer[SAME70_ETH_TX_BUFFER_COUNT][SAME70_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(8), __section__(".ram_no_cache")));
+//RX buffer
+static uint8_t rxBuffer[SAME70_ETH_RX_BUFFER_COUNT][SAME70_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(8), __section__(".ram_no_cache")));
+//TX buffer descriptors
+static Same70TxBufferDesc txBufferDesc[SAME70_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+//RX buffer descriptors
+static Same70RxBufferDesc rxBufferDesc[SAME70_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+
+//Dummy TX buffer
+static uint8_t dummyTxBuffer[SAME70_ETH_DUMMY_BUFFER_COUNT][SAME70_ETH_DUMMY_BUFFER_SIZE]
+   __attribute__((aligned(8), __section__(".ram_no_cache")));
+//Dummy RX buffer
+static uint8_t dummyRxBuffer[SAME70_ETH_DUMMY_BUFFER_COUNT][SAME70_ETH_DUMMY_BUFFER_SIZE]
+   __attribute__((aligned(8), __section__(".ram_no_cache")));
+//Dummy TX buffer descriptors
+static Same70TxBufferDesc dummyTxBufferDesc[SAME70_ETH_DUMMY_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+//Dummy RX buffer descriptors
+static Same70RxBufferDesc dummyRxBufferDesc[SAME70_ETH_DUMMY_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+
+#endif
+
+//TX buffer index
+static uint_t txBufferIndex;
+//RX buffer index
+static uint_t rxBufferIndex;
+
+
+/**
+ * @brief SAME70 Ethernet MAC driver
+ **/
+
+const NicDriver same70EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   same70EthInit,
+   same70EthTick,
+   same70EthEnableIrq,
+   same70EthDisableIrq,
+   same70EthEventHandler,
+   same70EthSendPacket,
+   same70EthSetMulticastFilter,
+   same70EthUpdateMacConfig,
+   same70EthWritePhyReg,
+   same70EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief SAME70 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t same70EthInit(NetInterface *interface)
+{
+   error_t error;
+   volatile uint32_t status;
+
+   //Debug message
+   TRACE_INFO("Initializing SAME70 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Enable GMAC peripheral clock
+   PMC->PMC_PCER1 = (1 << (ID_GMAC - 32));
+
+   //GPIO configuration
+   same70EthInitGpio(interface);
+
+   //Configure MDC clock speed
+   GMAC->GMAC_NCFGR = GMAC_NCFGR_CLK_MCK_96;
+   //Enable management port (MDC and MDIO)
+   GMAC->GMAC_NCR |= GMAC_NCR_MPE;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Set the MAC address
+   GMAC->GMAC_SA[0].GMAC_SAB = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   GMAC->GMAC_SA[0].GMAC_SAT = interface->macAddr.w[2];
+
+   //Configure the receive filter
+   GMAC->GMAC_NCFGR |= GMAC_NCFGR_UNIHEN | GMAC_NCFGR_MTIHEN;
+
+   //DMA configuration
+   GMAC->GMAC_DCFGR = GMAC_DCFGR_DRBS(SAME70_ETH_RX_BUFFER_SIZE / 64) |
+      GMAC_DCFGR_TXPBMS | GMAC_DCFGR_RXBMS_FULL | GMAC_DCFGR_FBLDO_INCR4;
+
+   GMAC->GMAC_RBSRPQ[0] = GMAC_RBSRPQ_RBS(SAME70_ETH_DUMMY_BUFFER_SIZE / 64);
+   GMAC->GMAC_RBSRPQ[1] = GMAC_RBSRPQ_RBS(SAME70_ETH_DUMMY_BUFFER_SIZE / 64);
+
+   //Initialize hash table
+   GMAC->GMAC_HRB = 0;
+   GMAC->GMAC_HRT = 0;
+
+   //Initialize buffer descriptors
+   same70EthInitBufferDesc(interface);
+
+   //Clear transmit status register
+   GMAC->GMAC_TSR = GMAC_TSR_HRESP | GMAC_TSR_TXCOMP | GMAC_TSR_TFC |
+      GMAC_TSR_TXGO | GMAC_TSR_RLE | GMAC_TSR_COL | GMAC_TSR_UBR;
+   //Clear receive status register
+   GMAC->GMAC_RSR = GMAC_RSR_HNO | GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA;
+
+   //First disable all GMAC interrupts
+   GMAC->GMAC_IDR = 0xFFFFFFFF;
+   GMAC->GMAC_IDRPQ[0] = 0xFFFFFFFF;
+   GMAC->GMAC_IDRPQ[1] = 0xFFFFFFFF;
+
+   //Only the desired ones are enabled
+   GMAC->GMAC_IER = GMAC_IER_HRESP | GMAC_IER_ROVR | GMAC_IER_TCOMP | GMAC_IER_TFC |
+      GMAC_IER_RLEX | GMAC_IER_TUR | GMAC_IER_RXUBR | GMAC_IER_RCOMP;
+
+   //Read GMAC ISR register to clear any pending interrupt
+   status = GMAC->GMAC_ISR;
+
+   //Set priority grouping (3 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(SAME70_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure GMAC interrupt priority
+   NVIC_SetPriority(GMAC_IRQn, NVIC_EncodePriority(SAME70_ETH_IRQ_PRIORITY_GROUPING,
+      SAME70_ETH_IRQ_GROUP_PRIORITY, SAME70_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable the GMAC to transmit and receive data
+   GMAC->GMAC_NCR |= GMAC_NCR_TXEN | GMAC_NCR_RXEN;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//SAME70-Xplained evaluation board?
+#if defined(USE_SAME70_XPLAINED)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void same70EthInitGpio(NetInterface *interface)
+{
+   //Enable PIO peripheral clocks
+   PMC->PMC_PCER0 = (1 << ID_PIOC) | (1 << ID_PIOD);
+
+   //Disable pull-up resistors on RMII pins
+   PIOD->PIO_PUDR = GMAC_RMII_MASK;
+   //Disable interrupts-on-change
+   PIOD->PIO_IDR = GMAC_RMII_MASK;
+   //Assign RMII pins to peripheral A function
+   PIOD->PIO_ABCDSR[0] &= ~GMAC_RMII_MASK;
+   PIOD->PIO_ABCDSR[1] &= ~GMAC_RMII_MASK;
+   //Disable the PIO from controlling the corresponding pins
+   PIOD->PIO_PDR = GMAC_RMII_MASK;
+
+   //Select RMII operation mode
+   GMAC->GMAC_UR &= ~GMAC_UR_RMII;
+
+   //Configure PHY_RESET as an output
+   PIOC->PIO_PER = PIO_PC10;
+   PIOC->PIO_OER = PIO_PC10;
+
+   //Reset PHY transceiver
+   PIOC->PIO_CODR = PIO_PC10;
+   sleep(10);
+
+   //Take the PHY transceiver out of reset
+   PIOC->PIO_SODR = PIO_PC10;
+   sleep(10);
+}
+
+#endif
+
+
+/**
+ * @brief Initialize buffer descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void same70EthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+   uint32_t address;
+
+   //Initialize TX buffer descriptors
+   for(i = 0; i < SAME70_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current TX buffer
+      address = (uint32_t) txBuffer[i];
+      //Write the address to the descriptor entry
+      txBufferDesc[i].address = address;
+      //Initialize status field
+      txBufferDesc[i].status = GMAC_TX_USED;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   txBufferDesc[i - 1].status |= GMAC_TX_WRAP;
+   //Initialize TX buffer index
+   txBufferIndex = 0;
+
+   //Initialize RX buffer descriptors
+   for(i = 0; i < SAME70_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current RX buffer
+      address = (uint32_t) rxBuffer[i];
+      //Write the address to the descriptor entry
+      rxBufferDesc[i].address = address & GMAC_RX_ADDRESS;
+      //Clear status field
+      rxBufferDesc[i].status = 0;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   rxBufferDesc[i - 1].address |= GMAC_RX_WRAP;
+   //Initialize RX buffer index
+   rxBufferIndex = 0;
+
+   //Initialize dummy TX buffer descriptors
+   for(i = 0; i < SAME70_ETH_DUMMY_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current TX buffer
+      address = (uint32_t) dummyTxBuffer[i];
+      //Write the address to the descriptor entry
+      dummyTxBufferDesc[i].address = address;
+      //Initialize status field
+      dummyTxBufferDesc[i].status = GMAC_TX_USED;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   dummyTxBufferDesc[i - 1].status |= GMAC_TX_WRAP;
+
+   //Initialize dummy RX buffer descriptors
+   for(i = 0; i < SAME70_ETH_DUMMY_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current RX buffer
+      address = (uint32_t) dummyRxBuffer[i];
+      //Write the address to the descriptor entry
+      dummyRxBufferDesc[i].address = (address & GMAC_RX_ADDRESS) | GMAC_RX_OWNERSHIP;
+      //Clear status field
+      dummyRxBufferDesc[i].status = 0;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   dummyRxBufferDesc[i - 1].address |= GMAC_RX_WRAP;
+
+   //Start location of the TX descriptor list
+   GMAC->GMAC_TBQB = (uint32_t) txBufferDesc;
+   GMAC->GMAC_TBQBAPQ[0] = (uint32_t) dummyTxBufferDesc;
+   GMAC->GMAC_TBQBAPQ[1] = (uint32_t) dummyTxBufferDesc;
+
+   //Start location of the RX descriptor list
+   GMAC->GMAC_RBQB = (uint32_t) rxBufferDesc;
+   GMAC->GMAC_RBQBAPQ[0] = (uint32_t) dummyRxBufferDesc;
+   GMAC->GMAC_RBQBAPQ[1] = (uint32_t) dummyRxBufferDesc;
+}
+
+
+/**
+ * @brief SAME70 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void same70EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void same70EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(GMAC_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void same70EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(GMAC_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief SAME70 Ethernet MAC interrupt service routine
+ **/
+
+void GMAC_Handler(void)
+{
+   bool_t flag;
+   volatile uint32_t isr;
+   volatile uint32_t tsr;
+   volatile uint32_t rsr;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Each time the software reads GMAC_ISR, it has to check the
+   //contents of GMAC_TSR, GMAC_RSR and GMAC_NSR
+   isr = GMAC->GMAC_ISRPQ[0];
+   isr = GMAC->GMAC_ISRPQ[1];
+   isr = GMAC->GMAC_ISR;
+   tsr = GMAC->GMAC_TSR;
+   rsr = GMAC->GMAC_RSR;
+
+   //A packet has been transmitted?
+   if(tsr & (GMAC_TSR_HRESP | GMAC_TSR_TXCOMP | GMAC_TSR_TFC |
+      GMAC_TSR_TXGO | GMAC_TSR_RLE | GMAC_TSR_COL | GMAC_TSR_UBR))
+   {
+      //Only clear TSR flags that are currently set
+      GMAC->GMAC_TSR = tsr;
+
+      //Check whether the TX buffer is available for writing
+      if(txBufferDesc[txBufferIndex].status & GMAC_TX_USED)
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(rsr & (GMAC_RSR_HNO | GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA))
+   {
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief SAME70 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void same70EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint32_t rsr;
+
+   //Read receive status
+   rsr = GMAC->GMAC_RSR;
+
+   //Packet received?
+   if(rsr & (GMAC_RSR_HNO | GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA))
+   {
+      //Only clear RSR flags that are currently set
+      GMAC->GMAC_RSR = rsr;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = same70EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t same70EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   static uint8_t temp[SAME70_ETH_TX_BUFFER_SIZE];
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > SAME70_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(!(txBufferDesc[txBufferIndex].status & GMAC_TX_USED))
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(temp, buffer, offset, length);
+   memcpy(txBuffer[txBufferIndex], temp, (length + 3) & ~3UL);
+
+   //Set the necessary flags in the descriptor entry
+   if(txBufferIndex < (SAME70_ETH_TX_BUFFER_COUNT - 1))
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status =
+         GMAC_TX_LAST | (length & GMAC_TX_LENGTH);
+
+      //Point to the next buffer
+      txBufferIndex++;
+   }
+   else
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status = GMAC_TX_WRAP |
+         GMAC_TX_LAST | (length & GMAC_TX_LENGTH);
+
+      //Wrap around
+      txBufferIndex = 0;
+   }
+
+   //Data synchronization barrier
+   __DSB();
+
+   //Set the TSTART bit to initiate transmission
+   GMAC->GMAC_NCR |= GMAC_NCR_TSTART;
+
+   //Check whether the next buffer is available for writing
+   if(txBufferDesc[txBufferIndex].status & GMAC_TX_USED)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t same70EthReceivePacket(NetInterface *interface)
+{
+   static uint8_t temp[ETH_MAX_FRAME_SIZE];
+   error_t error;
+   uint_t i;
+   uint_t j;
+   uint_t sofIndex;
+   uint_t eofIndex;
+   size_t n;
+   size_t size;
+   size_t length;
+
+   //Initialize SOF and EOF indices
+   sofIndex = UINT_MAX;
+   eofIndex = UINT_MAX;
+
+   //Search for SOF and EOF flags
+   for(i = 0; i < SAME70_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Point to the current entry
+      j = rxBufferIndex + i;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(j >= SAME70_ETH_RX_BUFFER_COUNT)
+         j -= SAME70_ETH_RX_BUFFER_COUNT;
+
+      //No more entries to process?
+      if(!(rxBufferDesc[j].address & GMAC_RX_OWNERSHIP))
+      {
+         //Stop processing
+         break;
+      }
+      //A valid SOF has been found?
+      if(rxBufferDesc[j].status & GMAC_RX_SOF)
+      {
+         //Save the position of the SOF
+         sofIndex = i;
+      }
+      //A valid EOF has been found?
+      if((rxBufferDesc[j].status & GMAC_RX_EOF) && sofIndex != UINT_MAX)
+      {
+         //Save the position of the EOF
+         eofIndex = i;
+         //Retrieve the length of the frame
+         size = rxBufferDesc[j].status & GMAC_RX_LENGTH;
+         //Limit the number of data to read
+         size = MIN(size, ETH_MAX_FRAME_SIZE);
+         //Stop processing since we have reached the end of the frame
+         break;
+      }
+   }
+
+   //Determine the number of entries to process
+   if(eofIndex != UINT_MAX)
+      j = eofIndex + 1;
+   else if(sofIndex != UINT_MAX)
+      j = sofIndex;
+   else
+      j = i;
+
+   //Total number of bytes that have been copied from the receive buffer
+   length = 0;
+
+   //Process incoming frame
+   for(i = 0; i < j; i++)
+   {
+      //Any data to copy from current buffer?
+      if(eofIndex != UINT_MAX && i >= sofIndex && i <= eofIndex)
+      {
+         //Calculate the number of bytes to read at a time
+         n = MIN(size, SAME70_ETH_RX_BUFFER_SIZE);
+         //Copy data from receive buffer
+         memcpy(temp + length, rxBuffer[rxBufferIndex], (n + 3) & ~3UL);
+         //Update byte counters
+         length += n;
+         size -= n;
+      }
+
+      //Mark the current buffer as free
+      rxBufferDesc[rxBufferIndex].address &= ~GMAC_RX_OWNERSHIP;
+
+      //Point to the following entry
+      rxBufferIndex++;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(rxBufferIndex >= SAME70_ETH_RX_BUFFER_COUNT)
+         rxBufferIndex = 0;
+   }
+
+   //Any packet to process?
+   if(length > 0)
+   {
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, temp, length);
+      //Valid packet received
+      error = NO_ERROR;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t same70EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint8_t *p;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating SAME70 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Point to the MAC address
+         p = entry->addr.b;
+
+         //Apply the hash function
+         k = (p[0] >> 6) ^ p[0];
+         k ^= (p[1] >> 4) ^ (p[1] << 2);
+         k ^= (p[2] >> 2) ^ (p[2] << 4);
+         k ^= (p[3] >> 6) ^ p[3];
+         k ^= (p[4] >> 4) ^ (p[4] << 2);
+         k ^= (p[5] >> 2) ^ (p[5] << 4);
+
+         //The hash value is reduced to a 6-bit index
+         k &= 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   GMAC->GMAC_HRB = hashTable[0];
+   GMAC->GMAC_HRT = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HRB = %08" PRIX32 "\r\n", GMAC->GMAC_HRB);
+   TRACE_DEBUG("  HRT = %08" PRIX32 "\r\n", GMAC->GMAC_HRT);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t same70EthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read network configuration register
+   config = GMAC->GMAC_NCFGR;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= GMAC_NCFGR_SPD;
+   else
+      config &= ~GMAC_NCFGR_SPD;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= GMAC_NCFGR_FD;
+   else
+      config &= ~GMAC_NCFGR_FD;
+
+   //Write configuration value back to NCFGR register
+   GMAC->GMAC_NCFGR = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void same70EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Set up a write operation
+   value = GMAC_MAN_CLTTO | GMAC_MAN_OP(1) | GMAC_MAN_WTN(2);
+   //PHY address
+   value |= GMAC_MAN_PHYA(phyAddr);
+   //Register address
+   value |= GMAC_MAN_REGA(regAddr);
+   //Register value
+   value |= GMAC_MAN_DATA(data);
+
+   //Start a write operation
+   GMAC->GMAC_MAN = value;
+   //Wait for the write to complete
+   while(!(GMAC->GMAC_NSR & GMAC_NSR_IDLE));
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t same70EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Set up a read operation
+   value = GMAC_MAN_CLTTO | GMAC_MAN_OP(2) | GMAC_MAN_WTN(2);
+   //PHY address
+   value |= GMAC_MAN_PHYA(phyAddr);
+   //Register address
+   value |= GMAC_MAN_REGA(regAddr);
+
+   //Start a read operation
+   GMAC->GMAC_MAN = value;
+   //Wait for the read to complete
+   while(!(GMAC->GMAC_NSR & GMAC_NSR_IDLE));
+
+   //Return PHY register contents
+   return GMAC->GMAC_MAN & GMAC_MAN_DATA_Msk;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/same70_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,184 @@
+/**
+ * @file same70_eth.h
+ * @brief SAME70 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SAME70_ETH_H
+#define _SAME70_ETH_H
+
+//Number of TX buffers
+#ifndef SAME70_ETH_TX_BUFFER_COUNT
+   #define SAME70_ETH_TX_BUFFER_COUNT 4
+#elif (SAME70_ETH_TX_BUFFER_COUNT < 1)
+   #error SAME70_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef SAME70_ETH_TX_BUFFER_SIZE
+   #define SAME70_ETH_TX_BUFFER_SIZE 1536
+#elif (SAME70_ETH_TX_BUFFER_SIZE != 1536)
+   #error SAME70_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef SAME70_ETH_RX_BUFFER_COUNT
+   #define SAME70_ETH_RX_BUFFER_COUNT 96
+#elif (SAME70_ETH_RX_BUFFER_COUNT < 12)
+   #error SAME70_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef SAME70_ETH_RX_BUFFER_SIZE
+   #define SAME70_ETH_RX_BUFFER_SIZE 128
+#elif (SAME70_ETH_RX_BUFFER_SIZE != 128)
+   #error SAME70_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of dummy buffers
+#ifndef SAME70_ETH_DUMMY_BUFFER_COUNT
+   #define SAME70_ETH_DUMMY_BUFFER_COUNT 2
+#elif (SAME70_ETH_DUMMY_BUFFER_COUNT < 1)
+   #error SAME70_ETH_DUMMY_BUFFER_COUNT parameter is not valid
+#endif
+
+//Dummy buffer size
+#ifndef SAME70_ETH_DUMMY_BUFFER_SIZE
+   #define SAME70_ETH_DUMMY_BUFFER_SIZE 128
+#elif (SAME70_ETH_DUMMY_BUFFER_SIZE != 128)
+   #error SAME70_ETH_DUMMY_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef SAME70_ETH_IRQ_PRIORITY_GROUPING
+   #define SAME70_ETH_IRQ_PRIORITY_GROUPING 4
+#elif (SAME70_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error SAME70_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef SAME70_ETH_IRQ_GROUP_PRIORITY
+   #define SAME70_ETH_IRQ_GROUP_PRIORITY 6
+#elif (SAME70_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error SAME70_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef SAME70_ETH_IRQ_SUB_PRIORITY
+   #define SAME70_ETH_IRQ_SUB_PRIORITY 0
+#elif (SAME70_ETH_IRQ_SUB_PRIORITY < 0)
+   #error SAME70_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//RMII signals
+#define GMAC_RMII_MASK (PIO_PD9A_GMDIO | PIO_PD8A_GMDC | \
+   PIO_PD7A_GRXER | PIO_PD6A_GRX1 | PIO_PD5A_GRX0 | PIO_PD4A_GRXDV | \
+   PIO_PD3A_GTX1 | PIO_PD2A_GTX0 | PIO_PD1A_GTXEN | PIO_PD0A_GTXCK)
+
+//TX buffer descriptor flags
+#define GMAC_TX_USED           0x80000000
+#define GMAC_TX_WRAP           0x40000000
+#define GMAC_TX_RLE_ERROR      0x20000000
+#define GMAC_TX_UNDERRUN_ERROR 0x10000000
+#define GMAC_TX_AHB_ERROR      0x08000000
+#define GMAC_TX_LATE_COL_ERROR 0x04000000
+#define GMAC_TX_CHECKSUM_ERROR 0x00700000
+#define GMAC_TX_NO_CRC         0x00010000
+#define GMAC_TX_LAST           0x00008000
+#define GMAC_TX_LENGTH         0x00003FFF
+
+//RX buffer descriptor flags
+#define GMAC_RX_ADDRESS        0xFFFFFFFC
+#define GMAC_RX_WRAP           0x00000002
+#define GMAC_RX_OWNERSHIP      0x00000001
+#define GMAC_RX_BROADCAST      0x80000000
+#define GMAC_RX_MULTICAST_HASH 0x40000000
+#define GMAC_RX_UNICAST_HASH   0x20000000
+#define GMAC_RX_SAR            0x08000000
+#define GMAC_RX_SAR_MASK       0x06000000
+#define GMAC_RX_TYPE_ID        0x01000000
+#define GMAC_RX_SNAP           0x01000000
+#define GMAC_RX_TYPE_ID_MASK   0x00C00000
+#define GMAC_RX_CHECKSUM_VALID 0x00C00000
+#define GMAC_RX_VLAN_TAG       0x00200000
+#define GMAC_RX_PRIORITY_TAG   0x00100000
+#define GMAC_RX_VLAN_PRIORITY  0x000E0000
+#define GMAC_RX_CFI            0x00010000
+#define GMAC_RX_EOF            0x00008000
+#define GMAC_RX_SOF            0x00004000
+#define GMAC_RX_LENGTH_MSB     0x00002000
+#define GMAC_RX_BAD_FCS        0x00002000
+#define GMAC_RX_LENGTH         0x00001FFF
+
+
+/**
+ * @brief Transmit buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Same70TxBufferDesc;
+
+
+/**
+ * @brief Receive buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Same70RxBufferDesc;
+
+
+//SAME70 Ethernet MAC driver
+extern const NicDriver same70EthDriver;
+
+//SAME70 Ethernet MAC related functions
+error_t same70EthInit(NetInterface *interface);
+void same70EthInitGpio(NetInterface *interface);
+void same70EthInitBufferDesc(NetInterface *interface);
+
+void same70EthTick(NetInterface *interface);
+
+void same70EthEnableIrq(NetInterface *interface);
+void same70EthDisableIrq(NetInterface *interface);
+void same70EthEventHandler(NetInterface *interface);
+
+error_t same70EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t same70EthReceivePacket(NetInterface *interface);
+
+error_t same70EthSetMulticastFilter(NetInterface *interface);
+error_t same70EthUpdateMacConfig(NetInterface *interface);
+
+void same70EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t same70EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/samv71_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,825 @@
+/**
+ * @file samv71_eth.c
+ * @brief SAMV71 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include <limits.h>
+#include "samv71.h"
+#include "core/net.h"
+#include "drivers/samv71_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//TX buffer
+#pragma data_alignment = 8
+#pragma location = ".ram_no_cache"
+static uint8_t txBuffer[SAMV71_ETH_TX_BUFFER_COUNT][SAMV71_ETH_TX_BUFFER_SIZE];
+//RX buffer
+#pragma data_alignment = 8
+#pragma location = ".ram_no_cache"
+static uint8_t rxBuffer[SAMV71_ETH_RX_BUFFER_COUNT][SAMV71_ETH_RX_BUFFER_SIZE];
+//TX buffer descriptors
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static Samv71TxBufferDesc txBufferDesc[SAMV71_ETH_TX_BUFFER_COUNT];
+//RX buffer descriptors
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static Samv71RxBufferDesc rxBufferDesc[SAMV71_ETH_RX_BUFFER_COUNT];
+
+//Dummy TX buffer
+#pragma data_alignment = 8
+#pragma location = ".ram_no_cache"
+static uint8_t dummyTxBuffer[SAMV71_ETH_DUMMY_BUFFER_COUNT][SAMV71_ETH_DUMMY_BUFFER_SIZE];
+//Dummy RX buffer
+#pragma data_alignment = 8
+#pragma location = ".ram_no_cache"
+static uint8_t dummyRxBuffer[SAMV71_ETH_DUMMY_BUFFER_COUNT][SAMV71_ETH_DUMMY_BUFFER_SIZE];
+//Dummy TX buffer descriptors
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static Samv71TxBufferDesc dummyTxBufferDesc[SAMV71_ETH_DUMMY_BUFFER_COUNT];
+//Dummy RX buffer descriptors
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static Samv71RxBufferDesc dummyRxBufferDesc[SAMV71_ETH_DUMMY_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//TX buffer
+static uint8_t txBuffer[SAMV71_ETH_TX_BUFFER_COUNT][SAMV71_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(8), __section__(".ram_no_cache")));
+//RX buffer
+static uint8_t rxBuffer[SAMV71_ETH_RX_BUFFER_COUNT][SAMV71_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(8), __section__(".ram_no_cache")));
+//TX buffer descriptors
+static Samv71TxBufferDesc txBufferDesc[SAMV71_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+//RX buffer descriptors
+static Samv71RxBufferDesc rxBufferDesc[SAMV71_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+
+//Dummy TX buffer
+static uint8_t dummyTxBuffer[SAMV71_ETH_DUMMY_BUFFER_COUNT][SAMV71_ETH_DUMMY_BUFFER_SIZE]
+   __attribute__((aligned(8), __section__(".ram_no_cache")));
+//Dummy RX buffer
+static uint8_t dummyRxBuffer[SAMV71_ETH_DUMMY_BUFFER_COUNT][SAMV71_ETH_DUMMY_BUFFER_SIZE]
+   __attribute__((aligned(8), __section__(".ram_no_cache")));
+//Dummy TX buffer descriptors
+static Samv71TxBufferDesc dummyTxBufferDesc[SAMV71_ETH_DUMMY_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+//Dummy RX buffer descriptors
+static Samv71RxBufferDesc dummyRxBufferDesc[SAMV71_ETH_DUMMY_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+
+#endif
+
+//TX buffer index
+static uint_t txBufferIndex;
+//RX buffer index
+static uint_t rxBufferIndex;
+
+
+/**
+ * @brief SAMV71 Ethernet MAC driver
+ **/
+
+const NicDriver samv71EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   samv71EthInit,
+   samv71EthTick,
+   samv71EthEnableIrq,
+   samv71EthDisableIrq,
+   samv71EthEventHandler,
+   samv71EthSendPacket,
+   samv71EthSetMulticastFilter,
+   samv71EthUpdateMacConfig,
+   samv71EthWritePhyReg,
+   samv71EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief SAMV71 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t samv71EthInit(NetInterface *interface)
+{
+   error_t error;
+   volatile uint32_t status;
+
+   //Debug message
+   TRACE_INFO("Initializing SAMV71 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Enable GMAC peripheral clock
+   PMC->PMC_PCER1 = (1 << (ID_GMAC - 32));
+
+   //GPIO configuration
+   samv71EthInitGpio(interface);
+
+   //Configure MDC clock speed
+   GMAC->GMAC_NCFGR = GMAC_NCFGR_CLK_MCK_96;
+   //Enable management port (MDC and MDIO)
+   GMAC->GMAC_NCR |= GMAC_NCR_MPE;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Set the MAC address
+   GMAC->GMAC_SA[0].GMAC_SAB = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   GMAC->GMAC_SA[0].GMAC_SAT = interface->macAddr.w[2];
+
+   //Configure the receive filter
+   GMAC->GMAC_NCFGR |= GMAC_NCFGR_UNIHEN | GMAC_NCFGR_MTIHEN;
+
+   //DMA configuration
+   GMAC->GMAC_DCFGR = GMAC_DCFGR_DRBS(SAMV71_ETH_RX_BUFFER_SIZE / 64) |
+      GMAC_DCFGR_TXPBMS | GMAC_DCFGR_RXBMS_FULL | GMAC_DCFGR_FBLDO_INCR4;
+
+   GMAC->GMAC_RBSRPQ[0] = GMAC_RBSRPQ_RBS(SAMV71_ETH_DUMMY_BUFFER_SIZE / 64);
+   GMAC->GMAC_RBSRPQ[1] = GMAC_RBSRPQ_RBS(SAMV71_ETH_DUMMY_BUFFER_SIZE / 64);
+   GMAC->GMAC_RBSRPQ[2] = GMAC_RBSRPQ_RBS(SAMV71_ETH_DUMMY_BUFFER_SIZE / 64);
+
+   //Initialize hash table
+   GMAC->GMAC_HRB = 0;
+   GMAC->GMAC_HRT = 0;
+
+   //Initialize buffer descriptors
+   samv71EthInitBufferDesc(interface);
+
+   //Clear transmit status register
+   GMAC->GMAC_TSR = GMAC_TSR_HRESP | GMAC_TSR_TXCOMP | GMAC_TSR_TFC |
+      GMAC_TSR_TXGO | GMAC_TSR_RLE | GMAC_TSR_COL | GMAC_TSR_UBR;
+   //Clear receive status register
+   GMAC->GMAC_RSR = GMAC_RSR_HNO | GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA;
+
+   //First disable all GMAC interrupts
+   GMAC->GMAC_IDR = 0xFFFFFFFF;
+   GMAC->GMAC_IDRPQ[0] = 0xFFFFFFFF;
+   GMAC->GMAC_IDRPQ[1] = 0xFFFFFFFF;
+   GMAC->GMAC_IDRPQ[2] = 0xFFFFFFFF;
+
+   //Only the desired ones are enabled
+   GMAC->GMAC_IER = GMAC_IER_HRESP | GMAC_IER_ROVR | GMAC_IER_TCOMP | GMAC_IER_TFC |
+      GMAC_IER_RLEX | GMAC_IER_TUR | GMAC_IER_RXUBR | GMAC_IER_RCOMP;
+
+   //Read GMAC ISR register to clear any pending interrupt
+   status = GMAC->GMAC_ISR;
+
+   //Set priority grouping (3 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(SAMV71_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure GMAC interrupt priority
+   NVIC_SetPriority(GMAC_IRQn, NVIC_EncodePriority(SAMV71_ETH_IRQ_PRIORITY_GROUPING,
+      SAMV71_ETH_IRQ_GROUP_PRIORITY, SAMV71_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable the GMAC to transmit and receive data
+   GMAC->GMAC_NCR |= GMAC_NCR_TXEN | GMAC_NCR_RXEN;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//SAMV71-Xplained-Ultra evaluation board?
+#if defined(USE_SAMV71_XPLAINED_ULTRA)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void samv71EthInitGpio(NetInterface *interface)
+{
+   //Enable PIO peripheral clocks
+   PMC->PMC_PCER0 = (1 << ID_PIOC) | (1 << ID_PIOD);
+
+   //Disable pull-up resistors on RMII pins
+   PIOD->PIO_PUDR = GMAC_RMII_MASK;
+   //Disable interrupts-on-change
+   PIOD->PIO_IDR = GMAC_RMII_MASK;
+   //Assign RMII pins to peripheral A function
+   PIOD->PIO_ABCDSR[0] &= ~GMAC_RMII_MASK;
+   PIOD->PIO_ABCDSR[1] &= ~GMAC_RMII_MASK;
+   //Disable the PIO from controlling the corresponding pins
+   PIOD->PIO_PDR = GMAC_RMII_MASK;
+
+   //Select RMII operation mode
+   GMAC->GMAC_UR &= ~GMAC_UR_RMII;
+
+   //Configure PHY_RESET as an output
+   PIOC->PIO_PER = PIO_PC10;
+   PIOC->PIO_OER = PIO_PC10;
+
+   //Reset PHY transceiver
+   PIOC->PIO_CODR = PIO_PC10;
+   sleep(10);
+
+   //Take the PHY transceiver out of reset
+   PIOC->PIO_SODR = PIO_PC10;
+   sleep(10);
+}
+
+#endif
+
+
+/**
+ * @brief Initialize buffer descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void samv71EthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+   uint32_t address;
+
+   //Initialize TX buffer descriptors
+   for(i = 0; i < SAMV71_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current TX buffer
+      address = (uint32_t) txBuffer[i];
+      //Write the address to the descriptor entry
+      txBufferDesc[i].address = address;
+      //Initialize status field
+      txBufferDesc[i].status = GMAC_TX_USED;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   txBufferDesc[i - 1].status |= GMAC_TX_WRAP;
+   //Initialize TX buffer index
+   txBufferIndex = 0;
+
+   //Initialize RX buffer descriptors
+   for(i = 0; i < SAMV71_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current RX buffer
+      address = (uint32_t) rxBuffer[i];
+      //Write the address to the descriptor entry
+      rxBufferDesc[i].address = address & GMAC_RX_ADDRESS;
+      //Clear status field
+      rxBufferDesc[i].status = 0;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   rxBufferDesc[i - 1].address |= GMAC_RX_WRAP;
+   //Initialize RX buffer index
+   rxBufferIndex = 0;
+
+   //Initialize dummy TX buffer descriptors
+   for(i = 0; i < SAMV71_ETH_DUMMY_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current TX buffer
+      address = (uint32_t) dummyTxBuffer[i];
+      //Write the address to the descriptor entry
+      dummyTxBufferDesc[i].address = address;
+      //Initialize status field
+      dummyTxBufferDesc[i].status = GMAC_TX_USED;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   dummyTxBufferDesc[i - 1].status |= GMAC_TX_WRAP;
+
+   //Initialize dummy RX buffer descriptors
+   for(i = 0; i < SAMV71_ETH_DUMMY_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current RX buffer
+      address = (uint32_t) dummyRxBuffer[i];
+      //Write the address to the descriptor entry
+      dummyRxBufferDesc[i].address = (address & GMAC_RX_ADDRESS) | GMAC_RX_OWNERSHIP;
+      //Clear status field
+      dummyRxBufferDesc[i].status = 0;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   dummyRxBufferDesc[i - 1].address |= GMAC_RX_WRAP;
+
+   //Start location of the TX descriptor list
+   GMAC->GMAC_TBQB = (uint32_t) txBufferDesc;
+   GMAC->GMAC_TBQBAPQ[0] = (uint32_t) dummyTxBufferDesc;
+   GMAC->GMAC_TBQBAPQ[1] = (uint32_t) dummyTxBufferDesc;
+   GMAC->GMAC_TBQBAPQ[2] = (uint32_t) dummyTxBufferDesc;
+
+   //Start location of the RX descriptor list
+   GMAC->GMAC_RBQB = (uint32_t) rxBufferDesc;
+   GMAC->GMAC_RBQBAPQ[0] = (uint32_t) dummyRxBufferDesc;
+   GMAC->GMAC_RBQBAPQ[1] = (uint32_t) dummyRxBufferDesc;
+   GMAC->GMAC_RBQBAPQ[2] = (uint32_t) dummyRxBufferDesc;
+}
+
+
+/**
+ * @brief SAMV71 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void samv71EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void samv71EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(GMAC_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void samv71EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(GMAC_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief SAMV71 Ethernet MAC interrupt service routine
+ **/
+
+void GMAC_Handler(void)
+{
+   bool_t flag;
+   volatile uint32_t isr;
+   volatile uint32_t tsr;
+   volatile uint32_t rsr;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Each time the software reads GMAC_ISR, it has to check the
+   //contents of GMAC_TSR, GMAC_RSR and GMAC_NSR
+   isr = GMAC->GMAC_ISRPQ[0];
+   isr = GMAC->GMAC_ISRPQ[1];
+   isr = GMAC->GMAC_ISRPQ[2];
+   isr = GMAC->GMAC_ISR;
+   tsr = GMAC->GMAC_TSR;
+   rsr = GMAC->GMAC_RSR;
+
+   //A packet has been transmitted?
+   if(tsr & (GMAC_TSR_HRESP | GMAC_TSR_TXCOMP | GMAC_TSR_TFC |
+      GMAC_TSR_TXGO | GMAC_TSR_RLE | GMAC_TSR_COL | GMAC_TSR_UBR))
+   {
+      //Only clear TSR flags that are currently set
+      GMAC->GMAC_TSR = tsr;
+
+      //Check whether the TX buffer is available for writing
+      if(txBufferDesc[txBufferIndex].status & GMAC_TX_USED)
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(rsr & (GMAC_RSR_HNO | GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA))
+   {
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief SAMV71 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void samv71EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint32_t rsr;
+
+   //Read receive status
+   rsr = GMAC->GMAC_RSR;
+
+   //Packet received?
+   if(rsr & (GMAC_RSR_HNO | GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA))
+   {
+      //Only clear RSR flags that are currently set
+      GMAC->GMAC_RSR = rsr;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = samv71EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t samv71EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   static uint8_t temp[SAMV71_ETH_TX_BUFFER_SIZE];
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > SAMV71_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(!(txBufferDesc[txBufferIndex].status & GMAC_TX_USED))
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(temp, buffer, offset, length);
+   memcpy(txBuffer[txBufferIndex], temp, (length + 3) & ~3UL);
+
+   //Set the necessary flags in the descriptor entry
+   if(txBufferIndex < (SAMV71_ETH_TX_BUFFER_COUNT - 1))
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status =
+         GMAC_TX_LAST | (length & GMAC_TX_LENGTH);
+
+      //Point to the next buffer
+      txBufferIndex++;
+   }
+   else
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status = GMAC_TX_WRAP |
+         GMAC_TX_LAST | (length & GMAC_TX_LENGTH);
+
+      //Wrap around
+      txBufferIndex = 0;
+   }
+
+   //Data synchronization barrier
+   __DSB();
+
+   //Set the TSTART bit to initiate transmission
+   GMAC->GMAC_NCR |= GMAC_NCR_TSTART;
+
+   //Check whether the next buffer is available for writing
+   if(txBufferDesc[txBufferIndex].status & GMAC_TX_USED)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t samv71EthReceivePacket(NetInterface *interface)
+{
+   static uint8_t temp[ETH_MAX_FRAME_SIZE];
+   error_t error;
+   uint_t i;
+   uint_t j;
+   uint_t sofIndex;
+   uint_t eofIndex;
+   size_t n;
+   size_t size;
+   size_t length;
+
+   //Initialize SOF and EOF indices
+   sofIndex = UINT_MAX;
+   eofIndex = UINT_MAX;
+
+   //Search for SOF and EOF flags
+   for(i = 0; i < SAMV71_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Point to the current entry
+      j = rxBufferIndex + i;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(j >= SAMV71_ETH_RX_BUFFER_COUNT)
+         j -= SAMV71_ETH_RX_BUFFER_COUNT;
+
+      //No more entries to process?
+      if(!(rxBufferDesc[j].address & GMAC_RX_OWNERSHIP))
+      {
+         //Stop processing
+         break;
+      }
+      //A valid SOF has been found?
+      if(rxBufferDesc[j].status & GMAC_RX_SOF)
+      {
+         //Save the position of the SOF
+         sofIndex = i;
+      }
+      //A valid EOF has been found?
+      if((rxBufferDesc[j].status & GMAC_RX_EOF) && sofIndex != UINT_MAX)
+      {
+         //Save the position of the EOF
+         eofIndex = i;
+         //Retrieve the length of the frame
+         size = rxBufferDesc[j].status & GMAC_RX_LENGTH;
+         //Limit the number of data to read
+         size = MIN(size, ETH_MAX_FRAME_SIZE);
+         //Stop processing since we have reached the end of the frame
+         break;
+      }
+   }
+
+   //Determine the number of entries to process
+   if(eofIndex != UINT_MAX)
+      j = eofIndex + 1;
+   else if(sofIndex != UINT_MAX)
+      j = sofIndex;
+   else
+      j = i;
+
+   //Total number of bytes that have been copied from the receive buffer
+   length = 0;
+
+   //Process incoming frame
+   for(i = 0; i < j; i++)
+   {
+      //Any data to copy from current buffer?
+      if(eofIndex != UINT_MAX && i >= sofIndex && i <= eofIndex)
+      {
+         //Calculate the number of bytes to read at a time
+         n = MIN(size, SAMV71_ETH_RX_BUFFER_SIZE);
+         //Copy data from receive buffer
+         memcpy(temp + length, rxBuffer[rxBufferIndex], (n + 3) & ~3UL);
+         //Update byte counters
+         length += n;
+         size -= n;
+      }
+
+      //Mark the current buffer as free
+      rxBufferDesc[rxBufferIndex].address &= ~GMAC_RX_OWNERSHIP;
+
+      //Point to the following entry
+      rxBufferIndex++;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(rxBufferIndex >= SAMV71_ETH_RX_BUFFER_COUNT)
+         rxBufferIndex = 0;
+   }
+
+   //Any packet to process?
+   if(length > 0)
+   {
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, temp, length);
+      //Valid packet received
+      error = NO_ERROR;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t samv71EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint8_t *p;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating SAMV71 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Point to the MAC address
+         p = entry->addr.b;
+
+         //Apply the hash function
+         k = (p[0] >> 6) ^ p[0];
+         k ^= (p[1] >> 4) ^ (p[1] << 2);
+         k ^= (p[2] >> 2) ^ (p[2] << 4);
+         k ^= (p[3] >> 6) ^ p[3];
+         k ^= (p[4] >> 4) ^ (p[4] << 2);
+         k ^= (p[5] >> 2) ^ (p[5] << 4);
+
+         //The hash value is reduced to a 6-bit index
+         k &= 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   GMAC->GMAC_HRB = hashTable[0];
+   GMAC->GMAC_HRT = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HRB = %08" PRIX32 "\r\n", GMAC->GMAC_HRB);
+   TRACE_DEBUG("  HRT = %08" PRIX32 "\r\n", GMAC->GMAC_HRT);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t samv71EthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read network configuration register
+   config = GMAC->GMAC_NCFGR;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= GMAC_NCFGR_SPD;
+   else
+      config &= ~GMAC_NCFGR_SPD;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= GMAC_NCFGR_FD;
+   else
+      config &= ~GMAC_NCFGR_FD;
+
+   //Write configuration value back to NCFGR register
+   GMAC->GMAC_NCFGR = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void samv71EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Set up a write operation
+   value = GMAC_MAN_CLTTO | GMAC_MAN_OP(1) | GMAC_MAN_WTN(2);
+   //PHY address
+   value |= GMAC_MAN_PHYA(phyAddr);
+   //Register address
+   value |= GMAC_MAN_REGA(regAddr);
+   //Register value
+   value |= GMAC_MAN_DATA(data);
+
+   //Start a write operation
+   GMAC->GMAC_MAN = value;
+   //Wait for the write to complete
+   while(!(GMAC->GMAC_NSR & GMAC_NSR_IDLE));
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t samv71EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Set up a read operation
+   value = GMAC_MAN_CLTTO | GMAC_MAN_OP(2) | GMAC_MAN_WTN(2);
+   //PHY address
+   value |= GMAC_MAN_PHYA(phyAddr);
+   //Register address
+   value |= GMAC_MAN_REGA(regAddr);
+
+   //Start a read operation
+   GMAC->GMAC_MAN = value;
+   //Wait for the read to complete
+   while(!(GMAC->GMAC_NSR & GMAC_NSR_IDLE));
+
+   //Return PHY register contents
+   return GMAC->GMAC_MAN & GMAC_MAN_DATA_Msk;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/samv71_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,184 @@
+/**
+ * @file samv71_eth.h
+ * @brief SAMV71 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SAMV71_ETH_H
+#define _SAMV71_ETH_H
+
+//Number of TX buffers
+#ifndef SAMV71_ETH_TX_BUFFER_COUNT
+   #define SAMV71_ETH_TX_BUFFER_COUNT 4
+#elif (SAMV71_ETH_TX_BUFFER_COUNT < 1)
+   #error SAMV71_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef SAMV71_ETH_TX_BUFFER_SIZE
+   #define SAMV71_ETH_TX_BUFFER_SIZE 1536
+#elif (SAMV71_ETH_TX_BUFFER_SIZE != 1536)
+   #error SAMV71_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef SAMV71_ETH_RX_BUFFER_COUNT
+   #define SAMV71_ETH_RX_BUFFER_COUNT 96
+#elif (SAMV71_ETH_RX_BUFFER_COUNT < 12)
+   #error SAMV71_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef SAMV71_ETH_RX_BUFFER_SIZE
+   #define SAMV71_ETH_RX_BUFFER_SIZE 128
+#elif (SAMV71_ETH_RX_BUFFER_SIZE != 128)
+   #error SAMV71_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of dummy buffers
+#ifndef SAMV71_ETH_DUMMY_BUFFER_COUNT
+   #define SAMV71_ETH_DUMMY_BUFFER_COUNT 2
+#elif (SAMV71_ETH_DUMMY_BUFFER_COUNT < 1)
+   #error SAMV71_ETH_DUMMY_BUFFER_COUNT parameter is not valid
+#endif
+
+//Dummy buffer size
+#ifndef SAMV71_ETH_DUMMY_BUFFER_SIZE
+   #define SAMV71_ETH_DUMMY_BUFFER_SIZE 128
+#elif (SAMV71_ETH_DUMMY_BUFFER_SIZE != 128)
+   #error SAMV71_ETH_DUMMY_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef SAMV71_ETH_IRQ_PRIORITY_GROUPING
+   #define SAMV71_ETH_IRQ_PRIORITY_GROUPING 4
+#elif (SAMV71_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error SAMV71_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef SAMV71_ETH_IRQ_GROUP_PRIORITY
+   #define SAMV71_ETH_IRQ_GROUP_PRIORITY 6
+#elif (SAMV71_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error SAMV71_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef SAMV71_ETH_IRQ_SUB_PRIORITY
+   #define SAMV71_ETH_IRQ_SUB_PRIORITY 0
+#elif (SAMV71_ETH_IRQ_SUB_PRIORITY < 0)
+   #error SAMV71_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//RMII signals
+#define GMAC_RMII_MASK (PIO_PD9A_GMDIO | PIO_PD8A_GMDC | \
+   PIO_PD7A_GRXER | PIO_PD6A_GRX1 | PIO_PD5A_GRX0 | PIO_PD4A_GRXDV | \
+   PIO_PD3A_GTX1 | PIO_PD2A_GTX0 | PIO_PD1A_GTXEN | PIO_PD0A_GTXCK)
+
+//TX buffer descriptor flags
+#define GMAC_TX_USED           0x80000000
+#define GMAC_TX_WRAP           0x40000000
+#define GMAC_TX_RLE_ERROR      0x20000000
+#define GMAC_TX_UNDERRUN_ERROR 0x10000000
+#define GMAC_TX_AHB_ERROR      0x08000000
+#define GMAC_TX_LATE_COL_ERROR 0x04000000
+#define GMAC_TX_CHECKSUM_ERROR 0x00700000
+#define GMAC_TX_NO_CRC         0x00010000
+#define GMAC_TX_LAST           0x00008000
+#define GMAC_TX_LENGTH         0x00003FFF
+
+//RX buffer descriptor flags
+#define GMAC_RX_ADDRESS        0xFFFFFFFC
+#define GMAC_RX_WRAP           0x00000002
+#define GMAC_RX_OWNERSHIP      0x00000001
+#define GMAC_RX_BROADCAST      0x80000000
+#define GMAC_RX_MULTICAST_HASH 0x40000000
+#define GMAC_RX_UNICAST_HASH   0x20000000
+#define GMAC_RX_SAR            0x08000000
+#define GMAC_RX_SAR_MASK       0x06000000
+#define GMAC_RX_TYPE_ID        0x01000000
+#define GMAC_RX_SNAP           0x01000000
+#define GMAC_RX_TYPE_ID_MASK   0x00C00000
+#define GMAC_RX_CHECKSUM_VALID 0x00C00000
+#define GMAC_RX_VLAN_TAG       0x00200000
+#define GMAC_RX_PRIORITY_TAG   0x00100000
+#define GMAC_RX_VLAN_PRIORITY  0x000E0000
+#define GMAC_RX_CFI            0x00010000
+#define GMAC_RX_EOF            0x00008000
+#define GMAC_RX_SOF            0x00004000
+#define GMAC_RX_LENGTH_MSB     0x00002000
+#define GMAC_RX_BAD_FCS        0x00002000
+#define GMAC_RX_LENGTH         0x00001FFF
+
+
+/**
+ * @brief Transmit buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Samv71TxBufferDesc;
+
+
+/**
+ * @brief Receive buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Samv71RxBufferDesc;
+
+
+//SAMV71 Ethernet MAC driver
+extern const NicDriver samv71EthDriver;
+
+//SAMV71 Ethernet MAC related functions
+error_t samv71EthInit(NetInterface *interface);
+void samv71EthInitGpio(NetInterface *interface);
+void samv71EthInitBufferDesc(NetInterface *interface);
+
+void samv71EthTick(NetInterface *interface);
+
+void samv71EthEnableIrq(NetInterface *interface);
+void samv71EthDisableIrq(NetInterface *interface);
+void samv71EthEventHandler(NetInterface *interface);
+
+error_t samv71EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t samv71EthReceivePacket(NetInterface *interface);
+
+error_t samv71EthSetMulticastFilter(NetInterface *interface);
+error_t samv71EthUpdateMacConfig(NetInterface *interface);
+
+void samv71EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t samv71EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/st802rt1a.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,270 @@
+/**
+ * @file st802rt1a.c
+ * @brief ST802RT1A Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/st802rt1a.h"
+#include "debug.h"
+
+
+/**
+ * @brief ST802RT1A Ethernet PHY driver
+ **/
+
+const PhyDriver st802rt1aPhyDriver =
+{
+   st802rt1aInit,
+   st802rt1aTick,
+   st802rt1aEnableIrq,
+   st802rt1aDisableIrq,
+   st802rt1aEventHandler,
+};
+
+
+/**
+ * @brief ST802RT1A PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t st802rt1aInit(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing ST802RT1A...\r\n");
+
+   //Reset PHY transceiver
+   st802rt1aWritePhyReg(interface, ST802RT1A_PHY_REG_RN00, RN00_SOFT_RESET);
+   //Wait for the reset to complete
+   while(st802rt1aReadPhyReg(interface, ST802RT1A_PHY_REG_RN00) & RN00_SOFT_RESET);
+
+   //Dump PHY registers for debugging purpose
+   st802rt1aDumpPhyReg(interface);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief ST802RT1A timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void st802rt1aTick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //Read status register
+   value = st802rt1aReadPhyReg(interface, ST802RT1A_PHY_REG_RN01);
+   //Retrieve current link state
+   linkState = (value & RN01_LINK_STATUS) ? TRUE : FALSE;
+
+   //Link up event?
+   if(linkState && !interface->linkState)
+   {
+      //Set event flag
+      interface->phyEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      osSetEvent(&netEvent);
+   }
+   //Link down event?
+   else if(!linkState && interface->linkState)
+   {
+      //Set event flag
+      interface->phyEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      osSetEvent(&netEvent);
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void st802rt1aEnableIrq(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void st802rt1aDisableIrq(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief ST802RT1A event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void st802rt1aEventHandler(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //Read status register
+   value = st802rt1aReadPhyReg(interface, ST802RT1A_PHY_REG_RN01);
+   //Retrieve current link state
+   linkState = (value & RN01_LINK_STATUS) ? TRUE : FALSE;
+
+   //Link is up?
+   if(linkState && !interface->linkState)
+   {
+      //Read RN13 register
+      value = st802rt1aReadPhyReg(interface, ST802RT1A_PHY_REG_RN13);
+
+      //Check current operation mode
+      switch(value & RN13_CMODE_MASK)
+      {
+      //10BASE-T
+      case RN13_CMODE_10BT:
+         interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+         interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+         break;
+      //10BASE-T full-duplex
+      case RN13_CMODE_10BT_FD:
+         interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+         interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+         break;
+      //100BASE-TX
+      case RN13_CMODE_100BTX:
+         interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+         interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+         break;
+      //100BASE-TX full-duplex
+      case RN13_CMODE_100BTX_FD:
+         interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+         interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+         break;
+      //Unknown operation mode
+      default:
+         //Debug message
+         TRACE_WARNING("Invalid Duplex mode\r\n");
+         break;
+      }
+
+      //Update link state
+      interface->linkState = TRUE;
+
+      //Adjust MAC configuration parameters for proper operation
+      interface->nicDriver->updateMacConfig(interface);
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+   //Link is down?
+   else if(!linkState && interface->linkState)
+   {
+      //Update link state
+      interface->linkState = FALSE;
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register Register address
+ * @param[in] data Register value
+ **/
+
+void st802rt1aWritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = ST802RT1A_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t st802rt1aReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = ST802RT1A_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void st802rt1aDumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, st802rt1aReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/st802rt1a.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,301 @@
+/**
+ * @file st802rt1a.h
+ * @brief ST802RT1A Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _ST802RT1A_H
+#define _ST802RT1A_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#define ST802RT1A_PHY_ADDR 1
+
+//ST802RT1A registers
+#define ST802RT1A_PHY_REG_RN00    0x00
+#define ST802RT1A_PHY_REG_RN01    0x01
+#define ST802RT1A_PHY_REG_RN02    0x02
+#define ST802RT1A_PHY_REG_RN03    0x03
+#define ST802RT1A_PHY_REG_RN04    0x04
+#define ST802RT1A_PHY_REG_RN05    0x05
+#define ST802RT1A_PHY_REG_RN06    0x06
+#define ST802RT1A_PHY_REG_RN07    0x07
+#define ST802RT1A_PHY_REG_RN08    0x08
+#define ST802RT1A_PHY_REG_RN10    0x10
+#define ST802RT1A_PHY_REG_RN11    0x11
+#define ST802RT1A_PHY_REG_RN12    0x12
+#define ST802RT1A_PHY_REG_RN13    0x13
+#define ST802RT1A_PHY_REG_RN14    0x14
+#define ST802RT1A_PHY_REG_RN18    0x18
+#define ST802RT1A_PHY_REG_RN19    0x19
+#define ST802RT1A_PHY_REG_RN1B    0x1B
+#define ST802RT1A_PHY_REG_RN1C    0x1C
+#define ST802RT1A_PHY_REG_RN1E    0x1E
+#define ST802RT1A_PHY_REG_RN1F    0x1F
+#define ST802RT1A_PHY_REG_RS1B    0x1B
+
+//RN00 register
+#define RN00_SOFT_RESET           (1 << 15)
+#define RN00_LOCAL_LOOPBACK       (1 << 14)
+#define RN00_SPEED_SEL            (1 << 13)
+#define RN00_AN_EN                (1 << 12)
+#define RN00_POWER_DOWN           (1 << 11)
+#define RN00_ISOLATE              (1 << 10)
+#define RN00_RESTART_AN           (1 << 9)
+#define RN00_DUPLEX_MODE          (1 << 8)
+#define RN00_COL_TEST             (1 << 7)
+
+//RN01 register
+#define RN01_100BT4               (1 << 15)
+#define RN01_100BTX_FD            (1 << 14)
+#define RN01_100BTX               (1 << 13)
+#define RN01_10BT_FD              (1 << 12)
+#define RN01_10BT                 (1 << 11)
+#define RN01_NO_PREAMBLE          (1 << 6)
+#define RN01_AN_COMPLETE          (1 << 5)
+#define RN01_REMOTE_FAULT         (1 << 4)
+#define RN01_AN_ABLE              (1 << 3)
+#define RN01_LINK_STATUS          (1 << 2)
+#define RN01_JABBER_DETECT        (1 << 1)
+#define RN01_EXTENDED_CAP         (1 << 0)
+
+//RN04 register
+#define RN04_NP                   (1 << 15)
+#define RN04_RF                   (1 << 13)
+#define RN04_ASYM_PAUSE           (1 << 11)
+#define RN04_PAUSE                (1 << 10)
+#define RN04_100BT4               (1 << 9)
+#define RN04_100BTX_FD            (1 << 8)
+#define RN04100BTX                (1 << 7)
+#define RN04_10BT_FD              (1 << 6)
+#define RN04_10BT                 (1 << 5)
+#define RN04_SELECTOR4            (1 << 4)
+#define RN04_SELECTOR3            (1 << 3)
+#define RN04_SELECTOR2            (1 << 2)
+#define RN04_SELECTOR1            (1 << 1)
+#define RN04_SELECTOR0            (1 << 0)
+
+//RN05 register
+#define RN05_NP                   (1 << 15)
+#define RN05_ACK                  (1 << 14)
+#define RN05_RF                   (1 << 13)
+#define RN05_ASYM_PAUSE           (1 << 11)
+#define RN05_PAUSE                (1 << 10)
+#define RN05_100BT4               (1 << 9)
+#define RN05_100BTX_FD            (1 << 8)
+#define RN05_100BTX               (1 << 7)
+#define RN05_10BT_FD              (1 << 6)
+#define RN05_10BT                 (1 << 5)
+#define RN05_SELECTOR4            (1 << 4)
+#define RN05_SELECTOR3            (1 << 3)
+#define RN05_SELECTOR2            (1 << 2)
+#define RN05_SELECTOR1            (1 << 1)
+#define RN05_SELECTOR0            (1 << 0)
+
+//RN06 register
+#define RN06_PD_FAULT             (1 << 4)
+#define RN06_LP_NP_ABLE           (1 << 3)
+#define RN06_NP_ABLE              (1 << 2)
+#define RN06_PAGE_RCVD            (1 << 1)
+#define RN06_LP_AN_ABLE           (1 << 0)
+
+//RN07 register
+#define RN07_NP                   (1 << 15)
+#define RN07_MP                   (1 << 13)
+#define RN07_ACK2                 (1 << 12)
+#define RN07_TOGGLE               (1 << 11)
+#define RN07_CODE10               (1 << 10)
+#define RN07_CODE9                (1 << 9)
+#define RN07_CODE8                (1 << 8)
+#define RN07_CODE7                (1 << 7)
+#define RN07_CODE6                (1 << 6)
+#define RN07_CODE5                (1 << 5)
+#define RN07_CODE4                (1 << 4)
+#define RN07_CODE3                (1 << 3)
+#define RN07_CODE2                (1 << 2)
+#define RN07_CODE1                (1 << 1)
+#define RN07_CODE0                (1 << 0)
+
+//RN08 register
+#define RN08_NP                   (1 << 15)
+#define RN08_ACK                  (1 << 14)
+#define RN08_MP                   (1 << 13)
+#define RN08_ACK2                 (1 << 12)
+#define RN08_TOGGLE               (1 << 11)
+#define RN08_CODE10               (1 << 10)
+#define RN08_CODE9                (1 << 9)
+#define RN08_CODE8                (1 << 8)
+#define RN08_CODE7                (1 << 7)
+#define RN08_CODE6                (1 << 6)
+#define RN08_CODE5                (1 << 5)
+#define RN08_CODE4                (1 << 4)
+#define RN08_CODE3                (1 << 3)
+#define RN08_CODE2                (1 << 2)
+#define RN08_CODE1                (1 << 1)
+#define RN08_CODE0                (1 << 0)
+
+//RN10 register
+#define RN10_MII_EN               (1 << 9)
+#define RN10_FEF_EN               (1 << 5)
+#define RN10_FIFO_EXT             (1 << 2)
+#define RN10_RMII_OOBS            (1 << 1)
+
+//RN11 register
+#define RN11_FX_MODE              (1 << 10)
+#define RN11_SPEED                (1 << 9)
+#define RN11_DUPLEX               (1 << 8)
+#define RN11_PAUSE                (1 << 7)
+#define RN11_AN_COMPLETE_INT      (1 << 6)
+#define RN11_REMOTE_FAULT_INT     (1 << 5)
+#define RN11_LINK_DOWN_INT        (1 << 4)
+#define RN11_AN_LCW_RCVD_INT      (1 << 3)
+#define RN11_PD_FAULT_INT         (1 << 2)
+#define RN11_PG_RCVD_INT          (1 << 1)
+#define RN11_RX_FUL_INT           (1 << 0)
+
+//RN12 register
+#define RN12_INT_OE_N             (1 << 8)
+#define RN12_INT_EN               (1 << 7)
+#define RN12_AN_COMPLETE_EN       (1 << 6)
+#define RN12_REMOTE_FAULT_EN      (1 << 5)
+#define RN12_LINK_DOWN_EN         (1 << 4)
+#define RN12_AN_LCW_RCVD_EN       (1 << 3)
+#define RN12_PD_FAULT_EN          (1 << 2)
+#define RN12_PG_RCVD_EN           (1 << 1)
+#define RN12_RX_FULL_EN           (1 << 0)
+
+//RN13 register
+#define RN13_RX_ERR_COUNTER_DIS   (1 << 13)
+#define RN13_AN_COMPLETE          (1 << 12)
+#define RN13_DC_REST_EN           (1 << 8)
+#define RN13_NRZ_CONV_EN          (1 << 7)
+#define RN13_TX_ISOLATE           (1 << 5)
+#define RN13_CMODE2               (1 << 4)
+#define RN13_CMODE1               (1 << 3)
+#define RN13_CMODE0               (1 << 2)
+#define RN13_MLT3_DIS             (1 << 1)
+#define RN13_SCRAMBLER_DIS        (1 << 0)
+
+#define RN13_CMODE_MASK           (7 << 2)
+#define RN13_CMODE_AN             (0 << 2)
+#define RN13_CMODE_10BT           (1 << 2)
+#define RN13_CMODE_100BTX         (2 << 2)
+#define RN13_CMODE_10BT_FD        (5 << 2)
+#define RN13_CMODE_100BTX_FD      (6 << 2)
+#define RN13_CMODE_TX_ISOLATE     (7 << 2)
+
+//RN14 register
+#define RN14_PHY_ADDR4            (1 << 7)
+#define RN14_PHY_ADDR3            (1 << 6)
+#define RN14_PHY_ADDR2            (1 << 5)
+#define RN14_PHY_ADDR1            (1 << 4)
+#define RN14_PHY_ADDR0            (1 << 3)
+#define RN14_NO_PREAMBLE          (1 << 1)
+
+//RN18 register
+#define RN18_JABBER_DIS           (1 << 15)
+#define RN18_MDIO_PS              (1 << 4)
+
+//RN19 register
+#define RN19_AN_COMPLETE          (1 << 15)
+#define RN19_AN_ACK               (1 << 14)
+#define RN19_AN_DETECT            (1 << 13)
+#define RN19_LP_AN_ABLE_DETECT    (1 << 12)
+#define RN19_AN_PAUSE             (1 << 11)
+#define RN19_AN_HCD2              (1 << 10)
+#define RN19_AN_HCD1              (1 << 9)
+#define RN19_AN_HCD0              (1 << 8)
+#define RN19_PD_FAULT             (1 << 7)
+#define RN19_REMOTE_FAULT         (1 << 6)
+#define RN19_PAGE_RCVD            (1 << 5)
+#define RN19_LP_AN_ABLE           (1 << 4)
+#define RN19_SP100                (1 << 3)
+#define RN19_LINK_STATUS          (1 << 2)
+#define RN19_AN_EN                (1 << 1)
+#define RN19_JABBER_DETECT        (1 << 0)
+
+//RN1B register
+#define RN1B_LED_MODE             (1 << 9)
+#define RN1B_10BT_ECHO_DIS        (1 << 7)
+#define RN1B_MI_SQE_DIS           (1 << 3)
+
+//RN1C register
+#define RN1C_MDIX_STATUS          (1 << 13)
+#define RN1C_MDIX_SWAP            (1 << 12)
+#define RN1C_MDIX_DIS             (1 << 11)
+#define RN1C_JABBER_DETECT        (1 << 9)
+#define RN1C_POLARITY_CHANGED     (1 << 8)
+
+//RN1E
+#define RN1E_HCD_100BTX_FD        (1 << 15)
+#define RN1E_HCD_100BT4           (1 << 14)
+#define RN1E_HCD_100BTX           (1 << 13)
+#define RN1E_HCD_10BT_FD          (1 << 12)
+#define RN1E_HCD_10BT             (1 << 11)
+#define RN1E_AN_RESTART           (1 << 8)
+#define RN1E_AN_COMPLETE          (1 << 7)
+#define RN1E_AN_ACK_COMPLETE      (1 << 6)
+#define RN1E_AN_ACK               (1 << 5)
+#define RN1E_AN_ABLE              (1 << 4)
+#define RN1E_SUPER_ISOLATE        (1 << 3)
+
+//RN1F register
+#define RN1F_SHADOW_REG_EN        (1 << 7)
+
+//RS1B
+#define RS1B_MLT3_DETECT          (1 << 15)
+#define RS1B_TX_CABLE_LEN2        (1 << 14)
+#define RS1B_TX_CABLE_LEN1        (1 << 13)
+#define RS1B_TX_CABLE_LEN0        (1 << 12)
+#define RS1B_LED_TEST_CTRL        (1 << 10)
+#define RS1B_DESCRAMBLER_LOCKED   (1 << 9)
+#define RS1B_FALSE_CARRIER_DETECT (1 << 8)
+#define RS1B_BAD_ESD_DETECT       (1 << 7)
+#define RS1B_RX_ERROR_DETECT      (1 << 6)
+#define RS1B_LOCK_ERROR_DETECT    (1 << 4)
+#define RS1B_MLT3_ERROR_DETECT    (1 << 3)
+
+//ST802RT1A Ethernet PHY driver
+extern const PhyDriver st802rt1aPhyDriver;
+
+//ST802RT1A related functions
+error_t st802rt1aInit(NetInterface *interface);
+
+void st802rt1aTick(NetInterface *interface);
+
+void st802rt1aEnableIrq(NetInterface *interface);
+void st802rt1aDisableIrq(NetInterface *interface);
+
+void st802rt1aEventHandler(NetInterface *interface);
+
+void st802rt1aWritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t st802rt1aReadPhyReg(NetInterface *interface, uint8_t address);
+
+void st802rt1aDumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/stm32f107_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,934 @@
+/**
+ * @file stm32f107_eth.c
+ * @brief STM32F107 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#if defined(USE_HAL_DRIVER)
+   #include "stm32f1xx.h"
+#elif defined(USE_STDPERIPH_DRIVER)
+   #include "stm32f10x.h"
+#endif
+
+#include "core/net.h"
+#include "drivers/stm32f107_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+static uint8_t txBuffer[STM32F107_ETH_TX_BUFFER_COUNT][STM32F107_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+static uint8_t rxBuffer[STM32F107_ETH_RX_BUFFER_COUNT][STM32F107_ETH_RX_BUFFER_SIZE];
+//Transmit DMA descriptors
+#pragma data_alignment = 4
+static Stm32f107TxDmaDesc txDmaDesc[STM32F107_ETH_TX_BUFFER_COUNT];
+//Receive DMA descriptors
+#pragma data_alignment = 4
+static Stm32f107RxDmaDesc rxDmaDesc[STM32F107_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[STM32F107_ETH_TX_BUFFER_COUNT][STM32F107_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[STM32F107_ETH_RX_BUFFER_COUNT][STM32F107_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Transmit DMA descriptors
+static Stm32f107TxDmaDesc txDmaDesc[STM32F107_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive DMA descriptors
+static Stm32f107RxDmaDesc rxDmaDesc[STM32F107_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+
+#endif
+
+//Pointer to the current TX DMA descriptor
+static Stm32f107TxDmaDesc *txCurDmaDesc;
+//Pointer to the current RX DMA descriptor
+static Stm32f107RxDmaDesc *rxCurDmaDesc;
+
+
+/**
+ * @brief STM32F107 Ethernet MAC driver
+ **/
+
+const NicDriver stm32f107EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   stm32f107EthInit,
+   stm32f107EthTick,
+   stm32f107EthEnableIrq,
+   stm32f107EthDisableIrq,
+   stm32f107EthEventHandler,
+   stm32f107EthSendPacket,
+   stm32f107EthSetMulticastFilter,
+   stm32f107EthUpdateMacConfig,
+   stm32f107EthWritePhyReg,
+   stm32f107EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief STM32F107 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f107EthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing STM32F107 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //GPIO configuration
+   stm32f107EthInitGpio(interface);
+
+#if defined(USE_HAL_DRIVER)
+   //Enable Ethernet MAC clock
+   __HAL_RCC_ETHMAC_CLK_ENABLE();
+   __HAL_RCC_ETHMACTX_CLK_ENABLE();
+   __HAL_RCC_ETHMACRX_CLK_ENABLE();
+
+   //Reset Ethernet MAC peripheral
+   __HAL_RCC_ETHMAC_FORCE_RESET();
+   __HAL_RCC_ETHMAC_RELEASE_RESET();
+
+#elif defined(USE_STDPERIPH_DRIVER)
+   //Enable Ethernet MAC clock
+   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ETH_MAC |
+      RCC_AHBPeriph_ETH_MAC_Tx | RCC_AHBPeriph_ETH_MAC_Rx, ENABLE);
+
+   //Reset Ethernet MAC peripheral
+   RCC_AHBPeriphResetCmd(RCC_AHBPeriph_ETH_MAC, ENABLE);
+   RCC_AHBPeriphResetCmd(RCC_AHBPeriph_ETH_MAC, DISABLE);
+#endif
+
+   //Perform a software reset
+   ETH->DMABMR |= ETH_DMABMR_SR;
+   //Wait for the reset to complete
+   while(ETH->DMABMR & ETH_DMABMR_SR);
+
+//Adjust MDC clock range depending on HCLK frequency
+#if defined(USE_HAL_DRIVER)
+   ETH->MACMIIAR = ETH_MACMIIAR_CR_DIV42;
+#elif defined(USE_STDPERIPH_DRIVER)
+   ETH->MACMIIAR = ETH_MACMIIAR_CR_Div42;
+#endif
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Use default MAC configuration
+   ETH->MACCR = ETH_MACCR_ROD;
+
+   //Set the MAC address
+   ETH->MACA0LR = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   ETH->MACA0HR = interface->macAddr.w[2];
+
+   //Initialize hash table
+   ETH->MACHTLR = 0;
+   ETH->MACHTHR = 0;
+
+   //Configure the receive filter
+   ETH->MACFFR = ETH_MACFFR_HPF | ETH_MACFFR_HM;
+   //Disable flow control
+   ETH->MACFCR = 0;
+   //Enable store and forward mode
+   ETH->DMAOMR = ETH_DMAOMR_RSF | ETH_DMAOMR_TSF;
+
+   //Configure DMA bus mode
+   ETH->DMABMR = ETH_DMABMR_AAB | ETH_DMABMR_USP | ETH_DMABMR_RDP_1Beat |
+      ETH_DMABMR_RTPR_1_1 | ETH_DMABMR_PBL_1Beat;
+
+   //Initialize DMA descriptor lists
+   stm32f107EthInitDmaDesc(interface);
+
+   //Prevent interrupts from being generated when the transmit statistic
+   //counters reach half their maximum value
+   ETH->MMCTIMR = ETH_MMCTIMR_TGFM | ETH_MMCTIMR_TGFMSCM | ETH_MMCTIMR_TGFSCM;
+
+   //Prevent interrupts from being generated when the receive statistic
+   //counters reach half their maximum value
+   ETH->MMCRIMR = ETH_MMCRIMR_RGUFM | ETH_MMCRIMR_RFAEM | ETH_MMCRIMR_RFCEM;
+
+   //Disable MAC interrupts
+   ETH->MACIMR = ETH_MACIMR_TSTIM | ETH_MACIMR_PMTIM;
+   //Enable the desired DMA interrupts
+   ETH->DMAIER = ETH_DMAIER_NISE | ETH_DMAIER_RIE | ETH_DMAIER_TIE;
+
+   //Set priority grouping (4 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(STM32F107_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure Ethernet interrupt priority
+   NVIC_SetPriority(ETH_IRQn, NVIC_EncodePriority(STM32F107_ETH_IRQ_PRIORITY_GROUPING,
+      STM32F107_ETH_IRQ_GROUP_PRIORITY, STM32F107_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable MAC transmission and reception
+   ETH->MACCR |= ETH_MACCR_TE | ETH_MACCR_RE;
+   //Enable DMA transmission and reception
+   ETH->DMAOMR |= ETH_DMAOMR_ST | ETH_DMAOMR_SR;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//STM3210C-EVAL or STM32-P107 evaluation board?
+#if defined(USE_STM3210C_EVAL) || defined(USE_STM32_P107)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f107EthInitGpio(NetInterface *interface)
+{
+   GPIO_InitTypeDef GPIO_InitStructure;
+
+//STM3210C-EVAL evaluation board?
+#if defined(USE_STM3210C_EVAL) && defined(USE_HAL_DRIVER)
+   //Enable AFIO clock
+   __HAL_RCC_AFIO_CLK_ENABLE();
+
+   //Enable GPIO clocks
+   __HAL_RCC_GPIOA_CLK_ENABLE();
+   __HAL_RCC_GPIOB_CLK_ENABLE();
+   __HAL_RCC_GPIOC_CLK_ENABLE();
+   __HAL_RCC_GPIOD_CLK_ENABLE();
+
+   //Configure MCO (PA8) as an output
+   GPIO_InitStructure.Pin = GPIO_PIN_8;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure MCO pin to output the HSE clock (25MHz)
+   HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, 1);
+
+   //Select MII interface mode
+   __HAL_AFIO_ETH_MII();
+
+   //Configure MII_MDIO (PA2)
+   GPIO_InitStructure.Pin = GPIO_PIN_2;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure MII_PPS_OUT (PB5), ETH_MII_TXD3 (PB8), MII_TX_EN (PB11),
+   //MII_TXD0 (PB12) and MII_TXD1 (PB13)
+   GPIO_InitStructure.Pin = GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
+   HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
+
+   //Configure MII_MDC (PC1) and MII_TXD2 (PC2)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
+   HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Configure ETH_MII_CRS (PA0), ETH_MII_RX_CLK (PA1) and ETH_MII_COL (PA3)
+   GPIO_InitStructure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_3;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_INPUT;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_MII_RX_ER (PB10)
+   GPIO_InitStructure.Pin = GPIO_PIN_10;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_INPUT;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
+   HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
+
+   //Configure ETH_MII_TX_CLK (PC3)
+   GPIO_InitStructure.Pin = GPIO_PIN_3;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_INPUT;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
+   HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Configure ETH_MII_RX_DV (PD8), ETH_MII_RXD0 (PD9), ETH_MII_RXD1 (PD10),
+   //ETH_MII_RXD2 (PD11) and ETH_MII_RXD3 (PD12)
+   GPIO_InitStructure.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_INPUT;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
+   HAL_GPIO_Init(GPIOD, &GPIO_InitStructure);
+
+   //Remap Ethernet pins
+   __HAL_AFIO_REMAP_ETH_ENABLE();
+
+#elif defined(USE_STM3210C_EVAL) && defined(USE_STDPERIPH_DRIVER)
+   //Enable AFIO clock
+   RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
+
+   //Enable GPIO clocks
+   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
+      RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE);
+
+   //Configure MCO (PA8) as an output
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
+   GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure MCO pin to output the HSE clock (25MHz)
+   RCC_MCOConfig(RCC_MCO_HSE);
+
+   //Select MII interface mode
+   GPIO_ETH_MediaInterfaceConfig(GPIO_ETH_MediaInterface_MII);
+
+   //Configure MII_MDIO (PA2)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
+   GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure MII_PPS_OUT (PB5), ETH_MII_TXD3 (PB8), MII_TX_EN (PB11),
+   //MII_TXD0 (PB12) and MII_TXD1 (PB13)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_8 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
+   GPIO_Init(GPIOB, &GPIO_InitStructure);
+
+   //Configure MII_MDC (PC1) and MII_TXD2 (PC2)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
+   GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Configure ETH_MII_CRS (PA0), ETH_MII_RX_CLK (PA1) and ETH_MII_COL (PA3)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_3;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
+   GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_MII_RX_ER (PB10)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
+   GPIO_Init(GPIOB, &GPIO_InitStructure);
+
+   //Configure ETH_MII_TX_CLK (PC3)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
+   GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Configure ETH_MII_RX_DV (PD8), ETH_MII_RXD0 (PD9), ETH_MII_RXD1 (PD10),
+   //ETH_MII_RXD2 (PD11) and ETH_MII_RXD3 (PD12)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
+   GPIO_Init(GPIOD, &GPIO_InitStructure);
+
+   //Remap Ethernet pins
+   GPIO_PinRemapConfig(GPIO_Remap_ETH, ENABLE);
+
+//STM32-P107 evaluation board?
+#elif defined(USE_STM32_P107) && defined(USE_STDPERIPH_DRIVER)
+   //Enable AFIO clock
+   RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
+
+   //Enable GPIO clocks
+   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |
+      RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE);
+
+   //Configure MCO (PA8) as an output
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
+   GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure PLL3 to output a 50MHz clock
+   RCC_PLL3Config(RCC_PLL3Mul_10);
+   //Enable PLL3
+   RCC_PLL3Cmd(ENABLE);
+
+   //Wait for the PLL3 to lock
+   while(RCC_GetFlagStatus(RCC_FLAG_PLL3RDY) == RESET);
+
+   //Configure MCO pin to output the PLL3 clock
+   RCC_MCOConfig(RCC_MCO_PLL3CLK);
+
+   //Select RMII interface mode
+   GPIO_ETH_MediaInterfaceConfig(GPIO_ETH_MediaInterface_RMII);
+
+   //Configure ETH_MDIO (PA2)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
+   GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_RMII_TX_EN (PB11), ETH_RMII_TXD0 (PB12) and ETH_RMII_TXD1 (PB13)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
+   GPIO_Init(GPIOB, &GPIO_InitStructure);
+
+   //Configure ETH_MDC (PC1)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
+   GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Configure ETH_RMII_REF_CLK (PA1) and ETH_RMII_CRS_DV (PA7)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_7;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
+   GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_RMII_RXD0 (PC4) and ETH_RMII_RXD1 (PC5)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
+   GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Do not remap Ethernet pins
+   GPIO_PinRemapConfig(GPIO_Remap_ETH, DISABLE);
+#endif
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f107EthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX DMA descriptor list
+   for(i = 0; i < STM32F107_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Use chain structure rather than ring structure
+      txDmaDesc[i].tdes0 = ETH_TDES0_IC | ETH_TDES0_TCH;
+      //Initialize transmit buffer size
+      txDmaDesc[i].tdes1 = 0;
+      //Transmit buffer address
+      txDmaDesc[i].tdes2 = (uint32_t) txBuffer[i];
+      //Next descriptor address
+      txDmaDesc[i].tdes3 = (uint32_t) &txDmaDesc[i + 1];
+   }
+
+   //The last descriptor is chained to the first entry
+   txDmaDesc[i - 1].tdes3 = (uint32_t) &txDmaDesc[0];
+   //Point to the very first descriptor
+   txCurDmaDesc = &txDmaDesc[0];
+
+   //Initialize RX DMA descriptor list
+   for(i = 0; i < STM32F107_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the DMA
+      rxDmaDesc[i].rdes0 = ETH_RDES0_OWN;
+      //Use chain structure rather than ring structure
+      rxDmaDesc[i].rdes1 = ETH_RDES1_RCH | (STM32F107_ETH_RX_BUFFER_SIZE & ETH_RDES1_RBS1);
+      //Receive buffer address
+      rxDmaDesc[i].rdes2 = (uint32_t) rxBuffer[i];
+      //Next descriptor address
+      rxDmaDesc[i].rdes3 = (uint32_t) &rxDmaDesc[i + 1];
+   }
+
+   //The last descriptor is chained to the first entry
+   rxDmaDesc[i - 1].rdes3 = (uint32_t) &rxDmaDesc[0];
+   //Point to the very first descriptor
+   rxCurDmaDesc = &rxDmaDesc[0];
+
+   //Start location of the TX descriptor list
+   ETH->DMATDLAR = (uint32_t) txDmaDesc;
+   //Start location of the RX descriptor list
+   ETH->DMARDLAR = (uint32_t) rxDmaDesc;
+}
+
+
+/**
+ * @brief STM32F107 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f107EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f107EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(ETH_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f107EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(ETH_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief STM32F107 Ethernet MAC interrupt service routine
+ **/
+
+void ETH_IRQHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read DMA status register
+   status = ETH->DMASR;
+
+   //A packet has been transmitted?
+   if(status & ETH_DMASR_TS)
+   {
+      //Clear TS interrupt flag
+      ETH->DMASR = ETH_DMASR_TS;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & ETH_DMASR_RS)
+   {
+      //Disable RIE interrupt
+      ETH->DMAIER &= ~ETH_DMAIER_RIE;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Clear NIS interrupt flag
+   ETH->DMASR = ETH_DMASR_NIS;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief STM32F107 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f107EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(ETH->DMASR & ETH_DMASR_RS)
+   {
+      //Clear interrupt flag
+      ETH->DMASR = ETH_DMASR_RS;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = stm32f107EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable DMA interrupts
+   ETH->DMAIER |= ETH_DMAIER_NISE | ETH_DMAIER_RIE | ETH_DMAIER_TIE;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t stm32f107EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > STM32F107_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurDmaDesc->tdes0 & ETH_TDES0_OWN)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txCurDmaDesc->tdes2, buffer, offset, length);
+
+   //Write the number of bytes to send
+   txCurDmaDesc->tdes1 = length & ETH_TDES1_TBS1;
+   //Set LS and FS flags as the data fits in a single buffer
+   txCurDmaDesc->tdes0 |= ETH_TDES0_LS | ETH_TDES0_FS;
+   //Give the ownership of the descriptor to the DMA
+   txCurDmaDesc->tdes0 |= ETH_TDES0_OWN;
+
+   //Clear TBUS flag to resume processing
+   ETH->DMASR = ETH_DMASR_TBUS;
+   //Instruct the DMA to poll the transmit descriptor list
+   ETH->DMATPDR = 0;
+
+   //Point to the next descriptor in the list
+   txCurDmaDesc = (Stm32f107TxDmaDesc *) txCurDmaDesc->tdes3;
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f107EthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_OWN))
+   {
+      //FS and LS flags should be set
+      if((rxCurDmaDesc->rdes0 & ETH_RDES0_FS) && (rxCurDmaDesc->rdes0 & ETH_RDES0_LS))
+      {
+         //Make sure no error occurred
+         if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_ES))
+         {
+            //Retrieve the length of the frame
+            n = (rxCurDmaDesc->rdes0 & ETH_RDES0_FL) >> 16;
+            //Limit the number of data to read
+            n = MIN(n, STM32F107_ETH_RX_BUFFER_SIZE);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, (uint8_t *) rxCurDmaDesc->rdes2, n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Give the ownership of the descriptor back to the DMA
+      rxCurDmaDesc->rdes0 = ETH_RDES0_OWN;
+      //Point to the next descriptor in the list
+      rxCurDmaDesc = (Stm32f107RxDmaDesc *) rxCurDmaDesc->rdes3;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Clear RBUS flag to resume processing
+   ETH->DMASR = ETH_DMASR_RBUS;
+   //Instruct the DMA to poll the receive descriptor list
+   ETH->DMARPDR = 0;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f107EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating STM32F107 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = stm32f107EthCalcCrc(&entry->addr, sizeof(MacAddr));
+
+         //The upper 6 bits in the CRC register are used to index the
+         //contents of the hash table
+         k = (crc >> 26) & 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   ETH->MACHTLR = hashTable[0];
+   ETH->MACHTHR = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  MACHTLR = %08" PRIX32 "\r\n", ETH->MACHTLR);
+   TRACE_DEBUG("  MACHTHR = %08" PRIX32 "\r\n", ETH->MACHTHR);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f107EthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config ;
+
+   //Read current MAC configuration
+   config = ETH->MACCR;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= ETH_MACCR_FES;
+   else
+      config &= ~ETH_MACCR_FES;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= ETH_MACCR_DM;
+   else
+      config &= ~ETH_MACCR_DM;
+
+   //Update MAC configuration register
+   ETH->MACCR = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void stm32f107EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = ETH->MACMIIAR & ETH_MACMIIAR_CR;
+   //Set up a write operation
+   value |= ETH_MACMIIAR_MW | ETH_MACMIIAR_MB;
+   //PHY address
+   value |= (phyAddr << 11) & ETH_MACMIIAR_PA;
+   //Register address
+   value |= (regAddr << 6) & ETH_MACMIIAR_MR;
+
+   //Data to be written in the PHY register
+   ETH->MACMIIDR = data & ETH_MACMIIDR_MD;
+
+   //Start a write operation
+   ETH->MACMIIAR = value;
+   //Wait for the write to complete
+   while(ETH->MACMIIAR & ETH_MACMIIAR_MB);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t stm32f107EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = ETH->MACMIIAR & ETH_MACMIIAR_CR;
+   //Set up a read operation
+   value |= ETH_MACMIIAR_MB;
+   //PHY address
+   value |= (phyAddr << 11) & ETH_MACMIIAR_PA;
+   //Register address
+   value |= (regAddr << 6) & ETH_MACMIIAR_MR;
+
+   //Start a read operation
+   ETH->MACMIIAR = value;
+   //Wait for the read to complete
+   while(ETH->MACMIIAR & ETH_MACMIIAR_MB);
+
+   //Return PHY register contents
+   return ETH->MACMIIDR & ETH_MACMIIDR_MD;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t stm32f107EthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return ~crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/stm32f107_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,198 @@
+/**
+ * @file stm32f107_eth.h
+ * @brief STM32F107 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _STM32F107_ETH_H
+#define _STM32F107_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef STM32F107_ETH_TX_BUFFER_COUNT
+   #define STM32F107_ETH_TX_BUFFER_COUNT 2
+#elif (STM32F107_ETH_TX_BUFFER_COUNT < 1)
+   #error STM32F107_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef STM32F107_ETH_TX_BUFFER_SIZE
+   #define STM32F107_ETH_TX_BUFFER_SIZE 1536
+#elif (STM32F107_ETH_TX_BUFFER_SIZE != 1536)
+   #error STM32F107_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef STM32F107_ETH_RX_BUFFER_COUNT
+   #define STM32F107_ETH_RX_BUFFER_COUNT 4
+#elif (STM32F107_ETH_RX_BUFFER_COUNT < 1)
+   #error STM32F107_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef STM32F107_ETH_RX_BUFFER_SIZE
+   #define STM32F107_ETH_RX_BUFFER_SIZE 1536
+#elif (STM32F107_ETH_RX_BUFFER_SIZE != 1536)
+   #error STM32F107_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef STM32F107_ETH_IRQ_PRIORITY_GROUPING
+   #define STM32F107_ETH_IRQ_PRIORITY_GROUPING 3
+#elif (STM32F107_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error STM32F107_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef STM32F107_ETH_IRQ_GROUP_PRIORITY
+   #define STM32F107_ETH_IRQ_GROUP_PRIORITY 12
+#elif (STM32F107_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error STM32F107_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef STM32F107_ETH_IRQ_SUB_PRIORITY
+   #define STM32F107_ETH_IRQ_SUB_PRIORITY 0
+#elif (STM32F107_ETH_IRQ_SUB_PRIORITY < 0)
+   #error STM32F107_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//Transmit DMA descriptor flags
+#define ETH_TDES0_OWN   0x80000000
+#define ETH_TDES0_IC    0x40000000
+#define ETH_TDES0_LS    0x20000000
+#define ETH_TDES0_FS    0x10000000
+#define ETH_TDES0_DC    0x08000000
+#define ETH_TDES0_DP    0x04000000
+#define ETH_TDES0_TTSE  0x02000000
+#define ETH_TDES0_CIC   0x00C00000
+#define ETH_TDES0_TER   0x00200000
+#define ETH_TDES0_TCH   0x00100000
+#define ETH_TDES0_TTSS  0x00020000
+#define ETH_TDES0_IHE   0x00010000
+#define ETH_TDES0_ES    0x00008000
+#define ETH_TDES0_JT    0x00004000
+#define ETH_TDES0_FF    0x00002000
+#define ETH_TDES0_IPE   0x00001000
+#define ETH_TDES0_LCA   0x00000800
+#define ETH_TDES0_NC    0x00000400
+#define ETH_TDES0_LCO   0x00000200
+#define ETH_TDES0_EC    0x00000100
+#define ETH_TDES0_VF    0x00000080
+#define ETH_TDES0_CC    0x00000078
+#define ETH_TDES0_ED    0x00000004
+#define ETH_TDES0_UF    0x00000002
+#define ETH_TDES0_DB    0x00000001
+#define ETH_TDES1_TBS2  0x1FFF0000
+#define ETH_TDES1_TBS1  0x00001FFF
+#define ETH_TDES2_TBAP1 0xFFFFFFFF
+#define ETH_TDES3_TBAP2 0xFFFFFFFF
+
+//Receive DMA descriptor flags
+#define ETH_RDES0_OWN   0x80000000
+#define ETH_RDES0_AFM   0x40000000
+#define ETH_RDES0_FL    0x3FFF0000
+#define ETH_RDES0_ES    0x00008000
+#define ETH_RDES0_DE    0x00004000
+#define ETH_RDES0_SAF   0x00002000
+#define ETH_RDES0_LE    0x00001000
+#define ETH_RDES0_OE    0x00000800
+#define ETH_RDES0_VLAN  0x00000400
+#define ETH_RDES0_FS    0x00000200
+#define ETH_RDES0_LS    0x00000100
+#define ETH_RDES0_IPHCE 0x00000080
+#define ETH_RDES0_LCO   0x00000040
+#define ETH_RDES0_FT    0x00000020
+#define ETH_RDES0_RWT   0x00000010
+#define ETH_RDES0_RE    0x00000008
+#define ETH_RDES0_DBE   0x00000004
+#define ETH_RDES0_CE    0x00000002
+#define ETH_RDES0_PCE   0x00000001
+#define ETH_RDES1_DIC   0x80000000
+#define ETH_RDES1_RBS2  0x1FFF0000
+#define ETH_RDES1_RER   0x00008000
+#define ETH_RDES1_RCH   0x00004000
+#define ETH_RDES1_RBS1  0x00001FFF
+#define ETH_RDES2_RBAP1 0xFFFFFFFF
+#define ETH_RDES3_RBAP2 0xFFFFFFFF
+
+
+/**
+ * @brief Transmit DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t tdes0;
+   uint32_t tdes1;
+   uint32_t tdes2;
+   uint32_t tdes3;
+} Stm32f107TxDmaDesc;
+
+
+/**
+ * @brief Receive DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t rdes0;
+   uint32_t rdes1;
+   uint32_t rdes2;
+   uint32_t rdes3;
+} Stm32f107RxDmaDesc;
+
+
+//STM32F107 Ethernet MAC driver
+extern const NicDriver stm32f107EthDriver;
+
+//STM32F107 Ethernet MAC related functions
+error_t stm32f107EthInit(NetInterface *interface);
+void stm32f107EthInitGpio(NetInterface *interface);
+void stm32f107EthInitDmaDesc(NetInterface *interface);
+
+void stm32f107EthTick(NetInterface *interface);
+
+void stm32f107EthEnableIrq(NetInterface *interface);
+void stm32f107EthDisableIrq(NetInterface *interface);
+void stm32f107EthEventHandler(NetInterface *interface);
+
+error_t stm32f107EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t stm32f107EthReceivePacket(NetInterface *interface);
+
+error_t stm32f107EthSetMulticastFilter(NetInterface *interface);
+error_t stm32f107EthUpdateMacConfig(NetInterface *interface);
+
+void stm32f107EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t stm32f107EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t stm32f107EthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/stm32f2x7_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,959 @@
+/**
+ * @file stm32f2x7_eth.c
+ * @brief STM32F207/217 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "stm32f2xx.h"
+#include "core/net.h"
+#include "drivers/stm32f2x7_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+static uint8_t txBuffer[STM32F2X7_ETH_TX_BUFFER_COUNT][STM32F2X7_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+static uint8_t rxBuffer[STM32F2X7_ETH_RX_BUFFER_COUNT][STM32F2X7_ETH_RX_BUFFER_SIZE];
+//Transmit DMA descriptors
+#pragma data_alignment = 4
+static Stm32f2x7TxDmaDesc txDmaDesc[STM32F2X7_ETH_TX_BUFFER_COUNT];
+//Receive DMA descriptors
+#pragma data_alignment = 4
+static Stm32f2x7RxDmaDesc rxDmaDesc[STM32F2X7_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[STM32F2X7_ETH_TX_BUFFER_COUNT][STM32F2X7_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[STM32F2X7_ETH_RX_BUFFER_COUNT][STM32F2X7_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Transmit DMA descriptors
+static Stm32f2x7TxDmaDesc txDmaDesc[STM32F2X7_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive DMA descriptors
+static Stm32f2x7RxDmaDesc rxDmaDesc[STM32F2X7_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+
+#endif
+
+//Pointer to the current TX DMA descriptor
+static Stm32f2x7TxDmaDesc *txCurDmaDesc;
+//Pointer to the current RX DMA descriptor
+static Stm32f2x7RxDmaDesc *rxCurDmaDesc;
+
+
+/**
+ * @brief STM32F207/217 Ethernet MAC driver
+ **/
+
+const NicDriver stm32f2x7EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   stm32f2x7EthInit,
+   stm32f2x7EthTick,
+   stm32f2x7EthEnableIrq,
+   stm32f2x7EthDisableIrq,
+   stm32f2x7EthEventHandler,
+   stm32f2x7EthSendPacket,
+   stm32f2x7EthSetMulticastFilter,
+   stm32f2x7EthUpdateMacConfig,
+   stm32f2x7EthWritePhyReg,
+   stm32f2x7EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief STM32F207/217 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f2x7EthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing STM32F2x7 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //GPIO configuration
+   stm32f2x7EthInitGpio(interface);
+
+#if defined(USE_HAL_DRIVER)
+   //Enable Ethernet MAC clock
+   __HAL_RCC_ETHMAC_CLK_ENABLE();
+   __HAL_RCC_ETHMACTX_CLK_ENABLE();
+   __HAL_RCC_ETHMACRX_CLK_ENABLE();
+
+   //Reset Ethernet MAC peripheral
+   __HAL_RCC_ETHMAC_FORCE_RESET();
+   __HAL_RCC_ETHMAC_RELEASE_RESET();
+
+#elif defined(USE_STDPERIPH_DRIVER)
+   //Enable Ethernet MAC clock
+   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_ETH_MAC |
+      RCC_AHB1Periph_ETH_MAC_Tx | RCC_AHB1Periph_ETH_MAC_Rx, ENABLE);
+
+   //Reset Ethernet MAC peripheral
+   RCC_AHB1PeriphResetCmd(RCC_AHB1Periph_ETH_MAC, ENABLE);
+   RCC_AHB1PeriphResetCmd(RCC_AHB1Periph_ETH_MAC, DISABLE);
+#endif
+
+   //Perform a software reset
+   ETH->DMABMR |= ETH_DMABMR_SR;
+   //Wait for the reset to complete
+   while(ETH->DMABMR & ETH_DMABMR_SR);
+
+   //Adjust MDC clock range depending on HCLK frequency
+   ETH->MACMIIAR = ETH_MACMIIAR_CR_Div62;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Use default MAC configuration
+   ETH->MACCR = ETH_MACCR_ROD;
+
+   //Set the MAC address
+   ETH->MACA0LR = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   ETH->MACA0HR = interface->macAddr.w[2];
+
+   //Initialize hash table
+   ETH->MACHTLR = 0;
+   ETH->MACHTHR = 0;
+
+   //Configure the receive filter
+   ETH->MACFFR = ETH_MACFFR_HPF | ETH_MACFFR_HM;
+   //Disable flow control
+   ETH->MACFCR = 0;
+   //Enable store and forward mode
+   ETH->DMAOMR = ETH_DMAOMR_RSF | ETH_DMAOMR_TSF;
+
+   //Configure DMA bus mode
+   ETH->DMABMR = ETH_DMABMR_AAB | ETH_DMABMR_USP | ETH_DMABMR_RDP_1Beat |
+      ETH_DMABMR_RTPR_1_1 | ETH_DMABMR_PBL_1Beat | ETH_DMABMR_EDE;
+
+   //Initialize DMA descriptor lists
+   stm32f2x7EthInitDmaDesc(interface);
+
+   //Prevent interrupts from being generated when the transmit statistic
+   //counters reach half their maximum value
+   ETH->MMCTIMR = ETH_MMCTIMR_TGFM | ETH_MMCTIMR_TGFMSCM | ETH_MMCTIMR_TGFSCM;
+
+   //Prevent interrupts from being generated when the receive statistic
+   //counters reach half their maximum value
+   ETH->MMCRIMR = ETH_MMCRIMR_RGUFM | ETH_MMCRIMR_RFAEM | ETH_MMCRIMR_RFCEM;
+
+   //Disable MAC interrupts
+   ETH->MACIMR = ETH_MACIMR_TSTIM | ETH_MACIMR_PMTIM;
+   //Enable the desired DMA interrupts
+   ETH->DMAIER = ETH_DMAIER_NISE | ETH_DMAIER_RIE | ETH_DMAIER_TIE;
+
+   //Set priority grouping (4 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(STM32F2X7_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure Ethernet interrupt priority
+   NVIC_SetPriority(ETH_IRQn, NVIC_EncodePriority(STM32F2X7_ETH_IRQ_PRIORITY_GROUPING,
+      STM32F2X7_ETH_IRQ_GROUP_PRIORITY, STM32F2X7_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable MAC transmission and reception
+   ETH->MACCR |= ETH_MACCR_TE | ETH_MACCR_RE;
+   //Enable DMA transmission and reception
+   ETH->DMAOMR |= ETH_DMAOMR_ST | ETH_DMAOMR_SR;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//STM3220G-EVAL, MCBSTM32F200 or Nucleo-F207ZG evaluation board?
+#if defined(USE_STM322xG_EVAL) || defined(USE_MCBSTM32F200) || \
+   defined(USE_STM32F2XX_NUCLEO_144)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f2x7EthInitGpio(NetInterface *interface)
+{
+   GPIO_InitTypeDef GPIO_InitStructure;
+
+//STM3220G-EVAL evaluation board?
+#if defined(USE_STM322xG_EVAL) && defined(USE_HAL_DRIVER)
+   //Enable SYSCFG clock
+   __HAL_RCC_SYSCFG_CLK_ENABLE();
+
+   //Enable GPIO clocks
+   __HAL_RCC_GPIOA_CLK_ENABLE();
+   __HAL_RCC_GPIOB_CLK_ENABLE();
+   __HAL_RCC_GPIOC_CLK_ENABLE();
+   __HAL_RCC_GPIOG_CLK_ENABLE();
+   __HAL_RCC_GPIOH_CLK_ENABLE();
+   __HAL_RCC_GPIOI_CLK_ENABLE();
+
+   //Configure MCO1 (PA8) as an output
+   GPIO_InitStructure.Pin = GPIO_PIN_8;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF0_MCO;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure MCO1 pin to output the HSE clock (25MHz)
+   HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1);
+
+   //Select MII interface mode
+   SYSCFG->PMC &= ~SYSCFG_PMC_MII_RMII_SEL;
+
+   //Configure MII pins
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
+
+   //Configure ETH_MII_RX_CLK (PA1), ETH_MDIO (PA2) and ETH_MII_RX_DV (PA7)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_PPS_OUT (PB5) and ETH_MII_TXD3 (PB8)
+   GPIO_InitStructure.Pin = GPIO_PIN_5 | GPIO_PIN_8;
+   HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
+
+   //Configure ETH_MDC (PC1), ETH_MII_TXD2 (PC2), ETH_MII_TX_CLK (PC3),
+   //ETH_MII_RXD0 (PC4) and ETH_MII_RXD1 (PC5)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5;
+   HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Configure ETH_MII_TX_EN (PG11), ETH_MII_TXD0 (PG13) and ETH_MII_TXD1 (PG14)
+   GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14;
+   HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
+
+   //Configure ETH_MII_CRS (PH2), ETH_MII_COL (PH3), ETH_MII_RXD2 (PH6) and ETH_MII_RXD3 (PH7)
+   GPIO_InitStructure.Pin = GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_6 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOH, &GPIO_InitStructure);
+
+   //Configure ETH_MII_RX_ER (PI10)
+   GPIO_InitStructure.Pin = GPIO_PIN_10;
+   HAL_GPIO_Init(GPIOI, &GPIO_InitStructure);
+
+#elif defined(USE_STM322xG_EVAL) && defined(USE_STDPERIPH_DRIVER)
+   //Enable SYSCFG clock
+   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
+
+   //Enable GPIO clocks
+   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB |
+      RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOG |
+      RCC_AHB1Periph_GPIOH | RCC_AHB1Periph_GPIOI, ENABLE);
+
+   //Configure MCO1 (PA8) as an output
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
+   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
+   GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure MCO1 pin to output the HSE clock (25MHz)
+   RCC_MCO1Config(RCC_MCO1Source_HSE, RCC_MCO1Div_1);
+
+   //Select MII interface mode
+   SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_MII);
+
+   //Configure ETH_MII_RX_CLK (PA1), ETH_MDIO (PA2) and ETH_MII_RX_DV (PA7)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_7;
+   GPIO_Init(GPIOA, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_ETH);
+
+   //Configure ETH_PPS_OUT (PB5) and ETH_MII_TXD3 (PB8)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_8;
+   GPIO_Init(GPIOB, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_ETH);
+
+   //Configure ETH_MDC (PC1), ETH_MII_TXD2 (PC2), ETH_MII_TX_CLK (PC3),
+   //ETH_MII_RXD0 (PC4) and ETH_MII_RXD1 (PC5)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
+   GPIO_Init(GPIOC, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource1, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource2, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource3, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource4, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource5, GPIO_AF_ETH);
+
+   //Configure ETH_MII_TX_EN (PG11), ETH_MII_TXD0 (PG13) and ETH_MII_TXD1 (PG14)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_14;
+   GPIO_Init(GPIOG, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOG, GPIO_PinSource11, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOG, GPIO_PinSource13, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOG, GPIO_PinSource14, GPIO_AF_ETH);
+
+   //Configure ETH_MII_CRS (PH2), ETH_MII_COL (PH3), ETH_MII_RXD2 (PH6) and ETH_MII_RXD3 (PH7)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_6 | GPIO_Pin_7;
+   GPIO_Init(GPIOH, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOH, GPIO_PinSource2, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOH, GPIO_PinSource3, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOH, GPIO_PinSource6, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOH, GPIO_PinSource7, GPIO_AF_ETH);
+
+   //Configure ETH_MII_RX_ER (PI10)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
+   GPIO_Init(GPIOI, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOI, GPIO_PinSource10, GPIO_AF_ETH);
+
+//MCBSTM32F200 evaluation board?
+#elif defined(USE_MCBSTM32F200) && defined(USE_HAL_DRIVER)
+   //Enable SYSCFG clock
+   __HAL_RCC_SYSCFG_CLK_ENABLE();
+
+   //Enable GPIO clocks
+   __HAL_RCC_GPIOA_CLK_ENABLE();
+   __HAL_RCC_GPIOC_CLK_ENABLE();
+   __HAL_RCC_GPIOG_CLK_ENABLE();
+
+   //Select RMII interface mode
+   SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL;
+
+   //Configure RMII pins
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
+
+   //Configure ETH_RMII_REF_CLK (PA1), ETH_MDIO (PA2) and ETH_RMII_CRS_DV (PA7)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_MDC (PC1), ETH_RMII_RXD0 (PC4) and ETH_RMII_RXD1 (PC5)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
+   HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Configure ETH_RMII_TX_EN (PG11), ETH_RMII_TXD0 (PG13) and ETH_RMII_TXD1 (PG14)
+   GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14;
+   HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
+
+#elif defined(USE_MCBSTM32F200) && defined(USE_STDPERIPH_DRIVER)
+   //Enable SYSCFG clock
+   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
+
+   //Enable GPIO clocks
+   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA |
+      RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOG, ENABLE);
+
+   //Select RMII interface mode
+   SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_RMII);
+
+   //Configure ETH_RMII_REF_CLK (PA1), ETH_MDIO (PA2) and ETH_RMII_CRS_DV (PA7)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_7;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
+   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
+   GPIO_Init(GPIOA, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_ETH);
+
+   //Configure ETH_MDC (PC1), ETH_RMII_RXD0 (PC4) and ETH_RMII_RXD1 (PC5)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5;
+   GPIO_Init(GPIOC, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource1, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource4, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource5, GPIO_AF_ETH);
+
+   //Configure ETH_RMII_TX_EN (PG11), ETH_RMII_TXD0 (PG13) and ETH_RMII_TXD1 (PG14)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_14;
+   GPIO_Init(GPIOG, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOG, GPIO_PinSource11, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOG, GPIO_PinSource13, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOG, GPIO_PinSource14, GPIO_AF_ETH);
+
+//Nucleo-F207ZG evaluation board?
+#elif defined(USE_STM32F2XX_NUCLEO_144)
+   //Enable SYSCFG clock
+   __HAL_RCC_SYSCFG_CLK_ENABLE();
+
+   //Enable GPIO clocks
+   __HAL_RCC_GPIOA_CLK_ENABLE();
+   __HAL_RCC_GPIOB_CLK_ENABLE();
+   __HAL_RCC_GPIOC_CLK_ENABLE();
+   __HAL_RCC_GPIOG_CLK_ENABLE();
+
+   //Select RMII interface mode
+   SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL;
+
+   //Configure RMII pins
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
+
+   //Configure ETH_RMII_REF_CLK (PA1), ETH_MDIO (PA2) and ETH_RMII_CRS_DV (PA7)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_RMII_TXD1 (PB13)
+   GPIO_InitStructure.Pin = GPIO_PIN_13;
+   HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
+
+   //Configure ETH_MDC (PC1), ETH_RMII_RXD0 (PC4) and ETH_RMII_RXD1 (PC5)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
+   HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Configure RMII_TX_EN (PG11), ETH_RMII_TXD0 (PG13)
+   GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13;
+   HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
+#endif
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f2x7EthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX DMA descriptor list
+   for(i = 0; i < STM32F2X7_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Use chain structure rather than ring structure
+      txDmaDesc[i].tdes0 = ETH_TDES0_IC | ETH_TDES0_TCH;
+      //Initialize transmit buffer size
+      txDmaDesc[i].tdes1 = 0;
+      //Transmit buffer address
+      txDmaDesc[i].tdes2 = (uint32_t) txBuffer[i];
+      //Next descriptor address
+      txDmaDesc[i].tdes3 = (uint32_t) &txDmaDesc[i + 1];
+      //Reserved fields
+      txDmaDesc[i].tdes4 = 0;
+      txDmaDesc[i].tdes5 = 0;
+      //Transmit frame time stamp
+      txDmaDesc[i].tdes6 = 0;
+      txDmaDesc[i].tdes7 = 0;
+   }
+
+   //The last descriptor is chained to the first entry
+   txDmaDesc[i - 1].tdes3 = (uint32_t) &txDmaDesc[0];
+   //Point to the very first descriptor
+   txCurDmaDesc = &txDmaDesc[0];
+
+   //Initialize RX DMA descriptor list
+   for(i = 0; i < STM32F2X7_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the DMA
+      rxDmaDesc[i].rdes0 = ETH_RDES0_OWN;
+      //Use chain structure rather than ring structure
+      rxDmaDesc[i].rdes1 = ETH_RDES1_RCH | (STM32F2X7_ETH_RX_BUFFER_SIZE & ETH_RDES1_RBS1);
+      //Receive buffer address
+      rxDmaDesc[i].rdes2 = (uint32_t) rxBuffer[i];
+      //Next descriptor address
+      rxDmaDesc[i].rdes3 = (uint32_t) &rxDmaDesc[i + 1];
+      //Extended status
+      rxDmaDesc[i].rdes4 = 0;
+      //Reserved field
+      rxDmaDesc[i].rdes5 = 0;
+      //Receive frame time stamp
+      rxDmaDesc[i].rdes6 = 0;
+      rxDmaDesc[i].rdes7 = 0;
+   }
+
+   //The last descriptor is chained to the first entry
+   rxDmaDesc[i - 1].rdes3 = (uint32_t) &rxDmaDesc[0];
+   //Point to the very first descriptor
+   rxCurDmaDesc = &rxDmaDesc[0];
+
+   //Start location of the TX descriptor list
+   ETH->DMATDLAR = (uint32_t) txDmaDesc;
+   //Start location of the RX descriptor list
+   ETH->DMARDLAR = (uint32_t) rxDmaDesc;
+}
+
+
+/**
+ * @brief STM32F207/217 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f2x7EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f2x7EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(ETH_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f2x7EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(ETH_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief STM32F207/217 Ethernet MAC interrupt service routine
+ **/
+
+void ETH_IRQHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read DMA status register
+   status = ETH->DMASR;
+
+   //A packet has been transmitted?
+   if(status & ETH_DMASR_TS)
+   {
+      //Clear TS interrupt flag
+      ETH->DMASR = ETH_DMASR_TS;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & ETH_DMASR_RS)
+   {
+      //Disable RIE interrupt
+      ETH->DMAIER &= ~ETH_DMAIER_RIE;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Clear NIS interrupt flag
+   ETH->DMASR = ETH_DMASR_NIS;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief STM32F207/217 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f2x7EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(ETH->DMASR & ETH_DMASR_RS)
+   {
+      //Clear interrupt flag
+      ETH->DMASR = ETH_DMASR_RS;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = stm32f2x7EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable DMA interrupts
+   ETH->DMAIER |= ETH_DMAIER_NISE | ETH_DMAIER_RIE | ETH_DMAIER_TIE;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t stm32f2x7EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > STM32F2X7_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurDmaDesc->tdes0 & ETH_TDES0_OWN)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txCurDmaDesc->tdes2, buffer, offset, length);
+
+   //Write the number of bytes to send
+   txCurDmaDesc->tdes1 = length & ETH_TDES1_TBS1;
+   //Set LS and FS flags as the data fits in a single buffer
+   txCurDmaDesc->tdes0 |= ETH_TDES0_LS | ETH_TDES0_FS;
+   //Give the ownership of the descriptor to the DMA
+   txCurDmaDesc->tdes0 |= ETH_TDES0_OWN;
+
+   //Clear TBUS flag to resume processing
+   ETH->DMASR = ETH_DMASR_TBUS;
+   //Instruct the DMA to poll the transmit descriptor list
+   ETH->DMATPDR = 0;
+
+   //Point to the next descriptor in the list
+   txCurDmaDesc = (Stm32f2x7TxDmaDesc *) txCurDmaDesc->tdes3;
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f2x7EthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_OWN))
+   {
+      //FS and LS flags should be set
+      if((rxCurDmaDesc->rdes0 & ETH_RDES0_FS) && (rxCurDmaDesc->rdes0 & ETH_RDES0_LS))
+      {
+         //Make sure no error occurred
+         if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_ES))
+         {
+            //Retrieve the length of the frame
+            n = (rxCurDmaDesc->rdes0 & ETH_RDES0_FL) >> 16;
+            //Limit the number of data to read
+            n = MIN(n, STM32F2X7_ETH_RX_BUFFER_SIZE);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, (uint8_t *) rxCurDmaDesc->rdes2, n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Give the ownership of the descriptor back to the DMA
+      rxCurDmaDesc->rdes0 = ETH_RDES0_OWN;
+      //Point to the next descriptor in the list
+      rxCurDmaDesc = (Stm32f2x7RxDmaDesc *) rxCurDmaDesc->rdes3;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Clear RBUS flag to resume processing
+   ETH->DMASR = ETH_DMASR_RBUS;
+   //Instruct the DMA to poll the receive descriptor list
+   ETH->DMARPDR = 0;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f2x7EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating STM32F2x7 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = stm32f2x7EthCalcCrc(&entry->addr, sizeof(MacAddr));
+
+         //The upper 6 bits in the CRC register are used to index the
+         //contents of the hash table
+         k = (crc >> 26) & 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   ETH->MACHTLR = hashTable[0];
+   ETH->MACHTHR = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  MACHTLR = %08" PRIX32 "\r\n", ETH->MACHTLR);
+   TRACE_DEBUG("  MACHTHR = %08" PRIX32 "\r\n", ETH->MACHTHR);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f2x7EthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read current MAC configuration
+   config = ETH->MACCR;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= ETH_MACCR_FES;
+   else
+      config &= ~ETH_MACCR_FES;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= ETH_MACCR_DM;
+   else
+      config &= ~ETH_MACCR_DM;
+
+   //Update MAC configuration register
+   ETH->MACCR = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void stm32f2x7EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = ETH->MACMIIAR & ETH_MACMIIAR_CR;
+   //Set up a write operation
+   value |= ETH_MACMIIAR_MW | ETH_MACMIIAR_MB;
+   //PHY address
+   value |= (phyAddr << 11) & ETH_MACMIIAR_PA;
+   //Register address
+   value |= (regAddr << 6) & ETH_MACMIIAR_MR;
+
+   //Data to be written in the PHY register
+   ETH->MACMIIDR = data & ETH_MACMIIDR_MD;
+
+   //Start a write operation
+   ETH->MACMIIAR = value;
+   //Wait for the write to complete
+   while(ETH->MACMIIAR & ETH_MACMIIAR_MB);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t stm32f2x7EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = ETH->MACMIIAR & ETH_MACMIIAR_CR;
+   //Set up a read operation
+   value |= ETH_MACMIIAR_MB;
+   //PHY address
+   value |= (phyAddr << 11) & ETH_MACMIIAR_PA;
+   //Register address
+   value |= (regAddr << 6) & ETH_MACMIIAR_MR;
+
+   //Start a read operation
+   ETH->MACMIIAR = value;
+   //Wait for the read to complete
+   while(ETH->MACMIIAR & ETH_MACMIIAR_MB);
+
+   //Return PHY register contents
+   return ETH->MACMIIDR & ETH_MACMIIDR_MD;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t stm32f2x7EthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return ~crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/stm32f2x7_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,219 @@
+/**
+ * @file stm32f2x7_eth.h
+ * @brief STM32F207/217 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _STM32F2X7_ETH_H
+#define _STM32F2X7_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef STM32F2X7_ETH_TX_BUFFER_COUNT
+   #define STM32F2X7_ETH_TX_BUFFER_COUNT 3
+#elif (STM32F2X7_ETH_TX_BUFFER_COUNT < 1)
+   #error STM32F2X7_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef STM32F2X7_ETH_TX_BUFFER_SIZE
+   #define STM32F2X7_ETH_TX_BUFFER_SIZE 1536
+#elif (STM32F2X7_ETH_TX_BUFFER_SIZE != 1536)
+   #error STM32F2X7_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef STM32F2X7_ETH_RX_BUFFER_COUNT
+   #define STM32F2X7_ETH_RX_BUFFER_COUNT 6
+#elif (STM32F2X7_ETH_RX_BUFFER_COUNT < 1)
+   #error STM32F2X7_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef STM32F2X7_ETH_RX_BUFFER_SIZE
+   #define STM32F2X7_ETH_RX_BUFFER_SIZE 1536
+#elif (STM32F2X7_ETH_RX_BUFFER_SIZE != 1536)
+   #error STM32F2X7_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef STM32F2X7_ETH_IRQ_PRIORITY_GROUPING
+   #define STM32F2X7_ETH_IRQ_PRIORITY_GROUPING 3
+#elif (STM32F2X7_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error STM32F2X7_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef STM32F2X7_ETH_IRQ_GROUP_PRIORITY
+   #define STM32F2X7_ETH_IRQ_GROUP_PRIORITY 12
+#elif (STM32F2X7_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error STM32F2X7_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef STM32F2X7_ETH_IRQ_SUB_PRIORITY
+   #define STM32F2X7_ETH_IRQ_SUB_PRIORITY 0
+#elif (STM32F2X7_ETH_IRQ_SUB_PRIORITY < 0)
+   #error STM32F2X7_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//Transmit DMA descriptor flags
+#define ETH_TDES0_OWN    0x80000000
+#define ETH_TDES0_IC     0x40000000
+#define ETH_TDES0_LS     0x20000000
+#define ETH_TDES0_FS     0x10000000
+#define ETH_TDES0_DC     0x08000000
+#define ETH_TDES0_DP     0x04000000
+#define ETH_TDES0_TTSE   0x02000000
+#define ETH_TDES0_CIC    0x00C00000
+#define ETH_TDES0_TER    0x00200000
+#define ETH_TDES0_TCH    0x00100000
+#define ETH_TDES0_TTSS   0x00020000
+#define ETH_TDES0_IHE    0x00010000
+#define ETH_TDES0_ES     0x00008000
+#define ETH_TDES0_JT     0x00004000
+#define ETH_TDES0_FF     0x00002000
+#define ETH_TDES0_IPE    0x00001000
+#define ETH_TDES0_LCA    0x00000800
+#define ETH_TDES0_NC     0x00000400
+#define ETH_TDES0_LCO    0x00000200
+#define ETH_TDES0_EC     0x00000100
+#define ETH_TDES0_VF     0x00000080
+#define ETH_TDES0_CC     0x00000078
+#define ETH_TDES0_ED     0x00000004
+#define ETH_TDES0_UF     0x00000002
+#define ETH_TDES0_DB     0x00000001
+#define ETH_TDES1_TBS2   0x1FFF0000
+#define ETH_TDES1_TBS1   0x00001FFF
+#define ETH_TDES2_TBAP1  0xFFFFFFFF
+#define ETH_TDES3_TBAP2  0xFFFFFFFF
+#define ETH_TDES6_TTSL   0xFFFFFFFF
+#define ETH_TDES7_TTSH   0xFFFFFFFF
+
+//Receive DMA descriptor flags
+#define ETH_RDES0_OWN    0x80000000
+#define ETH_RDES0_AFM    0x40000000
+#define ETH_RDES0_FL     0x3FFF0000
+#define ETH_RDES0_ES     0x00008000
+#define ETH_RDES0_DE     0x00004000
+#define ETH_RDES0_SAF    0x00002000
+#define ETH_RDES0_LE     0x00001000
+#define ETH_RDES0_OE     0x00000800
+#define ETH_RDES0_VLAN   0x00000400
+#define ETH_RDES0_FS     0x00000200
+#define ETH_RDES0_LS     0x00000100
+#define ETH_RDES0_IPHCE  0x00000080
+#define ETH_RDES0_LCO    0x00000040
+#define ETH_RDES0_FT     0x00000020
+#define ETH_RDES0_RWT    0x00000010
+#define ETH_RDES0_RE     0x00000008
+#define ETH_RDES0_DBE    0x00000004
+#define ETH_RDES0_CE     0x00000002
+#define ETH_RDES0_PCE    0x00000001
+#define ETH_RDES1_DIC    0x80000000
+#define ETH_RDES1_RBS2   0x1FFF0000
+#define ETH_RDES1_RER    0x00008000
+#define ETH_RDES1_RCH    0x00004000
+#define ETH_RDES1_RBS1   0x00001FFF
+#define ETH_RDES2_RBAP1  0xFFFFFFFF
+#define ETH_RDES3_RBAP2  0xFFFFFFFF
+#define ETH_RDES4_PV     0x00002000
+#define ETH_RDES4_PFT    0x00001000
+#define ETH_RDES4_PMT    0x00000F00
+#define ETH_RDES4_IPV6PR 0x00000080
+#define ETH_RDES4_IPV4PR 0x00000040
+#define ETH_RDES4_IPCB   0x00000020
+#define ETH_RDES4_IPPE   0x00000010
+#define ETH_RDES4_IPHE   0x00000008
+#define ETH_RDES4_IPPT   0x00000007
+#define ETH_RDES6_RTSL   0xFFFFFFFF
+#define ETH_RDES7_RTSH   0xFFFFFFFF
+
+
+/**
+ * @brief Enhanced TX DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t tdes0;
+   uint32_t tdes1;
+   uint32_t tdes2;
+   uint32_t tdes3;
+   uint32_t tdes4;
+   uint32_t tdes5;
+   uint32_t tdes6;
+   uint32_t tdes7;
+} Stm32f2x7TxDmaDesc;
+
+
+/**
+ * @brief Enhanced RX DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t rdes0;
+   uint32_t rdes1;
+   uint32_t rdes2;
+   uint32_t rdes3;
+   uint32_t rdes4;
+   uint32_t rdes5;
+   uint32_t rdes6;
+   uint32_t rdes7;
+} Stm32f2x7RxDmaDesc;
+
+
+//STM32F207/217 Ethernet MAC driver
+extern const NicDriver stm32f2x7EthDriver;
+
+//STM32F207/217 Ethernet MAC related functions
+error_t stm32f2x7EthInit(NetInterface *interface);
+void stm32f2x7EthInitGpio(NetInterface *interface);
+void stm32f2x7EthInitDmaDesc(NetInterface *interface);
+
+void stm32f2x7EthTick(NetInterface *interface);
+
+void stm32f2x7EthEnableIrq(NetInterface *interface);
+void stm32f2x7EthDisableIrq(NetInterface *interface);
+void stm32f2x7EthEventHandler(NetInterface *interface);
+
+error_t stm32f2x7EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t stm32f2x7EthReceivePacket(NetInterface *interface);
+
+error_t stm32f2x7EthSetMulticastFilter(NetInterface *interface);
+error_t stm32f2x7EthUpdateMacConfig(NetInterface *interface);
+
+void stm32f2x7EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t stm32f2x7EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t stm32f2x7EthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/stm32f4x7_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1165 @@
+/**
+ * @file stm32f4x7_eth.c
+ * @brief STM32F407/417/427/437 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "stm32f4xx.h"
+#include "core/net.h"
+#include "drivers/stm32f4x7_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+static uint8_t txBuffer[STM32F4X7_ETH_TX_BUFFER_COUNT][STM32F4X7_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+static uint8_t rxBuffer[STM32F4X7_ETH_RX_BUFFER_COUNT][STM32F4X7_ETH_RX_BUFFER_SIZE];
+//Transmit DMA descriptors
+#pragma data_alignment = 4
+static Stm32f4x7TxDmaDesc txDmaDesc[STM32F4X7_ETH_TX_BUFFER_COUNT];
+//Receive DMA descriptors
+#pragma data_alignment = 4
+static Stm32f4x7RxDmaDesc rxDmaDesc[STM32F4X7_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[STM32F4X7_ETH_TX_BUFFER_COUNT][STM32F4X7_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[STM32F4X7_ETH_RX_BUFFER_COUNT][STM32F4X7_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Transmit DMA descriptors
+static Stm32f4x7TxDmaDesc txDmaDesc[STM32F4X7_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive DMA descriptors
+static Stm32f4x7RxDmaDesc rxDmaDesc[STM32F4X7_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+
+#endif
+
+//Pointer to the current TX DMA descriptor
+static Stm32f4x7TxDmaDesc *txCurDmaDesc;
+//Pointer to the current RX DMA descriptor
+static Stm32f4x7RxDmaDesc *rxCurDmaDesc;
+
+
+/**
+ * @brief STM32F407/417/427/437 Ethernet MAC driver
+ **/
+
+const NicDriver stm32f4x7EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   stm32f4x7EthInit,
+   stm32f4x7EthTick,
+   stm32f4x7EthEnableIrq,
+   stm32f4x7EthDisableIrq,
+   stm32f4x7EthEventHandler,
+   stm32f4x7EthSendPacket,
+   stm32f4x7EthSetMulticastFilter,
+   stm32f4x7EthUpdateMacConfig,
+   stm32f4x7EthWritePhyReg,
+   stm32f4x7EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief STM32F407/417/427/437 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f4x7EthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing STM32F4x7 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //GPIO configuration
+   stm32f4x7EthInitGpio(interface);
+
+#if defined(USE_HAL_DRIVER)
+   //Enable Ethernet MAC clock
+   __HAL_RCC_ETHMAC_CLK_ENABLE();
+   __HAL_RCC_ETHMACTX_CLK_ENABLE();
+   __HAL_RCC_ETHMACRX_CLK_ENABLE();
+
+   //Reset Ethernet MAC peripheral
+   __HAL_RCC_ETHMAC_FORCE_RESET();
+   __HAL_RCC_ETHMAC_RELEASE_RESET();
+
+#elif defined(USE_STDPERIPH_DRIVER)
+   //Enable Ethernet MAC clock
+   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_ETH_MAC |
+      RCC_AHB1Periph_ETH_MAC_Tx | RCC_AHB1Periph_ETH_MAC_Rx, ENABLE);
+
+   //Reset Ethernet MAC peripheral
+   RCC_AHB1PeriphResetCmd(RCC_AHB1Periph_ETH_MAC, ENABLE);
+   RCC_AHB1PeriphResetCmd(RCC_AHB1Periph_ETH_MAC, DISABLE);
+#endif
+
+   //Perform a software reset
+   ETH->DMABMR |= ETH_DMABMR_SR;
+   //Wait for the reset to complete
+   while(ETH->DMABMR & ETH_DMABMR_SR);
+
+   //Adjust MDC clock range depending on HCLK frequency
+   ETH->MACMIIAR = ETH_MACMIIAR_CR_Div102;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Use default MAC configuration
+   ETH->MACCR = ETH_MACCR_ROD;
+
+   //Set the MAC address
+   ETH->MACA0LR = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   ETH->MACA0HR = interface->macAddr.w[2];
+
+   //Initialize hash table
+   ETH->MACHTLR = 0;
+   ETH->MACHTHR = 0;
+
+   //Configure the receive filter
+   ETH->MACFFR = ETH_MACFFR_HPF | ETH_MACFFR_HM;
+   //Disable flow control
+   ETH->MACFCR = 0;
+   //Enable store and forward mode
+   ETH->DMAOMR = ETH_DMAOMR_RSF | ETH_DMAOMR_TSF;
+
+   //Configure DMA bus mode
+   ETH->DMABMR = ETH_DMABMR_AAB | ETH_DMABMR_USP | ETH_DMABMR_RDP_1Beat |
+      ETH_DMABMR_RTPR_1_1 | ETH_DMABMR_PBL_1Beat | ETH_DMABMR_EDE;
+
+   //Initialize DMA descriptor lists
+   stm32f4x7EthInitDmaDesc(interface);
+
+   //Prevent interrupts from being generated when the transmit statistic
+   //counters reach half their maximum value
+   ETH->MMCTIMR = ETH_MMCTIMR_TGFM | ETH_MMCTIMR_TGFMSCM | ETH_MMCTIMR_TGFSCM;
+
+   //Prevent interrupts from being generated when the receive statistic
+   //counters reach half their maximum value
+   ETH->MMCRIMR = ETH_MMCRIMR_RGUFM | ETH_MMCRIMR_RFAEM | ETH_MMCRIMR_RFCEM;
+
+   //Disable MAC interrupts
+   ETH->MACIMR = ETH_MACIMR_TSTIM | ETH_MACIMR_PMTIM;
+   //Enable the desired DMA interrupts
+   ETH->DMAIER = ETH_DMAIER_NISE | ETH_DMAIER_RIE | ETH_DMAIER_TIE;
+
+   //Set priority grouping (4 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(STM32F4X7_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure Ethernet interrupt priority
+   NVIC_SetPriority(ETH_IRQn, NVIC_EncodePriority(STM32F4X7_ETH_IRQ_PRIORITY_GROUPING,
+      STM32F4X7_ETH_IRQ_GROUP_PRIORITY, STM32F4X7_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable MAC transmission and reception
+   ETH->MACCR |= ETH_MACCR_TE | ETH_MACCR_RE;
+   //Enable DMA transmission and reception
+   ETH->DMAOMR |= ETH_DMAOMR_ST | ETH_DMAOMR_SR;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//STM3240G-EVAL, STM32F4-DISCOVERY, MCBSTM32F400, STM32-E407
+//or STM-P407 evaluation board?
+#if defined(USE_STM324xG_EVAL) || defined(USE_STM32F4_DISCOVERY) || \
+   defined(USE_MCBSTM32F400) || defined(USE_STM32_E407) || defined(USE_STM32_P407)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f4x7EthInitGpio(NetInterface *interface)
+{
+   GPIO_InitTypeDef GPIO_InitStructure;
+
+//STM3240G-EVAL evaluation board?
+#if defined(USE_STM324xG_EVAL) && defined(USE_HAL_DRIVER)
+   //Enable SYSCFG clock
+   __HAL_RCC_SYSCFG_CLK_ENABLE();
+
+   //Enable GPIO clocks
+   __HAL_RCC_GPIOA_CLK_ENABLE();
+   __HAL_RCC_GPIOB_CLK_ENABLE();
+   __HAL_RCC_GPIOC_CLK_ENABLE();
+   __HAL_RCC_GPIOG_CLK_ENABLE();
+   __HAL_RCC_GPIOH_CLK_ENABLE();
+   __HAL_RCC_GPIOI_CLK_ENABLE();
+
+   //Configure MCO1 (PA8) as an output
+   GPIO_InitStructure.Pin = GPIO_PIN_8;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF0_MCO;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure MCO1 pin to output the HSE clock (25MHz)
+   HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1);
+
+   //Select MII interface mode
+   SYSCFG->PMC &= ~SYSCFG_PMC_MII_RMII_SEL;
+
+   //Configure MII pins
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
+
+   //Configure ETH_MII_RX_CLK (PA1), ETH_MDIO (PA2) and ETH_MII_RX_DV (PA7)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_PPS_OUT (PB5) and ETH_MII_TXD3 (PB8)
+   GPIO_InitStructure.Pin = GPIO_PIN_5 | GPIO_PIN_8;
+   HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
+
+   //Configure ETH_MDC (PC1), ETH_MII_TXD2 (PC2), ETH_MII_TX_CLK (PC3),
+   //ETH_MII_RXD0 (PC4) and ETH_MII_RXD1 (PC5)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5;
+   HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Configure ETH_MII_TX_EN (PG11), ETH_MII_TXD0 (PG13) and ETH_MII_TXD1 (PG14)
+   GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14;
+   HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
+
+   //Configure ETH_MII_CRS (PH2), ETH_MII_COL (PH3), ETH_MII_RXD2 (PH6) and ETH_MII_RXD3 (PH7)
+   GPIO_InitStructure.Pin = GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_6 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOH, &GPIO_InitStructure);
+
+   //Configure ETH_MII_RX_ER (PI10)
+   GPIO_InitStructure.Pin = GPIO_PIN_10;
+   HAL_GPIO_Init(GPIOI, &GPIO_InitStructure);
+
+#elif defined(USE_STM324xG_EVAL) && defined(USE_STDPERIPH_DRIVER)
+   //Enable SYSCFG clock
+   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
+
+   //Enable GPIO clocks
+   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB |
+      RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOG |
+      RCC_AHB1Periph_GPIOH | RCC_AHB1Periph_GPIOI, ENABLE);
+
+   //Configure MCO1 (PA8) as an output
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
+   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
+   GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure MCO1 pin to output the HSE clock (25MHz)
+   RCC_MCO1Config(RCC_MCO1Source_HSE, RCC_MCO1Div_1);
+
+   //Select MII interface mode
+   SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_MII);
+
+   //Configure ETH_MII_RX_CLK (PA1), ETH_MDIO (PA2) and ETH_MII_RX_DV (PA7)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_7;
+   GPIO_Init(GPIOA, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_ETH);
+
+   //Configure ETH_PPS_OUT (PB5) and ETH_MII_TXD3 (PB8)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_8;
+   GPIO_Init(GPIOB, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_ETH);
+
+   //Configure ETH_MDC (PC1), ETH_MII_TXD2 (PC2), ETH_MII_TX_CLK (PC3),
+   //ETH_MII_RXD0 (PC4) and ETH_MII_RXD1 (PC5)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
+   GPIO_Init(GPIOC, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource1, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource2, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource3, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource4, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource5, GPIO_AF_ETH);
+
+   //Configure ETH_MII_TX_EN (PG11), ETH_MII_TXD0 (PG13) and ETH_MII_TXD1 (PG14)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_14;
+   GPIO_Init(GPIOG, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOG, GPIO_PinSource11, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOG, GPIO_PinSource13, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOG, GPIO_PinSource14, GPIO_AF_ETH);
+
+   //Configure ETH_MII_CRS (PH2), ETH_MII_COL (PH3), ETH_MII_RXD2 (PH6) and ETH_MII_RXD3 (PH7)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_6 | GPIO_Pin_7;
+   GPIO_Init(GPIOH, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOH, GPIO_PinSource2, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOH, GPIO_PinSource3, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOH, GPIO_PinSource6, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOH, GPIO_PinSource7, GPIO_AF_ETH);
+
+   //Configure ETH_MII_RX_ER (PI10)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
+   GPIO_Init(GPIOI, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOI, GPIO_PinSource10, GPIO_AF_ETH);
+
+//STM32F4-DISCOVERY evaluation board?
+#elif defined(USE_STM32F4_DISCOVERY) && defined(USE_HAL_DRIVER)
+   //Enable SYSCFG clock
+   __HAL_RCC_SYSCFG_CLK_ENABLE();
+
+   //Enable GPIO clocks
+   __HAL_RCC_GPIOA_CLK_ENABLE();
+   __HAL_RCC_GPIOB_CLK_ENABLE();
+   __HAL_RCC_GPIOC_CLK_ENABLE();
+
+   //Select RMII interface mode
+   SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL;
+
+   //Configure RMII pins
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
+
+   //Configure ETH_RMII_REF_CLK (PA1), ETH_MDIO (PA2) and ETH_RMII_CRS_DV (PA7)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_RMII_TX_EN (PB11), ETH_RMII_TXD0 (PB12) and ETH_RMII_TXD1 (PB13)
+   GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13;
+   HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
+
+   //Configure ETH_MDC (PC1), ETH_RMII_RXD0 (PC4) and ETH_RMII_RXD1 (PC5)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
+   HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+#elif defined(USE_STM32F4_DISCOVERY) && defined(USE_STDPERIPH_DRIVER)
+   //Enable SYSCFG clock
+   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
+
+   //Enable GPIO clocks
+   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA |
+      RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC, ENABLE);
+
+   //Select RMII interface mode
+   SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_RMII);
+
+   //Configure ETH_RMII_REF_CLK (PA1), ETH_MDIO (PA2) and ETH_RMII_CRS_DV (PA7)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_7;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
+   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
+   GPIO_Init(GPIOA, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_ETH);
+
+   //Configure ETH_RMII_TX_EN (PB11), ETH_RMII_TXD0 (PB12) and ETH_RMII_TXD1 (PB13)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13;
+   GPIO_Init(GPIOB, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOB, GPIO_PinSource12, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_ETH);
+
+   //Configure ETH_MDC (PC1), ETH_RMII_RXD0 (PC4) and ETH_RMII_RXD1 (PC5)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5;
+   GPIO_Init(GPIOC, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource1, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource4, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource5, GPIO_AF_ETH);
+
+//MCBSTM32F400 evaluation board?
+#elif defined(USE_MCBSTM32F400) && defined(USE_HAL_DRIVER)
+   //Enable SYSCFG clock
+   __HAL_RCC_SYSCFG_CLK_ENABLE();
+
+   //Enable GPIO clocks
+   __HAL_RCC_GPIOA_CLK_ENABLE();
+   __HAL_RCC_GPIOC_CLK_ENABLE();
+   __HAL_RCC_GPIOG_CLK_ENABLE();
+
+   //Select RMII interface mode
+   SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL;
+
+   //Configure RMII pins
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
+
+   //Configure ETH_RMII_REF_CLK (PA1), ETH_MDIO (PA2) and ETH_RMII_CRS_DV (PA7)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_MDC (PC1), ETH_RMII_RXD0 (PC4) and ETH_RMII_RXD1 (PC5)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
+   HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Configure ETH_RMII_TX_EN (PG11), ETH_RMII_TXD0 (PG13) and ETH_RMII_TXD1 (PG14)
+   GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14;
+   HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
+
+#elif defined(USE_MCBSTM32F400) && defined(USE_STDPERIPH_DRIVER)
+   //Enable SYSCFG clock
+   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
+
+   //Enable GPIO clocks
+   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA |
+      RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOG, ENABLE);
+
+   //Select RMII interface mode
+   SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_RMII);
+
+   //Configure ETH_RMII_REF_CLK (PA1), ETH_MDIO (PA2) and ETH_RMII_CRS_DV (PA7)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_7;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
+   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
+   GPIO_Init(GPIOA, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_ETH);
+
+   //Configure ETH_MDC (PC1), ETH_RMII_RXD0 (PC4) and ETH_RMII_RXD1 (PC5)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5;
+   GPIO_Init(GPIOC, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource1, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource4, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource5, GPIO_AF_ETH);
+
+   //Configure ETH_RMII_TX_EN (PG11), ETH_RMII_TXD0 (PG13) and ETH_RMII_TXD1 (PG14)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_14;
+   GPIO_Init(GPIOG, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOG, GPIO_PinSource11, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOG, GPIO_PinSource13, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOG, GPIO_PinSource14, GPIO_AF_ETH);
+
+//STM32-E407 evaluation board?
+#elif defined(USE_STM32_E407) && defined(USE_HAL_DRIVER)
+   //Enable SYSCFG clock
+   __HAL_RCC_SYSCFG_CLK_ENABLE();
+
+   //Enable GPIO clocks
+   __HAL_RCC_GPIOA_CLK_ENABLE();
+   __HAL_RCC_GPIOC_CLK_ENABLE();
+   __HAL_RCC_GPIOG_CLK_ENABLE();
+
+   //Select RMII interface mode
+   SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL;
+
+   //Configure RMII pins
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
+
+   //Configure ETH_RMII_REF_CLK (PA1), ETH_MDIO (PA2) and ETH_RMII_CRS_DV (PA7)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_MDC (PC1), ETH_RMII_RXD0 (PC4) and ETH_RMII_RXD1 (PC5)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
+   HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Configure ETH_RMII_TX_EN (PG11), ETH_RMII_TXD0 (PG13) and ETH_RMII_TXD1 (PG14)
+   GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14;
+   HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
+
+   //Configure PHY_RST (PG6)
+   GPIO_InitStructure.GPIO_Pin = Pin = GPIO_PIN_6;
+   GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_LOW;
+   HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
+
+   //Reset PHY transceiver
+   HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, GPIO_PIN_RESET);
+   sleep(10);
+
+   //Take the PHY transceiver out of reset
+   HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, GPIO_PIN_SET);
+   sleep(10);
+
+#elif defined(USE_STM32_E407) && defined(USE_STDPERIPH_DRIVER)
+   //Enable SYSCFG clock
+   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
+
+   //Enable GPIO clocks
+   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA |
+      RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOG, ENABLE);
+
+   //Select RMII interface mode
+   SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_RMII);
+
+   //Configure ETH_RMII_REF_CLK (PA1), ETH_MDIO (PA2) and ETH_RMII_CRS_DV (PA7)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_7;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
+   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
+   GPIO_Init(GPIOA, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_ETH);
+
+   //Configure ETH_MDC (PC1), ETH_RMII_RXD0 (PC4) and ETH_RMII_RXD1 (PC5)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5;
+   GPIO_Init(GPIOC, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource1, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource4, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource5, GPIO_AF_ETH);
+
+   //Configure ETH_RMII_TX_EN (PG11), ETH_RMII_TXD0 (PG13) and ETH_RMII_TXD1 (PG14)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_14;
+   GPIO_Init(GPIOG, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOG, GPIO_PinSource11, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOG, GPIO_PinSource13, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOG, GPIO_PinSource14, GPIO_AF_ETH);
+
+   //Configure PHY_RST (PG6)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
+   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
+   GPIO_Init(GPIOG, &GPIO_InitStructure);
+
+   //Reset PHY transceiver
+   GPIO_ResetBits(GPIOG, GPIO_Pin_6);
+   sleep(10);
+
+   //Take the PHY transceiver out of reset
+   GPIO_SetBits(GPIOG, GPIO_Pin_6);
+   sleep(10);
+
+//STM32-P407 evaluation board?
+#elif defined(USE_STM32_P407) && defined(USE_HAL_DRIVER)
+   //Enable SYSCFG clock
+   __HAL_RCC_SYSCFG_CLK_ENABLE();
+
+   //Enable GPIO clocks
+   __HAL_RCC_GPIOA_CLK_ENABLE();
+   __HAL_RCC_GPIOB_CLK_ENABLE();
+   __HAL_RCC_GPIOC_CLK_ENABLE();
+   __HAL_RCC_GPIOG_CLK_ENABLE();
+
+   //Select RMII interface mode
+   SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL;
+
+   //Configure RMII pins
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
+
+   //Configure ETH_RMII_REF_CLK (PA1), ETH_MDIO (PA2) and ETH_RMII_CRS_DV (PA7)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_RMII_TX_EN (PB11)
+   GPIO_InitStructure.Pin = GPIO_PIN_11;
+   HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
+
+   //Configure ETH_MDC (PC1), ETH_RMII_RXD0 (PC4) and ETH_RMII_RXD1 (PC5)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
+   HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Configure ETH_RMII_TXD0 (PG13) and ETH_RMII_TXD1 (PG14)
+   GPIO_InitStructure.Pin = GPIO_PIN_13 | GPIO_PIN_14;
+   HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
+
+#elif defined(USE_STM32_P407) && defined(USE_STDPERIPH_DRIVER)
+   //Enable SYSCFG clock
+   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
+
+   //Enable GPIO clocks
+   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB |
+      RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOG, ENABLE);
+
+   //Select RMII interface mode
+   SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_RMII);
+
+   //Configure ETH_RMII_REF_CLK (PA1), ETH_MDIO (PA2) and ETH_RMII_CRS_DV (PA7)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_7;
+   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
+   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
+   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
+   GPIO_Init(GPIOA, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_ETH);
+
+   //Configure ETH_RMII_TX_EN (PB11)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
+   GPIO_Init(GPIOB, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_ETH);
+
+   //Configure ETH_MDC (PC1), ETH_RMII_RXD0 (PC4) and ETH_RMII_RXD1 (PC5)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5;
+   GPIO_Init(GPIOC, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource1, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource4, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOC, GPIO_PinSource5, GPIO_AF_ETH);
+
+   //Configure ETH_RMII_TXD0 (PG13) and ETH_RMII_TXD1 (PG14)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14;
+   GPIO_Init(GPIOG, &GPIO_InitStructure);
+   GPIO_PinAFConfig(GPIOG, GPIO_PinSource13, GPIO_AF_ETH);
+   GPIO_PinAFConfig(GPIOG, GPIO_PinSource14, GPIO_AF_ETH);
+#endif
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f4x7EthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX DMA descriptor list
+   for(i = 0; i < STM32F4X7_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Use chain structure rather than ring structure
+      txDmaDesc[i].tdes0 = ETH_TDES0_IC | ETH_TDES0_TCH;
+      //Initialize transmit buffer size
+      txDmaDesc[i].tdes1 = 0;
+      //Transmit buffer address
+      txDmaDesc[i].tdes2 = (uint32_t) txBuffer[i];
+      //Next descriptor address
+      txDmaDesc[i].tdes3 = (uint32_t) &txDmaDesc[i + 1];
+      //Reserved fields
+      txDmaDesc[i].tdes4 = 0;
+      txDmaDesc[i].tdes5 = 0;
+      //Transmit frame time stamp
+      txDmaDesc[i].tdes6 = 0;
+      txDmaDesc[i].tdes7 = 0;
+   }
+
+   //The last descriptor is chained to the first entry
+   txDmaDesc[i - 1].tdes3 = (uint32_t) &txDmaDesc[0];
+   //Point to the very first descriptor
+   txCurDmaDesc = &txDmaDesc[0];
+
+   //Initialize RX DMA descriptor list
+   for(i = 0; i < STM32F4X7_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the DMA
+      rxDmaDesc[i].rdes0 = ETH_RDES0_OWN;
+      //Use chain structure rather than ring structure
+      rxDmaDesc[i].rdes1 = ETH_RDES1_RCH | (STM32F4X7_ETH_RX_BUFFER_SIZE & ETH_RDES1_RBS1);
+      //Receive buffer address
+      rxDmaDesc[i].rdes2 = (uint32_t) rxBuffer[i];
+      //Next descriptor address
+      rxDmaDesc[i].rdes3 = (uint32_t) &rxDmaDesc[i + 1];
+      //Extended status
+      rxDmaDesc[i].rdes4 = 0;
+      //Reserved field
+      rxDmaDesc[i].rdes5 = 0;
+      //Receive frame time stamp
+      rxDmaDesc[i].rdes6 = 0;
+      rxDmaDesc[i].rdes7 = 0;
+   }
+
+   //The last descriptor is chained to the first entry
+   rxDmaDesc[i - 1].rdes3 = (uint32_t) &rxDmaDesc[0];
+   //Point to the very first descriptor
+   rxCurDmaDesc = &rxDmaDesc[0];
+
+   //Start location of the TX descriptor list
+   ETH->DMATDLAR = (uint32_t) txDmaDesc;
+   //Start location of the RX descriptor list
+   ETH->DMARDLAR = (uint32_t) rxDmaDesc;
+}
+
+
+/**
+ * @brief STM32F407/417/427/437 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f4x7EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f4x7EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(ETH_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f4x7EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(ETH_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief STM32F407/417/427/437 Ethernet MAC interrupt service routine
+ **/
+
+void ETH_IRQHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read DMA status register
+   status = ETH->DMASR;
+
+   //A packet has been transmitted?
+   if(status & ETH_DMASR_TS)
+   {
+      //Clear TS interrupt flag
+      ETH->DMASR = ETH_DMASR_TS;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & ETH_DMASR_RS)
+   {
+      //Disable RIE interrupt
+      ETH->DMAIER &= ~ETH_DMAIER_RIE;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Clear NIS interrupt flag
+   ETH->DMASR = ETH_DMASR_NIS;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief STM32F407/417/427/437 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f4x7EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(ETH->DMASR & ETH_DMASR_RS)
+   {
+      //Clear interrupt flag
+      ETH->DMASR = ETH_DMASR_RS;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = stm32f4x7EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable DMA interrupts
+   ETH->DMAIER |= ETH_DMAIER_NISE | ETH_DMAIER_RIE | ETH_DMAIER_TIE;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t stm32f4x7EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > STM32F4X7_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurDmaDesc->tdes0 & ETH_TDES0_OWN)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txCurDmaDesc->tdes2, buffer, offset, length);
+
+   //Write the number of bytes to send
+   txCurDmaDesc->tdes1 = length & ETH_TDES1_TBS1;
+   //Set LS and FS flags as the data fits in a single buffer
+   txCurDmaDesc->tdes0 |= ETH_TDES0_LS | ETH_TDES0_FS;
+   //Give the ownership of the descriptor to the DMA
+   txCurDmaDesc->tdes0 |= ETH_TDES0_OWN;
+
+   //Clear TBUS flag to resume processing
+   ETH->DMASR = ETH_DMASR_TBUS;
+   //Instruct the DMA to poll the transmit descriptor list
+   ETH->DMATPDR = 0;
+
+   //Point to the next descriptor in the list
+   txCurDmaDesc = (Stm32f4x7TxDmaDesc *) txCurDmaDesc->tdes3;
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f4x7EthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_OWN))
+   {
+      //FS and LS flags should be set
+      if((rxCurDmaDesc->rdes0 & ETH_RDES0_FS) && (rxCurDmaDesc->rdes0 & ETH_RDES0_LS))
+      {
+         //Make sure no error occurred
+         if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_ES))
+         {
+            //Retrieve the length of the frame
+            n = (rxCurDmaDesc->rdes0 & ETH_RDES0_FL) >> 16;
+            //Limit the number of data to read
+            n = MIN(n, STM32F4X7_ETH_RX_BUFFER_SIZE);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, (uint8_t *) rxCurDmaDesc->rdes2, n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Give the ownership of the descriptor back to the DMA
+      rxCurDmaDesc->rdes0 = ETH_RDES0_OWN;
+      //Point to the next descriptor in the list
+      rxCurDmaDesc = (Stm32f4x7RxDmaDesc *) rxCurDmaDesc->rdes3;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Clear RBUS flag to resume processing
+   ETH->DMASR = ETH_DMASR_RBUS;
+   //Instruct the DMA to poll the receive descriptor list
+   ETH->DMARPDR = 0;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f4x7EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating STM32F4x7 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = stm32f4x7EthCalcCrc(&entry->addr, sizeof(MacAddr));
+
+         //The upper 6 bits in the CRC register are used to index the
+         //contents of the hash table
+         k = (crc >> 26) & 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   ETH->MACHTLR = hashTable[0];
+   ETH->MACHTHR = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  MACHTLR = %08" PRIX32 "\r\n", ETH->MACHTLR);
+   TRACE_DEBUG("  MACHTHR = %08" PRIX32 "\r\n", ETH->MACHTHR);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f4x7EthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read current MAC configuration
+   config = ETH->MACCR;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= ETH_MACCR_FES;
+   else
+      config &= ~ETH_MACCR_FES;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= ETH_MACCR_DM;
+   else
+      config &= ~ETH_MACCR_DM;
+
+   //Update MAC configuration register
+   ETH->MACCR = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void stm32f4x7EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = ETH->MACMIIAR & ETH_MACMIIAR_CR;
+   //Set up a write operation
+   value |= ETH_MACMIIAR_MW | ETH_MACMIIAR_MB;
+   //PHY address
+   value |= (phyAddr << 11) & ETH_MACMIIAR_PA;
+   //Register address
+   value |= (regAddr << 6) & ETH_MACMIIAR_MR;
+
+   //Data to be written in the PHY register
+   ETH->MACMIIDR = data & ETH_MACMIIDR_MD;
+
+   //Start a write operation
+   ETH->MACMIIAR = value;
+   //Wait for the write to complete
+   while(ETH->MACMIIAR & ETH_MACMIIAR_MB);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t stm32f4x7EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = ETH->MACMIIAR & ETH_MACMIIAR_CR;
+   //Set up a read operation
+   value |= ETH_MACMIIAR_MB;
+   //PHY address
+   value |= (phyAddr << 11) & ETH_MACMIIAR_PA;
+   //Register address
+   value |= (regAddr << 6) & ETH_MACMIIAR_MR;
+
+   //Start a read operation
+   ETH->MACMIIAR = value;
+   //Wait for the read to complete
+   while(ETH->MACMIIAR & ETH_MACMIIAR_MB);
+
+   //Return PHY register contents
+   return ETH->MACMIIDR & ETH_MACMIIDR_MD;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t stm32f4x7EthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return ~crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/stm32f4x7_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,219 @@
+/**
+ * @file stm32f4x7_eth.h
+ * @brief STM32F407/417/427/437 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _STM32F4X7_ETH_H
+#define _STM32F4X7_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef STM32F4X7_ETH_TX_BUFFER_COUNT
+   #define STM32F4X7_ETH_TX_BUFFER_COUNT 3
+#elif (STM32F4X7_ETH_TX_BUFFER_COUNT < 1)
+   #error STM32F4X7_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef STM32F4X7_ETH_TX_BUFFER_SIZE
+   #define STM32F4X7_ETH_TX_BUFFER_SIZE 1536
+#elif (STM32F4X7_ETH_TX_BUFFER_SIZE != 1536)
+   #error STM32F4X7_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef STM32F4X7_ETH_RX_BUFFER_COUNT
+   #define STM32F4X7_ETH_RX_BUFFER_COUNT 6
+#elif (STM32F4X7_ETH_RX_BUFFER_COUNT < 1)
+   #error STM32F4X7_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef STM32F4X7_ETH_RX_BUFFER_SIZE
+   #define STM32F4X7_ETH_RX_BUFFER_SIZE 1536
+#elif (STM32F4X7_ETH_RX_BUFFER_SIZE != 1536)
+   #error STM32F4X7_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef STM32F4X7_ETH_IRQ_PRIORITY_GROUPING
+   #define STM32F4X7_ETH_IRQ_PRIORITY_GROUPING 3
+#elif (STM32F4X7_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error STM32F4X7_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef STM32F4X7_ETH_IRQ_GROUP_PRIORITY
+   #define STM32F4X7_ETH_IRQ_GROUP_PRIORITY 12
+#elif (STM32F4X7_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error STM32F4X7_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef STM32F4X7_ETH_IRQ_SUB_PRIORITY
+   #define STM32F4X7_ETH_IRQ_SUB_PRIORITY 0
+#elif (STM32F4X7_ETH_IRQ_SUB_PRIORITY < 0)
+   #error STM32F4X7_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//Transmit DMA descriptor flags
+#define ETH_TDES0_OWN    0x80000000
+#define ETH_TDES0_IC     0x40000000
+#define ETH_TDES0_LS     0x20000000
+#define ETH_TDES0_FS     0x10000000
+#define ETH_TDES0_DC     0x08000000
+#define ETH_TDES0_DP     0x04000000
+#define ETH_TDES0_TTSE   0x02000000
+#define ETH_TDES0_CIC    0x00C00000
+#define ETH_TDES0_TER    0x00200000
+#define ETH_TDES0_TCH    0x00100000
+#define ETH_TDES0_TTSS   0x00020000
+#define ETH_TDES0_IHE    0x00010000
+#define ETH_TDES0_ES     0x00008000
+#define ETH_TDES0_JT     0x00004000
+#define ETH_TDES0_FF     0x00002000
+#define ETH_TDES0_IPE    0x00001000
+#define ETH_TDES0_LCA    0x00000800
+#define ETH_TDES0_NC     0x00000400
+#define ETH_TDES0_LCO    0x00000200
+#define ETH_TDES0_EC     0x00000100
+#define ETH_TDES0_VF     0x00000080
+#define ETH_TDES0_CC     0x00000078
+#define ETH_TDES0_ED     0x00000004
+#define ETH_TDES0_UF     0x00000002
+#define ETH_TDES0_DB     0x00000001
+#define ETH_TDES1_TBS2   0x1FFF0000
+#define ETH_TDES1_TBS1   0x00001FFF
+#define ETH_TDES2_TBAP1  0xFFFFFFFF
+#define ETH_TDES3_TBAP2  0xFFFFFFFF
+#define ETH_TDES6_TTSL   0xFFFFFFFF
+#define ETH_TDES7_TTSH   0xFFFFFFFF
+
+//Receive DMA descriptor flags
+#define ETH_RDES0_OWN    0x80000000
+#define ETH_RDES0_AFM    0x40000000
+#define ETH_RDES0_FL     0x3FFF0000
+#define ETH_RDES0_ES     0x00008000
+#define ETH_RDES0_DE     0x00004000
+#define ETH_RDES0_SAF    0x00002000
+#define ETH_RDES0_LE     0x00001000
+#define ETH_RDES0_OE     0x00000800
+#define ETH_RDES0_VLAN   0x00000400
+#define ETH_RDES0_FS     0x00000200
+#define ETH_RDES0_LS     0x00000100
+#define ETH_RDES0_IPHCE  0x00000080
+#define ETH_RDES0_LCO    0x00000040
+#define ETH_RDES0_FT     0x00000020
+#define ETH_RDES0_RWT    0x00000010
+#define ETH_RDES0_RE     0x00000008
+#define ETH_RDES0_DBE    0x00000004
+#define ETH_RDES0_CE     0x00000002
+#define ETH_RDES0_PCE    0x00000001
+#define ETH_RDES1_DIC    0x80000000
+#define ETH_RDES1_RBS2   0x1FFF0000
+#define ETH_RDES1_RER    0x00008000
+#define ETH_RDES1_RCH    0x00004000
+#define ETH_RDES1_RBS1   0x00001FFF
+#define ETH_RDES2_RBAP1  0xFFFFFFFF
+#define ETH_RDES3_RBAP2  0xFFFFFFFF
+#define ETH_RDES4_PV     0x00002000
+#define ETH_RDES4_PFT    0x00001000
+#define ETH_RDES4_PMT    0x00000F00
+#define ETH_RDES4_IPV6PR 0x00000080
+#define ETH_RDES4_IPV4PR 0x00000040
+#define ETH_RDES4_IPCB   0x00000020
+#define ETH_RDES4_IPPE   0x00000010
+#define ETH_RDES4_IPHE   0x00000008
+#define ETH_RDES4_IPPT   0x00000007
+#define ETH_RDES6_RTSL   0xFFFFFFFF
+#define ETH_RDES7_RTSH   0xFFFFFFFF
+
+
+/**
+ * @brief Enhanced TX DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t tdes0;
+   uint32_t tdes1;
+   uint32_t tdes2;
+   uint32_t tdes3;
+   uint32_t tdes4;
+   uint32_t tdes5;
+   uint32_t tdes6;
+   uint32_t tdes7;
+} Stm32f4x7TxDmaDesc;
+
+
+/**
+ * @brief Enhanced RX DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t rdes0;
+   uint32_t rdes1;
+   uint32_t rdes2;
+   uint32_t rdes3;
+   uint32_t rdes4;
+   uint32_t rdes5;
+   uint32_t rdes6;
+   uint32_t rdes7;
+} Stm32f4x7RxDmaDesc;
+
+
+//STM32F407/417/427/437 Ethernet MAC driver
+extern const NicDriver stm32f4x7EthDriver;
+
+//STM32F407/417/427/437 Ethernet MAC related functions
+error_t stm32f4x7EthInit(NetInterface *interface);
+void stm32f4x7EthInitGpio(NetInterface *interface);
+void stm32f4x7EthInitDmaDesc(NetInterface *interface);
+
+void stm32f4x7EthTick(NetInterface *interface);
+
+void stm32f4x7EthEnableIrq(NetInterface *interface);
+void stm32f4x7EthDisableIrq(NetInterface *interface);
+void stm32f4x7EthEventHandler(NetInterface *interface);
+
+error_t stm32f4x7EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t stm32f4x7EthReceivePacket(NetInterface *interface);
+
+error_t stm32f4x7EthSetMulticastFilter(NetInterface *interface);
+error_t stm32f4x7EthUpdateMacConfig(NetInterface *interface);
+
+void stm32f4x7EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t stm32f4x7EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t stm32f4x7EthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/stm32f4x9_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,889 @@
+/**
+ * @file stm32f4x9_eth.c
+ * @brief STM32F429/439 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "stm32f4xx.h"
+#include "core/net.h"
+#include "drivers/stm32f4x9_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+static uint8_t txBuffer[STM32F4X9_ETH_TX_BUFFER_COUNT][STM32F4X9_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+static uint8_t rxBuffer[STM32F4X9_ETH_RX_BUFFER_COUNT][STM32F4X9_ETH_RX_BUFFER_SIZE];
+//Transmit DMA descriptors
+#pragma data_alignment = 4
+static Stm32f4x9TxDmaDesc txDmaDesc[STM32F4X9_ETH_TX_BUFFER_COUNT];
+//Receive DMA descriptors
+#pragma data_alignment = 4
+static Stm32f4x9RxDmaDesc rxDmaDesc[STM32F4X9_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[STM32F4X9_ETH_TX_BUFFER_COUNT][STM32F4X9_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[STM32F4X9_ETH_RX_BUFFER_COUNT][STM32F4X9_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Transmit DMA descriptors
+static Stm32f4x9TxDmaDesc txDmaDesc[STM32F4X9_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive DMA descriptors
+static Stm32f4x9RxDmaDesc rxDmaDesc[STM32F4X9_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+
+#endif
+
+//Pointer to the current TX DMA descriptor
+static Stm32f4x9TxDmaDesc *txCurDmaDesc;
+//Pointer to the current RX DMA descriptor
+static Stm32f4x9RxDmaDesc *rxCurDmaDesc;
+
+
+/**
+ * @brief STM32F429/439 Ethernet MAC driver
+ **/
+
+const NicDriver stm32f4x9EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   stm32f4x9EthInit,
+   stm32f4x9EthTick,
+   stm32f4x9EthEnableIrq,
+   stm32f4x9EthDisableIrq,
+   stm32f4x9EthEventHandler,
+   stm32f4x9EthSendPacket,
+   stm32f4x9EthSetMulticastFilter,
+   stm32f4x9EthUpdateMacConfig,
+   stm32f4x9EthWritePhyReg,
+   stm32f4x9EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief STM32F429/439 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f4x9EthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing STM32F4x9 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //GPIO configuration
+   stm32f4x9EthInitGpio(interface);
+
+   //Enable Ethernet MAC clock
+   __HAL_RCC_ETHMAC_CLK_ENABLE();
+   __HAL_RCC_ETHMACTX_CLK_ENABLE();
+   __HAL_RCC_ETHMACRX_CLK_ENABLE();
+
+   //Reset Ethernet MAC peripheral
+   __HAL_RCC_ETHMAC_FORCE_RESET();
+   __HAL_RCC_ETHMAC_RELEASE_RESET();
+
+   //Perform a software reset
+   ETH->DMABMR |= ETH_DMABMR_SR;
+   //Wait for the reset to complete
+   while(ETH->DMABMR & ETH_DMABMR_SR);
+
+   //Adjust MDC clock range depending on HCLK frequency
+   ETH->MACMIIAR = ETH_MACMIIAR_CR_Div102;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Use default MAC configuration
+   ETH->MACCR = ETH_MACCR_ROD;
+
+   //Set the MAC address
+   ETH->MACA0LR = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   ETH->MACA0HR = interface->macAddr.w[2];
+
+   //Initialize hash table
+   ETH->MACHTLR = 0;
+   ETH->MACHTHR = 0;
+
+   //Configure the receive filter
+   ETH->MACFFR = ETH_MACFFR_HPF | ETH_MACFFR_HM;
+   //Disable flow control
+   ETH->MACFCR = 0;
+   //Enable store and forward mode
+   ETH->DMAOMR = ETH_DMAOMR_RSF | ETH_DMAOMR_TSF;
+
+   //Configure DMA bus mode
+   ETH->DMABMR = ETH_DMABMR_AAB | ETH_DMABMR_USP | ETH_DMABMR_RDP_1Beat |
+      ETH_DMABMR_RTPR_1_1 | ETH_DMABMR_PBL_1Beat | ETH_DMABMR_EDE;
+
+   //Initialize DMA descriptor lists
+   stm32f4x9EthInitDmaDesc(interface);
+
+   //Prevent interrupts from being generated when the transmit statistic
+   //counters reach half their maximum value
+   ETH->MMCTIMR = ETH_MMCTIMR_TGFM | ETH_MMCTIMR_TGFMSCM | ETH_MMCTIMR_TGFSCM;
+
+   //Prevent interrupts from being generated when the receive statistic
+   //counters reach half their maximum value
+   ETH->MMCRIMR = ETH_MMCRIMR_RGUFM | ETH_MMCRIMR_RFAEM | ETH_MMCRIMR_RFCEM;
+
+   //Disable MAC interrupts
+   ETH->MACIMR = ETH_MACIMR_TSTIM | ETH_MACIMR_PMTIM;
+   //Enable the desired DMA interrupts
+   ETH->DMAIER = ETH_DMAIER_NISE | ETH_DMAIER_RIE | ETH_DMAIER_TIE;
+
+   //Set priority grouping (4 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(STM32F4X9_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure Ethernet interrupt priority
+   NVIC_SetPriority(ETH_IRQn, NVIC_EncodePriority(STM32F4X9_ETH_IRQ_PRIORITY_GROUPING,
+      STM32F4X9_ETH_IRQ_GROUP_PRIORITY, STM32F4X9_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable MAC transmission and reception
+   ETH->MACCR |= ETH_MACCR_TE | ETH_MACCR_RE;
+   //Enable DMA transmission and reception
+   ETH->DMAOMR |= ETH_DMAOMR_ST | ETH_DMAOMR_SR;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//STM324x9I-EVAL, STM32469I-EVAL, Nucleo-F429ZI or Nucleo-F446ZE evaluation board?
+#if defined(USE_STM324x9I_EVAL) || defined(USE_STM32F469I_EVAL) || \
+   defined(USE_STM32F4XX_NUCLEO_144)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f4x9EthInitGpio(NetInterface *interface)
+{
+   GPIO_InitTypeDef GPIO_InitStructure;
+
+//STM324x9I-EVAL evaluation board?
+#if defined(USE_STM324x9I_EVAL)
+   //Enable SYSCFG clock
+   __HAL_RCC_SYSCFG_CLK_ENABLE();
+
+   //Enable GPIO clocks
+   __HAL_RCC_GPIOA_CLK_ENABLE();
+   __HAL_RCC_GPIOB_CLK_ENABLE();
+   __HAL_RCC_GPIOC_CLK_ENABLE();
+   __HAL_RCC_GPIOG_CLK_ENABLE();
+   __HAL_RCC_GPIOH_CLK_ENABLE();
+   __HAL_RCC_GPIOI_CLK_ENABLE();
+
+   //Configure MCO1 (PA8) as an output
+   GPIO_InitStructure.Pin = GPIO_PIN_8;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF0_MCO;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure MCO1 pin to output the HSE clock (25MHz)
+   HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1);
+
+   //Select MII interface mode
+   SYSCFG->PMC &= ~SYSCFG_PMC_MII_RMII_SEL;
+
+   //Configure MII pins
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
+
+   //Configure ETH_MII_CRS (PA0)
+   //GPIO_InitStructure.Pin = GPIO_PIN_0;
+   //HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_MII_RX_CLK (PA1), ETH_MDIO (PA2) and ETH_MII_RX_DV (PA7)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_MII_TXD3 (PB8)
+   GPIO_InitStructure.Pin = GPIO_PIN_8;
+   HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
+
+   //Configure ETH_MDC (PC1), ETH_MII_TXD2 (PC2), ETH_MII_TX_CLK (PC3),
+   //ETH_MII_RXD0 (PC4) and ETH_MII_RXD1 (PC5)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5;
+   HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Configure ETH_MII_TX_EN (PG11), ETH_MII_TXD0 (PG13) and ETH_MII_TXD1 (PG14)
+   GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14;
+   HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
+
+   //Configure ETH_MII_COL (PH3)
+   //GPIO_InitStructure.Pin = GPIO_PIN_3;
+   //HAL_GPIO_Init(GPIOH, &GPIO_InitStructure);
+
+   //Configure ETH_MII_RXD2 (PH6) and ETH_MII_RXD3 (PH7)
+   GPIO_InitStructure.Pin = GPIO_PIN_6 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOH, &GPIO_InitStructure);
+
+   //Configure ETH_MII_RX_ER (PI10)
+   //GPIO_InitStructure.Pin = GPIO_PIN_10;
+   //HAL_GPIO_Init(GPIOI, &GPIO_InitStructure);
+
+//STM32469I-EVAL evaluation board?
+#elif defined(USE_STM32F469I_EVAL)
+   //Enable SYSCFG clock
+   __HAL_RCC_SYSCFG_CLK_ENABLE();
+
+   //Enable GPIO clocks
+   __HAL_RCC_GPIOA_CLK_ENABLE();
+   __HAL_RCC_GPIOC_CLK_ENABLE();
+   __HAL_RCC_GPIOE_CLK_ENABLE();
+   __HAL_RCC_GPIOG_CLK_ENABLE();
+   __HAL_RCC_GPIOH_CLK_ENABLE();
+   __HAL_RCC_GPIOI_CLK_ENABLE();
+
+   //Configure MCO1 (PA8) as an output
+   GPIO_InitStructure.Pin = GPIO_PIN_8;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF0_MCO;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure MCO1 pin to output the HSE clock (25MHz)
+   HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1);
+
+   //Select MII interface mode
+   SYSCFG->PMC &= ~SYSCFG_PMC_MII_RMII_SEL;
+
+   //Configure MII pins
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
+
+   //Configure ETH_MII_CRS (PA0)
+   //GPIO_InitStructure.Pin = GPIO_PIN_0;
+   //HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_MII_RX_CLK (PA1), ETH_MDIO (PA2) and ETH_MII_RX_DV (PA7)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_MDC (PC1), ETH_MII_TXD2 (PC2), ETH_MII_TX_CLK (PC3),
+   //ETH_MII_RXD0 (PC4) and ETH_MII_RXD1 (PC5)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5;
+   HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Configure ETH_MII_TXD3 (PE2)
+   GPIO_InitStructure.Pin = GPIO_PIN_2;
+   HAL_GPIO_Init(GPIOE, &GPIO_InitStructure);
+
+   //Configure ETH_MII_TX_EN (PG11), ETH_MII_TXD0 (PG13) and ETH_MII_TXD1 (PG14)
+   GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14;
+   HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
+
+   //Configure ETH_MII_COL (PH3)
+   //GPIO_InitStructure.Pin = GPIO_PIN_3;
+   //HAL_GPIO_Init(GPIOH, &GPIO_InitStructure);
+
+   //Configure ETH_MII_RXD2 (PH6) and ETH_MII_RXD3 (PH7)
+   GPIO_InitStructure.Pin = GPIO_PIN_6 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOH, &GPIO_InitStructure);
+
+   //Configure ETH_MII_RX_ER (PI10)
+   //GPIO_InitStructure.Pin = GPIO_PIN_10;
+   //HAL_GPIO_Init(GPIOI, &GPIO_InitStructure);
+
+//Nucleo-F429ZI or Nucleo-F446ZE evaluation board?
+#elif defined(USE_STM32F4XX_NUCLEO_144)
+   //Enable SYSCFG clock
+   __HAL_RCC_SYSCFG_CLK_ENABLE();
+
+   //Enable GPIO clocks
+   __HAL_RCC_GPIOA_CLK_ENABLE();
+   __HAL_RCC_GPIOB_CLK_ENABLE();
+   __HAL_RCC_GPIOC_CLK_ENABLE();
+   __HAL_RCC_GPIOG_CLK_ENABLE();
+
+   //Select RMII interface mode
+   SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL;
+
+   //Configure RMII pins
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
+
+   //Configure ETH_RMII_REF_CLK (PA1), ETH_MDIO (PA2) and ETH_RMII_CRS_DV (PA7)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_RMII_TXD1 (PB13)
+   GPIO_InitStructure.Pin = GPIO_PIN_13;
+   HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
+
+   //Configure ETH_MDC (PC1), ETH_RMII_RXD0 (PC4) and ETH_RMII_RXD1 (PC5)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
+   HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Configure RMII_TX_EN (PG11), ETH_RMII_TXD0 (PG13)
+   GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13;
+   HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
+#endif
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f4x9EthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX DMA descriptor list
+   for(i = 0; i < STM32F4X9_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Use chain structure rather than ring structure
+      txDmaDesc[i].tdes0 = ETH_TDES0_IC | ETH_TDES0_TCH;
+      //Initialize transmit buffer size
+      txDmaDesc[i].tdes1 = 0;
+      //Transmit buffer address
+      txDmaDesc[i].tdes2 = (uint32_t) txBuffer[i];
+      //Next descriptor address
+      txDmaDesc[i].tdes3 = (uint32_t) &txDmaDesc[i + 1];
+      //Reserved fields
+      txDmaDesc[i].tdes4 = 0;
+      txDmaDesc[i].tdes5 = 0;
+      //Transmit frame time stamp
+      txDmaDesc[i].tdes6 = 0;
+      txDmaDesc[i].tdes7 = 0;
+   }
+
+   //The last descriptor is chained to the first entry
+   txDmaDesc[i - 1].tdes3 = (uint32_t) &txDmaDesc[0];
+   //Point to the very first descriptor
+   txCurDmaDesc = &txDmaDesc[0];
+
+   //Initialize RX DMA descriptor list
+   for(i = 0; i < STM32F4X9_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the DMA
+      rxDmaDesc[i].rdes0 = ETH_RDES0_OWN;
+      //Use chain structure rather than ring structure
+      rxDmaDesc[i].rdes1 = ETH_RDES1_RCH | (STM32F4X9_ETH_RX_BUFFER_SIZE & ETH_RDES1_RBS1);
+      //Receive buffer address
+      rxDmaDesc[i].rdes2 = (uint32_t) rxBuffer[i];
+      //Next descriptor address
+      rxDmaDesc[i].rdes3 = (uint32_t) &rxDmaDesc[i + 1];
+      //Extended status
+      rxDmaDesc[i].rdes4 = 0;
+      //Reserved field
+      rxDmaDesc[i].rdes5 = 0;
+      //Receive frame time stamp
+      rxDmaDesc[i].rdes6 = 0;
+      rxDmaDesc[i].rdes7 = 0;
+   }
+
+   //The last descriptor is chained to the first entry
+   rxDmaDesc[i - 1].rdes3 = (uint32_t) &rxDmaDesc[0];
+   //Point to the very first descriptor
+   rxCurDmaDesc = &rxDmaDesc[0];
+
+   //Start location of the TX descriptor list
+   ETH->DMATDLAR = (uint32_t) txDmaDesc;
+   //Start location of the RX descriptor list
+   ETH->DMARDLAR = (uint32_t) rxDmaDesc;
+}
+
+
+/**
+ * @brief STM32F429/439 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f4x9EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f4x9EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(ETH_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f4x9EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(ETH_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief STM32F429/439 Ethernet MAC interrupt service routine
+ **/
+
+void ETH_IRQHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read DMA status register
+   status = ETH->DMASR;
+
+   //A packet has been transmitted?
+   if(status & ETH_DMASR_TS)
+   {
+      //Clear TS interrupt flag
+      ETH->DMASR = ETH_DMASR_TS;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & ETH_DMASR_RS)
+   {
+      //Disable RIE interrupt
+      ETH->DMAIER &= ~ETH_DMAIER_RIE;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Clear NIS interrupt flag
+   ETH->DMASR = ETH_DMASR_NIS;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief STM32F429/439 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f4x9EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(ETH->DMASR & ETH_DMASR_RS)
+   {
+      //Clear interrupt flag
+      ETH->DMASR = ETH_DMASR_RS;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = stm32f4x9EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable DMA interrupts
+   ETH->DMAIER |= ETH_DMAIER_NISE | ETH_DMAIER_RIE | ETH_DMAIER_TIE;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t stm32f4x9EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > STM32F4X9_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurDmaDesc->tdes0 & ETH_TDES0_OWN)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txCurDmaDesc->tdes2, buffer, offset, length);
+
+   //Write the number of bytes to send
+   txCurDmaDesc->tdes1 = length & ETH_TDES1_TBS1;
+   //Set LS and FS flags as the data fits in a single buffer
+   txCurDmaDesc->tdes0 |= ETH_TDES0_LS | ETH_TDES0_FS;
+   //Give the ownership of the descriptor to the DMA
+   txCurDmaDesc->tdes0 |= ETH_TDES0_OWN;
+
+   //Clear TBUS flag to resume processing
+   ETH->DMASR = ETH_DMASR_TBUS;
+   //Instruct the DMA to poll the transmit descriptor list
+   ETH->DMATPDR = 0;
+
+   //Point to the next descriptor in the list
+   txCurDmaDesc = (Stm32f4x9TxDmaDesc *) txCurDmaDesc->tdes3;
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f4x9EthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_OWN))
+   {
+      //FS and LS flags should be set
+      if((rxCurDmaDesc->rdes0 & ETH_RDES0_FS) && (rxCurDmaDesc->rdes0 & ETH_RDES0_LS))
+      {
+         //Make sure no error occurred
+         if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_ES))
+         {
+            //Retrieve the length of the frame
+            n = (rxCurDmaDesc->rdes0 & ETH_RDES0_FL) >> 16;
+            //Limit the number of data to read
+            n = MIN(n, STM32F4X9_ETH_RX_BUFFER_SIZE);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, (uint8_t *) rxCurDmaDesc->rdes2, n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Give the ownership of the descriptor back to the DMA
+      rxCurDmaDesc->rdes0 = ETH_RDES0_OWN;
+      //Point to the next descriptor in the list
+      rxCurDmaDesc = (Stm32f4x9RxDmaDesc *) rxCurDmaDesc->rdes3;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Clear RBUS flag to resume processing
+   ETH->DMASR = ETH_DMASR_RBUS;
+   //Instruct the DMA to poll the receive descriptor list
+   ETH->DMARPDR = 0;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f4x9EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating STM32F4x9 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = stm32f4x9EthCalcCrc(&entry->addr, sizeof(MacAddr));
+
+         //The upper 6 bits in the CRC register are used to index the
+         //contents of the hash table
+         k = (crc >> 26) & 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   ETH->MACHTLR = hashTable[0];
+   ETH->MACHTHR = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  MACHTLR = %08" PRIX32 "\r\n", ETH->MACHTLR);
+   TRACE_DEBUG("  MACHTHR = %08" PRIX32 "\r\n", ETH->MACHTHR);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f4x9EthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read current MAC configuration
+   config = ETH->MACCR;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= ETH_MACCR_FES;
+   else
+      config &= ~ETH_MACCR_FES;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= ETH_MACCR_DM;
+   else
+      config &= ~ETH_MACCR_DM;
+
+   //Update MAC configuration register
+   ETH->MACCR = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void stm32f4x9EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = ETH->MACMIIAR & ETH_MACMIIAR_CR;
+   //Set up a write operation
+   value |= ETH_MACMIIAR_MW | ETH_MACMIIAR_MB;
+   //PHY address
+   value |= (phyAddr << 11) & ETH_MACMIIAR_PA;
+   //Register address
+   value |= (regAddr << 6) & ETH_MACMIIAR_MR;
+
+   //Data to be written in the PHY register
+   ETH->MACMIIDR = data & ETH_MACMIIDR_MD;
+
+   //Start a write operation
+   ETH->MACMIIAR = value;
+   //Wait for the write to complete
+   while(ETH->MACMIIAR & ETH_MACMIIAR_MB);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t stm32f4x9EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = ETH->MACMIIAR & ETH_MACMIIAR_CR;
+   //Set up a read operation
+   value |= ETH_MACMIIAR_MB;
+   //PHY address
+   value |= (phyAddr << 11) & ETH_MACMIIAR_PA;
+   //Register address
+   value |= (regAddr << 6) & ETH_MACMIIAR_MR;
+
+   //Start a read operation
+   ETH->MACMIIAR = value;
+   //Wait for the read to complete
+   while(ETH->MACMIIAR & ETH_MACMIIAR_MB);
+
+   //Return PHY register contents
+   return ETH->MACMIIDR & ETH_MACMIIDR_MD;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t stm32f4x9EthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return ~crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/stm32f4x9_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,219 @@
+/**
+ * @file stm32f4x9_eth.h
+ * @brief STM32F429/439 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _STM32F4X9_ETH_H
+#define _STM32F4X9_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef STM32F4X9_ETH_TX_BUFFER_COUNT
+   #define STM32F4X9_ETH_TX_BUFFER_COUNT 3
+#elif (STM32F4X9_ETH_TX_BUFFER_COUNT < 1)
+   #error STM32F4X9_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef STM32F4X9_ETH_TX_BUFFER_SIZE
+   #define STM32F4X9_ETH_TX_BUFFER_SIZE 1536
+#elif (STM32F4X9_ETH_TX_BUFFER_SIZE != 1536)
+   #error STM32F4X9_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef STM32F4X9_ETH_RX_BUFFER_COUNT
+   #define STM32F4X9_ETH_RX_BUFFER_COUNT 6
+#elif (STM32F4X9_ETH_RX_BUFFER_COUNT < 1)
+   #error STM32F4X9_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef STM32F4X9_ETH_RX_BUFFER_SIZE
+   #define STM32F4X9_ETH_RX_BUFFER_SIZE 1536
+#elif (STM32F4X9_ETH_RX_BUFFER_SIZE != 1536)
+   #error STM32F4X9_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef STM32F4X9_ETH_IRQ_PRIORITY_GROUPING
+   #define STM32F4X9_ETH_IRQ_PRIORITY_GROUPING 3
+#elif (STM32F4X9_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error STM32F4X9_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef STM32F4X9_ETH_IRQ_GROUP_PRIORITY
+   #define STM32F4X9_ETH_IRQ_GROUP_PRIORITY 12
+#elif (STM32F4X9_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error STM32F4X9_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef STM32F4X9_ETH_IRQ_SUB_PRIORITY
+   #define STM32F4X9_ETH_IRQ_SUB_PRIORITY 0
+#elif (STM32F4X9_ETH_IRQ_SUB_PRIORITY < 0)
+   #error STM32F4X9_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//Transmit DMA descriptor flags
+#define ETH_TDES0_OWN    0x80000000
+#define ETH_TDES0_IC     0x40000000
+#define ETH_TDES0_LS     0x20000000
+#define ETH_TDES0_FS     0x10000000
+#define ETH_TDES0_DC     0x08000000
+#define ETH_TDES0_DP     0x04000000
+#define ETH_TDES0_TTSE   0x02000000
+#define ETH_TDES0_CIC    0x00C00000
+#define ETH_TDES0_TER    0x00200000
+#define ETH_TDES0_TCH    0x00100000
+#define ETH_TDES0_TTSS   0x00020000
+#define ETH_TDES0_IHE    0x00010000
+#define ETH_TDES0_ES     0x00008000
+#define ETH_TDES0_JT     0x00004000
+#define ETH_TDES0_FF     0x00002000
+#define ETH_TDES0_IPE    0x00001000
+#define ETH_TDES0_LCA    0x00000800
+#define ETH_TDES0_NC     0x00000400
+#define ETH_TDES0_LCO    0x00000200
+#define ETH_TDES0_EC     0x00000100
+#define ETH_TDES0_VF     0x00000080
+#define ETH_TDES0_CC     0x00000078
+#define ETH_TDES0_ED     0x00000004
+#define ETH_TDES0_UF     0x00000002
+#define ETH_TDES0_DB     0x00000001
+#define ETH_TDES1_TBS2   0x1FFF0000
+#define ETH_TDES1_TBS1   0x00001FFF
+#define ETH_TDES2_TBAP1  0xFFFFFFFF
+#define ETH_TDES3_TBAP2  0xFFFFFFFF
+#define ETH_TDES6_TTSL   0xFFFFFFFF
+#define ETH_TDES7_TTSH   0xFFFFFFFF
+
+//Receive DMA descriptor flags
+#define ETH_RDES0_OWN    0x80000000
+#define ETH_RDES0_AFM    0x40000000
+#define ETH_RDES0_FL     0x3FFF0000
+#define ETH_RDES0_ES     0x00008000
+#define ETH_RDES0_DE     0x00004000
+#define ETH_RDES0_SAF    0x00002000
+#define ETH_RDES0_LE     0x00001000
+#define ETH_RDES0_OE     0x00000800
+#define ETH_RDES0_VLAN   0x00000400
+#define ETH_RDES0_FS     0x00000200
+#define ETH_RDES0_LS     0x00000100
+#define ETH_RDES0_IPHCE  0x00000080
+#define ETH_RDES0_LCO    0x00000040
+#define ETH_RDES0_FT     0x00000020
+#define ETH_RDES0_RWT    0x00000010
+#define ETH_RDES0_RE     0x00000008
+#define ETH_RDES0_DBE    0x00000004
+#define ETH_RDES0_CE     0x00000002
+#define ETH_RDES0_PCE    0x00000001
+#define ETH_RDES1_DIC    0x80000000
+#define ETH_RDES1_RBS2   0x1FFF0000
+#define ETH_RDES1_RER    0x00008000
+#define ETH_RDES1_RCH    0x00004000
+#define ETH_RDES1_RBS1   0x00001FFF
+#define ETH_RDES2_RBAP1  0xFFFFFFFF
+#define ETH_RDES3_RBAP2  0xFFFFFFFF
+#define ETH_RDES4_PV     0x00002000
+#define ETH_RDES4_PFT    0x00001000
+#define ETH_RDES4_PMT    0x00000F00
+#define ETH_RDES4_IPV6PR 0x00000080
+#define ETH_RDES4_IPV4PR 0x00000040
+#define ETH_RDES4_IPCB   0x00000020
+#define ETH_RDES4_IPPE   0x00000010
+#define ETH_RDES4_IPHE   0x00000008
+#define ETH_RDES4_IPPT   0x00000007
+#define ETH_RDES6_RTSL   0xFFFFFFFF
+#define ETH_RDES7_RTSH   0xFFFFFFFF
+
+
+/**
+ * @brief Enhanced TX DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t tdes0;
+   uint32_t tdes1;
+   uint32_t tdes2;
+   uint32_t tdes3;
+   uint32_t tdes4;
+   uint32_t tdes5;
+   uint32_t tdes6;
+   uint32_t tdes7;
+} Stm32f4x9TxDmaDesc;
+
+
+/**
+ * @brief Enhanced RX DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t rdes0;
+   uint32_t rdes1;
+   uint32_t rdes2;
+   uint32_t rdes3;
+   uint32_t rdes4;
+   uint32_t rdes5;
+   uint32_t rdes6;
+   uint32_t rdes7;
+} Stm32f4x9RxDmaDesc;
+
+
+//STM32F429/439 Ethernet MAC driver
+extern const NicDriver stm32f4x9EthDriver;
+
+//STM32F429/439 Ethernet MAC related functions
+error_t stm32f4x9EthInit(NetInterface *interface);
+void stm32f4x9EthInitGpio(NetInterface *interface);
+void stm32f4x9EthInitDmaDesc(NetInterface *interface);
+
+void stm32f4x9EthTick(NetInterface *interface);
+
+void stm32f4x9EthEnableIrq(NetInterface *interface);
+void stm32f4x9EthDisableIrq(NetInterface *interface);
+void stm32f4x9EthEventHandler(NetInterface *interface);
+
+error_t stm32f4x9EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t stm32f4x9EthReceivePacket(NetInterface *interface);
+
+error_t stm32f4x9EthSetMulticastFilter(NetInterface *interface);
+error_t stm32f4x9EthUpdateMacConfig(NetInterface *interface);
+
+void stm32f4x9EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t stm32f4x9EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t stm32f4x9EthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/stm32f7xx_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1057 @@
+/**
+ * @file stm32f7xx_eth.c
+ * @brief STM32F746/756 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "stm32f7xx.h"
+#include "core/net.h"
+#include "drivers/stm32f7xx_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static uint8_t txBuffer[STM32F7XX_ETH_TX_BUFFER_COUNT][STM32F7XX_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static uint8_t rxBuffer[STM32F7XX_ETH_RX_BUFFER_COUNT][STM32F7XX_ETH_RX_BUFFER_SIZE];
+//Transmit DMA descriptors
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static Stm32f7xxTxDmaDesc txDmaDesc[STM32F7XX_ETH_TX_BUFFER_COUNT];
+//Receive DMA descriptors
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static Stm32f7xxRxDmaDesc rxDmaDesc[STM32F7XX_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[STM32F7XX_ETH_TX_BUFFER_COUNT][STM32F7XX_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+//Receive buffer
+static uint8_t rxBuffer[STM32F7XX_ETH_RX_BUFFER_COUNT][STM32F7XX_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+//Transmit DMA descriptors
+static Stm32f7xxTxDmaDesc txDmaDesc[STM32F7XX_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+//Receive DMA descriptors
+static Stm32f7xxRxDmaDesc rxDmaDesc[STM32F7XX_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+
+#endif
+
+//Pointer to the current TX DMA descriptor
+static Stm32f7xxTxDmaDesc *txCurDmaDesc;
+//Pointer to the current RX DMA descriptor
+static Stm32f7xxRxDmaDesc *rxCurDmaDesc;
+
+
+/**
+ * @brief STM32F746/756 Ethernet MAC driver
+ **/
+
+const NicDriver stm32f7xxEthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   stm32f7xxEthInit,
+   stm32f7xxEthTick,
+   stm32f7xxEthEnableIrq,
+   stm32f7xxEthDisableIrq,
+   stm32f7xxEthEventHandler,
+   stm32f7xxEthSendPacket,
+   stm32f7xxEthSetMulticastFilter,
+   stm32f7xxEthUpdateMacConfig,
+   stm32f7xxEthWritePhyReg,
+   stm32f7xxEthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief STM32F746/756 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f7xxEthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing STM32F7xx Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //GPIO configuration
+   stm32f7xxEthInitGpio(interface);
+
+   //Enable Ethernet MAC clock
+   __HAL_RCC_ETHMAC_CLK_ENABLE();
+   __HAL_RCC_ETHMACTX_CLK_ENABLE();
+   __HAL_RCC_ETHMACRX_CLK_ENABLE();
+
+   //Reset Ethernet MAC peripheral
+   __HAL_RCC_ETHMAC_FORCE_RESET();
+   __HAL_RCC_ETHMAC_RELEASE_RESET();
+
+   //Perform a software reset
+   ETH->DMABMR |= ETH_DMABMR_SR;
+   //Wait for the reset to complete
+   while(ETH->DMABMR & ETH_DMABMR_SR);
+
+   //Adjust MDC clock range depending on HCLK frequency
+   ETH->MACMIIAR = ETH_MACMIIAR_CR_Div102;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Use default MAC configuration
+   ETH->MACCR = ETH_MACCR_ROD;
+
+   //Set the MAC address
+   ETH->MACA0LR = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   ETH->MACA0HR = interface->macAddr.w[2];
+
+   //Initialize hash table
+   ETH->MACHTLR = 0;
+   ETH->MACHTHR = 0;
+
+   //Configure the receive filter
+   ETH->MACFFR = ETH_MACFFR_HPF | ETH_MACFFR_HM;
+   //Disable flow control
+   ETH->MACFCR = 0;
+   //Enable store and forward mode
+   ETH->DMAOMR = ETH_DMAOMR_RSF | ETH_DMAOMR_TSF;
+
+   //Configure DMA bus mode
+   ETH->DMABMR = ETH_DMABMR_AAB | ETH_DMABMR_USP | ETH_DMABMR_RDP_1Beat |
+      ETH_DMABMR_RTPR_1_1 | ETH_DMABMR_PBL_1Beat | ETH_DMABMR_EDE;
+
+   //Initialize DMA descriptor lists
+   stm32f7xxEthInitDmaDesc(interface);
+
+   //Prevent interrupts from being generated when the transmit statistic
+   //counters reach half their maximum value
+   ETH->MMCTIMR = ETH_MMCTIMR_TGFM | ETH_MMCTIMR_TGFMSCM | ETH_MMCTIMR_TGFSCM;
+
+   //Prevent interrupts from being generated when the receive statistic
+   //counters reach half their maximum value
+   ETH->MMCRIMR = ETH_MMCRIMR_RGUFM | ETH_MMCRIMR_RFAEM | ETH_MMCRIMR_RFCEM;
+
+   //Disable MAC interrupts
+   ETH->MACIMR = ETH_MACIMR_TSTIM | ETH_MACIMR_PMTIM;
+   //Enable the desired DMA interrupts
+   ETH->DMAIER = ETH_DMAIER_NISE | ETH_DMAIER_RIE | ETH_DMAIER_TIE;
+
+   //Set priority grouping (4 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(STM32F7XX_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure Ethernet interrupt priority
+   NVIC_SetPriority(ETH_IRQn, NVIC_EncodePriority(STM32F7XX_ETH_IRQ_PRIORITY_GROUPING,
+      STM32F7XX_ETH_IRQ_GROUP_PRIORITY, STM32F7XX_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable MAC transmission and reception
+   ETH->MACCR |= ETH_MACCR_TE | ETH_MACCR_RE;
+   //Enable DMA transmission and reception
+   ETH->DMAOMR |= ETH_DMAOMR_ST | ETH_DMAOMR_SR;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//STM32756G-EVAL, STM32F769I-EVAL, STM32F746G-DISCOVERY,
+//Nucleo-F746ZG or Nucleo-F767ZI evaluation board?
+#if defined(USE_STM32756G_EVAL) || defined(USE_STM32F769I_EVAL) || \
+   defined(USE_STM32746G_DISCO) || defined(USE_STM32F7XX_NUCLEO_144)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f7xxEthInitGpio(NetInterface *interface)
+{
+   GPIO_InitTypeDef GPIO_InitStructure;
+
+//STM32756G-EVAL or STM32F769I-EVAL evaluation board?
+#if defined(USE_STM32756G_EVAL) || defined(USE_STM32F769I_EVAL)
+   //Enable SYSCFG clock
+   __HAL_RCC_SYSCFG_CLK_ENABLE();
+
+   //Enable GPIO clocks
+   __HAL_RCC_GPIOA_CLK_ENABLE();
+   __HAL_RCC_GPIOC_CLK_ENABLE();
+   __HAL_RCC_GPIOE_CLK_ENABLE();
+   __HAL_RCC_GPIOG_CLK_ENABLE();
+   __HAL_RCC_GPIOH_CLK_ENABLE();
+   __HAL_RCC_GPIOI_CLK_ENABLE();
+
+   //Configure MCO1 (PA8) as an output
+   GPIO_InitStructure.Pin = GPIO_PIN_8;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF0_MCO;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure MCO1 pin to output the HSE clock (25MHz)
+   HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1);
+
+   //Select MII interface mode
+   SYSCFG->PMC &= ~SYSCFG_PMC_MII_RMII_SEL;
+
+#if defined(STM32F7XX_ETH_MDIO_PIN) && defined(STM32F7XX_ETH_MDC_PIN)
+   //Configure ETH_MDIO as a GPIO
+   GPIO_InitStructure.Pin = STM32F7XX_ETH_MDIO_PIN;
+   GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
+   GPIO_InitStructure.Pull = GPIO_PULLUP;
+   GPIO_InitStructure.Speed = GPIO_SPEED_MEDIUM;
+   HAL_GPIO_Init(STM32F7XX_ETH_MDIO_GPIO, &GPIO_InitStructure);
+
+   //Configure ETH_MDC as a GPIO
+   GPIO_InitStructure.Pin = STM32F7XX_ETH_MDC_PIN;
+   GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_MEDIUM;
+   HAL_GPIO_Init(STM32F7XX_ETH_MDC_GPIO, &GPIO_InitStructure);
+
+   //Deassert MDC
+   HAL_GPIO_WritePin(STM32F7XX_ETH_MDC_GPIO,
+      STM32F7XX_ETH_MDC_PIN, GPIO_PIN_RESET);
+#else
+   //Configure ETH_MDIO (PA2)
+   GPIO_InitStructure.Pin = GPIO_PIN_2;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_PULLUP;
+   GPIO_InitStructure.Speed = GPIO_SPEED_MEDIUM;
+   GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_MDC (PC1)
+   GPIO_InitStructure.Pin = GPIO_PIN_1;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_MEDIUM;
+   GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
+   HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
+#endif
+
+   //Configure MII pins
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
+
+   //Configure ETH_MII_CRS (PA0)
+   //GPIO_InitStructure.Pin = GPIO_PIN_0;
+   //HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_MII_RX_CLK (PA1) and ETH_MII_RX_DV (PA7)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_MII_TXD2 (PC2), ETH_MII_TX_CLK (PC3), ETH_MII_RXD0 (PC4)
+   //and ETH_MII_RXD1 (PC5)
+   GPIO_InitStructure.Pin = GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5;
+   HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Configure ETH_MII_TXD3 (PE2)
+   GPIO_InitStructure.Pin = GPIO_PIN_2;
+   HAL_GPIO_Init(GPIOE, &GPIO_InitStructure);
+
+   //Configure ETH_MII_TX_EN (PG11), ETH_MII_TXD0 (PG13) and ETH_MII_TXD1 (PG14)
+   GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14;
+   HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
+
+   //Configure ETH_MII_COL (PH3)
+   //GPIO_InitStructure.Pin = GPIO_PIN_3;
+   //HAL_GPIO_Init(GPIOH, &GPIO_InitStructure);
+
+   //Configure ETH_MII_RXD2 (PH6) and ETH_MII_RXD3 (PH7)
+   GPIO_InitStructure.Pin = GPIO_PIN_6 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOH, &GPIO_InitStructure);
+
+   //Configure ETH_MII_RX_ER (PI10)
+   //GPIO_InitStructure.Pin = GPIO_PIN_10;
+   //HAL_GPIO_Init(GPIOI, &GPIO_InitStructure);
+
+//STM32F746G-DISCOVERY evaluation board?
+#elif defined(USE_STM32746G_DISCO)
+   //Enable SYSCFG clock
+   __HAL_RCC_SYSCFG_CLK_ENABLE();
+
+   //Enable GPIO clocks
+   __HAL_RCC_GPIOA_CLK_ENABLE();
+   __HAL_RCC_GPIOC_CLK_ENABLE();
+   __HAL_RCC_GPIOG_CLK_ENABLE();
+
+   //Select RMII interface mode
+   SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL;
+
+   //Configure RMII pins
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
+
+   //Configure ETH_RMII_REF_CLK (PA1), ETH_MDIO (PA2) and ETH_RMII_CRS_DV (PA7)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_MDC (PC1), ETH_RMII_RXD0 (PC4) and ETH_RMII_RXD1 (PC5)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
+   HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Configure ETH_RMII_RXER (PG2), RMII_TX_EN (PG11), ETH_RMII_TXD0 (PG13)
+   //and ETH_RMII_TXD1 (PG14)
+   GPIO_InitStructure.Pin = GPIO_PIN_2 | GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14;
+   HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
+
+//Nucleo-F746ZG or Nucleo-F767ZI evaluation board?
+#elif defined(USE_STM32F7XX_NUCLEO_144)
+   //Enable SYSCFG clock
+   __HAL_RCC_SYSCFG_CLK_ENABLE();
+
+   //Enable GPIO clocks
+   __HAL_RCC_GPIOA_CLK_ENABLE();
+   __HAL_RCC_GPIOB_CLK_ENABLE();
+   __HAL_RCC_GPIOC_CLK_ENABLE();
+   __HAL_RCC_GPIOG_CLK_ENABLE();
+
+   //Select RMII interface mode
+   SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL;
+
+   //Configure RMII pins
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+   GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
+
+   //Configure ETH_RMII_REF_CLK (PA1), ETH_MDIO (PA2) and ETH_RMII_CRS_DV (PA7)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
+   HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
+
+   //Configure ETH_RMII_TXD1 (PB13)
+   GPIO_InitStructure.Pin = GPIO_PIN_13;
+   HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
+
+   //Configure ETH_MDC (PC1), ETH_RMII_RXD0 (PC4) and ETH_RMII_RXD1 (PC5)
+   GPIO_InitStructure.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
+   HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+   //Configure RMII_TX_EN (PG11), ETH_RMII_TXD0 (PG13)
+   GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_13;
+   HAL_GPIO_Init(GPIOG, &GPIO_InitStructure);
+#endif
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f7xxEthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX DMA descriptor list
+   for(i = 0; i < STM32F7XX_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Use chain structure rather than ring structure
+      txDmaDesc[i].tdes0 = ETH_TDES0_IC | ETH_TDES0_TCH;
+      //Initialize transmit buffer size
+      txDmaDesc[i].tdes1 = 0;
+      //Transmit buffer address
+      txDmaDesc[i].tdes2 = (uint32_t) txBuffer[i];
+      //Next descriptor address
+      txDmaDesc[i].tdes3 = (uint32_t) &txDmaDesc[i + 1];
+      //Reserved fields
+      txDmaDesc[i].tdes4 = 0;
+      txDmaDesc[i].tdes5 = 0;
+      //Transmit frame time stamp
+      txDmaDesc[i].tdes6 = 0;
+      txDmaDesc[i].tdes7 = 0;
+   }
+
+   //The last descriptor is chained to the first entry
+   txDmaDesc[i - 1].tdes3 = (uint32_t) &txDmaDesc[0];
+   //Point to the very first descriptor
+   txCurDmaDesc = &txDmaDesc[0];
+
+   //Initialize RX DMA descriptor list
+   for(i = 0; i < STM32F7XX_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the DMA
+      rxDmaDesc[i].rdes0 = ETH_RDES0_OWN;
+      //Use chain structure rather than ring structure
+      rxDmaDesc[i].rdes1 = ETH_RDES1_RCH | (STM32F7XX_ETH_RX_BUFFER_SIZE & ETH_RDES1_RBS1);
+      //Receive buffer address
+      rxDmaDesc[i].rdes2 = (uint32_t) rxBuffer[i];
+      //Next descriptor address
+      rxDmaDesc[i].rdes3 = (uint32_t) &rxDmaDesc[i + 1];
+      //Extended status
+      rxDmaDesc[i].rdes4 = 0;
+      //Reserved field
+      rxDmaDesc[i].rdes5 = 0;
+      //Receive frame time stamp
+      rxDmaDesc[i].rdes6 = 0;
+      rxDmaDesc[i].rdes7 = 0;
+   }
+
+   //The last descriptor is chained to the first entry
+   rxDmaDesc[i - 1].rdes3 = (uint32_t) &rxDmaDesc[0];
+   //Point to the very first descriptor
+   rxCurDmaDesc = &rxDmaDesc[0];
+
+   //Start location of the TX descriptor list
+   ETH->DMATDLAR = (uint32_t) txDmaDesc;
+   //Start location of the RX descriptor list
+   ETH->DMARDLAR = (uint32_t) rxDmaDesc;
+}
+
+
+/**
+ * @brief STM32F746/756 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f7xxEthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f7xxEthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(ETH_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f7xxEthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(ETH_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief STM32F746/756 Ethernet MAC interrupt service routine
+ **/
+
+void ETH_IRQHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read DMA status register
+   status = ETH->DMASR;
+
+   //A packet has been transmitted?
+   if(status & ETH_DMASR_TS)
+   {
+      //Clear TS interrupt flag
+      ETH->DMASR = ETH_DMASR_TS;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & ETH_DMASR_RS)
+   {
+      //Disable RIE interrupt
+      ETH->DMAIER &= ~ETH_DMAIER_RIE;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Clear NIS interrupt flag
+   ETH->DMASR = ETH_DMASR_NIS;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief STM32F746/756 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void stm32f7xxEthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(ETH->DMASR & ETH_DMASR_RS)
+   {
+      //Clear interrupt flag
+      ETH->DMASR = ETH_DMASR_RS;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = stm32f7xxEthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable DMA interrupts
+   ETH->DMAIER |= ETH_DMAIER_NISE | ETH_DMAIER_RIE | ETH_DMAIER_TIE;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t stm32f7xxEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   static uint8_t temp[STM32F7XX_ETH_TX_BUFFER_SIZE];
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > STM32F7XX_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurDmaDesc->tdes0 & ETH_TDES0_OWN)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(temp, buffer, offset, length);
+   memcpy((uint8_t *) txCurDmaDesc->tdes2, temp, (length + 3) & ~3UL);
+
+   //Write the number of bytes to send
+   txCurDmaDesc->tdes1 = length & ETH_TDES1_TBS1;
+   //Set LS and FS flags as the data fits in a single buffer
+   txCurDmaDesc->tdes0 |= ETH_TDES0_LS | ETH_TDES0_FS;
+   //Give the ownership of the descriptor to the DMA
+   txCurDmaDesc->tdes0 |= ETH_TDES0_OWN;
+
+   //Data synchronization barrier
+   __DSB();
+
+   //Clear TBUS flag to resume processing
+   ETH->DMASR = ETH_DMASR_TBUS;
+   //Instruct the DMA to poll the transmit descriptor list
+   ETH->DMATPDR = 0;
+
+   //Point to the next descriptor in the list
+   txCurDmaDesc = (Stm32f7xxTxDmaDesc *) txCurDmaDesc->tdes3;
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f7xxEthReceivePacket(NetInterface *interface)
+{
+   static uint8_t temp[STM32F7XX_ETH_RX_BUFFER_SIZE];
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_OWN))
+   {
+      //FS and LS flags should be set
+      if((rxCurDmaDesc->rdes0 & ETH_RDES0_FS) && (rxCurDmaDesc->rdes0 & ETH_RDES0_LS))
+      {
+         //Make sure no error occurred
+         if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_ES))
+         {
+            //Retrieve the length of the frame
+            n = (rxCurDmaDesc->rdes0 & ETH_RDES0_FL) >> 16;
+            //Limit the number of data to read
+            n = MIN(n, STM32F7XX_ETH_RX_BUFFER_SIZE);
+
+            //Copy data from the receive buffer
+            memcpy(temp, (uint8_t *) rxCurDmaDesc->rdes2, (n + 3) & ~3UL);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, temp, n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Give the ownership of the descriptor back to the DMA
+      rxCurDmaDesc->rdes0 = ETH_RDES0_OWN;
+      //Point to the next descriptor in the list
+      rxCurDmaDesc = (Stm32f7xxRxDmaDesc *) rxCurDmaDesc->rdes3;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Clear RBUS flag to resume processing
+   ETH->DMASR = ETH_DMASR_RBUS;
+   //Instruct the DMA to poll the receive descriptor list
+   ETH->DMARPDR = 0;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f7xxEthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating STM32F7xx hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = stm32f7xxEthCalcCrc(&entry->addr, sizeof(MacAddr));
+
+         //The upper 6 bits in the CRC register are used to index the
+         //contents of the hash table
+         k = (crc >> 26) & 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   ETH->MACHTLR = hashTable[0];
+   ETH->MACHTHR = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  MACHTLR = %08" PRIX32 "\r\n", ETH->MACHTLR);
+   TRACE_DEBUG("  MACHTHR = %08" PRIX32 "\r\n", ETH->MACHTHR);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t stm32f7xxEthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read current MAC configuration
+   config = ETH->MACCR;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= ETH_MACCR_FES;
+   else
+      config &= ~ETH_MACCR_FES;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= ETH_MACCR_DM;
+   else
+      config &= ~ETH_MACCR_DM;
+
+   //Update MAC configuration register
+   ETH->MACCR = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void stm32f7xxEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+#if defined(STM32F7XX_ETH_MDC_PIN) && defined(STM32F7XX_ETH_MDIO_PIN)
+   //Synchronization pattern
+   stm32f7xxEthWriteSmi(SMI_SYNC, 32);
+   //Start of frame
+   stm32f7xxEthWriteSmi(SMI_START, 2);
+   //Set up a write operation
+   stm32f7xxEthWriteSmi(SMI_WRITE, 2);
+   //Write PHY address
+   stm32f7xxEthWriteSmi(phyAddr, 5);
+   //Write register address
+   stm32f7xxEthWriteSmi(regAddr, 5);
+   //Turnaround
+   stm32f7xxEthWriteSmi(SMI_TA, 2);
+   //Write register value
+   stm32f7xxEthWriteSmi(data, 16);
+   //Release MDIO
+   stm32f7xxEthReadSmi(1);
+#else
+   uint32_t temp;
+
+   //Take care not to alter MDC clock configuration
+   temp = ETH->MACMIIAR & ETH_MACMIIAR_CR;
+   //Set up a write operation
+   temp |= ETH_MACMIIAR_MW | ETH_MACMIIAR_MB;
+   //PHY address
+   temp |= (phyAddr << 11) & ETH_MACMIIAR_PA;
+   //Register address
+   temp |= (regAddr << 6) & ETH_MACMIIAR_MR;
+
+   //Data to be written in the PHY register
+   ETH->MACMIIDR = data & ETH_MACMIIDR_MD;
+
+   //Start a write operation
+   ETH->MACMIIAR = temp;
+   //Wait for the write to complete
+   while(ETH->MACMIIAR & ETH_MACMIIAR_MB);
+#endif
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t stm32f7xxEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+#if defined(STM32F7XX_ETH_MDC_PIN) && defined(STM32F7XX_ETH_MDIO_PIN)
+   uint16_t data;
+
+   //Synchronization pattern
+   stm32f7xxEthWriteSmi(SMI_SYNC, 32);
+   //Start of frame
+   stm32f7xxEthWriteSmi(SMI_START, 2);
+   //Set up a read operation
+   stm32f7xxEthWriteSmi(SMI_READ, 2);
+   //Write PHY address
+   stm32f7xxEthWriteSmi(phyAddr, 5);
+   //Write register address
+   stm32f7xxEthWriteSmi(regAddr, 5);
+   //Turnaround to avoid contention
+   stm32f7xxEthReadSmi(1);
+   //Read register value
+   data = stm32f7xxEthReadSmi(16);
+   //Force the PHY to release the MDIO pin
+   stm32f7xxEthReadSmi(1);
+#else
+   uint16_t data;
+   uint32_t temp;
+
+   //Take care not to alter MDC clock configuration
+   temp = ETH->MACMIIAR & ETH_MACMIIAR_CR;
+   //Set up a read operation
+   temp |= ETH_MACMIIAR_MB;
+   //PHY address
+   temp |= (phyAddr << 11) & ETH_MACMIIAR_PA;
+   //Register address
+   temp |= (regAddr << 6) & ETH_MACMIIAR_MR;
+
+   //Start a read operation
+   ETH->MACMIIAR = temp;
+   //Wait for the read to complete
+   while(ETH->MACMIIAR & ETH_MACMIIAR_MB);
+
+   //Read register value
+   data = ETH->MACMIIDR & ETH_MACMIIDR_MD;
+#endif
+
+   //Return PHY register contents
+   return data;
+}
+
+
+/**
+ * @brief SMI write operation
+ * @param[in] data Raw data to be written
+ * @param[in] length Number of bits to be written
+ **/
+
+void stm32f7xxEthWriteSmi(uint32_t data, uint_t length)
+{
+#if defined(STM32F7XX_ETH_MDC_PIN) && defined(STM32F7XX_ETH_MDIO_PIN)
+   GPIO_InitTypeDef GPIO_InitStructure;
+
+   //Skip the most significant bits since they are meaningless
+   data <<= 32 - length;
+
+   //Configure MDIO as an output
+   GPIO_InitStructure.Pin = STM32F7XX_ETH_MDIO_PIN;
+   GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
+   GPIO_InitStructure.Pull = GPIO_NOPULL;
+   GPIO_InitStructure.Speed = GPIO_SPEED_MEDIUM;
+   HAL_GPIO_Init(STM32F7XX_ETH_MDIO_GPIO, &GPIO_InitStructure);
+
+   //Write the specified number of bits
+   while(length--)
+   {
+      //Write MDIO
+      if(data & 0x80000000)
+      {
+         HAL_GPIO_WritePin(STM32F7XX_ETH_MDIO_GPIO,
+            STM32F7XX_ETH_MDIO_PIN, GPIO_PIN_SET);
+      }
+      else
+      {
+         HAL_GPIO_WritePin(STM32F7XX_ETH_MDIO_GPIO,
+            STM32F7XX_ETH_MDIO_PIN, GPIO_PIN_RESET);
+      }
+
+      //Delay
+      usleep(1);
+
+      //Assert MDC
+      HAL_GPIO_WritePin(STM32F7XX_ETH_MDC_GPIO,
+         STM32F7XX_ETH_MDC_PIN, GPIO_PIN_SET);
+
+      //Delay
+      usleep(1);
+
+      //Deassert MDC
+      HAL_GPIO_WritePin(STM32F7XX_ETH_MDC_GPIO,
+         STM32F7XX_ETH_MDC_PIN, GPIO_PIN_RESET);
+
+      //Rotate data
+      data <<= 1;
+   }
+#endif
+}
+
+
+/**
+ * @brief SMI read operation
+ * @param[in] length Number of bits to be read
+ * @return Data resulting from the MDIO read operation
+ **/
+
+uint32_t stm32f7xxEthReadSmi(uint_t length)
+{
+   uint32_t data = 0;
+
+#if defined(STM32F7XX_ETH_MDC_PIN) && defined(STM32F7XX_ETH_MDIO_PIN)
+   GPIO_InitTypeDef GPIO_InitStructure;
+
+   //Configure MDIO as an input
+   GPIO_InitStructure.Pin = STM32F7XX_ETH_MDIO_PIN;
+   GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
+   GPIO_InitStructure.Pull = GPIO_PULLUP;
+   GPIO_InitStructure.Speed = GPIO_SPEED_MEDIUM;
+   HAL_GPIO_Init(STM32F7XX_ETH_MDIO_GPIO, &GPIO_InitStructure);
+
+   //Read the specified number of bits
+   while(length--)
+   {
+      //Rotate data
+      data <<= 1;
+
+      //Assert MDC
+      HAL_GPIO_WritePin(STM32F7XX_ETH_MDC_GPIO,
+         STM32F7XX_ETH_MDC_PIN, GPIO_PIN_SET);
+
+      //Delay
+      usleep(1);
+
+      //Deassert MDC
+      HAL_GPIO_WritePin(STM32F7XX_ETH_MDC_GPIO,
+         STM32F7XX_ETH_MDC_PIN, GPIO_PIN_RESET);
+
+      //Delay
+      usleep(1);
+
+      //Check MDIO state
+      if(HAL_GPIO_ReadPin(STM32F7XX_ETH_MDIO_GPIO, STM32F7XX_ETH_MDIO_PIN))
+         data |= 0x00000001;
+   }
+#endif
+
+   //Return the received data
+   return data;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t stm32f7xxEthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return ~crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/stm32f7xx_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,229 @@
+/**
+ * @file stm32f7xx_eth.h
+ * @brief STM32F746/756 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _STM32F7XX_ETH_H
+#define _STM32F7XX_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef STM32F7XX_ETH_TX_BUFFER_COUNT
+   #define STM32F7XX_ETH_TX_BUFFER_COUNT 3
+#elif (STM32F7XX_ETH_TX_BUFFER_COUNT < 1)
+   #error STM32F7XX_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef STM32F7XX_ETH_TX_BUFFER_SIZE
+   #define STM32F7XX_ETH_TX_BUFFER_SIZE 1536
+#elif (STM32F7XX_ETH_TX_BUFFER_SIZE != 1536)
+   #error STM32F7XX_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef STM32F7XX_ETH_RX_BUFFER_COUNT
+   #define STM32F7XX_ETH_RX_BUFFER_COUNT 6
+#elif (STM32F7XX_ETH_RX_BUFFER_COUNT < 1)
+   #error STM32F7XX_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef STM32F7XX_ETH_RX_BUFFER_SIZE
+   #define STM32F7XX_ETH_RX_BUFFER_SIZE 1536
+#elif (STM32F7XX_ETH_RX_BUFFER_SIZE != 1536)
+   #error STM32F7XX_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef STM32F7XX_ETH_IRQ_PRIORITY_GROUPING
+   #define STM32F7XX_ETH_IRQ_PRIORITY_GROUPING 3
+#elif (STM32F7XX_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error STM32F7XX_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef STM32F7XX_ETH_IRQ_GROUP_PRIORITY
+   #define STM32F7XX_ETH_IRQ_GROUP_PRIORITY 12
+#elif (STM32F7XX_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error STM32F7XX_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef STM32F7XX_ETH_IRQ_SUB_PRIORITY
+   #define STM32F7XX_ETH_IRQ_SUB_PRIORITY 0
+#elif (STM32F7XX_ETH_IRQ_SUB_PRIORITY < 0)
+   #error STM32F7XX_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//Transmit DMA descriptor flags
+#define ETH_TDES0_OWN    0x80000000
+#define ETH_TDES0_IC     0x40000000
+#define ETH_TDES0_LS     0x20000000
+#define ETH_TDES0_FS     0x10000000
+#define ETH_TDES0_DC     0x08000000
+#define ETH_TDES0_DP     0x04000000
+#define ETH_TDES0_TTSE   0x02000000
+#define ETH_TDES0_CIC    0x00C00000
+#define ETH_TDES0_TER    0x00200000
+#define ETH_TDES0_TCH    0x00100000
+#define ETH_TDES0_TTSS   0x00020000
+#define ETH_TDES0_IHE    0x00010000
+#define ETH_TDES0_ES     0x00008000
+#define ETH_TDES0_JT     0x00004000
+#define ETH_TDES0_FF     0x00002000
+#define ETH_TDES0_IPE    0x00001000
+#define ETH_TDES0_LCA    0x00000800
+#define ETH_TDES0_NC     0x00000400
+#define ETH_TDES0_LCO    0x00000200
+#define ETH_TDES0_EC     0x00000100
+#define ETH_TDES0_VF     0x00000080
+#define ETH_TDES0_CC     0x00000078
+#define ETH_TDES0_ED     0x00000004
+#define ETH_TDES0_UF     0x00000002
+#define ETH_TDES0_DB     0x00000001
+#define ETH_TDES1_TBS2   0x1FFF0000
+#define ETH_TDES1_TBS1   0x00001FFF
+#define ETH_TDES2_TBAP1  0xFFFFFFFF
+#define ETH_TDES3_TBAP2  0xFFFFFFFF
+#define ETH_TDES6_TTSL   0xFFFFFFFF
+#define ETH_TDES7_TTSH   0xFFFFFFFF
+
+//Receive DMA descriptor flags
+#define ETH_RDES0_OWN    0x80000000
+#define ETH_RDES0_AFM    0x40000000
+#define ETH_RDES0_FL     0x3FFF0000
+#define ETH_RDES0_ES     0x00008000
+#define ETH_RDES0_DE     0x00004000
+#define ETH_RDES0_SAF    0x00002000
+#define ETH_RDES0_LE     0x00001000
+#define ETH_RDES0_OE     0x00000800
+#define ETH_RDES0_VLAN   0x00000400
+#define ETH_RDES0_FS     0x00000200
+#define ETH_RDES0_LS     0x00000100
+#define ETH_RDES0_IPHCE  0x00000080
+#define ETH_RDES0_LCO    0x00000040
+#define ETH_RDES0_FT     0x00000020
+#define ETH_RDES0_RWT    0x00000010
+#define ETH_RDES0_RE     0x00000008
+#define ETH_RDES0_DBE    0x00000004
+#define ETH_RDES0_CE     0x00000002
+#define ETH_RDES0_PCE    0x00000001
+#define ETH_RDES1_DIC    0x80000000
+#define ETH_RDES1_RBS2   0x1FFF0000
+#define ETH_RDES1_RER    0x00008000
+#define ETH_RDES1_RCH    0x00004000
+#define ETH_RDES1_RBS1   0x00001FFF
+#define ETH_RDES2_RBAP1  0xFFFFFFFF
+#define ETH_RDES3_RBAP2  0xFFFFFFFF
+#define ETH_RDES4_PV     0x00002000
+#define ETH_RDES4_PFT    0x00001000
+#define ETH_RDES4_PMT    0x00000F00
+#define ETH_RDES4_IPV6PR 0x00000080
+#define ETH_RDES4_IPV4PR 0x00000040
+#define ETH_RDES4_IPCB   0x00000020
+#define ETH_RDES4_IPPE   0x00000010
+#define ETH_RDES4_IPHE   0x00000008
+#define ETH_RDES4_IPPT   0x00000007
+#define ETH_RDES6_RTSL   0xFFFFFFFF
+#define ETH_RDES7_RTSH   0xFFFFFFFF
+
+//Serial Management Interface
+#define SMI_SYNC         0xFFFFFFFF
+#define SMI_START        0x00000001
+#define SMI_WRITE        0x00000001
+#define SMI_READ         0x00000002
+#define SMI_TA           0x00000002
+
+
+/**
+ * @brief Enhanced TX DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t tdes0;
+   uint32_t tdes1;
+   uint32_t tdes2;
+   uint32_t tdes3;
+   uint32_t tdes4;
+   uint32_t tdes5;
+   uint32_t tdes6;
+   uint32_t tdes7;
+} Stm32f7xxTxDmaDesc;
+
+
+/**
+ * @brief Enhanced RX DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t rdes0;
+   uint32_t rdes1;
+   uint32_t rdes2;
+   uint32_t rdes3;
+   uint32_t rdes4;
+   uint32_t rdes5;
+   uint32_t rdes6;
+   uint32_t rdes7;
+} Stm32f7xxRxDmaDesc;
+
+
+//STM32F746/756 Ethernet MAC driver
+extern const NicDriver stm32f7xxEthDriver;
+
+//STM32F746/756 Ethernet MAC related functions
+error_t stm32f7xxEthInit(NetInterface *interface);
+void stm32f7xxEthInitGpio(NetInterface *interface);
+void stm32f7xxEthInitDmaDesc(NetInterface *interface);
+
+void stm32f7xxEthTick(NetInterface *interface);
+
+void stm32f7xxEthEnableIrq(NetInterface *interface);
+void stm32f7xxEthDisableIrq(NetInterface *interface);
+void stm32f7xxEthEventHandler(NetInterface *interface);
+
+error_t stm32f7xxEthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t stm32f7xxEthReceivePacket(NetInterface *interface);
+
+error_t stm32f7xxEthSetMulticastFilter(NetInterface *interface);
+error_t stm32f7xxEthUpdateMacConfig(NetInterface *interface);
+
+void stm32f7xxEthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t stm32f7xxEthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+void stm32f7xxEthWriteSmi(uint32_t data, uint_t length);
+uint32_t stm32f7xxEthReadSmi(uint_t length);
+
+uint32_t stm32f7xxEthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/str912_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,724 @@
+/**
+ * @file str912_eth.c
+ * @brief STR9 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "91x_lib.h"
+#include "core/net.h"
+#include "drivers/str912_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+static uint8_t txBuffer[STR912_ETH_TX_BUFFER_COUNT][STR912_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+static uint8_t rxBuffer[STR912_ETH_RX_BUFFER_COUNT][STR912_ETH_RX_BUFFER_SIZE];
+//Transmit DMA descriptors
+#pragma data_alignment = 4
+static Str912TxDmaDesc txDmaDesc[STR912_ETH_TX_BUFFER_COUNT];
+//Receive DMA descriptors
+#pragma data_alignment = 4
+static Str912RxDmaDesc rxDmaDesc[STR912_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[STR912_ETH_TX_BUFFER_COUNT][STR912_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[STR912_ETH_RX_BUFFER_COUNT][STR912_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Transmit DMA descriptors
+static Str912TxDmaDesc txDmaDesc[STR912_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive DMA descriptors
+static Str912RxDmaDesc rxDmaDesc[STR912_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+
+#endif
+
+//Pointer to the current TX DMA descriptor
+static Str912TxDmaDesc *txCurDmaDesc;
+//Pointer to the current RX DMA descriptor
+static Str912RxDmaDesc *rxCurDmaDesc;
+
+
+/**
+ * @brief STR912 Ethernet MAC driver
+ **/
+
+const NicDriver str912EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   str912EthInit,
+   str912EthTick,
+   str912EthEnableIrq,
+   str912EthDisableIrq,
+   str912EthEventHandler,
+   str912EthSendPacket,
+   str912EthSetMulticastFilter,
+   str912EthUpdateMacConfig,
+   str912EthWritePhyReg,
+   str912EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief STR912 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t str912EthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing STR912 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //GPIO configuration
+   str912EthInitGpio(interface);
+
+   //Enable Ethernet MAC clock
+   SCU_AHBPeriphClockConfig(__ENET, ENABLE);
+
+   //Reset Ethernet MAC peripheral
+   SCU_AHBPeriphReset(__ENET, ENABLE);
+   SCU_AHBPeriphReset(__ENET, DISABLE);
+
+   //MAC DMA software reset
+   ENET_DMA->SCR |= ENET_SCR_SRESET;
+   ENET_DMA->SCR &= ~ENET_SCR_SRESET;
+
+   //Use default MAC configuration
+   ENET_MAC->MCR = ENET_MCR_AFM_1 | ENET_MCR_RVFF |
+      ENET_MCR_BL_1 | ENET_MCR_DCE | ENET_MCR_RVBE;
+
+   //Adjust HCLK divider depending on system clock frequency
+   if(SCU_GetHCLKFreqValue() > 50000)
+      ENET_MAC->MCR |= ENET_MCR_PS_1;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Set the MAC address
+   ENET_MAC->MAL = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   ENET_MAC->MAH = interface->macAddr.w[2];
+
+   //Initialize hash table
+   ENET_MAC->MCLA = 0;
+   ENET_MAC->MCHA = 0;
+
+   //DMA configuration
+   //ENET_DMA->SCR = 0;
+
+   //Force a DMA abort
+   ENET_DMA->TXSTR |= ENET_TXSTR_DMA_EN;
+   ENET_DMA->RXSTR |= ENET_RXSTR_DMA_EN;
+
+   //Set descriptor fetch delay
+   ENET_DMA->TXSTR = ENET_TXSTR_DFETCH_DLY_DEFAULT | ENET_TXSTR_UNDER_RUN;
+   ENET_DMA->RXSTR = ENET_RXSTR_DFETCH_DLY_DEFAULT;
+
+   //Initialize DMA descriptor lists
+   str912EthInitDmaDesc(interface);
+
+   //Clear interrupt flags
+   ENET_DMA->ISR = ENET_ISR_TX_CURR_DONE | ENET_ISR_RX_CURR_DONE;
+   //Configure DMA interrupts as desired
+   ENET_DMA->IER = ENET_IER_TX_CURR_DONE_EN | ENET_IER_RX_CURR_DONE_EN;
+
+   //Configure Ethernet interrupt priority
+   VIC_Config(ENET_ITLine, VIC_IRQ, STR912_ETH_IRQ_PRIORITY);
+
+   //Enable MAC transmission and reception
+   ENET_MAC->MCR |= ENET_MCR_TE | ENET_MCR_RE;
+   //Instruct the DMA to poll the receive descriptor list
+   ENET_DMA->RXSTR |= ENET_RXSTR_START_FETCH;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//STR-E912 evaluation board?
+#if defined(USE_STR_E912)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void str912EthInitGpio(NetInterface *interface)
+{
+   GPIO_InitTypeDef GPIO_InitStructure;
+
+   //Enable GPIO clocks
+   SCU_APBPeriphClockConfig(__GPIO0, ENABLE);
+   SCU_APBPeriphClockConfig(__GPIO1, ENABLE);
+   SCU_APBPeriphClockConfig(__GPIO5, ENABLE);
+
+   //Enable MII_PHYCLK clock
+   SCU_PHYCLKConfig(ENABLE);
+
+   //Configure MII_TX_CLK (P0.0), MII_RXD0 (P0.2), MII_RXD1 (P0.3), MII_RXD2 (P0.4),
+   //MII_RXD3 (P0.5), MII_RX_CLK (P0.6) and MII_RX_DV (P0.7)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_3 |
+      GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
+
+   GPIO_InitStructure.GPIO_Direction = GPIO_PinInput;
+   GPIO_InitStructure.GPIO_IPInputConnected = GPIO_IPInputConnected_Disable;
+   GPIO_InitStructure.GPIO_Alternate = GPIO_InputAlt1;
+   GPIO_Init(GPIO0, &GPIO_InitStructure);
+
+   //Configure MII_RX_ER (P1.0), MII_COL (P1.5) and MII_CRS (P1.6)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_5 | GPIO_Pin_6;
+   GPIO_InitStructure.GPIO_Direction = GPIO_PinInput;
+   GPIO_InitStructure.GPIO_IPInputConnected = GPIO_IPInputConnected_Disable;
+   GPIO_InitStructure.GPIO_Alternate = GPIO_InputAlt1;
+   GPIO_Init(GPIO1, &GPIO_InitStructure);
+
+   //Configure MII_TXD0 (P1.1), MII_TXD1 (P1.2), MII_TXD2 (P1.3),
+   //MII_TXD3 (P1.4) and MII_MDC (P1.7)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 |
+      GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_7;
+
+   GPIO_InitStructure.GPIO_Direction = GPIO_PinOutput;
+   GPIO_InitStructure.GPIO_Type = GPIO_Type_PushPull;
+   GPIO_InitStructure.GPIO_IPInputConnected = GPIO_IPInputConnected_Disable;
+   GPIO_InitStructure.GPIO_Alternate = GPIO_OutputAlt2;
+   GPIO_Init(GPIO1, &GPIO_InitStructure);
+
+   //Configure MII_PHYCLK (P5.2) and MII_TX_EN (P5.3)
+   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
+   GPIO_InitStructure.GPIO_Direction = GPIO_PinOutput;
+   GPIO_InitStructure.GPIO_Type = GPIO_Type_PushPull;
+   GPIO_InitStructure.GPIO_IPInputConnected = GPIO_IPInputConnected_Disable;
+   GPIO_InitStructure.GPIO_Alternate = GPIO_OutputAlt2;
+   GPIO_Init(GPIO5, &GPIO_InitStructure);
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void str912EthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX DMA descriptor list
+   for(i = 0; i < STR912_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Control word
+      txDmaDesc[i].ctrl = ENET_TDES_CTRL_NXT_EN;
+      //Transmit buffer address
+      txDmaDesc[i].start = (uint32_t) txBuffer[i];
+      //Next descriptor address
+      txDmaDesc[i].next = (uint32_t) &txDmaDesc[i + 1] | ENET_TDES_NEXT_NPOL_EN;
+      //Status word
+      txDmaDesc[i].status = 0;
+   }
+
+   //The last descriptor is chained to the first entry
+   txDmaDesc[i - 1].next = (uint32_t) &txDmaDesc[0] | ENET_TDES_NEXT_NPOL_EN;
+   //Point to the very first descriptor
+   txCurDmaDesc = &txDmaDesc[0];
+
+   //Initialize RX DMA descriptor list
+   for(i = 0; i < STR912_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Control word
+      rxDmaDesc[i].ctrl = ENET_RDES_CTRL_NXT_EN | STR912_ETH_RX_BUFFER_SIZE;
+      //Receive buffer address
+      rxDmaDesc[i].start = (uint32_t) rxBuffer[i];
+      //Next descriptor address
+      rxDmaDesc[i].next = (uint32_t) &rxDmaDesc[i + 1] | ENET_RDES_NEXT_NPOL_EN;
+      //Status word
+      rxDmaDesc[i].status = ENET_RDES_STATUS_VALID;
+   }
+
+   //The last descriptor is chained to the first entry
+   rxDmaDesc[i - 1].next = (uint32_t) &rxDmaDesc[0] | ENET_RDES_NEXT_NPOL_EN;
+   //Point to the very first descriptor
+   rxCurDmaDesc = &rxDmaDesc[0];
+
+   //Start location of the TX descriptor list
+   ENET_DMA->TXNDAR = (uint32_t) txDmaDesc | ENET_TDES_NEXT_NPOL_EN;
+   //Start location of the RX descriptor list
+   ENET_DMA->RXNDAR = (uint32_t) rxDmaDesc | ENET_RDES_NEXT_NPOL_EN;
+}
+
+
+/**
+ * @brief STR912 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void str912EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void str912EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   VIC_ITCmd(ENET_ITLine, ENABLE);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void str912EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   VIC_ITCmd(ENET_ITLine, DISABLE);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief STR912 Ethernet MAC interrupt service routine
+ **/
+
+void ENET_IRQHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read DMA status register
+   status = ENET_DMA->ISR;
+
+   //A packet has been transmitted?
+   if(status & ENET_ISR_TX_CURR_DONE)
+   {
+      //Clear TX_CURR_DONE interrupt flag
+      ENET_DMA->ISR = ENET_ISR_TX_CURR_DONE;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurDmaDesc->status & ENET_TDES_STATUS_VALID))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & ENET_ISR_RX_CURR_DONE)
+   {
+      //Disable RX_CURR_DONE interrupt
+      ENET_DMA->IER &= ~ENET_IER_RX_CURR_DONE_EN;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief STR912 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void str912EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(ENET_DMA->ISR & ENET_ISR_RX_CURR_DONE)
+   {
+      //Clear interrupt flag
+      ENET_DMA->ISR = ENET_ISR_RX_CURR_DONE;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = str912EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable DMA interrupts
+   ENET_DMA->IER = ENET_IER_TX_CURR_DONE_EN | ENET_IER_RX_CURR_DONE_EN;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t str912EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > STR912_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurDmaDesc->status & ENET_TDES_STATUS_VALID)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) (txCurDmaDesc->start & ENET_TDES_START_ADDR),
+      buffer, offset, length);
+
+   //Write the number of bytes to send
+   txCurDmaDesc->ctrl = ENET_TDES_CTRL_NXT_EN | length;
+   //Give the ownership of the descriptor to the DMA
+   txCurDmaDesc->status = ENET_TDES_STATUS_VALID;
+
+   //Instruct the DMA to poll the transmit descriptor list
+   ENET_DMA->TXSTR |= ENET_TXSTR_START_FETCH;
+
+   //Point to the next descriptor in the list
+   txCurDmaDesc = (Str912TxDmaDesc *) (txCurDmaDesc->next & ENET_TDES_NEXT_ADDR);
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurDmaDesc->status & ENET_TDES_STATUS_VALID))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t str912EthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+   uint8_t *p;
+
+   //The current buffer is available for reading?
+   if(!(rxCurDmaDesc->status & ENET_RDES_STATUS_VALID))
+   {
+      //Make sure no error occurred
+      if(!(rxCurDmaDesc->status & ENET_RDES_STATUS_ERROR))
+      {
+         //Point to the received frame
+         p = (uint8_t *) (rxCurDmaDesc->start & ENET_RDES_START_ADDR);
+
+         //Retrieve the length of the frame
+         n = rxCurDmaDesc->status & ENET_RDES_STATUS_FL;
+         //Limit the number of data to read
+         n = MIN(n, STR912_ETH_RX_BUFFER_SIZE);
+
+         //Pass the packet to the upper layer
+         nicProcessPacket(interface, p, n);
+
+         //Valid packet received
+         error = NO_ERROR;
+      }
+      else
+      {
+         //The received packet contains an error
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Give the ownership of the descriptor back to the DMA
+      rxCurDmaDesc->status = ENET_RDES_STATUS_VALID;
+      //Point to the next descriptor in the list
+      rxCurDmaDesc = (Str912RxDmaDesc *) (rxCurDmaDesc->next & ENET_RDES_NEXT_ADDR);
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Instruct the DMA to poll the receive descriptor list
+   ENET_DMA->RXSTR |= ENET_RXSTR_START_FETCH;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t str912EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating STR912 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = str912EthCalcCrc(&entry->addr, sizeof(MacAddr));
+
+         //The upper 6 bits in the CRC register are used to index the
+         //contents of the hash table
+         k = (crc >> 26) & 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   ENET_MAC->MCLA = hashTable[0];
+   ENET_MAC->MCHA = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  ENET_MCLA = %08" PRIX32 "\r\n", ENET_MAC->MCLA);
+   TRACE_DEBUG("  ENET_MCHA = %08" PRIX32 "\r\n", ENET_MAC->MCHA);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t str912EthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read current MAC configuration
+   config = ENET_MAC->MCR;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+   {
+      //Full-duplex mode
+      config |= ENET_MCR_FDM;
+      //Enable the reception path during transmission
+      config &= ~ENET_MCR_DRO;
+   }
+   else
+   {
+      //Half-duplex mode
+      config &= ~ENET_MCR_FDM;
+      //Disable the reception path during transmission
+      config |= ENET_MCR_DRO;
+   }
+
+   //Update MAC configuration register
+   ENET_MAC->MCR = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void str912EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Set up a write operation
+   value = ENET_MIIA_WR | ENET_MIIA_BUSY;
+   //PHY address
+   value |= (phyAddr << 11) & ENET_MIIA_PADDR;
+   //Register address
+   value |= (regAddr << 6) & ENET_MIIA_RADDR;
+
+   //Data to be written in the PHY register
+   ENET_MAC->MIID = data & ENET_MIID_RDATA;
+
+   //Start a write operation
+   ENET_MAC->MIIA = value;
+   //Wait for the write to complete
+   while(ENET_MAC->MIIA & ENET_MIIA_BUSY);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t str912EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Set up a read operation
+   value = ENET_MIIA_BUSY;
+   //PHY address
+   value |= (phyAddr << 11) & ENET_MIIA_PADDR;
+   //Register address
+   value |= (regAddr << 6) & ENET_MIIA_RADDR;
+
+   //Start a read operation
+   ENET_MAC->MIIA = value;
+   //Wait for the read to complete
+   while(ENET_MAC->MIIA & ENET_MIIA_BUSY);
+
+   //Return PHY register contents
+   return ENET_MAC->MIID & ENET_MIID_RDATA;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t str912EthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/str912_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,325 @@
+/**
+ * @file str912_eth.h
+ * @brief STR9 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _STR912_ETH_H
+#define _STR912_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef STR912_ETH_TX_BUFFER_COUNT
+   #define STR912_ETH_TX_BUFFER_COUNT 2
+#elif (STR912_ETH_TX_BUFFER_COUNT < 1)
+   #error STR912_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef STR912_ETH_TX_BUFFER_SIZE
+   #define STR912_ETH_TX_BUFFER_SIZE 1536
+#elif (STR912_ETH_TX_BUFFER_SIZE != 1536)
+   #error STR912_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef STR912_ETH_RX_BUFFER_COUNT
+   #define STR912_ETH_RX_BUFFER_COUNT 4
+#elif (STR912_ETH_RX_BUFFER_COUNT < 1)
+   #error STR912_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef STR912_ETH_RX_BUFFER_SIZE
+   #define STR912_ETH_RX_BUFFER_SIZE 1536
+#elif (STR912_ETH_RX_BUFFER_SIZE != 1536)
+   #error STR912_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Ethernet interrupt priority
+#ifndef STR912_ETH_IRQ_PRIORITY
+   #define STR912_ETH_IRQ_PRIORITY 15
+#elif (STR912_ETH_IRQ_PRIORITY < 0)
+   #error STR912_ETH_IRQ_PRIORITY parameter is not valid
+#endif
+
+//ENET_SCR register
+#define ENET_SCR_TX_FIFO_SIZE          0xF0000000
+#define ENET_SCR_TX_IO_DATA_WIDTH      0x0C000000
+#define ENET_SCR_TX_CHAN_STATUS        0x03000000
+#define ENET_SCR_RX_FIFO_SIZE          0x00F00000
+#define ENET_SCR_RX_IO_DATA_WIDTH      0x000C0000
+#define ENET_SCR_RX_CHAN_STATUS        0x00030000
+#define ENET_SCR_TX_MAX_BURST_SIZE     0x000000C0
+#define ENET_SCR_RX_MAX_BURST_SIZE     0x00000030
+#define ENET_SCR_LOOPB                 0x00000002
+#define ENET_SCR_SRESET                0x00000001
+
+//ENET_IER register
+#define ENET_IER_TX_CURR_DONE_EN       0x80000000
+#define ENET_IER_MAC_802_3_INT_EN      0x10000000
+#define ENET_IER_TX_MERR_INT_EN        0x02000000
+#define ENET_IER_TX_DONE_EN            0x00800000
+#define ENET_IER_TX_NEXT_EN            0x00400000
+#define ENET_IER_TX_TO_EN              0x00080000
+#define ENET_IER_TX_ENTRY_EN           0x00040000
+#define ENET_IER_TX_FULL_EN            0x00020000
+#define ENET_IER_TX_EMPTY_EN           0x00010000
+#define ENET_IER_RX_CURR_DONE_EN       0x00008000
+#define ENET_IER_RX_MERR_INT_EN        0x00000200
+#define ENET_IER_RX_DONE_EN            0x00000080
+#define ENET_IER_RX_NEXT_EN            0x00000040
+#define ENET_IER_PACKET_LOST_EN        0x00000020
+#define ENET_IER_RX_TO_EN              0x00000008
+#define ENET_IER_RX_ENTRY_EN           0x00000004
+#define ENET_IER_RX_FULL_EN            0x00000002
+#define ENET_IER_RX_EMPTY_EN           0x00000001
+
+//ENET_ISR register
+#define ENET_ISR_TX_CURR_DONE          0x80000000
+#define ENET_ISR_MAC_802_3_INT         0x10000000
+#define ENET_ISR_TX_MERR_INT           0x02000000
+#define ENET_ISR_TX_DONE               0x00800000
+#define ENET_ISR_TX_NEXT               0x00400000
+#define ENET_ISR_TX_TO                 0x00080000
+#define ENET_ISR_TX_ENTRY              0x00040000
+#define ENET_ISR_TX_FULL               0x00020000
+#define ENET_ISR_TX_EMPTY              0x00010000
+#define ENET_ISR_RX_CURR_DONE          0x00008000
+#define ENET_ISR_RX_MERR_INT           0x00000200
+#define ENET_ISR_RX_DONE               0x00000080
+#define ENET_ISR_RX_NEXT               0x00000040
+#define ENET_ISR_PACKET_LOST           0x00000020
+#define ENET_ISR_RX_TO                 0x00000008
+#define ENET_ISR_RX_ENTRY              0x00000004
+#define ENET_ISR_RX_FULL               0x00000002
+#define ENET_ISR_RX_EMPTY              0x00000001
+
+//ENET_CCR register
+#define ENET_CCR_SEL_CLK               0x0000000C
+
+#define ENET_CCR_SEL_CLK_0             0x00000000
+#define ENET_CCR_SEL_CLK_1             0x00000004
+
+//ENET_RXSTR register
+#define ENET_RXSTR_DFETCH_DLY          0x00FFFF00
+#define ENET_RXSTR_COLL_SEEN           0x00000080
+#define ENET_RXSTR_RUNT_FRAME          0x00000040
+#define ENET_RXSTR_FILTER_FAIL         0x00000020
+#define ENET_RXSTR_START_FETCH         0x00000004
+#define ENET_RXSTR_DMA_EN              0x00000001
+
+#define ENET_RXSTR_DFETCH_DLY_DEFAULT  0x00800000
+
+//ENET_TXSTR register
+#define ENET_TXSTR_DFETCH_DLY          0x00FFFF00
+#define ENET_TXSTR_UNDER_RUN           0x00000020
+#define ENET_TXSTR_START_FETCH         0x00000004
+#define ENET_TXSTR_DMA_EN              0x00000001
+
+#define ENET_TXSTR_DFETCH_DLY_DEFAULT  0x00800000
+
+//ENET_MCR register
+#define ENET_MCR_RA                    0x80000000
+#define ENET_MCR_EN                    0x40000000
+#define ENET_MCR_PS                    0x03000000
+#define ENET_MCR_DRO                   0x00800000
+#define ENET_MCR_LM                    0x00600000
+#define ENET_MCR_FDM                   0x00100000
+#define ENET_MCR_AFM                   0x000E0000
+#define ENET_MCR_PWF                   0x00010000
+#define ENET_MCR_VFM                   0x00008000
+#define ENET_MCR_ELC                   0x00001000
+#define ENET_MCR_DBF                   0x00000800
+#define ENET_MCR_DPR                   0x00000400
+#define ENET_MCR_RVFF                  0x00000200
+#define ENET_MCR_APR                   0x00000100
+#define ENET_MCR_BL                    0x000000C0
+#define ENET_MCR_DCE                   0x00000020
+#define ENET_MCR_RVBE                  0x00000010
+#define ENET_MCR_TE                    0x00000008
+#define ENET_MCR_RE                    0x00000004
+#define ENET_MCR_RCFA                  0x00000001
+
+#define ENET_MCR_PS_0                  0x00000000
+#define ENET_MCR_PS_1                  0x01000000
+
+#define ENET_MCR_AFM_0                 0x00000000
+#define ENET_MCR_AFM_1                 0x00020000
+#define ENET_MCR_AFM_2                 0x00040000
+#define ENET_MCR_AFM_3                 0x00060000
+#define ENET_MCR_AFM_4                 0x00080000
+#define ENET_MCR_AFM_5                 0x000A0000
+#define ENET_MCR_AFM_6                 0x000C0000
+#define ENET_MCR_AFM_7                 0x000E0000
+
+#define ENET_MCR_BL_0                  0x00000000
+#define ENET_MCR_BL_1                  0x00000040
+#define ENET_MCR_BL_2                  0x00000080
+#define ENET_MCR_BL_3                  0x000000C0
+
+//ENET_MIIA register
+#define ENET_MIIA_PADDR                0x0000F800
+#define ENET_MIIA_RADDR                0x000007C0
+#define ENET_MIIA_PR                   0x00000004
+#define ENET_MIIA_WR                   0x00000002
+#define ENET_MIIA_BUSY                 0x00000001
+
+//ENET_MIID register
+#define ENET_MIID_RDATA                0x0000FFFF
+
+//TX DMA descriptor (control word)
+#define ENET_TDES_CTRL_DLY_EN          0x00008000
+#define ENET_TDES_CTRL_NXT_EN          0x00004000
+#define ENET_TDES_CTRL_CONT_EN         0x00001000
+#define ENET_TDES_CTRL_FL              0x00000FFF
+
+//TX DMA descriptor (start address)
+#define ENET_TDES_START_ADDR           0xFFFFFFFC
+#define ENET_TDES_START_FIX_ADDR       0x00000002
+#define ENET_TDES_START_WRAP_EN        0x00000001
+
+//TX DMA descriptor (next descriptor address)
+#define ENET_TDES_NEXT_ADDR            0xFFFFFFFC
+#define ENET_TDES_NEXT_NPOL_EN         0x00000001
+
+//TX DMA descriptor (status word)
+#define ENET_TDES_STATUS_PR            0x80000000
+#define ENET_TDES_STATUS_BC            0x7FFC0000
+#define ENET_TDES_STATUS_VALID         0x00010000
+#define ENET_TDES_STATUS_CC            0x00003C00
+#define ENET_TDES_STATUS_LCO           0x00000200
+#define ENET_TDES_STATUS_DEF           0x00000100
+#define ENET_TDES_STATUS_UR            0x00000080
+#define ENET_TDES_STATUS_EC            0x00000040
+#define ENET_TDES_STATUS_LC            0x00000020
+#define ENET_TDES_STATUS_ED            0x00000010
+#define ENET_TDES_STATUS_LOC           0x00000008
+#define ENET_TDES_STATUS_NC            0x00000004
+#define ENET_TDES_STATUS_FA            0x00000001
+
+//RX DMA descriptor (control word)
+#define ENET_RDES_CTRL_DLY_EN          0x00008000
+#define ENET_RDES_CTRL_NXT_EN          0x00004000
+#define ENET_RDES_CTRL_CONT_EN         0x00001000
+#define ENET_RDES_CTRL_FL              0x00000FFF
+
+//RX DMA descriptor (start address)
+#define ENET_RDES_START_ADDR           0xFFFFFFFC
+#define ENET_RDES_START_FIX_ADDR       0x00000002
+#define ENET_RDES_START_WRAP_EN        0x00000001
+
+//RX DMA descriptor (next descriptor address)
+#define ENET_RDES_NEXT_ADDR            0xFFFFFFFC
+#define ENET_RDES_NEXT_NPOL_EN         0x00000001
+
+//RX DMA descriptor (status word)
+#define ENET_RDES_STATUS_FA            0x80000000
+#define ENET_RDES_STATUS_PF            0x40000000
+#define ENET_RDES_STATUS_FF            0x20000000
+#define ENET_RDES_STATUS_BF            0x10000000
+#define ENET_RDES_STATUS_MCF           0x08000000
+#define ENET_RDES_STATUS_UCF           0x04000000
+#define ENET_RDES_STATUS_CF            0x02000000
+#define ENET_RDES_STATUS_LE            0x01000000
+#define ENET_RDES_STATUS_VL2           0x00800000
+#define ENET_RDES_STATUS_VL1           0x00400000
+#define ENET_RDES_STATUS_CE            0x00200000
+#define ENET_RDES_STATUS_EB            0x00100000
+#define ENET_RDES_STATUS_ME            0x00080000
+#define ENET_RDES_STATUS_FT            0x00040000
+#define ENET_RDES_STATUS_LC            0x00020000
+#define ENET_RDES_STATUS_VALID         0x00010000
+#define ENET_RDES_STATUS_RF            0x00008000
+#define ENET_RDES_STATUS_WT            0x00004000
+#define ENET_RDES_STATUS_FCI           0x00002000
+#define ENET_RDES_STATUS_OL            0x00001000
+#define ENET_RDES_STATUS_FL            0x000007FF
+
+//Error mask
+#define ENET_RDES_STATUS_ERROR (ENET_RDES_STATUS_FA | \
+   ENET_RDES_STATUS_LE | ENET_RDES_STATUS_CE | \
+   ENET_RDES_STATUS_EB | ENET_RDES_STATUS_ME | \
+   ENET_RDES_STATUS_LC | ENET_RDES_STATUS_RF | \
+   ENET_RDES_STATUS_WT | ENET_RDES_STATUS_OL)
+
+
+/**
+ * @brief Transmit DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t ctrl;
+   uint32_t start;
+   uint32_t next;
+   uint32_t status;
+} Str912TxDmaDesc;
+
+
+/**
+ * @brief Receive DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t ctrl;
+   uint32_t start;
+   uint32_t next;
+   uint32_t status;
+} Str912RxDmaDesc;
+
+
+//STR912 Ethernet MAC driver
+extern const NicDriver str912EthDriver;
+
+//STR912 Ethernet MAC related functions
+error_t str912EthInit(NetInterface *interface);
+void str912EthInitGpio(NetInterface *interface);
+void str912EthInitDmaDesc(NetInterface *interface);
+
+void str912EthTick(NetInterface *interface);
+
+void str912EthEnableIrq(NetInterface *interface);
+void str912EthDisableIrq(NetInterface *interface);
+void str912EthEventHandler(NetInterface *interface);
+
+error_t str912EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t str912EthReceivePacket(NetInterface *interface);
+
+error_t str912EthSetMulticastFilter(NetInterface *interface);
+error_t str912EthUpdateMacConfig(NetInterface *interface);
+
+void str912EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t str912EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t str912EthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/tm4c129_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,840 @@
+/**
+ * @file tm4c129_eth.c
+ * @brief Tiva TM4C129 Ethernet controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//TM4C1294NCPDT device?
+#if defined(PART_TM4C1294NCPDT)
+   #include "tm4c1294ncpdt.h"
+//TM4C129XNCZAD device?
+#elif defined(PART_TM4C129XNCZAD)
+   #include "tm4c129xnczad.h"
+#endif
+
+//Dependencies
+#include <stdint.h>
+#include <stdbool.h>
+#include "inc/hw_emac.h"
+#include "inc/hw_memmap.h"
+#include "inc/hw_types.h"
+#include "driverlib/gpio.h"
+#include "driverlib/interrupt.h"
+#include "driverlib/pin_map.h"
+#include "driverlib/sysctl.h"
+#include "core/net.h"
+#include "drivers/tm4c129_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+static uint8_t txBuffer[TM4C129_ETH_TX_BUFFER_COUNT][TM4C129_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+static uint8_t rxBuffer[TM4C129_ETH_RX_BUFFER_COUNT][TM4C129_ETH_RX_BUFFER_SIZE];
+//Transmit DMA descriptors
+#pragma data_alignment = 4
+static Tm4c129TxDmaDesc txDmaDesc[TM4C129_ETH_TX_BUFFER_COUNT];
+//Receive DMA descriptors
+#pragma data_alignment = 4
+static Tm4c129RxDmaDesc rxDmaDesc[TM4C129_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[TM4C129_ETH_TX_BUFFER_COUNT][TM4C129_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Receive buffer
+static uint8_t rxBuffer[TM4C129_ETH_RX_BUFFER_COUNT][TM4C129_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4)));
+//Transmit DMA descriptors
+static Tm4c129TxDmaDesc txDmaDesc[TM4C129_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+//Receive DMA descriptors
+static Tm4c129RxDmaDesc rxDmaDesc[TM4C129_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4)));
+
+#endif
+
+//Pointer to the current TX DMA descriptor
+static Tm4c129TxDmaDesc *txCurDmaDesc;
+//Pointer to the current RX DMA descriptor
+static Tm4c129RxDmaDesc *rxCurDmaDesc;
+
+
+/**
+ * @brief Tiva TM4C129 Ethernet MAC driver
+ **/
+
+const NicDriver tm4c129EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   tm4c129EthInit,
+   tm4c129EthTick,
+   tm4c129EthEnableIrq,
+   tm4c129EthDisableIrq,
+   tm4c129EthEventHandler,
+   tm4c129EthSendPacket,
+   tm4c129EthSetMulticastFilter,
+   NULL,
+   NULL,
+   NULL,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief Tiva TM4C129 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t tm4c129EthInit(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing Tiva TM4C129 Ethernet controller...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Enable Ethernet controller clock
+   SysCtlPeripheralEnable(SYSCTL_PERIPH_EMAC0);
+   //Reset Ethernet controller
+   SysCtlPeripheralReset(SYSCTL_PERIPH_EMAC0);
+   //Wait for the reset to complete
+   while(!SysCtlPeripheralReady(SYSCTL_PERIPH_EMAC0));
+
+   //Enable internal PHY clock
+   SysCtlPeripheralEnable(SYSCTL_PERIPH_EPHY0);
+   //Reset internal PHY
+   SysCtlPeripheralReset(SYSCTL_PERIPH_EPHY0);
+   //Wait for the reset to complete
+   while(!SysCtlPeripheralReady(SYSCTL_PERIPH_EPHY0));
+
+   //GPIO configuration
+   tm4c129EthInitGpio(interface);
+
+   //Perform a software reset
+   EMAC0_DMABUSMOD_R |= EMAC_DMABUSMOD_SWR;
+   //Wait for the reset to complete
+   while(EMAC0_DMABUSMOD_R & EMAC_DMABUSMOD_SWR);
+
+   //Adjust MDC clock range depending on SYSCLK frequency
+   EMAC0_MIIADDR_R = EMAC_MIIADDR_CR_100_150;
+
+   //Reset PHY transceiver
+   tm4c129EthWritePhyReg(EPHY_BMCR, EPHY_BMCR_MIIRESET);
+   //Wait for the reset to complete
+   while(tm4c129EthReadPhyReg(EPHY_BMCR) & EPHY_BMCR_MIIRESET);
+
+   //Dump PHY registers for debugging purpose
+   tm4c129EthDumpPhyReg();
+
+   //Configure LED0, LED1 and LED2
+   tm4c129EthWritePhyReg(EPHY_LEDCFG, EPHY_LEDCFG_LED0_TX |
+      EPHY_LEDCFG_LED1_RX | EPHY_LEDCFG_LED2_LINK);
+
+   //Configure PHY interrupts as desired
+   tm4c129EthWritePhyReg(EPHY_MISR1, EPHY_MISR1_LINKSTATEN);
+   //Enable PHY interrupts
+   tm4c129EthWritePhyReg(EPHY_SCR, EPHY_SCR_INTEN);
+
+   //Use default MAC configuration
+   EMAC0_CFG_R = EMAC_CFG_DRO;
+
+   //Set the MAC address
+   EMAC0_ADDR0L_R = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   EMAC0_ADDR0H_R = interface->macAddr.w[2];
+
+   //Initialize hash table
+   EMAC0_HASHTBLL_R = 0;
+   EMAC0_HASHTBLH_R = 0;
+
+   //Configure the receive filter
+   EMAC0_FRAMEFLTR_R = EMAC_FRAMEFLTR_HPF | EMAC_FRAMEFLTR_HMC;
+   //Disable flow control
+   EMAC0_FLOWCTL_R = 0;
+   //Enable store and forward mode
+   EMAC0_DMAOPMODE_R = EMAC_DMAOPMODE_RSF | EMAC_DMAOPMODE_TSF;
+
+   //Configure DMA bus mode
+   EMAC0_DMABUSMOD_R = EMAC_DMABUSMOD_AAL | EMAC_DMABUSMOD_USP | EMAC_DMABUSMOD_RPBL_1 |
+      EMAC_DMABUSMOD_PR_1_1 | EMAC_DMABUSMOD_PBL_1 | EMAC_DMABUSMOD_ATDS;
+
+   //Initialize DMA descriptor lists
+   tm4c129EthInitDmaDesc(interface);
+
+   //Prevent interrupts from being generated when the transmit statistic
+   //counters reach half their maximum value
+   EMAC0_MMCTXIM_R = EMAC_MMCTXIM_OCTCNT | EMAC_MMCTXIM_MCOLLGF |
+      EMAC_MMCTXIM_SCOLLGF | EMAC_MMCTXIM_GBF;
+
+   //Prevent interrupts from being generated when the receive statistic
+   //counters reach half their maximum value
+   EMAC0_MMCRXIM_R = EMAC_MMCRXIM_UCGF | EMAC_MMCRXIM_ALGNERR |
+      EMAC_MMCRXIM_CRCERR | EMAC_MMCRXIM_GBF;
+
+   //Disable MAC interrupts
+   EMAC0_IM_R = EMAC_IM_TSI | EMAC_IM_PMT;
+   //Enable the desired DMA interrupts
+   EMAC0_DMAIM_R = EMAC_DMAIM_NIE | EMAC_DMAIM_RIE | EMAC_DMAIM_TIE;
+   //Enable PHY interrupts
+   EMAC0_EPHYIM_R = EMAC_EPHYIM_INT;
+
+   //Set priority grouping (3 bits for pre-emption priority, no bits for subpriority)
+   IntPriorityGroupingSet(TM4C129_ETH_IRQ_PRIORITY_GROUPING);
+   //Configure Ethernet interrupt priority
+   IntPrioritySet(INT_EMAC0, TM4C129_ETH_IRQ_PRIORITY);
+
+   //Enable MAC transmission and reception
+   EMAC0_CFG_R |= EMAC_CFG_TE | EMAC_CFG_RE;
+   //Enable DMA transmission and reception
+   EMAC0_DMAOPMODE_R |= EMAC_DMAOPMODE_ST | EMAC_DMAOPMODE_SR;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//DK-TM4C129X or EK-TM4C1294XL evaluation board?
+#if defined(USE_DK_TM4C129X) || defined(USE_EK_TM4C1294XL)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void tm4c129EthInitGpio(NetInterface *interface)
+{
+//DK-TM4C129X evaluation board?
+#if defined(USE_DK_TM4C129X)
+   //Enable GPIO clocks
+   SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
+   SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOK);
+
+   //Select the relevant alternate function for PF1, PK4 and PK6
+   GPIOPinConfigure(GPIO_PF1_EN0LED2);
+   GPIOPinConfigure(GPIO_PK4_EN0LED0);
+   GPIOPinConfigure(GPIO_PK6_EN0LED1);
+
+   //Configure Ethernet LED pins for proper operation
+   GPIOPinTypeEthernetLED(GPIO_PORTF_BASE, GPIO_PIN_1);
+   GPIOPinTypeEthernetLED(GPIO_PORTK_BASE, GPIO_PIN_4 | GPIO_PIN_6);
+
+//EK-TM4C1294XL evaluation board?
+#elif defined(USE_EK_TM4C1294XL)
+   //Enable GPIO clock
+   SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
+
+   //Select the relevant alternate function for PF0 and PF4
+   GPIOPinConfigure(GPIO_PF0_EN0LED0);
+   GPIOPinConfigure(GPIO_PF4_EN0LED1);
+
+   //Configure Ethernet LED pins for proper operation
+   GPIOPinTypeEthernetLED(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_4);
+#endif
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void tm4c129EthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX DMA descriptor list
+   for(i = 0; i < TM4C129_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Use chain structure rather than ring structure
+      txDmaDesc[i].tdes0 = EMAC_TDES0_IC | EMAC_TDES0_TCH;
+      //Initialize transmit buffer size
+      txDmaDesc[i].tdes1 = 0;
+      //Transmit buffer address
+      txDmaDesc[i].tdes2 = (uint32_t) txBuffer[i];
+      //Next descriptor address
+      txDmaDesc[i].tdes3 = (uint32_t) &txDmaDesc[i + 1];
+      //Reserved fields
+      txDmaDesc[i].tdes4 = 0;
+      txDmaDesc[i].tdes5 = 0;
+      //Transmit frame time stamp
+      txDmaDesc[i].tdes6 = 0;
+      txDmaDesc[i].tdes7 = 0;
+   }
+
+   //The last descriptor is chained to the first entry
+   txDmaDesc[i - 1].tdes3 = (uint32_t) &txDmaDesc[0];
+   //Point to the very first descriptor
+   txCurDmaDesc = &txDmaDesc[0];
+
+   //Initialize RX DMA descriptor list
+   for(i = 0; i < TM4C129_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the DMA
+      rxDmaDesc[i].rdes0 = EMAC_RDES0_OWN;
+      //Use chain structure rather than ring structure
+      rxDmaDesc[i].rdes1 = EMAC_RDES1_RCH | (TM4C129_ETH_RX_BUFFER_SIZE & EMAC_RDES1_RBS1);
+      //Receive buffer address
+      rxDmaDesc[i].rdes2 = (uint32_t) rxBuffer[i];
+      //Next descriptor address
+      rxDmaDesc[i].rdes3 = (uint32_t) &rxDmaDesc[i + 1];
+      //Extended status
+      rxDmaDesc[i].rdes4 = 0;
+      //Reserved field
+      rxDmaDesc[i].rdes5 = 0;
+      //Receive frame time stamp
+      rxDmaDesc[i].rdes6 = 0;
+      rxDmaDesc[i].rdes7 = 0;
+   }
+
+   //The last descriptor is chained to the first entry
+   rxDmaDesc[i - 1].rdes3 = (uint32_t) &rxDmaDesc[0];
+   //Point to the very first descriptor
+   rxCurDmaDesc = &rxDmaDesc[0];
+
+   //Start location of the TX descriptor list
+   EMAC0_TXDLADDR_R = (uint32_t) txDmaDesc;
+   //Start location of the RX descriptor list
+   EMAC0_RXDLADDR_R = (uint32_t) rxDmaDesc;
+}
+
+
+/**
+ * @brief TM4C129 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void tm4c129EthTick(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void tm4c129EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   IntEnable(INT_EMAC0);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void tm4c129EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   IntDisable(INT_EMAC0);
+}
+
+
+/**
+ * @brief TM4C129 Ethernet MAC interrupt service routine
+ **/
+
+void EMAC0_Handler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read PHY status register
+   status = EMAC0_EPHYRIS_R;
+
+   //PHY interrupt?
+   if(status & EMAC_EPHYRIS_INT)
+   {
+      //Disable PHY interrupt
+      EMAC0_EPHYIM_R &= ~EMAC_EPHYIM_INT;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Read DMA status register
+   status = EMAC0_DMARIS_R;
+
+   //A packet has been transmitted?
+   if(status & EMAC_DMARIS_TI)
+   {
+      //Clear TI interrupt flag
+      EMAC0_DMARIS_R = EMAC_DMARIS_TI;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurDmaDesc->tdes0 & EMAC_TDES0_OWN))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & EMAC_DMARIS_RI)
+   {
+      //Disable RIE interrupt
+      EMAC0_DMAIM_R &= ~EMAC_DMAIM_RIE;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Clear NIS interrupt flag
+   EMAC0_DMARIS_R = EMAC_DMARIS_NIS;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief TM4C129 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void tm4c129EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint32_t status;
+
+   //PHY interrupt?
+   if(EMAC0_EPHYRIS_R & EMAC_EPHYRIS_INT)
+   {
+      //Clear PHY interrupt flag
+      EMAC0_EPHYMISC_R = EMAC_EPHYMISC_INT;
+      //Read PHY interrupt status register
+      status = tm4c129EthReadPhyReg(EPHY_MISR1);
+
+      //Check whether the link state has changed?
+      if(status & EPHY_MISR1_LINKSTAT)
+      {
+         //Read BMSR register
+         status = tm4c129EthReadPhyReg(EPHY_BMSR);
+
+         //Check whether link is up?
+         if(status & EPHY_BMSR_LINKSTAT)
+         {
+            //Read PHY status register
+            status = tm4c129EthReadPhyReg(EPHY_STS);
+
+            //Check current speed
+            if(status & EPHY_STS_SPEED)
+            {
+               //10BASE-T operation
+               interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+               //Update MAC configuration
+               EMAC0_CFG_R &= ~EMAC_CFG_FES;
+            }
+            else
+            {
+               //100BASE-TX operation
+               interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+               //Update MAC configuration
+               EMAC0_CFG_R |= EMAC_CFG_FES;
+            }
+
+            //Check current duplex mode
+            if(status & EPHY_STS_DUPLEX)
+            {
+               //Full-Duplex mode
+               interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+               //Update MAC configuration
+               EMAC0_CFG_R |= EMAC_CFG_DUPM;
+            }
+            else
+            {
+               //Half-Duplex mode
+               interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+               //Update MAC configuration
+               EMAC0_CFG_R &= ~EMAC_CFG_DUPM;
+            }
+
+            //Update link state
+            interface->linkState = TRUE;
+         }
+         else
+         {
+            //Update link state
+            interface->linkState = FALSE;
+         }
+
+         //Process link state change event
+         nicNotifyLinkChange(interface);
+      }
+   }
+
+   //Packet received?
+   if(EMAC0_DMARIS_R & EMAC_DMARIS_RI)
+   {
+      //Clear interrupt flag
+      EMAC0_DMARIS_R = EMAC_DMARIS_RI;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = tm4c129EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable DMA interrupts
+   EMAC0_DMAIM_R |= EMAC_DMAIM_NIE | EMAC_DMAIM_RIE | EMAC_DMAIM_TIE;
+   //Re-enable PHY interrupts
+   EMAC0_EPHYIM_R |= EMAC_EPHYIM_INT;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t tm4c129EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > TM4C129_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurDmaDesc->tdes0 & EMAC_TDES0_OWN)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txCurDmaDesc->tdes2, buffer, offset, length);
+
+   //Write the number of bytes to send
+   txCurDmaDesc->tdes1 = length & EMAC_TDES1_TBS1;
+   //Set LS and FS flags as the data fits in a single buffer
+   txCurDmaDesc->tdes0 |= EMAC_TDES0_LS | EMAC_TDES0_FS;
+   //Give the ownership of the descriptor to the DMA
+   txCurDmaDesc->tdes0 |= EMAC_TDES0_OWN;
+
+   //Clear TU flag to resume processing
+   EMAC0_DMARIS_R = EMAC_DMARIS_TU;
+   //Instruct the DMA to poll the transmit descriptor list
+   EMAC0_TXPOLLD_R = 0;
+
+   //Point to the next descriptor in the list
+   txCurDmaDesc = (Tm4c129TxDmaDesc *) txCurDmaDesc->tdes3;
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurDmaDesc->tdes0 & EMAC_TDES0_OWN))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t tm4c129EthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxCurDmaDesc->rdes0 & EMAC_RDES0_OWN))
+   {
+      //FS and LS flags should be set
+      if((rxCurDmaDesc->rdes0 & EMAC_RDES0_FS) && (rxCurDmaDesc->rdes0 & EMAC_RDES0_LS))
+      {
+         //Make sure no error occurred
+         if(!(rxCurDmaDesc->rdes0 & EMAC_RDES0_ES))
+         {
+            //Retrieve the length of the frame
+            n = (rxCurDmaDesc->rdes0 & EMAC_RDES0_FL) >> 16;
+            //Limit the number of data to read
+            n = MIN(n, TM4C129_ETH_RX_BUFFER_SIZE);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, (uint8_t *) rxCurDmaDesc->rdes2, n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Give the ownership of the descriptor back to the DMA
+      rxCurDmaDesc->rdes0 = EMAC_RDES0_OWN;
+      //Point to the next descriptor in the list
+      rxCurDmaDesc = (Tm4c129RxDmaDesc *) rxCurDmaDesc->rdes3;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Clear RU flag to resume processing
+   EMAC0_DMARIS_R = EMAC_DMARIS_RU;
+   //Instruct the DMA to poll the receive descriptor list
+   EMAC0_RXPOLLD_R = 0;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t tm4c129EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating Tiva TM4C129 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = tm4c129EthCalcCrc(&entry->addr, sizeof(MacAddr));
+
+         //The upper 6 bits in the CRC register are used to index the
+         //contents of the hash table
+         k = (crc >> 26) & 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   EMAC0_HASHTBLL_R = hashTable[0];
+   EMAC0_HASHTBLH_R = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HASHTBLL = %08" PRIX32 "\r\n", EMAC0_HASHTBLL_R);
+   TRACE_DEBUG("  HASHTBLH = %08" PRIX32 "\r\n", EMAC0_HASHTBLH_R);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void tm4c129EthWritePhyReg(uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = EMAC0_MIIADDR_R & EMAC_MIIADDR_CR_M;
+   //Set up a write operation
+   value |= EMAC_MIIADDR_MIIW | EMAC_MIIADDR_MIIB;
+   //The address of the integrated PHY is 0
+   value |= (0 << EMAC_MIIADDR_PLA_S) & EMAC_MIIADDR_PLA_M;
+   //Register address
+   value |= (regAddr << EMAC_MIIADDR_MII_S) & EMAC_MIIADDR_MII_M;
+
+   //Data to be written in the PHY register
+   EMAC0_MIIDATA_R = data & EMAC_MIIDATA_DATA_M;
+
+   //Start a write operation
+   EMAC0_MIIADDR_R = value;
+   //Wait for the write to complete
+   while(EMAC0_MIIADDR_R & EMAC_MIIADDR_MIIB);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t tm4c129EthReadPhyReg(uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = EMAC0_MIIADDR_R & EMAC_MIIADDR_CR_M;
+   //Set up a read operation
+   value |= EMAC_MIIADDR_MIIB;
+   //The address of the integrated PHY is 0
+   value |= (0 << EMAC_MIIADDR_PLA_S) & EMAC_MIIADDR_PLA_M;
+   //Register address
+   value |= (regAddr << EMAC_MIIADDR_MII_S) & EMAC_MIIADDR_MII_M;
+
+   //Start a read operation
+   EMAC0_MIIADDR_R = value;
+   //Wait for the read to complete
+   while(EMAC0_MIIADDR_R & EMAC_MIIADDR_MIIB);
+
+   //Return PHY register contents
+   return EMAC0_MIIDATA_R & EMAC_MIIDATA_DATA_M;
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ **/
+
+void tm4c129EthDumpPhyReg(void)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, tm4c129EthReadPhyReg(i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t tm4c129EthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return ~crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/tm4c129_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,236 @@
+/**
+ * @file tm4c129_eth.h
+ * @brief Tiva TM4C129 Ethernet controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TM4C129_ETH_H
+#define _TM4C129_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef TM4C129_ETH_TX_BUFFER_COUNT
+   #define TM4C129_ETH_TX_BUFFER_COUNT 3
+#elif (TM4C129_ETH_TX_BUFFER_COUNT < 1)
+   #error TM4C129_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef TM4C129_ETH_TX_BUFFER_SIZE
+   #define TM4C129_ETH_TX_BUFFER_SIZE 1536
+#elif (TM4C129_ETH_TX_BUFFER_SIZE != 1536)
+   #error TM4C129_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef TM4C129_ETH_RX_BUFFER_COUNT
+   #define TM4C129_ETH_RX_BUFFER_COUNT 6
+#elif (TM4C129_ETH_RX_BUFFER_COUNT < 1)
+   #error TM4C129_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef TM4C129_ETH_RX_BUFFER_SIZE
+   #define TM4C129_ETH_RX_BUFFER_SIZE 1536
+#elif (TM4C129_ETH_RX_BUFFER_SIZE != 1536)
+   #error TM4C129_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef TM4C129_ETH_IRQ_PRIORITY_GROUPING
+   #define TM4C129_ETH_IRQ_PRIORITY_GROUPING 3
+#elif (TM4C129_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error TM4C129_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt priority
+#ifndef TM4C129_ETH_IRQ_PRIORITY
+   #define TM4C129_ETH_IRQ_PRIORITY 192
+#elif (TM4C129_ETH_IRQ_PRIORITY < 0)
+   #error TM4C129_ETH_IRQ_PRIORITY parameter is not valid
+#endif
+
+//DMABUSMOD register
+#define EMAC_DMABUSMOD_RPBL_1  (1 << EMAC_DMABUSMOD_RPBL_S)
+#define EMAC_DMABUSMOD_RPBL_2  (2 << EMAC_DMABUSMOD_RPBL_S)
+#define EMAC_DMABUSMOD_RPBL_4  (4 << EMAC_DMABUSMOD_RPBL_S)
+#define EMAC_DMABUSMOD_RPBL_8  (8 << EMAC_DMABUSMOD_RPBL_S)
+#define EMAC_DMABUSMOD_RPBL_16 (16 << EMAC_DMABUSMOD_RPBL_S)
+#define EMAC_DMABUSMOD_RPBL_32 (32 << EMAC_DMABUSMOD_RPBL_S)
+
+#define EMAC_DMABUSMOD_PR_1_1  (0 << EMAC_DMABUSMOD_PR_S)
+#define EMAC_DMABUSMOD_PR_2_1  (1 << EMAC_DMABUSMOD_PR_S)
+#define EMAC_DMABUSMOD_PR_3_1  (2 << EMAC_DMABUSMOD_PR_S)
+#define EMAC_DMABUSMOD_PR_4_1  (3 << EMAC_DMABUSMOD_PR_S)
+
+#define EMAC_DMABUSMOD_PBL_1   (1 << EMAC_DMABUSMOD_PBL_S)
+#define EMAC_DMABUSMOD_PBL_2   (2 << EMAC_DMABUSMOD_PBL_S)
+#define EMAC_DMABUSMOD_PBL_4   (4 << EMAC_DMABUSMOD_PBL_S)
+#define EMAC_DMABUSMOD_PBL_8   (8 << EMAC_DMABUSMOD_PBL_S)
+#define EMAC_DMABUSMOD_PBL_16  (16 << EMAC_DMABUSMOD_PBL_S)
+#define EMAC_DMABUSMOD_PBL_32  (32 << EMAC_DMABUSMOD_PBL_S)
+
+//Transmit DMA descriptor flags
+#define EMAC_TDES0_OWN    0x80000000
+#define EMAC_TDES0_IC     0x40000000
+#define EMAC_TDES0_LS     0x20000000
+#define EMAC_TDES0_FS     0x10000000
+#define EMAC_TDES0_DC     0x08000000
+#define EMAC_TDES0_DP     0x04000000
+#define EMAC_TDES0_TTSE   0x02000000
+#define EMAC_TDES0_CRCR   0x01000000
+#define EMAC_TDES0_CIC    0x00C00000
+#define EMAC_TDES0_TER    0x00200000
+#define EMAC_TDES0_TCH    0x00100000
+#define EMAC_TDES0_VLIC   0x000C0000
+#define EMAC_TDES0_TTSS   0x00020000
+#define EMAC_TDES0_IHE    0x00010000
+#define EMAC_TDES0_ES     0x00008000
+#define EMAC_TDES0_JT     0x00004000
+#define EMAC_TDES0_FF     0x00002000
+#define EMAC_TDES0_IPE    0x00001000
+#define EMAC_TDES0_LCA    0x00000800
+#define EMAC_TDES0_NC     0x00000400
+#define EMAC_TDES0_LCO    0x00000200
+#define EMAC_TDES0_EC     0x00000100
+#define EMAC_TDES0_VF     0x00000080
+#define EMAC_TDES0_CC     0x00000078
+#define EMAC_TDES0_ED     0x00000004
+#define EMAC_TDES0_UF     0x00000002
+#define EMAC_TDES0_DB     0x00000001
+#define EMAC_TDES1_SAIC   0xE0000000
+#define EMAC_TDES1_TBS2   0x1FFF0000
+#define EMAC_TDES1_TBS1   0x00001FFF
+#define EMAC_TDES2_TBAP1  0xFFFFFFFF
+#define EMAC_TDES3_TBAP2  0xFFFFFFFF
+#define EMAC_TDES6_TTSL   0xFFFFFFFF
+#define EMAC_TDES7_TTSH   0xFFFFFFFF
+
+//Receive DMA descriptor flags
+#define EMAC_RDES0_OWN    0x80000000
+#define EMAC_RDES0_AFM    0x40000000
+#define EMAC_RDES0_FL     0x3FFF0000
+#define EMAC_RDES0_ES     0x00008000
+#define EMAC_RDES0_DE     0x00004000
+#define EMAC_RDES0_SAF    0x00002000
+#define EMAC_RDES0_LE     0x00001000
+#define EMAC_RDES0_OE     0x00000800
+#define EMAC_RDES0_VLAN   0x00000400
+#define EMAC_RDES0_FS     0x00000200
+#define EMAC_RDES0_LS     0x00000100
+#define EMAC_RDES0_TSA_GF 0x00000080
+#define EMAC_RDES0_LCO    0x00000040
+#define EMAC_RDES0_FT     0x00000020
+#define EMAC_RDES0_RWT    0x00000010
+#define EMAC_RDES0_RE     0x00000008
+#define EMAC_RDES0_DBE    0x00000004
+#define EMAC_RDES0_CE     0x00000002
+#define EMAC_RDES0_ESA    0x00000001
+#define EMAC_RDES1_DIC    0x80000000
+#define EMAC_RDES1_RBS2   0x1FFF0000
+#define EMAC_RDES1_RER    0x00008000
+#define EMAC_RDES1_RCH    0x00004000
+#define EMAC_RDES1_RBS1   0x00001FFF
+#define EMAC_RDES2_RBAP1  0xFFFFFFFF
+#define EMAC_RDES3_RBAP2  0xFFFFFFFF
+#define EMAC_RDES4_TSD    0x00004000
+#define EMAC_RDES4_PV     0x00002000
+#define EMAC_RDES4_PFT    0x00001000
+#define EMAC_RDES4_PMT    0x00000F00
+#define EMAC_RDES4_IPV6PR 0x00000080
+#define EMAC_RDES4_IPV4PR 0x00000040
+#define EMAC_RDES4_IPCB   0x00000020
+#define EMAC_RDES4_IPPE   0x00000010
+#define EMAC_RDES4_IPHE   0x00000008
+#define EMAC_RDES4_IPPT   0x00000007
+#define EMAC_RDES6_RTSL   0xFFFFFFFF
+#define EMAC_RDES7_RTSH   0xFFFFFFFF
+
+
+/**
+ * @brief Enhanced TX DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t tdes0;
+   uint32_t tdes1;
+   uint32_t tdes2;
+   uint32_t tdes3;
+   uint32_t tdes4;
+   uint32_t tdes5;
+   uint32_t tdes6;
+   uint32_t tdes7;
+} Tm4c129TxDmaDesc;
+
+
+/**
+ * @brief Enhanced RX DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t rdes0;
+   uint32_t rdes1;
+   uint32_t rdes2;
+   uint32_t rdes3;
+   uint32_t rdes4;
+   uint32_t rdes5;
+   uint32_t rdes6;
+   uint32_t rdes7;
+} Tm4c129RxDmaDesc;
+
+
+//TM4C129 Ethernet MAC driver
+extern const NicDriver tm4c129EthDriver;
+
+//TM4C129 Ethernet MAC related functions
+error_t tm4c129EthInit(NetInterface *interface);
+void tm4c129EthInitGpio(NetInterface *interface);
+void tm4c129EthInitDmaDesc(NetInterface *interface);
+
+void tm4c129EthTick(NetInterface *interface);
+
+void tm4c129EthEnableIrq(NetInterface *interface);
+void tm4c129EthDisableIrq(NetInterface *interface);
+void tm4c129EthEventHandler(NetInterface *interface);
+
+error_t tm4c129EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t tm4c129EthReceivePacket(NetInterface *interface);
+
+error_t tm4c129EthSetMulticastFilter(NetInterface *interface);
+
+void tm4c129EthWritePhyReg(uint8_t regAddr, uint16_t data);
+uint16_t tm4c129EthReadPhyReg(uint8_t regAddr);
+void tm4c129EthDumpPhyReg(void);
+
+uint32_t tm4c129EthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/upd60611.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,273 @@
+/**
+ * @file upd60611.c
+ * @brief uPD60611 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "drivers/upd60611.h"
+#include "debug.h"
+
+
+/**
+ * @brief uPD60611 Ethernet PHY driver
+ **/
+
+const PhyDriver upd60611PhyDriver =
+{
+   upd60611Init,
+   upd60611Tick,
+   upd60611EnableIrq,
+   upd60611DisableIrq,
+   upd60611EventHandler,
+};
+
+
+/**
+ * @brief uPD60611 PHY transceiver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t upd60611Init(NetInterface *interface)
+{
+   //Debug message
+   TRACE_INFO("Initializing uPD60611...\r\n");
+
+   //Reset PHY transceiver
+   upd60611WritePhyReg(interface, UPD60611_PHY_REG_BMCR, BMCR_RESET);
+   //Wait for the reset to complete
+   while(upd60611ReadPhyReg(interface, UPD60611_PHY_REG_BMCR) & BMCR_RESET);
+
+   //Dump PHY registers for debugging purpose
+   upd60611DumpPhyReg(interface);
+
+   //Force the TCP/IP stack to poll the link state at startup
+   interface->phyEvent = TRUE;
+   //Notify the TCP/IP stack of the event
+   osSetEvent(&netEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief uPD60611 timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void upd60611Tick(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //Read basic status register
+   value = upd60611ReadPhyReg(interface, UPD60611_PHY_REG_BMSR);
+   //Retrieve current link state
+   linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+   //Link up event?
+   if(linkState && !interface->linkState)
+   {
+      //Set event flag
+      interface->phyEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      osSetEvent(&netEvent);
+   }
+   //Link down event?
+   else if(!linkState && interface->linkState)
+   {
+      //Set event flag
+      interface->phyEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      osSetEvent(&netEvent);
+   }
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void upd60611EnableIrq(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void upd60611DisableIrq(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief uPD60611 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void upd60611EventHandler(NetInterface *interface)
+{
+   uint16_t value;
+   bool_t linkState;
+
+   //Any link failure condition is latched in the BMSR register... Reading
+   //the register twice will always return the actual link status
+   value = upd60611ReadPhyReg(interface, UPD60611_PHY_REG_BMSR);
+   value = upd60611ReadPhyReg(interface, UPD60611_PHY_REG_BMSR);
+
+   //Retrieve current link state
+   linkState = (value & BMSR_LINK_STATUS) ? TRUE : FALSE;
+
+   //Link is up?
+   if(linkState && !interface->linkState)
+   {
+      //Read PHY special control/status register
+      value = upd60611ReadPhyReg(interface, UPD60611_PHY_REG_PSCSR);
+
+      //Check current operation mode
+      switch(value & PSCSR_HCDSPEED_MASK)
+      {
+      //10BASE-T
+      case PSCSR_HCDSPEED_10BT:
+         interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+         interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+         break;
+      //10BASE-T full-duplex
+      case PSCSR_HCDSPEED_10BT_FD:
+         interface->linkSpeed = NIC_LINK_SPEED_10MBPS;
+         interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+         break;
+      //100BASE-TX
+      case PSCSR_HCDSPEED_100BTX:
+         interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+         interface->duplexMode = NIC_HALF_DUPLEX_MODE;
+         break;
+      //100BASE-TX full-duplex
+      case PSCSR_HCDSPEED_100BTX_FD:
+         interface->linkSpeed = NIC_LINK_SPEED_100MBPS;
+         interface->duplexMode = NIC_FULL_DUPLEX_MODE;
+         break;
+      //Unknown operation mode
+      default:
+         //Debug message
+         TRACE_WARNING("Invalid Duplex mode\r\n");
+         break;
+      }
+
+      //Update link state
+      interface->linkState = TRUE;
+
+      //Adjust MAC configuration parameters for proper operation
+      interface->nicDriver->updateMacConfig(interface);
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+   //Link is down?
+   else if(!linkState && interface->linkState)
+   {
+      //Update link state
+      interface->linkState = FALSE;
+
+      //Process link state change event
+      nicNotifyLinkChange(interface);
+   }
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @param[in] data Register value
+ **/
+
+void upd60611WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = UPD60611_PHY_ADDR;
+
+   //Write the specified PHY register
+   interface->nicDriver->writePhyReg(phyAddr, address, data);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] interface Underlying network interface
+ * @param[in] address PHY register address
+ * @return Register value
+ **/
+
+uint16_t upd60611ReadPhyReg(NetInterface *interface, uint8_t address)
+{
+   uint8_t phyAddr;
+
+   //Get the address of the PHY transceiver
+   if(interface->phyAddr < 32)
+      phyAddr = interface->phyAddr;
+   else
+      phyAddr = UPD60611_PHY_ADDR;
+
+   //Read the specified PHY register
+   return interface->nicDriver->readPhyReg(phyAddr, address);
+}
+
+
+/**
+ * @brief Dump PHY registers for debugging purpose
+ * @param[in] interface Underlying network interface
+ **/
+
+void upd60611DumpPhyReg(NetInterface *interface)
+{
+   uint8_t i;
+
+   //Loop through PHY registers
+   for(i = 0; i < 32; i++)
+   {
+      //Display current PHY register
+      TRACE_DEBUG("%02" PRIu8 ": 0x%04" PRIX16 "\r\n", i, upd60611ReadPhyReg(interface, i));
+   }
+
+   //Terminate with a line feed
+   TRACE_DEBUG("\r\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/upd60611.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,287 @@
+/**
+ * @file upd60611.h
+ * @brief uPD60611 Ethernet PHY transceiver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _UPD60611_H
+#define _UPD60611_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PHY address
+#ifndef UPD60611_PHY_ADDR
+   #define UPD60611_PHY_ADDR 0
+#elif (UPD60611_PHY_ADDR < 0 || UPD60611_PHY_ADDR > 31)
+   #error UPD60611_PHY_ADDR parameter is not valid
+#endif
+
+//uPD60611 registers
+#define UPD60611_PHY_REG_BMCR       0x00
+#define UPD60611_PHY_REG_BMSR       0x01
+#define UPD60611_PHY_REG_PHYIDR1    0x02
+#define UPD60611_PHY_REG_PHYIDR2    0x03
+#define UPD60611_PHY_REG_ANAR       0x04
+#define UPD60611_PHY_REG_ANLPAR     0x05
+#define UPD60611_PHY_REG_ANER       0x06
+#define UPD60611_PHY_REG_ANNPTR     0x07
+#define UPD60611_PHY_REG_SRR        0x10
+#define UPD60611_PHY_REG_MCSR       0x11
+#define UPD60611_PHY_REG_SMR        0x12
+#define UPD60611_PHY_REG_EBSR       0x13
+#define UPD60611_PHY_REG_BER        0x17
+#define UPD60611_PHY_REG_FEQMR      0x18
+#define UPD60611_PHY_REG_DCSR       0x19
+#define UPD60611_PHY_REG_DCR        0x1A
+#define UPD60611_PHY_REG_SCSIR      0x1B
+#define UPD60611_PHY_REG_ISR        0x1D
+#define UPD60611_PHY_REG_IER        0x1E
+#define UPD60611_PHY_REG_PSCSR      0x1F
+
+//BMCR register
+#define BMCR_RESET                 (1 << 15)
+#define BMCR_LOOPBACK              (1 << 14)
+#define BMCR_SPEED_SEL             (1 << 13)
+#define BMCR_AN_EN                 (1 << 12)
+#define BMCR_POWER_DOWN            (1 << 11)
+#define BMCR_ISOLATE               (1 << 10)
+#define BMCR_RESTART_AN            (1 << 9)
+#define BMCR_DUPLEX_MODE           (1 << 8)
+#define BMCR_COL_TEST              (1 << 7)
+
+//BMSR register
+#define BMSR_100BT4                (1 << 15)
+#define BMSR_100BTX_FD             (1 << 14)
+#define BMSR_100BTX                (1 << 13)
+#define BMSR_10BT_FD               (1 << 12)
+#define BMSR_10BT                  (1 << 11)
+#define BMSR_AN_COMPLETE           (1 << 5)
+#define BMSR_REMOTE_FAULT          (1 << 4)
+#define BMSR_AN_ABLE               (1 << 3)
+#define BMSR_LINK_STATUS           (1 << 2)
+#define BMSR_JABBER_DETECT         (1 << 1)
+#define BMSR_EXTENDED_CAP          (1 << 0)
+
+//ANAR register
+#define ANAR_NP                    (1 << 15)
+#define ANAR_RF                    (1 << 13)
+#define ANAR_PAUSE1                (1 << 11)
+#define ANAR_PAUSE0                (1 << 10)
+#define ANAR_100BT4                (1 << 9)
+#define ANAR_100BTX_FD             (1 << 8)
+#define ANAR_100BTX                (1 << 7)
+#define ANAR_10BT_FD               (1 << 6)
+#define ANAR_10BT                  (1 << 5)
+#define ANAR_SELECTOR4             (1 << 4)
+#define ANAR_SELECTOR3             (1 << 3)
+#define ANAR_SELECTOR2             (1 << 2)
+#define ANAR_SELECTOR1             (1 << 1)
+#define ANAR_SELECTOR0             (1 << 0)
+
+//ANLPAR register
+#define ANLPAR_NP                  (1 << 15)
+#define ANLPAR_ACK                 (1 << 14)
+#define ANLPAR_RF                  (1 << 13)
+#define ANLPAR_PAUSE               (1 << 10)
+#define ANLPAR_100BT4              (1 << 9)
+#define ANLPAR_100BTX_FD           (1 << 8)
+#define ANLPAR_100BTX              (1 << 7)
+#define ANLPAR_10BT_FD             (1 << 6)
+#define ANLPAR_10BT                (1 << 5)
+#define ANLPAR_SELECTOR4           (1 << 4)
+#define ANLPAR_SELECTOR3           (1 << 3)
+#define ANLPAR_SELECTOR2           (1 << 2)
+#define ANLPAR_SELECTOR1           (1 << 1)
+#define ANLPAR_SELECTOR0           (1 << 0)
+
+//ANER register
+#define ANER_PDF                   (1 << 4)
+#define ANER_LP_NP_ABLE            (1 << 3)
+#define ANER_NP_ABLE               (1 << 2)
+#define ANER_PAGE_RX               (1 << 1)
+#define ANER_LP_AN_ABLE            (1 << 0)
+
+//ANNPTR register
+#define ANNPTR_NP                  (1 << 15)
+#define ANNPTR_MP                  (1 << 13)
+#define ANNPTR_ACK2                (1 << 12)
+#define ANNPTR_TOGGLE              (1 << 11)
+#define ANNPTR_CODE10              (1 << 10)
+#define ANNPTR_CODE9               (1 << 9)
+#define ANNPTR_CODE8               (1 << 8)
+#define ANNPTR_CODE7               (1 << 7)
+#define ANNPTR_CODE6               (1 << 6)
+#define ANNPTR_CODE5               (1 << 5)
+#define ANNPTR_CODE4               (1 << 4)
+#define ANNPTR_CODE3               (1 << 3)
+#define ANNPTR_CODE2               (1 << 2)
+#define ANNPTR_CODE1               (1 << 1)
+#define ANNPTR_CODE0               (1 << 0)
+
+//MCSR register
+#define MCSR_EDPWRDOWN             (1 << 13)
+#define MCSR_FARLOOPBACK           (1 << 9)
+#define MCSR_FASTEST               (1 << 8)
+#define MCSR_AUTOMDIX_EN           (1 << 7)
+#define MCSR_MDI MODE              (1 << 6)
+#define MCSR_FORCE_GOOD_LINK       (1 << 2)
+#define MCSR_ENERGYON              (1 << 1)
+
+//SMR register
+#define SMR_FX_MODE                (1 << 10)
+#define SMR_PHY_MODE3              (1 << 8)
+#define SMR_PHY_MODE2              (1 << 7)
+#define SMR_PHY_MODE1              (1 << 6)
+#define SMR_PHY_MODE0              (1 << 5)
+#define SMR_PHY_ADD_DEV1           (1 << 4)
+#define SMR_PHY_ADD_DEV0           (1 << 3)
+#define SMR_PHY_ADD_MOD2           (1 << 2)
+#define SMR_PHY_ADD_MOD1           (1 << 1)
+#define SMR_PHY_ADD_MOD0           (1 << 0)
+
+//EBSR register
+#define EBSR_T_EL_BUF_OVF          (1 << 7)
+#define EBSR_T_EL_BUF_UDF          (1 << 6)
+#define EBSR_R_EL_BUF_OVF          (1 << 5)
+#define EBSR_R_EL_BUF_UDF          (1 << 4)
+
+//BER register
+#define BER_LNK_OK                 (1 << 15)
+#define BER_CNT_LNK_EN             (1 << 14)
+#define BER_CNT_TRIG2              (1 << 13)
+#define BER_CNT_TRIG1              (1 << 12)
+#define BER_CNT_TRIG0              (1 << 11)
+#define BER_WINDOW3                (1 << 10)
+#define BER_WINDOW2                (1 << 9)
+#define BER_WINDOW1                (1 << 8)
+#define BER_WINDOW0                (1 << 7)
+#define BER_COUNT6                 (1 << 6)
+#define BER_COUNT5                 (1 << 5)
+#define BER_COUNT4                 (1 << 4)
+#define BER_COUNT3                 (1 << 3)
+#define BER_COUNT2                 (1 << 2)
+#define BER_COUNT1                 (1 << 1)
+#define BER_COUNT0                 (1 << 0)
+
+//DCSR register
+#define DCSR_DIAG_INIT             (1 << 14)
+#define DCSR_ADC_MAX_VALUE5        (1 << 13)
+#define DCSR_ADC_MAX_VALUE4        (1 << 12)
+#define DCSR_ADC_MAX_VALUE3        (1 << 11)
+#define DCSR_ADC_MAX_VALUE2        (1 << 10)
+#define DCSR_ADC_MAX_VALUE1        (1 << 9)
+#define DCSR_ADC_MAX_VALUE0        (1 << 8)
+#define DCSR_DIAG_DONE             (1 << 7)
+#define DCSR_DIAG_POL              (1 << 6)
+#define DCSR_DIAG_SEL_LINE         (1 << 5)
+#define DCSR_PW_DIAG4              (1 << 4)
+#define DCSR_PW_DIAG3              (1 << 3)
+#define DCSR_PW_DIAG2              (1 << 2)
+#define DCSR_PW_DIAG1              (1 << 1)
+#define DCSR_PW_DIAG0              (1 << 0)
+
+//DCR register
+#define DCR_CNT_WINDOW7            (1 << 15)
+#define DCR_CNT_WINDOW6            (1 << 14)
+#define DCR_CNT_WINDOW5            (1 << 13)
+#define DCR_CNT_WINDOW4            (1 << 12)
+#define DCR_CNT_WINDOW3            (1 << 11)
+#define DCR_CNT_WINDOW2            (1 << 10)
+#define DCR_CNT_WINDOW1            (1 << 9)
+#define DCR_CNT_WINDOW0            (1 << 8)
+#define DCR_DIAGCNT7               (1 << 7)
+#define DCR_DIAGCNT6               (1 << 6)
+#define DCR_DIAGCNT5               (1 << 5)
+#define DCR_DIAGCNT4               (1 << 4)
+#define DCR_DIAGCNT3               (1 << 3)
+#define DCR_DIAGCNT2               (1 << 2)
+#define DCR_DIAGCNT1               (1 << 1)
+#define DCR_DIAGCNT0               (1 << 0)
+
+//SCSIR register
+#define SCSIR_SWRST_FAST           (1 << 12)
+#define SCSIR_SQEOFF               (1 << 11)
+#define SCSIR_FEFIEN               (1 << 5)
+#define SCSIR_XPOL                 (1 << 4)
+
+//ISR register
+#define ISR_BER                    (1 << 10)
+#define ISR_FEQ                    (1 << 9)
+#define ISR_ENERGYON               (1 << 7)
+#define ISR_AN_COMPLETE            (1 << 6)
+#define ISR_REMOTE_FAULT           (1 << 5)
+#define ISR_LINK_DOWN              (1 << 4)
+#define ISR_AN_LP_ACK              (1 << 3)
+#define ISR_PD_FAULT               (1 << 2)
+#define ISR_AN_PAGE_RECEIVED       (1 << 1)
+
+//IER register
+#define IER_BER                    (1 << 10)
+#define IER_FEQ                    (1 << 9)
+#define IER_ENERGYON               (1 << 7)
+#define IER_AN_COMPLETE            (1 << 6)
+#define IER_REMOTE_FAULT           (1 << 5)
+#define IER_LINK_DOWN              (1 << 4)
+#define IER_AN_LP_ACK              (1 << 3)
+#define IER_PD_FAULT               (1 << 2)
+#define IER_AN_PAGE_RECEIVED       (1 << 1)
+
+//PSCSR register
+#define PSCSR_AUTODONE             (1 << 12)
+#define PSCSR_ENABLE_4B5B          (1 << 6)
+#define PSCSR_HCDSPEED2            (1 << 4)
+#define PSCSR_HCDSPEED1            (1 << 3)
+#define PSCSR_HCDSPEED0            (1 << 2)
+#define PSCSR_RX_DV_J2T            (1 << 1)
+#define PSCSR_SCRAMBLE_DIS         (1 << 0)
+
+//Speed indication
+#define PSCSR_HCDSPEED_MASK        (7 << 2)
+#define PSCSR_HCDSPEED_10BT        (1 << 2)
+#define PSCSR_HCDSPEED_100BTX      (2 << 2)
+#define PSCSR_HCDSPEED_10BT_FD     (5 << 2)
+#define PSCSR_HCDSPEED_100BTX_FD   (6 << 2)
+
+//uPD60611 Ethernet PHY driver
+extern const PhyDriver upd60611PhyDriver;
+
+//uPD60611 related functions
+error_t upd60611Init(NetInterface *interface);
+
+void upd60611Tick(NetInterface *interface);
+
+void upd60611EnableIrq(NetInterface *interface);
+void upd60611DisableIrq(NetInterface *interface);
+
+void upd60611EventHandler(NetInterface *interface);
+
+void upd60611WritePhyReg(NetInterface *interface, uint8_t address, uint16_t data);
+uint16_t upd60611ReadPhyReg(NetInterface *interface, uint8_t address);
+
+void upd60611DumpPhyReg(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/wilc1000_driver.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,594 @@
+/**
+ * @file wilc1000_driver.c
+ * @brief WILC1000 Wi-Fi controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "driver/include/m2m_wifi.h"
+#include "core/net.h"
+#include "wilc1000_driver.h"
+#include "wilc1000_config.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *wilc1000StaInterface = NULL;
+static NetInterface *wilc1000ApInterface = NULL;
+
+//Transmit buffer
+static uint8_t txBuffer[WILC1000_TX_BUFFER_SIZE];
+//Receive buffer
+static uint8_t rxBuffer[WILC1000_RX_BUFFER_SIZE];
+
+
+/**
+ * @brief WILC1000 driver (STA mode)
+ **/
+
+const NicDriver wilc1000StaDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   wilc1000Init,
+   wilc1000Tick,
+   wilc1000EnableIrq,
+   wilc1000DisableIrq,
+   wilc1000EventHandler,
+   wilc1000SendPacket,
+   wilc1000SetMulticastFilter,
+   NULL,
+   NULL,
+   NULL,
+   TRUE,
+   TRUE,
+   TRUE,
+   TRUE
+};
+
+
+/**
+ * @brief WILC1000 driver (AP mode)
+ **/
+
+const NicDriver wilc1000ApDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   wilc1000Init,
+   wilc1000Tick,
+   wilc1000EnableIrq,
+   wilc1000DisableIrq,
+   wilc1000EventHandler,
+   wilc1000SendPacket,
+   wilc1000SetMulticastFilter,
+   NULL,
+   NULL,
+   NULL,
+   TRUE,
+   TRUE,
+   TRUE,
+   TRUE
+};
+
+
+/**
+ * @brief WILC1000 initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t wilc1000Init(NetInterface *interface)
+{
+   int8_t status;
+   tstrWifiInitParam param;
+   MacAddr staMacAddr;
+   MacAddr apMacAddr;
+
+   //STA or AP mode?
+   if(interface->nicDriver == &wilc1000StaDriver)
+   {
+      //Debug message
+      TRACE_INFO("Initializing WILC1000 (STA mode)...\r\n");
+      //Save underlying network interface
+      wilc1000StaInterface = interface;
+   }
+   else
+   {
+      //Debug message
+      TRACE_INFO("Initializing WILC1000 (AP mode)...\r\n");
+      //Save underlying network interface
+      wilc1000ApInterface = interface;
+   }
+
+   //Start of exception handling block
+   do
+   {
+      //Initialization sequence is performed once
+      if(wilc1000StaInterface == NULL || wilc1000ApInterface == NULL)
+      {
+         //Low-level initialization
+         status = nm_bsp_init();
+
+         //Check status code
+         if(status != M2M_SUCCESS)
+            break;
+
+         //Set default parameters
+         memset(&param, 0, sizeof(param));
+
+         //Register callback functions
+         param.pfAppWifiCb = wilc1000AppWifiEvent;
+         param.pfAppMonCb = NULL;
+         param.strEthInitParam.pfAppWifiCb = NULL;
+         param.strEthInitParam.pfAppEthCb = wilc1000AppEthEvent;
+
+         //Set receive buffer
+         param.strEthInitParam.au8ethRcvBuf = rxBuffer;
+         param.strEthInitParam.u16ethRcvBufSize = WILC1000_RX_BUFFER_SIZE;
+
+         //Initialize WILC1000 controller
+         status = m2m_wifi_init(&param);
+
+         //Check status code
+         if(status != M2M_SUCCESS)
+            break;
+      }
+
+      //Retrieve current MAC addresses
+      status = m2m_wifi_get_mac_address(staMacAddr.b, apMacAddr.b);
+
+      //Check status code
+      if(status != M2M_SUCCESS)
+         break;
+
+      //Optionally set the MAC address
+      if(macCompAddr(&interface->macAddr, &MAC_UNSPECIFIED_ADDR))
+      {
+         //Use the factory preprogrammed MAC address
+         if(interface == wilc1000StaInterface)
+            interface->macAddr = staMacAddr;
+         else
+            interface->macAddr = apMacAddr;
+
+         //Generate the 64-bit interface identifier
+         macAddrToEui64(&interface->macAddr, &interface->eui64);
+      }
+      else
+      {
+         //Override the factory preprogrammed address
+         if(interface == wilc1000StaInterface)
+            staMacAddr = interface->macAddr;
+         else
+            apMacAddr = interface->macAddr;
+
+         //Assign MAC addresses
+         status = m2m_wifi_set_mac_address(apMacAddr.b, staMacAddr.b);
+
+         //Check status code
+         if(status != M2M_SUCCESS)
+            break;
+      }
+
+      //End of exception handling block
+   } while(0);
+
+   //WILC1000 is now ready to send
+   osSetEvent(&interface->nicTxEvent);
+
+   //Return status code
+   if(status == M2M_SUCCESS)
+      return NO_ERROR;
+   else
+      return ERROR_FAILURE;
+}
+
+
+/**
+ * @brief WILC1000 timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void wilc1000Tick(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void wilc1000EnableIrq(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void wilc1000DisableIrq(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief WILC1000 interrupt service routine
+ * @return TRUE if a higher priority task must be woken. Else FALSE is returned
+ **/
+
+bool_t wilc1000IrqHandler(void)
+{
+   bool_t flag;
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //STA and/or AP mode?
+   if(wilc1000StaInterface != NULL)
+      wilc1000StaInterface->nicEvent = TRUE;
+   else if(wilc1000ApInterface != NULL)
+      wilc1000ApInterface->nicEvent = TRUE;
+
+   //Notify the TCP/IP stack of the event
+   flag = osSetEventFromIsr(&netEvent);
+
+   //A higher priority task must be woken?
+   return flag;
+}
+
+
+/**
+ * @brief WILC1000 event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void wilc1000EventHandler(NetInterface *interface)
+{
+   //Process Wi-Fi events
+   m2m_wifi_handle_events(NULL);
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t wilc1000SendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   int8_t status;
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > WILC1000_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the link is up before transmitting the frame
+   if(!interface->linkState)
+   {
+      //The transmitter can accept another packet
+      osSetEventFromIsr(&interface->nicTxEvent);
+      //Drop current packet
+      return NO_ERROR;
+   }
+
+   //Copy user data to the transmit buffer
+   netBufferRead(txBuffer + M2M_ETHERNET_HDR_OFFSET + M2M_ETH_PAD_SIZE,
+      buffer, offset, length);
+
+   //STA or AP mode?
+   if(interface == wilc1000StaInterface)
+   {
+      //Send packet
+      status = m2m_wifi_send_ethernet_pkt(txBuffer, length);
+   }
+   else
+   {
+      //Send packet
+      status = m2m_wifi_send_ethernet_pkt_ifc1(txBuffer, length);
+   }
+
+   //The transmitter can accept another packet
+   osSetEvent(&interface->nicTxEvent);
+
+   //Return status code
+   if(status == M2M_SUCCESS)
+      return NO_ERROR;
+   else
+      return ERROR_FAILURE;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t wilc1000SetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t refCount;
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_INFO("Updating WILC1000 multicast filter...\r\n");
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(!macCompAddr(&entry->addr, &MAC_UNSPECIFIED_ADDR))
+      {
+         //Check whether the multicast MAC address has already been registered
+         //on the alternate interface
+         if(interface == wilc1000StaInterface)
+            refCount = wilc1000GetAddrRefCount(wilc1000ApInterface, &entry->addr);
+         else
+            refCount = wilc1000GetAddrRefCount(wilc1000StaInterface, &entry->addr);
+
+         //Ensure that there are not duplicate address entries in the table
+         if(refCount == 0)
+         {
+            //Update MAC filter table only if necessary
+            if(entry->addFlag)
+            {
+               //Add a new entry to the MAC filter table
+               m2m_wifi_enable_mac_mcast(entry->addr.b, TRUE);
+            }
+            else if(entry->deleteFlag)
+            {
+               //Remove the current entry from the MAC filter table
+               m2m_wifi_enable_mac_mcast(entry->addr.b, FALSE);
+            }
+         }
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Get reference count for the specified multicast MAC address
+ * @param[in] interface Underlying network interface
+ * @param[in] macAddr MAC address
+ * @return Reference count
+ **/
+
+bool_t wilc1000GetAddrRefCount(NetInterface *interface, const MacAddr *macAddr)
+{
+   uint_t i;
+   uint_t refCount;
+   MacFilterEntry *entry;
+
+   //Clear reference count
+   refCount = 0;
+
+   //Valid network interface?
+   if(interface != NULL)
+   {
+      //Go through the multicast filter table
+      for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+      {
+         //Point to the current entry
+         entry = &interface->macMulticastFilter[i];
+
+         //Valid entry?
+         if(entry->refCount > 0)
+         {
+            //Check whether the specified MAC address matches
+            //a multicast address in the table
+            if(macCompAddr(&entry->addr, macAddr))
+            {
+               //Get reference count
+               refCount = entry->refCount;
+               //We are done
+               break;
+            }
+         }
+      }
+   }
+
+   //Return reference count
+   return refCount;
+}
+
+
+/**
+ * @brief Callback function that handles Wi-Fi events
+ * @param[in] msgType Type of notification
+ * @param[in] msg Pointer to the buffer containing the notification parameters
+ **/
+
+void wilc1000AppWifiEvent(uint8_t msgType, void *msg)
+{
+   tstrM2mWifiStateChanged *stateChangedMsg;
+
+   //Debug message
+   TRACE_INFO("WILC1000 Wi-Fi event callback\r\n");
+
+   //Check message type
+   if(msgType == M2M_WIFI_RESP_FIRMWARE_STRTED)
+   {
+      //Debug message
+      TRACE_INFO("  M2M_WIFI_RESP_FIRMWARE_STRTED\r\n");
+   }
+   else if(msgType == M2M_WIFI_RESP_CON_STATE_CHANGED)
+   {
+      //Debug message
+      TRACE_INFO("  M2M_WIFI_RESP_CON_STATE_CHANGED\r\n");
+
+      //Connection state
+      stateChangedMsg = (tstrM2mWifiStateChanged*) msg;
+
+      //Check interface identifier
+      if(stateChangedMsg->u8IfcId == INTERFACE_1)
+      {
+         //Check whether STA mode is enabled
+         if(wilc1000StaInterface != NULL)
+         {
+            //Check link state
+            if(stateChangedMsg->u8CurrState == M2M_WIFI_CONNECTED)
+            {
+               //Link is up
+               wilc1000StaInterface->linkState = TRUE;
+            }
+            else
+            {
+               //Link is down
+               wilc1000StaInterface->linkState = FALSE;
+            }
+
+            //Process link state change event
+            nicNotifyLinkChange(wilc1000StaInterface);
+         }
+      }
+      else if(stateChangedMsg->u8IfcId == INTERFACE_2)
+      {
+         //Check whether AP mode is enabled
+         if(wilc1000ApInterface != NULL)
+         {
+            //Check link state
+            if(stateChangedMsg->u8CurrState == M2M_WIFI_CONNECTED)
+            {
+               //Link is up
+               wilc1000ApInterface->linkState = TRUE;
+            }
+            else
+            {
+               //Link is down
+               wilc1000ApInterface->linkState = FALSE;
+            }
+
+            //Process link state change event
+            nicNotifyLinkChange(wilc1000ApInterface);
+         }
+      }
+   }
+
+#if defined(CONF_WILC_EVENT_HOOK)
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+   //Invoke user callback function
+   CONF_WILC_EVENT_HOOK(msgType, msg);
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+#endif
+}
+
+
+/**
+ * @brief Callback function that handles events in bypass mode
+ * @param[in] msgType Type of notification
+ * @param[in] msg Pointer to the buffer containing the notification parameters
+ * @param[in] ctrlBuf Pointer to the control buffer
+ **/
+
+void wilc1000AppEthEvent(uint8_t msgType, void *msg, void *ctrlBuf)
+{
+   size_t length;
+   uint8_t *packet;
+   tstrM2mIpCtrlBuf *ctrl;
+
+   //Debug message
+   TRACE_DEBUG("WILC1000 RX event callback\r\n");
+
+   //Point to the control buffer
+   ctrl = (tstrM2mIpCtrlBuf *) ctrlBuf;
+
+   //Check message type
+   if(msgType == M2M_WIFI_RESP_ETHERNET_RX_PACKET)
+   {
+      //Debug message
+      TRACE_DEBUG("  M2M_WIFI_RESP_ETHERNET_RX_PACKET\r\n");
+
+      //Point to the beginning of the packet
+      packet = rxBuffer + ctrl->u8DataOffset;
+      //Retrieve the length of the packet
+      length = ctrl->u16DataSize;
+
+      //Check interface identifier
+      if(ctrl->u8IfcId == INTERFACE_1)
+      {
+         //Valid interface?
+         if(wilc1000StaInterface != NULL)
+         {
+            //Check destination MAC address
+            if(wilc1000ApInterface != NULL)
+            {
+               if(macCompAddr(packet, wilc1000ApInterface->macAddr.b))
+                  macCopyAddr(packet, wilc1000StaInterface->macAddr.b);
+            }
+
+            //Pass the packet to the upper layer (STA mode)
+            nicProcessPacket(wilc1000StaInterface, packet, length);
+         }
+      }
+      else if(ctrl->u8IfcId == INTERFACE_2)
+      {
+         //Valid interface?
+         if(wilc1000ApInterface != NULL)
+         {
+            //Check destination MAC address
+            if(wilc1000StaInterface != NULL)
+            {
+               if(macCompAddr(packet, wilc1000StaInterface->macAddr.b))
+                  macCopyAddr(packet, wilc1000ApInterface->macAddr.b);
+            }
+
+            //Pass the packet to the upper layer (STA mode)
+            nicProcessPacket(wilc1000ApInterface, packet, length);
+         }
+      }
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/wilc1000_driver.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,74 @@
+/**
+ * @file wilc1000_driver.h
+ * @brief WILC1000 Wi-Fi controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _WILC1000_DRIVER_H
+#define _WILC1000_DRIVER_H
+
+//Dependencies
+#include "core/nic.h"
+
+//TX buffer size
+#ifndef WILC1000_TX_BUFFER_SIZE
+   #define WILC1000_TX_BUFFER_SIZE 1600
+#elif (WILC1000_TX_BUFFER_SIZE != 1600)
+   #error WILC1000_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef WILC1000_RX_BUFFER_SIZE
+   #define WILC1000_RX_BUFFER_SIZE 1600
+#elif (WILC1000_RX_BUFFER_SIZE != 1600)
+   #error WILC1000_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//WILC1000 driver (STA mode)
+extern const NicDriver wilc1000StaDriver;
+//WILC1000 driver (AP mode)
+extern const NicDriver wilc1000ApDriver;
+
+//WILC1000 related functions
+error_t wilc1000Init(NetInterface *interface);
+
+void wilc1000Tick(NetInterface *interface);
+
+void wilc1000EnableIrq(NetInterface *interface);
+void wilc1000DisableIrq(NetInterface *interface);
+bool_t wilc1000IrqHandler(void);
+void wilc1000EventHandler(NetInterface *interface);
+
+error_t wilc1000SendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t wilc1000SetMulticastFilter(NetInterface *interface);
+bool_t wilc1000GetAddrRefCount(NetInterface *interface, const MacAddr *macAddr);
+
+void wilc1000AppWifiEvent(uint8_t msgType, void *msg);
+void wilc1000AppEthEvent(uint8_t msgType, void *msg, void *ctrlBuf);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/xmc4500_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,762 @@
+/**
+ * @file xmc4500_eth.c
+ * @brief Infineon XMC4500 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "xmc4500.h"
+#include "core/net.h"
+#include "drivers/xmc4500_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+#pragma location = "ETH_RAM"
+static uint8_t txBuffer[XMC4500_ETH_TX_BUFFER_COUNT][XMC4500_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+#pragma location = "ETH_RAM"
+static uint8_t rxBuffer[XMC4500_ETH_RX_BUFFER_COUNT][XMC4500_ETH_RX_BUFFER_SIZE];
+//Transmit DMA descriptors
+#pragma data_alignment = 4
+#pragma location = "ETH_RAM"
+static Xmc4500TxDmaDesc txDmaDesc[XMC4500_ETH_TX_BUFFER_COUNT];
+//Receive DMA descriptors
+#pragma data_alignment = 4
+#pragma location = "ETH_RAM"
+static Xmc4500RxDmaDesc rxDmaDesc[XMC4500_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[XMC4500_ETH_TX_BUFFER_COUNT][XMC4500_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4), __section__("ETH_RAM")));
+//Receive buffer
+static uint8_t rxBuffer[XMC4500_ETH_RX_BUFFER_COUNT][XMC4500_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4), __section__("ETH_RAM")));
+//Transmit DMA descriptors
+static Xmc4500TxDmaDesc txDmaDesc[XMC4500_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__("ETH_RAM")));
+//Receive DMA descriptors
+static Xmc4500RxDmaDesc rxDmaDesc[XMC4500_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__("ETH_RAM")));
+
+#endif
+
+//Pointer to the current TX DMA descriptor
+static Xmc4500TxDmaDesc *txCurDmaDesc;
+//Pointer to the current RX DMA descriptor
+static Xmc4500RxDmaDesc *rxCurDmaDesc;
+
+
+/**
+ * @brief XMC4500 Ethernet MAC driver
+ **/
+
+const NicDriver xmc4500EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   xmc4500EthInit,
+   xmc4500EthTick,
+   xmc4500EthEnableIrq,
+   xmc4500EthDisableIrq,
+   xmc4500EthEventHandler,
+   xmc4500EthSendPacket,
+   xmc4500EthSetMulticastFilter,
+   xmc4500EthUpdateMacConfig,
+   xmc4500EthWritePhyReg,
+   xmc4500EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief XMC4500 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t xmc4500EthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing XMC4500 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Disable parity error trap
+   SCU_PARITY->PETE = 0;
+   //Disable unaligned access trap
+   PPB->CCR &= ~PPB_CCR_UNALIGN_TRP_Msk;
+
+   //Enable ETH0 peripheral clock
+   SCU_CLK->CLKSET = SCU_CLK_CLKSET_ETH0CEN_Msk;
+
+   //GPIO configuration
+   xmc4500EthInitGpio(interface);
+
+   //Reset ETH0 peripheral
+   SCU_RESET->PRSET2 = SCU_RESET_PRSET2_ETH0RS_Msk;
+   SCU_RESET->PRCLR2 = SCU_RESET_PRCLR2_ETH0RS_Msk;
+
+   //Reset DMA controller
+   ETH0->BUS_MODE |= ETH_BUS_MODE_SWR_Msk;
+   //Wait for the reset to complete
+   while(ETH0->BUS_MODE & ETH_BUS_MODE_SWR_Msk);
+
+   //Adjust MDC clock range depending on ETH clock frequency
+   ETH0->GMII_ADDRESS = ETH_GMII_ADDRESS_CR_DIV62;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Use default MAC configuration
+   ETH0->MAC_CONFIGURATION = ETH_MAC_CONFIGURATION_RESERVED15_Msk |
+      ETH_MAC_CONFIGURATION_DO_Msk;
+
+   //Set the MAC address
+   ETH0->MAC_ADDRESS0_LOW = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   ETH0->MAC_ADDRESS0_HIGH = interface->macAddr.w[2];
+
+   //Initialize hash table
+   ETH0->HASH_TABLE_LOW = 0;
+   ETH0->HASH_TABLE_HIGH = 0;
+
+   //Configure the receive filter
+   ETH0->MAC_FRAME_FILTER = ETH_MAC_FRAME_FILTER_HPF_Msk | ETH_MAC_FRAME_FILTER_HMC_Msk;
+   //Disable flow control
+   ETH0->FLOW_CONTROL = 0;
+   //Enable store and forward mode
+   ETH0->OPERATION_MODE = ETH_OPERATION_MODE_RSF_Msk | ETH_OPERATION_MODE_TSF_Msk;
+
+   //Configure DMA bus mode
+   ETH0->BUS_MODE = ETH_BUS_MODE_AAL_Msk | ETH_BUS_MODE_USP_Msk |
+      ETH_BUS_MODE_RPBL_1 | ETH_BUS_MODE_PR_1_1 | ETH_BUS_MODE_PBL_1;
+
+   //Initialize DMA descriptor lists
+   xmc4500EthInitDmaDesc(interface);
+
+   //Prevent interrupts from being generated when statistic counters reach
+   //half their maximum value
+   ETH0->MMC_TRANSMIT_INTERRUPT_MASK = 0xFFFFFFFF;
+   ETH0->MMC_RECEIVE_INTERRUPT_MASK = 0xFFFFFFFF;
+   ETH0->MMC_IPC_RECEIVE_INTERRUPT_MASK = 0xFFFFFFFF;
+
+   //Disable MAC interrupts
+   ETH0->INTERRUPT_MASK = ETH_INTERRUPT_MASK_TSIM_Msk | ETH_INTERRUPT_MASK_PMTIM_Msk;
+
+   //Enable the desired DMA interrupts
+   ETH0->INTERRUPT_ENABLE = ETH_INTERRUPT_ENABLE_NIE_Msk |
+      ETH_INTERRUPT_ENABLE_RIE_Msk | ETH_INTERRUPT_ENABLE_TIE_Msk;
+
+   //Set priority grouping (6 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(XMC4500_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure Ethernet interrupt priority
+   NVIC_SetPriority(ETH0_0_IRQn, NVIC_EncodePriority(XMC4500_ETH_IRQ_PRIORITY_GROUPING,
+      XMC4500_ETH_IRQ_GROUP_PRIORITY, XMC4500_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable MAC transmission and reception
+   ETH0->MAC_CONFIGURATION |= ETH_MAC_CONFIGURATION_TE_Msk | ETH_MAC_CONFIGURATION_RE_Msk;
+   //Enable DMA transmission and reception
+   ETH0->OPERATION_MODE |= ETH_OPERATION_MODE_ST_Msk | ETH_OPERATION_MODE_SR_Msk;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//XMC4500 Relax Kit?
+#if defined(USE_XMC4500_RELAX_KIT)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void xmc4500EthInitGpio(NetInterface *interface)
+{
+   uint32_t temp;
+
+   //Configure ETH0.MDIO (P2.0), ETH0.RXD0A (P2.2) and ETH0.RXD1A (P2.3)
+   temp = PORT2->IOCR0;
+   temp &= ~(PORT2_IOCR0_PC0_Msk | PORT2_IOCR0_PC2_Msk | PORT2_IOCR0_PC3_Msk);
+   temp |= (0UL << PORT2_IOCR0_PC0_Pos) | (0UL << PORT2_IOCR0_PC2_Pos) | (0UL << PORT2_IOCR0_PC3_Pos);
+   PORT2->IOCR0 = temp;
+
+   //Configure ETH0.RXERA (P2.4), ETH0.TX_EN (P2.5) and ETH0.MDC (P2.7)
+   temp = PORT2->IOCR4;
+   temp &= ~(PORT2_IOCR4_PC4_Msk | PORT2_IOCR4_PC5_Msk | PORT2_IOCR4_PC7_Msk);
+   temp |= (0UL << PORT2_IOCR4_PC4_Pos) | (17UL << PORT2_IOCR4_PC5_Pos) | (17UL << PORT2_IOCR4_PC7_Pos);
+   PORT2->IOCR4 = temp;
+
+   //Configure ETH0.TXD0 (P2.8) and ETH0.TXD1 (P2.9)
+   temp = PORT2->IOCR8;
+   temp &= ~(PORT2_IOCR8_PC8_Msk | PORT2_IOCR8_PC9_Msk);
+   temp |= (17UL << PORT2_IOCR8_PC8_Pos) | (17UL << PORT2_IOCR8_PC9_Pos);
+   PORT2->IOCR8 = temp;
+
+   //Configure ETH0.CLK_RMIIC (P15.8) and ETH0.CRS_DVC (P15.9)
+   temp = PORT15->IOCR8;
+   temp &= ~(PORT15_IOCR8_PC8_Msk | PORT15_IOCR8_PC9_Msk);
+   temp |= (0UL << PORT15_IOCR8_PC8_Pos) | (0UL << PORT15_IOCR8_PC9_Pos);
+   PORT15->IOCR8 = temp;
+
+   //Assign ETH_MDIO (P2.0) to HW0
+   temp = PORT2->HWSEL & ~PORT2_HWSEL_HW0_Msk;
+   PORT2->HWSEL = temp | (1UL << PORT2_HWSEL_HW0_Pos);
+
+   //Select output driver strength for ETH0.TX_EN (P2.5)
+   temp = PORT2->PDR0;
+   temp &= ~PORT2_PDR0_PD5_Msk;
+   temp |= (0UL << PORT2_PDR0_PD5_Pos);
+   PORT2->PDR0 = temp;
+
+   //Select output driver strength for ETH0.TXD0 (P2.8) and ETH0.TXD1 (P2.9)
+   temp = PORT2->PDR1;
+   temp &= ~(PORT2_PDR1_PD8_Msk | PORT2_PDR1_PD9_Msk);
+   temp |= (0UL << PORT2_PDR1_PD8_Pos) | (0UL << PORT2_PDR1_PD9_Pos);
+   PORT2->PDR1 = temp;
+
+   //Use ETH0.CLK_RMIIC (P15.8) and ETH0.CRS_DVC (P15.9) as digital inputs
+   PORT15->PDISC &= ~(PORT15_PDISC_PDIS8_Msk | PORT15_PDISC_PDIS9_Msk);
+
+   //Select RMII operation mode
+   ETH0_CON->CON = ETH_CON_INFSEL_Msk | ETH_CON_MDIO_B | ETH_CON_RXER_A |
+      ETH_CON_CRS_DV_C | ETH_CON_CLK_RMII_C | ETH_CON_RXD1_A | ETH_CON_RXD0_A;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void xmc4500EthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX DMA descriptor list
+   for(i = 0; i < XMC4500_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Use chain structure rather than ring structure
+      txDmaDesc[i].tdes0 = ETH_TDES0_IC | ETH_TDES0_TCH;
+      //Initialize transmit buffer size
+      txDmaDesc[i].tdes1 = 0;
+      //Transmit buffer address
+      txDmaDesc[i].tdes2 = (uint32_t) txBuffer[i];
+      //Next descriptor address
+      txDmaDesc[i].tdes3 = (uint32_t) &txDmaDesc[i + 1];
+   }
+
+   //The last descriptor is chained to the first entry
+   txDmaDesc[i - 1].tdes3 = (uint32_t) &txDmaDesc[0];
+   //Point to the very first descriptor
+   txCurDmaDesc = &txDmaDesc[0];
+
+   //Initialize RX DMA descriptor list
+   for(i = 0; i < XMC4500_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the DMA
+      rxDmaDesc[i].rdes0 = ETH_RDES0_OWN;
+      //Use chain structure rather than ring structure
+      rxDmaDesc[i].rdes1 = ETH_RDES1_RCH | (XMC4500_ETH_RX_BUFFER_SIZE & ETH_RDES1_RBS1);
+      //Receive buffer address
+      rxDmaDesc[i].rdes2 = (uint32_t) rxBuffer[i];
+      //Next descriptor address
+      rxDmaDesc[i].rdes3 = (uint32_t) &rxDmaDesc[i + 1];
+   }
+
+   //The last descriptor is chained to the first entry
+   rxDmaDesc[i - 1].rdes3 = (uint32_t) &rxDmaDesc[0];
+   //Point to the very first descriptor
+   rxCurDmaDesc = &rxDmaDesc[0];
+
+   //Start location of the TX descriptor list
+   ETH0->TRANSMIT_DESCRIPTOR_LIST_ADDRESS = (uint32_t) txDmaDesc;
+   //Start location of the RX descriptor list
+   ETH0->RECEIVE_DESCRIPTOR_LIST_ADDRESS = (uint32_t) rxDmaDesc;
+}
+
+
+/**
+ * @brief XMC4500 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void xmc4500EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void xmc4500EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(ETH0_0_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void xmc4500EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(ETH0_0_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief XMC4500 Ethernet MAC interrupt service routine
+ **/
+
+void ETH0_0_IRQHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read DMA status register
+   status = ETH0->STATUS;
+
+   //A packet has been transmitted?
+   if(status & ETH_STATUS_TI_Msk)
+   {
+      //Clear TI interrupt flag
+      ETH0->STATUS = ETH_STATUS_TI_Msk;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & ETH_STATUS_RI_Msk)
+   {
+      //Disable RIE interrupt
+      ETH0->INTERRUPT_ENABLE &= ~ETH_INTERRUPT_ENABLE_RIE_Msk;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Clear NIS interrupt flag
+   ETH0->STATUS = ETH_STATUS_NIS_Msk;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief XMC4500 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void xmc4500EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(ETH0->STATUS & ETH_STATUS_RI_Msk)
+   {
+      //Clear interrupt flag
+      ETH0->STATUS = ETH_STATUS_RI_Msk;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = xmc4500EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable DMA interrupts
+   ETH0->INTERRUPT_ENABLE |= ETH_INTERRUPT_ENABLE_NIE_Msk |
+      ETH_INTERRUPT_ENABLE_RIE_Msk | ETH_INTERRUPT_ENABLE_TIE_Msk;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t xmc4500EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > XMC4500_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurDmaDesc->tdes0 & ETH_TDES0_OWN)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txCurDmaDesc->tdes2, buffer, offset, length);
+
+   //Write the number of bytes to send
+   txCurDmaDesc->tdes1 = length & ETH_TDES1_TBS1;
+   //Set LS and FS flags as the data fits in a single buffer
+   txCurDmaDesc->tdes0 |= ETH_TDES0_LS | ETH_TDES0_FS;
+   //Give the ownership of the descriptor to the DMA
+   txCurDmaDesc->tdes0 |= ETH_TDES0_OWN;
+
+   //Clear TU flag to resume processing
+   ETH0->STATUS = ETH_STATUS_TU_Msk;
+   //Instruct the DMA to poll the transmit descriptor list
+   ETH0->TRANSMIT_POLL_DEMAND = 0;
+
+   //Point to the next descriptor in the list
+   txCurDmaDesc = (Xmc4500TxDmaDesc *) txCurDmaDesc->tdes3;
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t xmc4500EthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_OWN))
+   {
+      //FS and LS flags should be set
+      if((rxCurDmaDesc->rdes0 & ETH_RDES0_FS) && (rxCurDmaDesc->rdes0 & ETH_RDES0_LS))
+      {
+         //Make sure no error occurred
+         if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_ES))
+         {
+            //Retrieve the length of the frame
+            n = (rxCurDmaDesc->rdes0 & ETH_RDES0_FL) >> 16;
+            //Limit the number of data to read
+            n = MIN(n, XMC4500_ETH_RX_BUFFER_SIZE);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, (uint8_t *) rxCurDmaDesc->rdes2, n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Give the ownership of the descriptor back to the DMA
+      rxCurDmaDesc->rdes0 = ETH_RDES0_OWN;
+      //Point to the next descriptor in the list
+      rxCurDmaDesc = (Xmc4500RxDmaDesc *) rxCurDmaDesc->rdes3;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Clear RU flag to resume processing
+   ETH0->STATUS = ETH_STATUS_RU_Msk;
+   //Instruct the DMA to poll the receive descriptor list
+   ETH0->RECEIVE_POLL_DEMAND = 0;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t xmc4500EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating XMC4500 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = xmc4500EthCalcCrc(&entry->addr, sizeof(MacAddr));
+
+         //The upper 6 bits in the CRC register are used to index the
+         //contents of the hash table
+         k = (crc >> 26) & 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   ETH0->HASH_TABLE_LOW = hashTable[0];
+   ETH0->HASH_TABLE_HIGH = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HTL = %08" PRIX32 "\r\n", ETH0->HASH_TABLE_LOW);
+   TRACE_DEBUG("  HTH = %08" PRIX32 "\r\n", ETH0->HASH_TABLE_HIGH);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t xmc4500EthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read current MAC configuration
+   config = ETH0->MAC_CONFIGURATION;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= ETH_MAC_CONFIGURATION_FES_Msk;
+   else
+      config &= ~ETH_MAC_CONFIGURATION_FES_Msk;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= ETH_MAC_CONFIGURATION_DM_Msk;
+   else
+      config &= ~ETH_MAC_CONFIGURATION_DM_Msk;
+
+   //Update MAC configuration register
+   ETH0->MAC_CONFIGURATION = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void xmc4500EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = ETH0->GMII_ADDRESS & ETH_GMII_ADDRESS_CR_Msk;
+   //Set up a write operation
+   value |= ETH_GMII_ADDRESS_MW_Msk | ETH_GMII_ADDRESS_MB_Msk;
+   //PHY address
+   value |= (phyAddr << ETH_GMII_ADDRESS_PA_Pos) & ETH_GMII_ADDRESS_PA_Msk;
+   //Register address
+   value |= (regAddr << ETH_GMII_ADDRESS_MR_Pos) & ETH_GMII_ADDRESS_MR_Msk;
+
+   //Data to be written in the PHY register
+   ETH0->GMII_DATA = data & ETH_GMII_DATA_MD_Msk;
+
+   //Start a write operation
+   ETH0->GMII_ADDRESS = value;
+   //Wait for the write to complete
+   while(ETH0->GMII_ADDRESS & ETH_GMII_ADDRESS_MB_Msk);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t xmc4500EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = ETH0->GMII_ADDRESS & ETH_GMII_ADDRESS_CR_Msk;
+   //Set up a read operation
+   value |= ETH_GMII_ADDRESS_MB_Msk;
+   //PHY address
+   value |= (phyAddr << ETH_GMII_ADDRESS_PA_Pos) & ETH_GMII_ADDRESS_PA_Msk;
+   //Register address
+   value |= (regAddr << ETH_GMII_ADDRESS_MR_Pos) & ETH_GMII_ADDRESS_MR_Msk;
+
+   //Start a read operation
+   ETH0->GMII_ADDRESS = value;
+   //Wait for the read to complete
+   while(ETH0->GMII_ADDRESS & ETH_GMII_ADDRESS_MB_Msk);
+
+   //Return PHY register contents
+   return ETH0->GMII_DATA & ETH_GMII_DATA_MD_Msk;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t xmc4500EthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return ~crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/xmc4500_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,285 @@
+/**
+ * @file xmc4500_eth.h
+ * @brief Infineon XMC4500 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _XMC4500_ETH_H
+#define _XMC4500_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef XMC4500_ETH_TX_BUFFER_COUNT
+   #define XMC4500_ETH_TX_BUFFER_COUNT 3
+#elif (XMC4500_ETH_TX_BUFFER_COUNT < 1)
+   #error XMC4500_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef XMC4500_ETH_TX_BUFFER_SIZE
+   #define XMC4500_ETH_TX_BUFFER_SIZE 1536
+#elif (XMC4500_ETH_TX_BUFFER_SIZE != 1536)
+   #error XMC4500_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef XMC4500_ETH_RX_BUFFER_COUNT
+   #define XMC4500_ETH_RX_BUFFER_COUNT 6
+#elif (XMC4500_ETH_RX_BUFFER_COUNT < 1)
+   #error XMC4500_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef XMC4500_ETH_RX_BUFFER_SIZE
+   #define XMC4500_ETH_RX_BUFFER_SIZE 1536
+#elif (XMC4500_ETH_RX_BUFFER_SIZE != 1536)
+   #error XMC4500_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef XMC4500_ETH_IRQ_PRIORITY_GROUPING
+   #define XMC4500_ETH_IRQ_PRIORITY_GROUPING 1
+#elif (XMC4500_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error XMC4500_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef XMC4500_ETH_IRQ_GROUP_PRIORITY
+   #define XMC4500_ETH_IRQ_GROUP_PRIORITY 48
+#elif (XMC4500_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error XMC4500_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef XMC4500_ETH_IRQ_SUB_PRIORITY
+   #define XMC4500_ETH_IRQ_SUB_PRIORITY 0
+#elif (XMC4500_ETH_IRQ_SUB_PRIORITY < 0)
+   #error XMC4500_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//ETH0_CON
+#define ETH_CON_MDIO_A     (0 << ETH_CON_MDIO_Pos)
+#define ETH_CON_MDIO_B     (1 << ETH_CON_MDIO_Pos)
+#define ETH_CON_MDIO_C     (2 << ETH_CON_MDIO_Pos)
+#define ETH_CON_MDIO_D     (3 << ETH_CON_MDIO_Pos)
+
+#define ETH_CON_CLK_TX_A   (0 << ETH_CON_CLK_TX_Pos)
+#define ETH_CON_CLK_TX_B   (1 << ETH_CON_CLK_TX_Pos)
+#define ETH_CON_CLK_TX_C   (2 << ETH_CON_CLK_TX_Pos)
+#define ETH_CON_CLK_TX_D   (3 << ETH_CON_CLK_TX_Pos)
+
+#define ETH_CON_COL_A      (0 << ETH_CON_COL_Pos)
+#define ETH_CON_COL_B      (1 << ETH_CON_COL_Pos)
+#define ETH_CON_COL_C      (2 << ETH_CON_COL_Pos)
+#define ETH_CON_COL_D      (3 << ETH_CON_COL_Pos)
+
+#define ETH_CON_RXER_A     (0 << ETH_CON_RXER_Pos)
+#define ETH_CON_RXER_B     (1 << ETH_CON_RXER_Pos)
+#define ETH_CON_RXER_C     (2 << ETH_CON_RXER_Pos)
+#define ETH_CON_RXER_D     (3 << ETH_CON_RXER_Pos)
+
+#define ETH_CON_CRS_A      (0 << ETH_CON_CRS_Pos)
+#define ETH_CON_CRS_B      (1 << ETH_CON_CRS_Pos)
+#define ETH_CON_CRS_C      (2 << ETH_CON_CRS_Pos)
+#define ETH_CON_CRS_D      (3 << ETH_CON_CRS_Pos)
+
+#define ETH_CON_CRS_DV_A   (0 << ETH_CON_CRS_DV_Pos)
+#define ETH_CON_CRS_DV_B   (1 << ETH_CON_CRS_DV_Pos)
+#define ETH_CON_CRS_DV_C   (2 << ETH_CON_CRS_DV_Pos)
+#define ETH_CON_CRS_DV_D   (3 << ETH_CON_CRS_DV_Pos)
+
+#define ETH_CON_CLK_RMII_A (0 << ETH_CON_CLK_RMII_Pos)
+#define ETH_CON_CLK_RMII_B (1 << ETH_CON_CLK_RMII_Pos)
+#define ETH_CON_CLK_RMII_C (2 << ETH_CON_CLK_RMII_Pos)
+#define ETH_CON_CLK_RMII_D (3 << ETH_CON_CLK_RMII_Pos)
+
+#define ETH_CON_RXD3_A     (0 << ETH_CON_RXD3_Pos)
+#define ETH_CON_RXD3_B     (1 << ETH_CON_RXD3_Pos)
+#define ETH_CON_RXD3_C     (2 << ETH_CON_RXD3_Pos)
+#define ETH_CON_RXD3_D     (3 << ETH_CON_RXD3_Pos)
+
+#define ETH_CON_RXD2_A     (0 << ETH_CON_RXD2_Pos)
+#define ETH_CON_RXD2_B     (1 << ETH_CON_RXD2_Pos)
+#define ETH_CON_RXD2_C     (2 << ETH_CON_RXD2_Pos)
+#define ETH_CON_RXD2_D     (3 << ETH_CON_RXD2_Pos)
+
+#define ETH_CON_RXD1_A     (0 << ETH_CON_RXD1_Pos)
+#define ETH_CON_RXD1_B     (1 << ETH_CON_RXD1_Pos)
+#define ETH_CON_RXD1_C     (2 << ETH_CON_RXD1_Pos)
+#define ETH_CON_RXD1_D     (3 << ETH_CON_RXD1_Pos)
+
+#define ETH_CON_RXD0_A     (0 << ETH_CON_RXD0_Pos)
+#define ETH_CON_RXD0_B     (1 << ETH_CON_RXD0_Pos)
+#define ETH_CON_RXD0_C     (2 << ETH_CON_RXD0_Pos)
+#define ETH_CON_RXD0_D     (3 << ETH_CON_RXD0_Pos)
+
+//ETH0_MAC_CONFIGURATION register
+#define ETH_MAC_CONFIGURATION_RESERVED15_Msk (1 << 15)
+
+//ETH0_GMII_ADDRESS register
+#define ETH_GMII_ADDRESS_CR_DIV42  (0 << ETH_GMII_ADDRESS_CR_Pos)
+#define ETH_GMII_ADDRESS_CR_DIV62  (1 << ETH_GMII_ADDRESS_CR_Pos)
+#define ETH_GMII_ADDRESS_CR_DIV16  (2 << ETH_GMII_ADDRESS_CR_Pos)
+#define ETH_GMII_ADDRESS_CR_DIV26  (3 << ETH_GMII_ADDRESS_CR_Pos)
+#define ETH_GMII_ADDRESS_CR_DIV102 (4 << ETH_GMII_ADDRESS_CR_Pos)
+#define ETH_GMII_ADDRESS_CR_DIV124 (5 << ETH_GMII_ADDRESS_CR_Pos)
+
+//ETH0_BUS_MODE register
+#define ETH_BUS_MODE_RPBL_1  (1 << ETH_BUS_MODE_RPBL_Pos)
+#define ETH_BUS_MODE_RPBL_2  (2 << ETH_BUS_MODE_RPBL_Pos)
+#define ETH_BUS_MODE_RPBL_4  (4 << ETH_BUS_MODE_RPBL_Pos)
+#define ETH_BUS_MODE_RPBL_8  (8 << ETH_BUS_MODE_RPBL_Pos)
+#define ETH_BUS_MODE_RPBL_16 (16 << ETH_BUS_MODE_RPBL_Pos)
+#define ETH_BUS_MODE_RPBL_32 (32 << ETH_BUS_MODE_RPBL_Pos)
+
+#define ETH_BUS_MODE_PR_1_1  (0 << ETH_BUS_MODE_PR_Pos)
+#define ETH_BUS_MODE_PR_2_1  (1 << ETH_BUS_MODE_PR_Pos)
+#define ETH_BUS_MODE_PR_3_1  (2 << ETH_BUS_MODE_PR_Pos)
+#define ETH_BUS_MODE_PR_4_1  (3 << ETH_BUS_MODE_PR_Pos)
+
+#define ETH_BUS_MODE_PBL_1   (1 << ETH_BUS_MODE_PBL_Pos)
+#define ETH_BUS_MODE_PBL_2   (2 << ETH_BUS_MODE_PBL_Pos)
+#define ETH_BUS_MODE_PBL_4   (4 << ETH_BUS_MODE_PBL_Pos)
+#define ETH_BUS_MODE_PBL_8   (8 << ETH_BUS_MODE_PBL_Pos)
+#define ETH_BUS_MODE_PBL_16  (16 << ETH_BUS_MODE_PBL_Pos)
+#define ETH_BUS_MODE_PBL_32  (32 << ETH_BUS_MODE_PBL_Pos)
+
+//Transmit DMA descriptor flags
+#define ETH_TDES0_OWN     0x80000000
+#define ETH_TDES0_IC      0x40000000
+#define ETH_TDES0_LS      0x20000000
+#define ETH_TDES0_FS      0x10000000
+#define ETH_TDES0_DC      0x08000000
+#define ETH_TDES0_DP      0x04000000
+#define ETH_TDES0_TTSE    0x02000000
+#define ETH_TDES0_CIC     0x00C00000
+#define ETH_TDES0_TER     0x00200000
+#define ETH_TDES0_TCH     0x00100000
+#define ETH_TDES0_TTSS    0x00020000
+#define ETH_TDES0_IHE     0x00010000
+#define ETH_TDES0_ES      0x00008000
+#define ETH_TDES0_JT      0x00004000
+#define ETH_TDES0_FF      0x00002000
+#define ETH_TDES0_IPE     0x00001000
+#define ETH_TDES0_LCA     0x00000800
+#define ETH_TDES0_NC      0x00000400
+#define ETH_TDES0_LCO     0x00000200
+#define ETH_TDES0_EC      0x00000100
+#define ETH_TDES0_VF      0x00000080
+#define ETH_TDES0_CC      0x00000078
+#define ETH_TDES0_ED      0x00000004
+#define ETH_TDES0_UF      0x00000002
+#define ETH_TDES0_DB      0x00000001
+#define ETH_TDES1_TBS2    0x1FFF0000
+#define ETH_TDES1_TBS1    0x00001FFF
+#define ETH_TDES2_TBAP1   0xFFFFFFFF
+#define ETH_TDES3_TBAP2   0xFFFFFFFF
+
+//Receive DMA descriptor flags
+#define ETH_RDES0_OWN     0x80000000
+#define ETH_RDES0_AFM     0x40000000
+#define ETH_RDES0_FL      0x3FFF0000
+#define ETH_RDES0_ES      0x00008000
+#define ETH_RDES0_DE      0x00004000
+#define ETH_RDES0_SAF     0x00002000
+#define ETH_RDES0_LE      0x00001000
+#define ETH_RDES0_OE      0x00000800
+#define ETH_RDES0_VLAN    0x00000400
+#define ETH_RDES0_FS      0x00000200
+#define ETH_RDES0_LS      0x00000100
+#define ETH_RDES0_IPCE_GF 0x00000080
+#define ETH_RDES0_LCO     0x00000040
+#define ETH_RDES0_FT      0x00000020
+#define ETH_RDES0_RWT     0x00000010
+#define ETH_RDES0_RE      0x00000008
+#define ETH_RDES0_DBE     0x00000004
+#define ETH_RDES0_CE      0x00000002
+#define ETH_RDES0_PCE     0x00000001
+#define ETH_RDES1_DIC     0x80000000
+#define ETH_RDES1_RBS2    0x1FFF0000
+#define ETH_RDES1_RER     0x00008000
+#define ETH_RDES1_RCH     0x00004000
+#define ETH_RDES1_RBS1    0x00001FFF
+#define ETH_RDES2_RBAP1   0xFFFFFFFF
+#define ETH_RDES3_RBAP2   0xFFFFFFFF
+
+
+/**
+ * @brief Transmit DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t tdes0;
+   uint32_t tdes1;
+   uint32_t tdes2;
+   uint32_t tdes3;
+} Xmc4500TxDmaDesc;
+
+
+/**
+ * @brief Receive DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t rdes0;
+   uint32_t rdes1;
+   uint32_t rdes2;
+   uint32_t rdes3;
+} Xmc4500RxDmaDesc;
+
+
+//XMC4500 Ethernet MAC driver
+extern const NicDriver xmc4500EthDriver;
+
+//XMC4500 Ethernet MAC related functions
+error_t xmc4500EthInit(NetInterface *interface);
+void xmc4500EthInitGpio(NetInterface *interface);
+void xmc4500EthInitDmaDesc(NetInterface *interface);
+
+void xmc4500EthTick(NetInterface *interface);
+
+void xmc4500EthEnableIrq(NetInterface *interface);
+void xmc4500EthDisableIrq(NetInterface *interface);
+void xmc4500EthEventHandler(NetInterface *interface);
+
+error_t xmc4500EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t xmc4500EthReceivePacket(NetInterface *interface);
+
+error_t xmc4500EthSetMulticastFilter(NetInterface *interface);
+error_t xmc4500EthUpdateMacConfig(NetInterface *interface);
+
+void xmc4500EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t xmc4500EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t xmc4500EthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/xmc4700_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,762 @@
+/**
+ * @file xmc4700_eth.c
+ * @brief Infineon XMC4700 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "xmc4700.h"
+#include "core/net.h"
+#include "drivers/xmc4700_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+#pragma location = "ETH_RAM"
+static uint8_t txBuffer[XMC4700_ETH_TX_BUFFER_COUNT][XMC4700_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+#pragma location = "ETH_RAM"
+static uint8_t rxBuffer[XMC4700_ETH_RX_BUFFER_COUNT][XMC4700_ETH_RX_BUFFER_SIZE];
+//Transmit DMA descriptors
+#pragma data_alignment = 4
+#pragma location = "ETH_RAM"
+static Xmc4700TxDmaDesc txDmaDesc[XMC4700_ETH_TX_BUFFER_COUNT];
+//Receive DMA descriptors
+#pragma data_alignment = 4
+#pragma location = "ETH_RAM"
+static Xmc4700RxDmaDesc rxDmaDesc[XMC4700_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[XMC4700_ETH_TX_BUFFER_COUNT][XMC4700_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4), __section__("ETH_RAM")));
+//Receive buffer
+static uint8_t rxBuffer[XMC4700_ETH_RX_BUFFER_COUNT][XMC4700_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4), __section__("ETH_RAM")));
+//Transmit DMA descriptors
+static Xmc4700TxDmaDesc txDmaDesc[XMC4700_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__("ETH_RAM")));
+//Receive DMA descriptors
+static Xmc4700RxDmaDesc rxDmaDesc[XMC4700_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__("ETH_RAM")));
+
+#endif
+
+//Pointer to the current TX DMA descriptor
+static Xmc4700TxDmaDesc *txCurDmaDesc;
+//Pointer to the current RX DMA descriptor
+static Xmc4700RxDmaDesc *rxCurDmaDesc;
+
+
+/**
+ * @brief XMC4700 Ethernet MAC driver
+ **/
+
+const NicDriver xmc4700EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   xmc4700EthInit,
+   xmc4700EthTick,
+   xmc4700EthEnableIrq,
+   xmc4700EthDisableIrq,
+   xmc4700EthEventHandler,
+   xmc4700EthSendPacket,
+   xmc4700EthSetMulticastFilter,
+   xmc4700EthUpdateMacConfig,
+   xmc4700EthWritePhyReg,
+   xmc4700EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief XMC4700 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t xmc4700EthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing XMC4700 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Disable parity error trap
+   SCU_PARITY->PETE = 0;
+   //Disable unaligned access trap
+   PPB->CCR &= ~PPB_CCR_UNALIGN_TRP_Msk;
+
+   //Enable ETH0 peripheral clock
+   SCU_CLK->CLKSET = SCU_CLK_CLKSET_ETH0CEN_Msk;
+
+   //GPIO configuration
+   xmc4700EthInitGpio(interface);
+
+   //Reset ETH0 peripheral
+   SCU_RESET->PRSET2 = SCU_RESET_PRSET2_ETH0RS_Msk;
+   SCU_RESET->PRCLR2 = SCU_RESET_PRCLR2_ETH0RS_Msk;
+
+   //Reset DMA controller
+   ETH0->BUS_MODE |= ETH_BUS_MODE_SWR_Msk;
+   //Wait for the reset to complete
+   while(ETH0->BUS_MODE & ETH_BUS_MODE_SWR_Msk);
+
+   //Adjust MDC clock range depending on ETH clock frequency
+   ETH0->GMII_ADDRESS = ETH_GMII_ADDRESS_CR_DIV62;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Use default MAC configuration
+   ETH0->MAC_CONFIGURATION = ETH_MAC_CONFIGURATION_RESERVED15_Msk |
+      ETH_MAC_CONFIGURATION_DO_Msk;
+
+   //Set the MAC address
+   ETH0->MAC_ADDRESS0_LOW = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   ETH0->MAC_ADDRESS0_HIGH = interface->macAddr.w[2];
+
+   //Initialize hash table
+   ETH0->HASH_TABLE_LOW = 0;
+   ETH0->HASH_TABLE_HIGH = 0;
+
+   //Configure the receive filter
+   ETH0->MAC_FRAME_FILTER = ETH_MAC_FRAME_FILTER_HPF_Msk | ETH_MAC_FRAME_FILTER_HMC_Msk;
+   //Disable flow control
+   ETH0->FLOW_CONTROL = 0;
+   //Enable store and forward mode
+   ETH0->OPERATION_MODE = ETH_OPERATION_MODE_RSF_Msk | ETH_OPERATION_MODE_TSF_Msk;
+
+   //Configure DMA bus mode
+   ETH0->BUS_MODE = ETH_BUS_MODE_AAL_Msk | ETH_BUS_MODE_USP_Msk |
+      ETH_BUS_MODE_RPBL_1 | ETH_BUS_MODE_PR_1_1 | ETH_BUS_MODE_PBL_1;
+
+   //Initialize DMA descriptor lists
+   xmc4700EthInitDmaDesc(interface);
+
+   //Prevent interrupts from being generated when statistic counters reach
+   //half their maximum value
+   ETH0->MMC_TRANSMIT_INTERRUPT_MASK = 0xFFFFFFFF;
+   ETH0->MMC_RECEIVE_INTERRUPT_MASK = 0xFFFFFFFF;
+   ETH0->MMC_IPC_RECEIVE_INTERRUPT_MASK = 0xFFFFFFFF;
+
+   //Disable MAC interrupts
+   ETH0->INTERRUPT_MASK = ETH_INTERRUPT_MASK_TSIM_Msk | ETH_INTERRUPT_MASK_PMTIM_Msk;
+
+   //Enable the desired DMA interrupts
+   ETH0->INTERRUPT_ENABLE = ETH_INTERRUPT_ENABLE_NIE_Msk |
+      ETH_INTERRUPT_ENABLE_RIE_Msk | ETH_INTERRUPT_ENABLE_TIE_Msk;
+
+   //Set priority grouping (6 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(XMC4700_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure Ethernet interrupt priority
+   NVIC_SetPriority(ETH0_0_IRQn, NVIC_EncodePriority(XMC4700_ETH_IRQ_PRIORITY_GROUPING,
+      XMC4700_ETH_IRQ_GROUP_PRIORITY, XMC4700_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable MAC transmission and reception
+   ETH0->MAC_CONFIGURATION |= ETH_MAC_CONFIGURATION_TE_Msk | ETH_MAC_CONFIGURATION_RE_Msk;
+   //Enable DMA transmission and reception
+   ETH0->OPERATION_MODE |= ETH_OPERATION_MODE_ST_Msk | ETH_OPERATION_MODE_SR_Msk;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//XMC4700 Relax Kit?
+#if defined(USE_XMC4700_RELAX_KIT)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void xmc4700EthInitGpio(NetInterface *interface)
+{
+   uint32_t temp;
+
+   //Configure ETH0.MDIO (P2.0), ETH0.RXD0A (P2.2) and ETH0.RXD1A (P2.3)
+   temp = PORT2->IOCR0;
+   temp &= ~(PORT2_IOCR0_PC0_Msk | PORT2_IOCR0_PC2_Msk | PORT2_IOCR0_PC3_Msk);
+   temp |= (0UL << PORT2_IOCR0_PC0_Pos) | (0UL << PORT2_IOCR0_PC2_Pos) | (0UL << PORT2_IOCR0_PC3_Pos);
+   PORT2->IOCR0 = temp;
+
+   //Configure ETH0.RXERA (P2.4), ETH0.TX_EN (P2.5) and ETH0.MDC (P2.7)
+   temp = PORT2->IOCR4;
+   temp &= ~(PORT2_IOCR4_PC4_Msk | PORT2_IOCR4_PC5_Msk | PORT2_IOCR4_PC7_Msk);
+   temp |= (0UL << PORT2_IOCR4_PC4_Pos) | (17UL << PORT2_IOCR4_PC5_Pos) | (17UL << PORT2_IOCR4_PC7_Pos);
+   PORT2->IOCR4 = temp;
+
+   //Configure ETH0.TXD0 (P2.8) and ETH0.TXD1 (P2.9)
+   temp = PORT2->IOCR8;
+   temp &= ~(PORT2_IOCR8_PC8_Msk | PORT2_IOCR8_PC9_Msk);
+   temp |= (17UL << PORT2_IOCR8_PC8_Pos) | (17UL << PORT2_IOCR8_PC9_Pos);
+   PORT2->IOCR8 = temp;
+
+   //Configure ETH0.CLK_RMIIC (P15.8) and ETH0.CRS_DVC (P15.9)
+   temp = PORT15->IOCR8;
+   temp &= ~(PORT15_IOCR8_PC8_Msk | PORT15_IOCR8_PC9_Msk);
+   temp |= (0UL << PORT15_IOCR8_PC8_Pos) | (0UL << PORT15_IOCR8_PC9_Pos);
+   PORT15->IOCR8 = temp;
+
+   //Assign ETH_MDIO (P2.0) to HW0
+   temp = PORT2->HWSEL & ~PORT2_HWSEL_HW0_Msk;
+   PORT2->HWSEL = temp | (1UL << PORT2_HWSEL_HW0_Pos);
+
+   //Select output driver strength for ETH0.TX_EN (P2.5)
+   temp = PORT2->PDR0;
+   temp &= ~PORT2_PDR0_PD5_Msk;
+   temp |= (0UL << PORT2_PDR0_PD5_Pos);
+   PORT2->PDR0 = temp;
+
+   //Select output driver strength for ETH0.TXD0 (P2.8) and ETH0.TXD1 (P2.9)
+   temp = PORT2->PDR1;
+   temp &= ~(PORT2_PDR1_PD8_Msk | PORT2_PDR1_PD9_Msk);
+   temp |= (0UL << PORT2_PDR1_PD8_Pos) | (0UL << PORT2_PDR1_PD9_Pos);
+   PORT2->PDR1 = temp;
+
+   //Use ETH0.CLK_RMIIC (P15.8) and ETH0.CRS_DVC (P15.9) as digital inputs
+   PORT15->PDISC &= ~(PORT15_PDISC_PDIS8_Msk | PORT15_PDISC_PDIS9_Msk);
+
+   //Select RMII operation mode
+   ETH0_CON->CON = ETH_CON_INFSEL_Msk | ETH_CON_MDIO_B | ETH_CON_RXER_A |
+      ETH_CON_CRS_DV_C | ETH_CON_CLK_RMII_C | ETH_CON_RXD1_A | ETH_CON_RXD0_A;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void xmc4700EthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX DMA descriptor list
+   for(i = 0; i < XMC4700_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Use chain structure rather than ring structure
+      txDmaDesc[i].tdes0 = ETH_TDES0_IC | ETH_TDES0_TCH;
+      //Initialize transmit buffer size
+      txDmaDesc[i].tdes1 = 0;
+      //Transmit buffer address
+      txDmaDesc[i].tdes2 = (uint32_t) txBuffer[i];
+      //Next descriptor address
+      txDmaDesc[i].tdes3 = (uint32_t) &txDmaDesc[i + 1];
+   }
+
+   //The last descriptor is chained to the first entry
+   txDmaDesc[i - 1].tdes3 = (uint32_t) &txDmaDesc[0];
+   //Point to the very first descriptor
+   txCurDmaDesc = &txDmaDesc[0];
+
+   //Initialize RX DMA descriptor list
+   for(i = 0; i < XMC4700_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the DMA
+      rxDmaDesc[i].rdes0 = ETH_RDES0_OWN;
+      //Use chain structure rather than ring structure
+      rxDmaDesc[i].rdes1 = ETH_RDES1_RCH | (XMC4700_ETH_RX_BUFFER_SIZE & ETH_RDES1_RBS1);
+      //Receive buffer address
+      rxDmaDesc[i].rdes2 = (uint32_t) rxBuffer[i];
+      //Next descriptor address
+      rxDmaDesc[i].rdes3 = (uint32_t) &rxDmaDesc[i + 1];
+   }
+
+   //The last descriptor is chained to the first entry
+   rxDmaDesc[i - 1].rdes3 = (uint32_t) &rxDmaDesc[0];
+   //Point to the very first descriptor
+   rxCurDmaDesc = &rxDmaDesc[0];
+
+   //Start location of the TX descriptor list
+   ETH0->TRANSMIT_DESCRIPTOR_LIST_ADDRESS = (uint32_t) txDmaDesc;
+   //Start location of the RX descriptor list
+   ETH0->RECEIVE_DESCRIPTOR_LIST_ADDRESS = (uint32_t) rxDmaDesc;
+}
+
+
+/**
+ * @brief XMC4700 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void xmc4700EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void xmc4700EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(ETH0_0_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void xmc4700EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(ETH0_0_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief XMC4700 Ethernet MAC interrupt service routine
+ **/
+
+void ETH0_0_IRQHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read DMA status register
+   status = ETH0->STATUS;
+
+   //A packet has been transmitted?
+   if(status & ETH_STATUS_TI_Msk)
+   {
+      //Clear TI interrupt flag
+      ETH0->STATUS = ETH_STATUS_TI_Msk;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & ETH_STATUS_RI_Msk)
+   {
+      //Disable RIE interrupt
+      ETH0->INTERRUPT_ENABLE &= ~ETH_INTERRUPT_ENABLE_RIE_Msk;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Clear NIS interrupt flag
+   ETH0->STATUS = ETH_STATUS_NIS_Msk;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief XMC4700 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void xmc4700EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(ETH0->STATUS & ETH_STATUS_RI_Msk)
+   {
+      //Clear interrupt flag
+      ETH0->STATUS = ETH_STATUS_RI_Msk;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = xmc4700EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable DMA interrupts
+   ETH0->INTERRUPT_ENABLE |= ETH_INTERRUPT_ENABLE_NIE_Msk |
+      ETH_INTERRUPT_ENABLE_RIE_Msk | ETH_INTERRUPT_ENABLE_TIE_Msk;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t xmc4700EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > XMC4700_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurDmaDesc->tdes0 & ETH_TDES0_OWN)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txCurDmaDesc->tdes2, buffer, offset, length);
+
+   //Write the number of bytes to send
+   txCurDmaDesc->tdes1 = length & ETH_TDES1_TBS1;
+   //Set LS and FS flags as the data fits in a single buffer
+   txCurDmaDesc->tdes0 |= ETH_TDES0_LS | ETH_TDES0_FS;
+   //Give the ownership of the descriptor to the DMA
+   txCurDmaDesc->tdes0 |= ETH_TDES0_OWN;
+
+   //Clear TU flag to resume processing
+   ETH0->STATUS = ETH_STATUS_TU_Msk;
+   //Instruct the DMA to poll the transmit descriptor list
+   ETH0->TRANSMIT_POLL_DEMAND = 0;
+
+   //Point to the next descriptor in the list
+   txCurDmaDesc = (Xmc4700TxDmaDesc *) txCurDmaDesc->tdes3;
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t xmc4700EthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_OWN))
+   {
+      //FS and LS flags should be set
+      if((rxCurDmaDesc->rdes0 & ETH_RDES0_FS) && (rxCurDmaDesc->rdes0 & ETH_RDES0_LS))
+      {
+         //Make sure no error occurred
+         if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_ES))
+         {
+            //Retrieve the length of the frame
+            n = (rxCurDmaDesc->rdes0 & ETH_RDES0_FL) >> 16;
+            //Limit the number of data to read
+            n = MIN(n, XMC4700_ETH_RX_BUFFER_SIZE);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, (uint8_t *) rxCurDmaDesc->rdes2, n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Give the ownership of the descriptor back to the DMA
+      rxCurDmaDesc->rdes0 = ETH_RDES0_OWN;
+      //Point to the next descriptor in the list
+      rxCurDmaDesc = (Xmc4700RxDmaDesc *) rxCurDmaDesc->rdes3;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Clear RU flag to resume processing
+   ETH0->STATUS = ETH_STATUS_RU_Msk;
+   //Instruct the DMA to poll the receive descriptor list
+   ETH0->RECEIVE_POLL_DEMAND = 0;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t xmc4700EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating XMC4700 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = xmc4700EthCalcCrc(&entry->addr, sizeof(MacAddr));
+
+         //The upper 6 bits in the CRC register are used to index the
+         //contents of the hash table
+         k = (crc >> 26) & 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   ETH0->HASH_TABLE_LOW = hashTable[0];
+   ETH0->HASH_TABLE_HIGH = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HTL = %08" PRIX32 "\r\n", ETH0->HASH_TABLE_LOW);
+   TRACE_DEBUG("  HTH = %08" PRIX32 "\r\n", ETH0->HASH_TABLE_HIGH);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t xmc4700EthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read current MAC configuration
+   config = ETH0->MAC_CONFIGURATION;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= ETH_MAC_CONFIGURATION_FES_Msk;
+   else
+      config &= ~ETH_MAC_CONFIGURATION_FES_Msk;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= ETH_MAC_CONFIGURATION_DM_Msk;
+   else
+      config &= ~ETH_MAC_CONFIGURATION_DM_Msk;
+
+   //Update MAC configuration register
+   ETH0->MAC_CONFIGURATION = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void xmc4700EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = ETH0->GMII_ADDRESS & ETH_GMII_ADDRESS_CR_Msk;
+   //Set up a write operation
+   value |= ETH_GMII_ADDRESS_MW_Msk | ETH_GMII_ADDRESS_MB_Msk;
+   //PHY address
+   value |= (phyAddr << ETH_GMII_ADDRESS_PA_Pos) & ETH_GMII_ADDRESS_PA_Msk;
+   //Register address
+   value |= (regAddr << ETH_GMII_ADDRESS_MR_Pos) & ETH_GMII_ADDRESS_MR_Msk;
+
+   //Data to be written in the PHY register
+   ETH0->GMII_DATA = data & ETH_GMII_DATA_MD_Msk;
+
+   //Start a write operation
+   ETH0->GMII_ADDRESS = value;
+   //Wait for the write to complete
+   while(ETH0->GMII_ADDRESS & ETH_GMII_ADDRESS_MB_Msk);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t xmc4700EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = ETH0->GMII_ADDRESS & ETH_GMII_ADDRESS_CR_Msk;
+   //Set up a read operation
+   value |= ETH_GMII_ADDRESS_MB_Msk;
+   //PHY address
+   value |= (phyAddr << ETH_GMII_ADDRESS_PA_Pos) & ETH_GMII_ADDRESS_PA_Msk;
+   //Register address
+   value |= (regAddr << ETH_GMII_ADDRESS_MR_Pos) & ETH_GMII_ADDRESS_MR_Msk;
+
+   //Start a read operation
+   ETH0->GMII_ADDRESS = value;
+   //Wait for the read to complete
+   while(ETH0->GMII_ADDRESS & ETH_GMII_ADDRESS_MB_Msk);
+
+   //Return PHY register contents
+   return ETH0->GMII_DATA & ETH_GMII_DATA_MD_Msk;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t xmc4700EthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return ~crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/xmc4700_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,285 @@
+/**
+ * @file xmc4700_eth.h
+ * @brief Infineon XMC4700 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _XMC4700_ETH_H
+#define _XMC4700_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef XMC4700_ETH_TX_BUFFER_COUNT
+   #define XMC4700_ETH_TX_BUFFER_COUNT 3
+#elif (XMC4700_ETH_TX_BUFFER_COUNT < 1)
+   #error XMC4700_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef XMC4700_ETH_TX_BUFFER_SIZE
+   #define XMC4700_ETH_TX_BUFFER_SIZE 1536
+#elif (XMC4700_ETH_TX_BUFFER_SIZE != 1536)
+   #error XMC4700_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef XMC4700_ETH_RX_BUFFER_COUNT
+   #define XMC4700_ETH_RX_BUFFER_COUNT 6
+#elif (XMC4700_ETH_RX_BUFFER_COUNT < 1)
+   #error XMC4700_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef XMC4700_ETH_RX_BUFFER_SIZE
+   #define XMC4700_ETH_RX_BUFFER_SIZE 1536
+#elif (XMC4700_ETH_RX_BUFFER_SIZE != 1536)
+   #error XMC4700_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef XMC4700_ETH_IRQ_PRIORITY_GROUPING
+   #define XMC4700_ETH_IRQ_PRIORITY_GROUPING 1
+#elif (XMC4700_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error XMC4700_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef XMC4700_ETH_IRQ_GROUP_PRIORITY
+   #define XMC4700_ETH_IRQ_GROUP_PRIORITY 48
+#elif (XMC4700_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error XMC4700_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef XMC4700_ETH_IRQ_SUB_PRIORITY
+   #define XMC4700_ETH_IRQ_SUB_PRIORITY 0
+#elif (XMC4700_ETH_IRQ_SUB_PRIORITY < 0)
+   #error XMC4700_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//ETH0_CON
+#define ETH_CON_MDIO_A     (0 << ETH_CON_MDIO_Pos)
+#define ETH_CON_MDIO_B     (1 << ETH_CON_MDIO_Pos)
+#define ETH_CON_MDIO_C     (2 << ETH_CON_MDIO_Pos)
+#define ETH_CON_MDIO_D     (3 << ETH_CON_MDIO_Pos)
+
+#define ETH_CON_CLK_TX_A   (0 << ETH_CON_CLK_TX_Pos)
+#define ETH_CON_CLK_TX_B   (1 << ETH_CON_CLK_TX_Pos)
+#define ETH_CON_CLK_TX_C   (2 << ETH_CON_CLK_TX_Pos)
+#define ETH_CON_CLK_TX_D   (3 << ETH_CON_CLK_TX_Pos)
+
+#define ETH_CON_COL_A      (0 << ETH_CON_COL_Pos)
+#define ETH_CON_COL_B      (1 << ETH_CON_COL_Pos)
+#define ETH_CON_COL_C      (2 << ETH_CON_COL_Pos)
+#define ETH_CON_COL_D      (3 << ETH_CON_COL_Pos)
+
+#define ETH_CON_RXER_A     (0 << ETH_CON_RXER_Pos)
+#define ETH_CON_RXER_B     (1 << ETH_CON_RXER_Pos)
+#define ETH_CON_RXER_C     (2 << ETH_CON_RXER_Pos)
+#define ETH_CON_RXER_D     (3 << ETH_CON_RXER_Pos)
+
+#define ETH_CON_CRS_A      (0 << ETH_CON_CRS_Pos)
+#define ETH_CON_CRS_B      (1 << ETH_CON_CRS_Pos)
+#define ETH_CON_CRS_C      (2 << ETH_CON_CRS_Pos)
+#define ETH_CON_CRS_D      (3 << ETH_CON_CRS_Pos)
+
+#define ETH_CON_CRS_DV_A   (0 << ETH_CON_CRS_DV_Pos)
+#define ETH_CON_CRS_DV_B   (1 << ETH_CON_CRS_DV_Pos)
+#define ETH_CON_CRS_DV_C   (2 << ETH_CON_CRS_DV_Pos)
+#define ETH_CON_CRS_DV_D   (3 << ETH_CON_CRS_DV_Pos)
+
+#define ETH_CON_CLK_RMII_A (0 << ETH_CON_CLK_RMII_Pos)
+#define ETH_CON_CLK_RMII_B (1 << ETH_CON_CLK_RMII_Pos)
+#define ETH_CON_CLK_RMII_C (2 << ETH_CON_CLK_RMII_Pos)
+#define ETH_CON_CLK_RMII_D (3 << ETH_CON_CLK_RMII_Pos)
+
+#define ETH_CON_RXD3_A     (0 << ETH_CON_RXD3_Pos)
+#define ETH_CON_RXD3_B     (1 << ETH_CON_RXD3_Pos)
+#define ETH_CON_RXD3_C     (2 << ETH_CON_RXD3_Pos)
+#define ETH_CON_RXD3_D     (3 << ETH_CON_RXD3_Pos)
+
+#define ETH_CON_RXD2_A     (0 << ETH_CON_RXD2_Pos)
+#define ETH_CON_RXD2_B     (1 << ETH_CON_RXD2_Pos)
+#define ETH_CON_RXD2_C     (2 << ETH_CON_RXD2_Pos)
+#define ETH_CON_RXD2_D     (3 << ETH_CON_RXD2_Pos)
+
+#define ETH_CON_RXD1_A     (0 << ETH_CON_RXD1_Pos)
+#define ETH_CON_RXD1_B     (1 << ETH_CON_RXD1_Pos)
+#define ETH_CON_RXD1_C     (2 << ETH_CON_RXD1_Pos)
+#define ETH_CON_RXD1_D     (3 << ETH_CON_RXD1_Pos)
+
+#define ETH_CON_RXD0_A     (0 << ETH_CON_RXD0_Pos)
+#define ETH_CON_RXD0_B     (1 << ETH_CON_RXD0_Pos)
+#define ETH_CON_RXD0_C     (2 << ETH_CON_RXD0_Pos)
+#define ETH_CON_RXD0_D     (3 << ETH_CON_RXD0_Pos)
+
+//ETH0_MAC_CONFIGURATION register
+#define ETH_MAC_CONFIGURATION_RESERVED15_Msk (1 << 15)
+
+//ETH0_GMII_ADDRESS register
+#define ETH_GMII_ADDRESS_CR_DIV42  (0 << ETH_GMII_ADDRESS_CR_Pos)
+#define ETH_GMII_ADDRESS_CR_DIV62  (1 << ETH_GMII_ADDRESS_CR_Pos)
+#define ETH_GMII_ADDRESS_CR_DIV16  (2 << ETH_GMII_ADDRESS_CR_Pos)
+#define ETH_GMII_ADDRESS_CR_DIV26  (3 << ETH_GMII_ADDRESS_CR_Pos)
+#define ETH_GMII_ADDRESS_CR_DIV102 (4 << ETH_GMII_ADDRESS_CR_Pos)
+#define ETH_GMII_ADDRESS_CR_DIV124 (5 << ETH_GMII_ADDRESS_CR_Pos)
+
+//ETH0_BUS_MODE register
+#define ETH_BUS_MODE_RPBL_1  (1 << ETH_BUS_MODE_RPBL_Pos)
+#define ETH_BUS_MODE_RPBL_2  (2 << ETH_BUS_MODE_RPBL_Pos)
+#define ETH_BUS_MODE_RPBL_4  (4 << ETH_BUS_MODE_RPBL_Pos)
+#define ETH_BUS_MODE_RPBL_8  (8 << ETH_BUS_MODE_RPBL_Pos)
+#define ETH_BUS_MODE_RPBL_16 (16 << ETH_BUS_MODE_RPBL_Pos)
+#define ETH_BUS_MODE_RPBL_32 (32 << ETH_BUS_MODE_RPBL_Pos)
+
+#define ETH_BUS_MODE_PR_1_1  (0 << ETH_BUS_MODE_PR_Pos)
+#define ETH_BUS_MODE_PR_2_1  (1 << ETH_BUS_MODE_PR_Pos)
+#define ETH_BUS_MODE_PR_3_1  (2 << ETH_BUS_MODE_PR_Pos)
+#define ETH_BUS_MODE_PR_4_1  (3 << ETH_BUS_MODE_PR_Pos)
+
+#define ETH_BUS_MODE_PBL_1   (1 << ETH_BUS_MODE_PBL_Pos)
+#define ETH_BUS_MODE_PBL_2   (2 << ETH_BUS_MODE_PBL_Pos)
+#define ETH_BUS_MODE_PBL_4   (4 << ETH_BUS_MODE_PBL_Pos)
+#define ETH_BUS_MODE_PBL_8   (8 << ETH_BUS_MODE_PBL_Pos)
+#define ETH_BUS_MODE_PBL_16  (16 << ETH_BUS_MODE_PBL_Pos)
+#define ETH_BUS_MODE_PBL_32  (32 << ETH_BUS_MODE_PBL_Pos)
+
+//Transmit DMA descriptor flags
+#define ETH_TDES0_OWN     0x80000000
+#define ETH_TDES0_IC      0x40000000
+#define ETH_TDES0_LS      0x20000000
+#define ETH_TDES0_FS      0x10000000
+#define ETH_TDES0_DC      0x08000000
+#define ETH_TDES0_DP      0x04000000
+#define ETH_TDES0_TTSE    0x02000000
+#define ETH_TDES0_CIC     0x00C00000
+#define ETH_TDES0_TER     0x00200000
+#define ETH_TDES0_TCH     0x00100000
+#define ETH_TDES0_TTSS    0x00020000
+#define ETH_TDES0_IHE     0x00010000
+#define ETH_TDES0_ES      0x00008000
+#define ETH_TDES0_JT      0x00004000
+#define ETH_TDES0_FF      0x00002000
+#define ETH_TDES0_IPE     0x00001000
+#define ETH_TDES0_LCA     0x00000800
+#define ETH_TDES0_NC      0x00000400
+#define ETH_TDES0_LCO     0x00000200
+#define ETH_TDES0_EC      0x00000100
+#define ETH_TDES0_VF      0x00000080
+#define ETH_TDES0_CC      0x00000078
+#define ETH_TDES0_ED      0x00000004
+#define ETH_TDES0_UF      0x00000002
+#define ETH_TDES0_DB      0x00000001
+#define ETH_TDES1_TBS2    0x1FFF0000
+#define ETH_TDES1_TBS1    0x00001FFF
+#define ETH_TDES2_TBAP1   0xFFFFFFFF
+#define ETH_TDES3_TBAP2   0xFFFFFFFF
+
+//Receive DMA descriptor flags
+#define ETH_RDES0_OWN     0x80000000
+#define ETH_RDES0_AFM     0x40000000
+#define ETH_RDES0_FL      0x3FFF0000
+#define ETH_RDES0_ES      0x00008000
+#define ETH_RDES0_DE      0x00004000
+#define ETH_RDES0_SAF     0x00002000
+#define ETH_RDES0_LE      0x00001000
+#define ETH_RDES0_OE      0x00000800
+#define ETH_RDES0_VLAN    0x00000400
+#define ETH_RDES0_FS      0x00000200
+#define ETH_RDES0_LS      0x00000100
+#define ETH_RDES0_IPCE_GF 0x00000080
+#define ETH_RDES0_LCO     0x00000040
+#define ETH_RDES0_FT      0x00000020
+#define ETH_RDES0_RWT     0x00000010
+#define ETH_RDES0_RE      0x00000008
+#define ETH_RDES0_DBE     0x00000004
+#define ETH_RDES0_CE      0x00000002
+#define ETH_RDES0_PCE     0x00000001
+#define ETH_RDES1_DIC     0x80000000
+#define ETH_RDES1_RBS2    0x1FFF0000
+#define ETH_RDES1_RER     0x00008000
+#define ETH_RDES1_RCH     0x00004000
+#define ETH_RDES1_RBS1    0x00001FFF
+#define ETH_RDES2_RBAP1   0xFFFFFFFF
+#define ETH_RDES3_RBAP2   0xFFFFFFFF
+
+
+/**
+ * @brief Transmit DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t tdes0;
+   uint32_t tdes1;
+   uint32_t tdes2;
+   uint32_t tdes3;
+} Xmc4700TxDmaDesc;
+
+
+/**
+ * @brief Receive DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t rdes0;
+   uint32_t rdes1;
+   uint32_t rdes2;
+   uint32_t rdes3;
+} Xmc4700RxDmaDesc;
+
+
+//XMC4700 Ethernet MAC driver
+extern const NicDriver xmc4700EthDriver;
+
+//XMC4700 Ethernet MAC related functions
+error_t xmc4700EthInit(NetInterface *interface);
+void xmc4700EthInitGpio(NetInterface *interface);
+void xmc4700EthInitDmaDesc(NetInterface *interface);
+
+void xmc4700EthTick(NetInterface *interface);
+
+void xmc4700EthEnableIrq(NetInterface *interface);
+void xmc4700EthDisableIrq(NetInterface *interface);
+void xmc4700EthEventHandler(NetInterface *interface);
+
+error_t xmc4700EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t xmc4700EthReceivePacket(NetInterface *interface);
+
+error_t xmc4700EthSetMulticastFilter(NetInterface *interface);
+error_t xmc4700EthUpdateMacConfig(NetInterface *interface);
+
+void xmc4700EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t xmc4700EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t xmc4700EthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/xmc4800_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,762 @@
+/**
+ * @file xmc4800_eth.c
+ * @brief Infineon XMC4800 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include "xmc4800.h"
+#include "core/net.h"
+#include "drivers/xmc4800_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//Transmit buffer
+#pragma data_alignment = 4
+#pragma location = "ETH_RAM"
+static uint8_t txBuffer[XMC4800_ETH_TX_BUFFER_COUNT][XMC4800_ETH_TX_BUFFER_SIZE];
+//Receive buffer
+#pragma data_alignment = 4
+#pragma location = "ETH_RAM"
+static uint8_t rxBuffer[XMC4800_ETH_RX_BUFFER_COUNT][XMC4800_ETH_RX_BUFFER_SIZE];
+//Transmit DMA descriptors
+#pragma data_alignment = 4
+#pragma location = "ETH_RAM"
+static Xmc4800TxDmaDesc txDmaDesc[XMC4800_ETH_TX_BUFFER_COUNT];
+//Receive DMA descriptors
+#pragma data_alignment = 4
+#pragma location = "ETH_RAM"
+static Xmc4800RxDmaDesc rxDmaDesc[XMC4800_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//Transmit buffer
+static uint8_t txBuffer[XMC4800_ETH_TX_BUFFER_COUNT][XMC4800_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(4), __section__("ETH_RAM")));
+//Receive buffer
+static uint8_t rxBuffer[XMC4800_ETH_RX_BUFFER_COUNT][XMC4800_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(4), __section__("ETH_RAM")));
+//Transmit DMA descriptors
+static Xmc4800TxDmaDesc txDmaDesc[XMC4800_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__("ETH_RAM")));
+//Receive DMA descriptors
+static Xmc4800RxDmaDesc rxDmaDesc[XMC4800_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__("ETH_RAM")));
+
+#endif
+
+//Pointer to the current TX DMA descriptor
+static Xmc4800TxDmaDesc *txCurDmaDesc;
+//Pointer to the current RX DMA descriptor
+static Xmc4800RxDmaDesc *rxCurDmaDesc;
+
+
+/**
+ * @brief XMC4800 Ethernet MAC driver
+ **/
+
+const NicDriver xmc4800EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   xmc4800EthInit,
+   xmc4800EthTick,
+   xmc4800EthEnableIrq,
+   xmc4800EthDisableIrq,
+   xmc4800EthEventHandler,
+   xmc4800EthSendPacket,
+   xmc4800EthSetMulticastFilter,
+   xmc4800EthUpdateMacConfig,
+   xmc4800EthWritePhyReg,
+   xmc4800EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief XMC4800 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t xmc4800EthInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing XMC4800 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Disable parity error trap
+   SCU_PARITY->PETE = 0;
+   //Disable unaligned access trap
+   PPB->CCR &= ~PPB_CCR_UNALIGN_TRP_Msk;
+
+   //Enable ETH0 peripheral clock
+   SCU_CLK->CLKSET = SCU_CLK_CLKSET_ETH0CEN_Msk;
+
+   //GPIO configuration
+   xmc4800EthInitGpio(interface);
+
+   //Reset ETH0 peripheral
+   SCU_RESET->PRSET2 = SCU_RESET_PRSET2_ETH0RS_Msk;
+   SCU_RESET->PRCLR2 = SCU_RESET_PRCLR2_ETH0RS_Msk;
+
+   //Reset DMA controller
+   ETH0->BUS_MODE |= ETH_BUS_MODE_SWR_Msk;
+   //Wait for the reset to complete
+   while(ETH0->BUS_MODE & ETH_BUS_MODE_SWR_Msk);
+
+   //Adjust MDC clock range depending on ETH clock frequency
+   ETH0->GMII_ADDRESS = ETH_GMII_ADDRESS_CR_DIV62;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Use default MAC configuration
+   ETH0->MAC_CONFIGURATION = ETH_MAC_CONFIGURATION_RESERVED15_Msk |
+      ETH_MAC_CONFIGURATION_DO_Msk;
+
+   //Set the MAC address
+   ETH0->MAC_ADDRESS0_LOW = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   ETH0->MAC_ADDRESS0_HIGH = interface->macAddr.w[2];
+
+   //Initialize hash table
+   ETH0->HASH_TABLE_LOW = 0;
+   ETH0->HASH_TABLE_HIGH = 0;
+
+   //Configure the receive filter
+   ETH0->MAC_FRAME_FILTER = ETH_MAC_FRAME_FILTER_HPF_Msk | ETH_MAC_FRAME_FILTER_HMC_Msk;
+   //Disable flow control
+   ETH0->FLOW_CONTROL = 0;
+   //Enable store and forward mode
+   ETH0->OPERATION_MODE = ETH_OPERATION_MODE_RSF_Msk | ETH_OPERATION_MODE_TSF_Msk;
+
+   //Configure DMA bus mode
+   ETH0->BUS_MODE = ETH_BUS_MODE_AAL_Msk | ETH_BUS_MODE_USP_Msk |
+      ETH_BUS_MODE_RPBL_1 | ETH_BUS_MODE_PR_1_1 | ETH_BUS_MODE_PBL_1;
+
+   //Initialize DMA descriptor lists
+   xmc4800EthInitDmaDesc(interface);
+
+   //Prevent interrupts from being generated when statistic counters reach
+   //half their maximum value
+   ETH0->MMC_TRANSMIT_INTERRUPT_MASK = 0xFFFFFFFF;
+   ETH0->MMC_RECEIVE_INTERRUPT_MASK = 0xFFFFFFFF;
+   ETH0->MMC_IPC_RECEIVE_INTERRUPT_MASK = 0xFFFFFFFF;
+
+   //Disable MAC interrupts
+   ETH0->INTERRUPT_MASK = ETH_INTERRUPT_MASK_TSIM_Msk | ETH_INTERRUPT_MASK_PMTIM_Msk;
+
+   //Enable the desired DMA interrupts
+   ETH0->INTERRUPT_ENABLE = ETH_INTERRUPT_ENABLE_NIE_Msk |
+      ETH_INTERRUPT_ENABLE_RIE_Msk | ETH_INTERRUPT_ENABLE_TIE_Msk;
+
+   //Set priority grouping (6 bits for pre-emption priority, no bits for subpriority)
+   NVIC_SetPriorityGrouping(XMC4800_ETH_IRQ_PRIORITY_GROUPING);
+
+   //Configure Ethernet interrupt priority
+   NVIC_SetPriority(ETH0_0_IRQn, NVIC_EncodePriority(XMC4800_ETH_IRQ_PRIORITY_GROUPING,
+      XMC4800_ETH_IRQ_GROUP_PRIORITY, XMC4800_ETH_IRQ_SUB_PRIORITY));
+
+   //Enable MAC transmission and reception
+   ETH0->MAC_CONFIGURATION |= ETH_MAC_CONFIGURATION_TE_Msk | ETH_MAC_CONFIGURATION_RE_Msk;
+   //Enable DMA transmission and reception
+   ETH0->OPERATION_MODE |= ETH_OPERATION_MODE_ST_Msk | ETH_OPERATION_MODE_SR_Msk;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+//XMC4800 Relax EtherCAT Kit?
+#if defined(USE_XMC4800_RELAX_ECAT_KIT)
+
+/**
+ * @brief GPIO configuration
+ * @param[in] interface Underlying network interface
+ **/
+
+void xmc4800EthInitGpio(NetInterface *interface)
+{
+   uint32_t temp;
+
+   //Configure ETH0.MDIO (P2.0), ETH0.RXD0A (P2.2) and ETH0.RXD1A (P2.3)
+   temp = PORT2->IOCR0;
+   temp &= ~(PORT2_IOCR0_PC0_Msk | PORT2_IOCR0_PC2_Msk | PORT2_IOCR0_PC3_Msk);
+   temp |= (0UL << PORT2_IOCR0_PC0_Pos) | (0UL << PORT2_IOCR0_PC2_Pos) | (0UL << PORT2_IOCR0_PC3_Pos);
+   PORT2->IOCR0 = temp;
+
+   //Configure ETH0.RXERA (P2.4), ETH0.TX_EN (P2.5) and ETH0.MDC (P2.7)
+   temp = PORT2->IOCR4;
+   temp &= ~(PORT2_IOCR4_PC4_Msk | PORT2_IOCR4_PC5_Msk | PORT2_IOCR4_PC7_Msk);
+   temp |= (0UL << PORT2_IOCR4_PC4_Pos) | (17UL << PORT2_IOCR4_PC5_Pos) | (17UL << PORT2_IOCR4_PC7_Pos);
+   PORT2->IOCR4 = temp;
+
+   //Configure ETH0.TXD0 (P2.8) and ETH0.TXD1 (P2.9)
+   temp = PORT2->IOCR8;
+   temp &= ~(PORT2_IOCR8_PC8_Msk | PORT2_IOCR8_PC9_Msk);
+   temp |= (17UL << PORT2_IOCR8_PC8_Pos) | (17UL << PORT2_IOCR8_PC9_Pos);
+   PORT2->IOCR8 = temp;
+
+   //Configure ETH0.CLK_RMIIC (P15.8) and ETH0.CRS_DVC (P15.9)
+   temp = PORT15->IOCR8;
+   temp &= ~(PORT15_IOCR8_PC8_Msk | PORT15_IOCR8_PC9_Msk);
+   temp |= (0UL << PORT15_IOCR8_PC8_Pos) | (0UL << PORT15_IOCR8_PC9_Pos);
+   PORT15->IOCR8 = temp;
+
+   //Assign ETH_MDIO (P2.0) to HW0
+   temp = PORT2->HWSEL & ~PORT2_HWSEL_HW0_Msk;
+   PORT2->HWSEL = temp | (1UL << PORT2_HWSEL_HW0_Pos);
+
+   //Select output driver strength for ETH0.TX_EN (P2.5)
+   temp = PORT2->PDR0;
+   temp &= ~PORT2_PDR0_PD5_Msk;
+   temp |= (0UL << PORT2_PDR0_PD5_Pos);
+   PORT2->PDR0 = temp;
+
+   //Select output driver strength for ETH0.TXD0 (P2.8) and ETH0.TXD1 (P2.9)
+   temp = PORT2->PDR1;
+   temp &= ~(PORT2_PDR1_PD8_Msk | PORT2_PDR1_PD9_Msk);
+   temp |= (0UL << PORT2_PDR1_PD8_Pos) | (0UL << PORT2_PDR1_PD9_Pos);
+   PORT2->PDR1 = temp;
+
+   //Use ETH0.CLK_RMIIC (P15.8) and ETH0.CRS_DVC (P15.9) as digital inputs
+   PORT15->PDISC &= ~(PORT15_PDISC_PDIS8_Msk | PORT15_PDISC_PDIS9_Msk);
+
+   //Select RMII operation mode
+   ETH0_CON->CON = ETH_CON_INFSEL_Msk | ETH_CON_MDIO_B | ETH_CON_RXER_A |
+      ETH_CON_CRS_DV_C | ETH_CON_CLK_RMII_C | ETH_CON_RXD1_A | ETH_CON_RXD0_A;
+}
+
+#endif
+
+
+/**
+ * @brief Initialize DMA descriptor lists
+ * @param[in] interface Underlying network interface
+ **/
+
+void xmc4800EthInitDmaDesc(NetInterface *interface)
+{
+   uint_t i;
+
+   //Initialize TX DMA descriptor list
+   for(i = 0; i < XMC4800_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Use chain structure rather than ring structure
+      txDmaDesc[i].tdes0 = ETH_TDES0_IC | ETH_TDES0_TCH;
+      //Initialize transmit buffer size
+      txDmaDesc[i].tdes1 = 0;
+      //Transmit buffer address
+      txDmaDesc[i].tdes2 = (uint32_t) txBuffer[i];
+      //Next descriptor address
+      txDmaDesc[i].tdes3 = (uint32_t) &txDmaDesc[i + 1];
+   }
+
+   //The last descriptor is chained to the first entry
+   txDmaDesc[i - 1].tdes3 = (uint32_t) &txDmaDesc[0];
+   //Point to the very first descriptor
+   txCurDmaDesc = &txDmaDesc[0];
+
+   //Initialize RX DMA descriptor list
+   for(i = 0; i < XMC4800_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //The descriptor is initially owned by the DMA
+      rxDmaDesc[i].rdes0 = ETH_RDES0_OWN;
+      //Use chain structure rather than ring structure
+      rxDmaDesc[i].rdes1 = ETH_RDES1_RCH | (XMC4800_ETH_RX_BUFFER_SIZE & ETH_RDES1_RBS1);
+      //Receive buffer address
+      rxDmaDesc[i].rdes2 = (uint32_t) rxBuffer[i];
+      //Next descriptor address
+      rxDmaDesc[i].rdes3 = (uint32_t) &rxDmaDesc[i + 1];
+   }
+
+   //The last descriptor is chained to the first entry
+   rxDmaDesc[i - 1].rdes3 = (uint32_t) &rxDmaDesc[0];
+   //Point to the very first descriptor
+   rxCurDmaDesc = &rxDmaDesc[0];
+
+   //Start location of the TX descriptor list
+   ETH0->TRANSMIT_DESCRIPTOR_LIST_ADDRESS = (uint32_t) txDmaDesc;
+   //Start location of the RX descriptor list
+   ETH0->RECEIVE_DESCRIPTOR_LIST_ADDRESS = (uint32_t) rxDmaDesc;
+}
+
+
+/**
+ * @brief XMC4800 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void xmc4800EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void xmc4800EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   NVIC_EnableIRQ(ETH0_0_IRQn);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void xmc4800EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   NVIC_DisableIRQ(ETH0_0_IRQn);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief XMC4800 Ethernet MAC interrupt service routine
+ **/
+
+void ETH0_0_IRQHandler(void)
+{
+   bool_t flag;
+   uint32_t status;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Read DMA status register
+   status = ETH0->STATUS;
+
+   //A packet has been transmitted?
+   if(status & ETH_STATUS_TI_Msk)
+   {
+      //Clear TI interrupt flag
+      ETH0->STATUS = ETH_STATUS_TI_Msk;
+
+      //Check whether the TX buffer is available for writing
+      if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(status & ETH_STATUS_RI_Msk)
+   {
+      //Disable RIE interrupt
+      ETH0->INTERRUPT_ENABLE &= ~ETH_INTERRUPT_ENABLE_RIE_Msk;
+
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Clear NIS interrupt flag
+   ETH0->STATUS = ETH_STATUS_NIS_Msk;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief XMC4800 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void xmc4800EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+
+   //Packet received?
+   if(ETH0->STATUS & ETH_STATUS_RI_Msk)
+   {
+      //Clear interrupt flag
+      ETH0->STATUS = ETH_STATUS_RI_Msk;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = xmc4800EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+
+   //Re-enable DMA interrupts
+   ETH0->INTERRUPT_ENABLE |= ETH_INTERRUPT_ENABLE_NIE_Msk |
+      ETH_INTERRUPT_ENABLE_RIE_Msk | ETH_INTERRUPT_ENABLE_TIE_Msk;
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t xmc4800EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > XMC4800_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(txCurDmaDesc->tdes0 & ETH_TDES0_OWN)
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead((uint8_t *) txCurDmaDesc->tdes2, buffer, offset, length);
+
+   //Write the number of bytes to send
+   txCurDmaDesc->tdes1 = length & ETH_TDES1_TBS1;
+   //Set LS and FS flags as the data fits in a single buffer
+   txCurDmaDesc->tdes0 |= ETH_TDES0_LS | ETH_TDES0_FS;
+   //Give the ownership of the descriptor to the DMA
+   txCurDmaDesc->tdes0 |= ETH_TDES0_OWN;
+
+   //Clear TU flag to resume processing
+   ETH0->STATUS = ETH_STATUS_TU_Msk;
+   //Instruct the DMA to poll the transmit descriptor list
+   ETH0->TRANSMIT_POLL_DEMAND = 0;
+
+   //Point to the next descriptor in the list
+   txCurDmaDesc = (Xmc4800TxDmaDesc *) txCurDmaDesc->tdes3;
+
+   //Check whether the next buffer is available for writing
+   if(!(txCurDmaDesc->tdes0 & ETH_TDES0_OWN))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t xmc4800EthReceivePacket(NetInterface *interface)
+{
+   error_t error;
+   size_t n;
+
+   //The current buffer is available for reading?
+   if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_OWN))
+   {
+      //FS and LS flags should be set
+      if((rxCurDmaDesc->rdes0 & ETH_RDES0_FS) && (rxCurDmaDesc->rdes0 & ETH_RDES0_LS))
+      {
+         //Make sure no error occurred
+         if(!(rxCurDmaDesc->rdes0 & ETH_RDES0_ES))
+         {
+            //Retrieve the length of the frame
+            n = (rxCurDmaDesc->rdes0 & ETH_RDES0_FL) >> 16;
+            //Limit the number of data to read
+            n = MIN(n, XMC4800_ETH_RX_BUFFER_SIZE);
+
+            //Pass the packet to the upper layer
+            nicProcessPacket(interface, (uint8_t *) rxCurDmaDesc->rdes2, n);
+
+            //Valid packet received
+            error = NO_ERROR;
+         }
+         else
+         {
+            //The received packet contains an error
+            error = ERROR_INVALID_PACKET;
+         }
+      }
+      else
+      {
+         //The packet is not valid
+         error = ERROR_INVALID_PACKET;
+      }
+
+      //Give the ownership of the descriptor back to the DMA
+      rxCurDmaDesc->rdes0 = ETH_RDES0_OWN;
+      //Point to the next descriptor in the list
+      rxCurDmaDesc = (Xmc4800RxDmaDesc *) rxCurDmaDesc->rdes3;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Clear RU flag to resume processing
+   ETH0->STATUS = ETH_STATUS_RU_Msk;
+   //Instruct the DMA to poll the receive descriptor list
+   ETH0->RECEIVE_POLL_DEMAND = 0;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t xmc4800EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint32_t crc;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating XMC4800 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Compute CRC over the current MAC address
+         crc = xmc4800EthCalcCrc(&entry->addr, sizeof(MacAddr));
+
+         //The upper 6 bits in the CRC register are used to index the
+         //contents of the hash table
+         k = (crc >> 26) & 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   ETH0->HASH_TABLE_LOW = hashTable[0];
+   ETH0->HASH_TABLE_HIGH = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HTL = %08" PRIX32 "\r\n", ETH0->HASH_TABLE_LOW);
+   TRACE_DEBUG("  HTH = %08" PRIX32 "\r\n", ETH0->HASH_TABLE_HIGH);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t xmc4800EthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+
+   //Read current MAC configuration
+   config = ETH0->MAC_CONFIGURATION;
+
+   //10BASE-T or 100BASE-TX operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+      config |= ETH_MAC_CONFIGURATION_FES_Msk;
+   else
+      config &= ~ETH_MAC_CONFIGURATION_FES_Msk;
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= ETH_MAC_CONFIGURATION_DM_Msk;
+   else
+      config &= ~ETH_MAC_CONFIGURATION_DM_Msk;
+
+   //Update MAC configuration register
+   ETH0->MAC_CONFIGURATION = config;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void xmc4800EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = ETH0->GMII_ADDRESS & ETH_GMII_ADDRESS_CR_Msk;
+   //Set up a write operation
+   value |= ETH_GMII_ADDRESS_MW_Msk | ETH_GMII_ADDRESS_MB_Msk;
+   //PHY address
+   value |= (phyAddr << ETH_GMII_ADDRESS_PA_Pos) & ETH_GMII_ADDRESS_PA_Msk;
+   //Register address
+   value |= (regAddr << ETH_GMII_ADDRESS_MR_Pos) & ETH_GMII_ADDRESS_MR_Msk;
+
+   //Data to be written in the PHY register
+   ETH0->GMII_DATA = data & ETH_GMII_DATA_MD_Msk;
+
+   //Start a write operation
+   ETH0->GMII_ADDRESS = value;
+   //Wait for the write to complete
+   while(ETH0->GMII_ADDRESS & ETH_GMII_ADDRESS_MB_Msk);
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t xmc4800EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Take care not to alter MDC clock configuration
+   value = ETH0->GMII_ADDRESS & ETH_GMII_ADDRESS_CR_Msk;
+   //Set up a read operation
+   value |= ETH_GMII_ADDRESS_MB_Msk;
+   //PHY address
+   value |= (phyAddr << ETH_GMII_ADDRESS_PA_Pos) & ETH_GMII_ADDRESS_PA_Msk;
+   //Register address
+   value |= (regAddr << ETH_GMII_ADDRESS_MR_Pos) & ETH_GMII_ADDRESS_MR_Msk;
+
+   //Start a read operation
+   ETH0->GMII_ADDRESS = value;
+   //Wait for the read to complete
+   while(ETH0->GMII_ADDRESS & ETH_GMII_ADDRESS_MB_Msk);
+
+   //Return PHY register contents
+   return ETH0->GMII_DATA & ETH_GMII_DATA_MD_Msk;
+}
+
+
+/**
+ * @brief CRC calculation
+ * @param[in] data Pointer to the data over which to calculate the CRC
+ * @param[in] length Number of bytes to process
+ * @return Resulting CRC value
+ **/
+
+uint32_t xmc4800EthCalcCrc(const void *data, size_t length)
+{
+   uint_t i;
+   uint_t j;
+
+   //Point to the data over which to calculate the CRC
+   const uint8_t *p = (uint8_t *) data;
+   //CRC preset value
+   uint32_t crc = 0xFFFFFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed bit by bit
+      for(j = 0; j < 8; j++)
+      {
+         //Update CRC value
+         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
+            crc = (crc << 1) ^ 0x04C11DB7;
+         else
+            crc = crc << 1;
+      }
+   }
+
+   //Return CRC value
+   return ~crc;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/xmc4800_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,285 @@
+/**
+ * @file xmc4800_eth.h
+ * @brief Infineon XMC4800 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _XMC4800_ETH_H
+#define _XMC4800_ETH_H
+
+//Dependencies
+#include "core/nic.h"
+
+//Number of TX buffers
+#ifndef XMC4800_ETH_TX_BUFFER_COUNT
+   #define XMC4800_ETH_TX_BUFFER_COUNT 3
+#elif (XMC4800_ETH_TX_BUFFER_COUNT < 1)
+   #error XMC4800_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef XMC4800_ETH_TX_BUFFER_SIZE
+   #define XMC4800_ETH_TX_BUFFER_SIZE 1536
+#elif (XMC4800_ETH_TX_BUFFER_SIZE != 1536)
+   #error XMC4800_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef XMC4800_ETH_RX_BUFFER_COUNT
+   #define XMC4800_ETH_RX_BUFFER_COUNT 6
+#elif (XMC4800_ETH_RX_BUFFER_COUNT < 1)
+   #error XMC4800_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef XMC4800_ETH_RX_BUFFER_SIZE
+   #define XMC4800_ETH_RX_BUFFER_SIZE 1536
+#elif (XMC4800_ETH_RX_BUFFER_SIZE != 1536)
+   #error XMC4800_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Interrupt priority grouping
+#ifndef XMC4800_ETH_IRQ_PRIORITY_GROUPING
+   #define XMC4800_ETH_IRQ_PRIORITY_GROUPING 1
+#elif (XMC4800_ETH_IRQ_PRIORITY_GROUPING < 0)
+   #error XMC4800_ETH_IRQ_PRIORITY_GROUPING parameter is not valid
+#endif
+
+//Ethernet interrupt group priority
+#ifndef XMC4800_ETH_IRQ_GROUP_PRIORITY
+   #define XMC4800_ETH_IRQ_GROUP_PRIORITY 48
+#elif (XMC4800_ETH_IRQ_GROUP_PRIORITY < 0)
+   #error XMC4800_ETH_IRQ_GROUP_PRIORITY parameter is not valid
+#endif
+
+//Ethernet interrupt subpriority
+#ifndef XMC4800_ETH_IRQ_SUB_PRIORITY
+   #define XMC4800_ETH_IRQ_SUB_PRIORITY 0
+#elif (XMC4800_ETH_IRQ_SUB_PRIORITY < 0)
+   #error XMC4800_ETH_IRQ_SUB_PRIORITY parameter is not valid
+#endif
+
+//ETH0_CON
+#define ETH_CON_MDIO_A     (0 << ETH_CON_MDIO_Pos)
+#define ETH_CON_MDIO_B     (1 << ETH_CON_MDIO_Pos)
+#define ETH_CON_MDIO_C     (2 << ETH_CON_MDIO_Pos)
+#define ETH_CON_MDIO_D     (3 << ETH_CON_MDIO_Pos)
+
+#define ETH_CON_CLK_TX_A   (0 << ETH_CON_CLK_TX_Pos)
+#define ETH_CON_CLK_TX_B   (1 << ETH_CON_CLK_TX_Pos)
+#define ETH_CON_CLK_TX_C   (2 << ETH_CON_CLK_TX_Pos)
+#define ETH_CON_CLK_TX_D   (3 << ETH_CON_CLK_TX_Pos)
+
+#define ETH_CON_COL_A      (0 << ETH_CON_COL_Pos)
+#define ETH_CON_COL_B      (1 << ETH_CON_COL_Pos)
+#define ETH_CON_COL_C      (2 << ETH_CON_COL_Pos)
+#define ETH_CON_COL_D      (3 << ETH_CON_COL_Pos)
+
+#define ETH_CON_RXER_A     (0 << ETH_CON_RXER_Pos)
+#define ETH_CON_RXER_B     (1 << ETH_CON_RXER_Pos)
+#define ETH_CON_RXER_C     (2 << ETH_CON_RXER_Pos)
+#define ETH_CON_RXER_D     (3 << ETH_CON_RXER_Pos)
+
+#define ETH_CON_CRS_A      (0 << ETH_CON_CRS_Pos)
+#define ETH_CON_CRS_B      (1 << ETH_CON_CRS_Pos)
+#define ETH_CON_CRS_C      (2 << ETH_CON_CRS_Pos)
+#define ETH_CON_CRS_D      (3 << ETH_CON_CRS_Pos)
+
+#define ETH_CON_CRS_DV_A   (0 << ETH_CON_CRS_DV_Pos)
+#define ETH_CON_CRS_DV_B   (1 << ETH_CON_CRS_DV_Pos)
+#define ETH_CON_CRS_DV_C   (2 << ETH_CON_CRS_DV_Pos)
+#define ETH_CON_CRS_DV_D   (3 << ETH_CON_CRS_DV_Pos)
+
+#define ETH_CON_CLK_RMII_A (0 << ETH_CON_CLK_RMII_Pos)
+#define ETH_CON_CLK_RMII_B (1 << ETH_CON_CLK_RMII_Pos)
+#define ETH_CON_CLK_RMII_C (2 << ETH_CON_CLK_RMII_Pos)
+#define ETH_CON_CLK_RMII_D (3 << ETH_CON_CLK_RMII_Pos)
+
+#define ETH_CON_RXD3_A     (0 << ETH_CON_RXD3_Pos)
+#define ETH_CON_RXD3_B     (1 << ETH_CON_RXD3_Pos)
+#define ETH_CON_RXD3_C     (2 << ETH_CON_RXD3_Pos)
+#define ETH_CON_RXD3_D     (3 << ETH_CON_RXD3_Pos)
+
+#define ETH_CON_RXD2_A     (0 << ETH_CON_RXD2_Pos)
+#define ETH_CON_RXD2_B     (1 << ETH_CON_RXD2_Pos)
+#define ETH_CON_RXD2_C     (2 << ETH_CON_RXD2_Pos)
+#define ETH_CON_RXD2_D     (3 << ETH_CON_RXD2_Pos)
+
+#define ETH_CON_RXD1_A     (0 << ETH_CON_RXD1_Pos)
+#define ETH_CON_RXD1_B     (1 << ETH_CON_RXD1_Pos)
+#define ETH_CON_RXD1_C     (2 << ETH_CON_RXD1_Pos)
+#define ETH_CON_RXD1_D     (3 << ETH_CON_RXD1_Pos)
+
+#define ETH_CON_RXD0_A     (0 << ETH_CON_RXD0_Pos)
+#define ETH_CON_RXD0_B     (1 << ETH_CON_RXD0_Pos)
+#define ETH_CON_RXD0_C     (2 << ETH_CON_RXD0_Pos)
+#define ETH_CON_RXD0_D     (3 << ETH_CON_RXD0_Pos)
+
+//ETH0_MAC_CONFIGURATION register
+#define ETH_MAC_CONFIGURATION_RESERVED15_Msk (1 << 15)
+
+//ETH0_GMII_ADDRESS register
+#define ETH_GMII_ADDRESS_CR_DIV42  (0 << ETH_GMII_ADDRESS_CR_Pos)
+#define ETH_GMII_ADDRESS_CR_DIV62  (1 << ETH_GMII_ADDRESS_CR_Pos)
+#define ETH_GMII_ADDRESS_CR_DIV16  (2 << ETH_GMII_ADDRESS_CR_Pos)
+#define ETH_GMII_ADDRESS_CR_DIV26  (3 << ETH_GMII_ADDRESS_CR_Pos)
+#define ETH_GMII_ADDRESS_CR_DIV102 (4 << ETH_GMII_ADDRESS_CR_Pos)
+#define ETH_GMII_ADDRESS_CR_DIV124 (5 << ETH_GMII_ADDRESS_CR_Pos)
+
+//ETH0_BUS_MODE register
+#define ETH_BUS_MODE_RPBL_1  (1 << ETH_BUS_MODE_RPBL_Pos)
+#define ETH_BUS_MODE_RPBL_2  (2 << ETH_BUS_MODE_RPBL_Pos)
+#define ETH_BUS_MODE_RPBL_4  (4 << ETH_BUS_MODE_RPBL_Pos)
+#define ETH_BUS_MODE_RPBL_8  (8 << ETH_BUS_MODE_RPBL_Pos)
+#define ETH_BUS_MODE_RPBL_16 (16 << ETH_BUS_MODE_RPBL_Pos)
+#define ETH_BUS_MODE_RPBL_32 (32 << ETH_BUS_MODE_RPBL_Pos)
+
+#define ETH_BUS_MODE_PR_1_1  (0 << ETH_BUS_MODE_PR_Pos)
+#define ETH_BUS_MODE_PR_2_1  (1 << ETH_BUS_MODE_PR_Pos)
+#define ETH_BUS_MODE_PR_3_1  (2 << ETH_BUS_MODE_PR_Pos)
+#define ETH_BUS_MODE_PR_4_1  (3 << ETH_BUS_MODE_PR_Pos)
+
+#define ETH_BUS_MODE_PBL_1   (1 << ETH_BUS_MODE_PBL_Pos)
+#define ETH_BUS_MODE_PBL_2   (2 << ETH_BUS_MODE_PBL_Pos)
+#define ETH_BUS_MODE_PBL_4   (4 << ETH_BUS_MODE_PBL_Pos)
+#define ETH_BUS_MODE_PBL_8   (8 << ETH_BUS_MODE_PBL_Pos)
+#define ETH_BUS_MODE_PBL_16  (16 << ETH_BUS_MODE_PBL_Pos)
+#define ETH_BUS_MODE_PBL_32  (32 << ETH_BUS_MODE_PBL_Pos)
+
+//Transmit DMA descriptor flags
+#define ETH_TDES0_OWN     0x80000000
+#define ETH_TDES0_IC      0x40000000
+#define ETH_TDES0_LS      0x20000000
+#define ETH_TDES0_FS      0x10000000
+#define ETH_TDES0_DC      0x08000000
+#define ETH_TDES0_DP      0x04000000
+#define ETH_TDES0_TTSE    0x02000000
+#define ETH_TDES0_CIC     0x00C00000
+#define ETH_TDES0_TER     0x00200000
+#define ETH_TDES0_TCH     0x00100000
+#define ETH_TDES0_TTSS    0x00020000
+#define ETH_TDES0_IHE     0x00010000
+#define ETH_TDES0_ES      0x00008000
+#define ETH_TDES0_JT      0x00004000
+#define ETH_TDES0_FF      0x00002000
+#define ETH_TDES0_IPE     0x00001000
+#define ETH_TDES0_LCA     0x00000800
+#define ETH_TDES0_NC      0x00000400
+#define ETH_TDES0_LCO     0x00000200
+#define ETH_TDES0_EC      0x00000100
+#define ETH_TDES0_VF      0x00000080
+#define ETH_TDES0_CC      0x00000078
+#define ETH_TDES0_ED      0x00000004
+#define ETH_TDES0_UF      0x00000002
+#define ETH_TDES0_DB      0x00000001
+#define ETH_TDES1_TBS2    0x1FFF0000
+#define ETH_TDES1_TBS1    0x00001FFF
+#define ETH_TDES2_TBAP1   0xFFFFFFFF
+#define ETH_TDES3_TBAP2   0xFFFFFFFF
+
+//Receive DMA descriptor flags
+#define ETH_RDES0_OWN     0x80000000
+#define ETH_RDES0_AFM     0x40000000
+#define ETH_RDES0_FL      0x3FFF0000
+#define ETH_RDES0_ES      0x00008000
+#define ETH_RDES0_DE      0x00004000
+#define ETH_RDES0_SAF     0x00002000
+#define ETH_RDES0_LE      0x00001000
+#define ETH_RDES0_OE      0x00000800
+#define ETH_RDES0_VLAN    0x00000400
+#define ETH_RDES0_FS      0x00000200
+#define ETH_RDES0_LS      0x00000100
+#define ETH_RDES0_IPCE_GF 0x00000080
+#define ETH_RDES0_LCO     0x00000040
+#define ETH_RDES0_FT      0x00000020
+#define ETH_RDES0_RWT     0x00000010
+#define ETH_RDES0_RE      0x00000008
+#define ETH_RDES0_DBE     0x00000004
+#define ETH_RDES0_CE      0x00000002
+#define ETH_RDES0_PCE     0x00000001
+#define ETH_RDES1_DIC     0x80000000
+#define ETH_RDES1_RBS2    0x1FFF0000
+#define ETH_RDES1_RER     0x00008000
+#define ETH_RDES1_RCH     0x00004000
+#define ETH_RDES1_RBS1    0x00001FFF
+#define ETH_RDES2_RBAP1   0xFFFFFFFF
+#define ETH_RDES3_RBAP2   0xFFFFFFFF
+
+
+/**
+ * @brief Transmit DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t tdes0;
+   uint32_t tdes1;
+   uint32_t tdes2;
+   uint32_t tdes3;
+} Xmc4800TxDmaDesc;
+
+
+/**
+ * @brief Receive DMA descriptor
+ **/
+
+typedef struct
+{
+   uint32_t rdes0;
+   uint32_t rdes1;
+   uint32_t rdes2;
+   uint32_t rdes3;
+} Xmc4800RxDmaDesc;
+
+
+//XMC4800 Ethernet MAC driver
+extern const NicDriver xmc4800EthDriver;
+
+//XMC4800 Ethernet MAC related functions
+error_t xmc4800EthInit(NetInterface *interface);
+void xmc4800EthInitGpio(NetInterface *interface);
+void xmc4800EthInitDmaDesc(NetInterface *interface);
+
+void xmc4800EthTick(NetInterface *interface);
+
+void xmc4800EthEnableIrq(NetInterface *interface);
+void xmc4800EthDisableIrq(NetInterface *interface);
+void xmc4800EthEventHandler(NetInterface *interface);
+
+error_t xmc4800EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t xmc4800EthReceivePacket(NetInterface *interface);
+
+error_t xmc4800EthSetMulticastFilter(NetInterface *interface);
+error_t xmc4800EthUpdateMacConfig(NetInterface *interface);
+
+void xmc4800EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t xmc4800EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+uint32_t xmc4800EthCalcCrc(const void *data, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/zynq7000_eth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,781 @@
+/**
+ * @file zynq7000_eth.c
+ * @brief Zynq-7000 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include <limits.h>
+#include "xemacps_hw.h"
+#include "xscugic.h"
+#include "xil_misc_psreset_api.h"
+#include "core/net.h"
+#include "drivers/zynq7000_eth.h"
+#include "debug.h"
+
+//Underlying network interface
+static NetInterface *nicDriverInterface;
+
+//GIC instance
+extern XScuGic ZYNQ7000_ETH_GIC_INSTANCE;
+
+//IAR EWARM compiler?
+#if defined(__ICCARM__)
+
+//TX buffer
+#pragma data_alignment = 8
+#pragma location = ".ram_no_cache"
+static uint8_t txBuffer[ZYNQ7000_ETH_TX_BUFFER_COUNT][ZYNQ7000_ETH_TX_BUFFER_SIZE];
+//RX buffer
+#pragma data_alignment = 8
+#pragma location = ".ram_no_cache"
+static uint8_t rxBuffer[ZYNQ7000_ETH_RX_BUFFER_COUNT][ZYNQ7000_ETH_RX_BUFFER_SIZE];
+//TX buffer descriptors
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static Zynq7000TxBufferDesc txBufferDesc[ZYNQ7000_ETH_TX_BUFFER_COUNT];
+//RX buffer descriptors
+#pragma data_alignment = 4
+#pragma location = ".ram_no_cache"
+static Zynq7000RxBufferDesc rxBufferDesc[ZYNQ7000_ETH_RX_BUFFER_COUNT];
+
+//Keil MDK-ARM or GCC compiler?
+#else
+
+//TX buffer
+static uint8_t txBuffer[ZYNQ7000_ETH_TX_BUFFER_COUNT][ZYNQ7000_ETH_TX_BUFFER_SIZE]
+   __attribute__((aligned(8), __section__(".ram_no_cache")));
+//RX buffer
+static uint8_t rxBuffer[ZYNQ7000_ETH_RX_BUFFER_COUNT][ZYNQ7000_ETH_RX_BUFFER_SIZE]
+   __attribute__((aligned(8), __section__(".ram_no_cache")));
+//TX buffer descriptors
+static Zynq7000TxBufferDesc txBufferDesc[ZYNQ7000_ETH_TX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+//RX buffer descriptors
+static Zynq7000RxBufferDesc rxBufferDesc[ZYNQ7000_ETH_RX_BUFFER_COUNT]
+   __attribute__((aligned(4), __section__(".ram_no_cache")));
+
+#endif
+
+//TX buffer index
+static uint_t txBufferIndex;
+//RX buffer index
+static uint_t rxBufferIndex;
+
+
+/**
+ * @brief Zynq-7000 Ethernet MAC driver
+ **/
+
+const NicDriver zynq7000EthDriver =
+{
+   NIC_TYPE_ETHERNET,
+   ETH_MTU,
+   zynq7000EthInit,
+   zynq7000EthTick,
+   zynq7000EthEnableIrq,
+   zynq7000EthDisableIrq,
+   zynq7000EthEventHandler,
+   zynq7000EthSendPacket,
+   zynq7000EthSetMulticastFilter,
+   zynq7000EthUpdateMacConfig,
+   zynq7000EthWritePhyReg,
+   zynq7000EthReadPhyReg,
+   TRUE,
+   TRUE,
+   TRUE,
+   FALSE
+};
+
+
+/**
+ * @brief Zynq-7000 Ethernet MAC initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t zynq7000EthInit(NetInterface *interface)
+{
+   error_t error;
+   volatile uint32_t temp;
+
+   //Debug message
+   TRACE_INFO("Initializing Zynq-7000 Ethernet MAC...\r\n");
+
+   //Save underlying network interface
+   nicDriverInterface = interface;
+
+   //Unlock SLCR
+   XSLCR_UNLOCK = XSLCR_UNLOCK_KEY_VALUE;
+
+   //Configure Ethernet controller reference clock
+   temp = XSLCR_GEM0_CLK_CTRL_CLKACT_MASK;
+   temp |= (XPAR_PS7_ETHERNET_0_ENET_SLCR_1000MBPS_DIV1 << 20) & XSLCR_GEM0_CLK_CTRL_DIV1_MASK;
+   temp |= (XPAR_PS7_ETHERNET_0_ENET_SLCR_1000MBPS_DIV0 << 8) & XSLCR_GEM0_CLK_CTRL_DIV0_MASK;
+   XSLCR_GEM0_CLK_CTRL = temp;
+
+   //Enable Ethernet controller RX clock
+   XSLCR_GEM0_RCLK_CTRL = XSLCR_GEM0_RCLK_CTRL_CLKACT_MASK;
+
+   //Lock SLCR
+   XSLCR_LOCK = XSLCR_LOCK_KEY_VALUE;
+
+   //Clear network control register
+   XEMACPS_NWCTRL = 0;
+   //Clear statistics registers
+   XEMACPS_NWCTRL |= XEMACPS_NWCTRL_STATCLR_MASK;
+
+   //Configure MDC clock speed
+   XEMACPS_NWCFG = (MDC_DIV_224 << XEMACPS_NWCFG_MDC_SHIFT_MASK) | XEMACPS_NWCFG_MDCCLKDIV_MASK;
+   //Enable management port (MDC and MDIO)
+   XEMACPS_NWCTRL |= XEMACPS_NWCTRL_MDEN_MASK;
+
+   //PHY transceiver initialization
+   error = interface->phyDriver->init(interface);
+   //Failed to initialize PHY transceiver?
+   if(error)
+      return error;
+
+   //Set the MAC address
+   XEMACPS_LADDR1L = interface->macAddr.w[0] | (interface->macAddr.w[1] << 16);
+   XEMACPS_LADDR1H = interface->macAddr.w[2];
+
+   //Configure the receive filter
+   XEMACPS_NWCFG |= XEMACPS_NWCFG_UCASTHASHEN_MASK | XEMACPS_NWCFG_MCASTHASHEN_MASK;
+
+   //Initialize hash table
+   XEMACPS_HASHL = 0;
+   XEMACPS_HASHH = 0;
+
+   //Initialize buffer descriptors
+   zynq7000EthInitBufferDesc(interface);
+
+   //Set RX buffer size
+   temp = ((ZYNQ7000_ETH_RX_BUFFER_SIZE / 64) << XEMACPS_DMACR_RXBUF_SHIFT) &
+      XEMACPS_DMACR_RXBUF_MASK;
+
+   //Use full configured addressable space for transmit and receive packet buffers
+   temp |= XEMACPS_DMACR_TXSIZE_MASK | XEMACPS_DMACR_RXSIZE_MASK;
+   //Select the burst length for DMA data operations
+   temp |= XEMACPS_DMACR_INCR16_AHB_BURST;
+   //Set DMA configuration register
+   XEMACPS_DMACR = temp;
+
+   //Clear transmit status register
+   XEMACPS_TXSR = XEMACPS_TXSR_TXCOMPL_MASK | XEMACPS_TXSR_TXGO_MASK |
+      XEMACPS_TXSR_ERROR_MASK;
+
+   //Clear receive status register
+   XEMACPS_RXSR = XEMACPS_RXSR_FRAMERX_MASK | XEMACPS_RXSR_ERROR_MASK;
+
+   //First disable all interrupts
+   XEMACPS_IDR = 0xFFFFFFFF;
+
+   //Only the desired ones are enabled
+   XEMACPS_IER = XEMACPS_IXR_HRESPNOK_MASK | XEMACPS_IXR_RXOVR_MASK |
+      XEMACPS_IXR_TXCOMPL_MASK | XEMACPS_IXR_TXEXH_MASK | XEMACPS_IXR_RETRY_MASK |
+      XEMACPS_IXR_URUN_MASK | XEMACPS_IXR_RXUSED_MASK | XEMACPS_IXR_FRAMERX_MASK;
+
+   //Read interrupt status register to clear any pending interrupt
+   temp = XEMACPS_ISR;
+
+   //Register interrupt handler
+   XScuGic_Connect(&ZYNQ7000_ETH_GIC_INSTANCE, XPS_GEM0_INT_ID,
+      (Xil_InterruptHandler) zynq7000EthIrqHandler, interface);
+
+   //Configure interrupt priority
+   XScuGic_SetPriorityTriggerType(&ZYNQ7000_ETH_GIC_INSTANCE,
+      XPS_GEM0_INT_ID, ZYNQ7000_ETH_IRQ_PRIORITY, 1);
+
+   //Enable the transmitter and the receiver
+   XEMACPS_NWCTRL |= XEMACPS_NWCTRL_TXEN_MASK | XEMACPS_NWCTRL_RXEN_MASK;
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize buffer descriptors
+ * @param[in] interface Underlying network interface
+ **/
+
+void zynq7000EthInitBufferDesc(NetInterface *interface)
+{
+   uint_t i;
+   uint32_t address;
+
+   //Initialize TX buffer descriptors
+   for(i = 0; i < ZYNQ7000_ETH_TX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current TX buffer
+      address = (uint32_t) txBuffer[i];
+      //Write the address to the descriptor entry
+      txBufferDesc[i].address = address;
+      //Initialize status field
+      txBufferDesc[i].status = XEMACPS_TX_USED;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   txBufferDesc[i - 1].status |= XEMACPS_TX_WRAP;
+   //Initialize TX buffer index
+   txBufferIndex = 0;
+
+   //Initialize RX buffer descriptors
+   for(i = 0; i < ZYNQ7000_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Calculate the address of the current RX buffer
+      address = (uint32_t) rxBuffer[i];
+      //Write the address to the descriptor entry
+      rxBufferDesc[i].address = address & XEMACPS_RX_ADDRESS;
+      //Clear status field
+      rxBufferDesc[i].status = 0;
+   }
+
+   //Mark the last descriptor entry with the wrap flag
+   rxBufferDesc[i - 1].address |= XEMACPS_RX_WRAP;
+   //Initialize RX buffer index
+   rxBufferIndex = 0;
+
+   //Start location of the TX descriptor list
+   XEMACPS_TXQBASE = (uint32_t) txBufferDesc;
+   //Start location of the RX descriptor list
+   XEMACPS_RXQBASE = (uint32_t) rxBufferDesc;
+}
+
+
+/**
+ * @brief Zynq-7000 Ethernet MAC timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void zynq7000EthTick(NetInterface *interface)
+{
+   //Handle periodic operations
+   interface->phyDriver->tick(interface);
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void zynq7000EthEnableIrq(NetInterface *interface)
+{
+   //Enable Ethernet MAC interrupts
+   XScuGic_Enable(&ZYNQ7000_ETH_GIC_INSTANCE, XPS_GEM0_INT_ID);
+   //Enable Ethernet PHY interrupts
+   interface->phyDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void zynq7000EthDisableIrq(NetInterface *interface)
+{
+   //Disable Ethernet MAC interrupts
+   XScuGic_Disable(&ZYNQ7000_ETH_GIC_INSTANCE, XPS_GEM0_INT_ID);
+   //Disable Ethernet PHY interrupts
+   interface->phyDriver->disableIrq(interface);
+}
+
+
+/**
+ * @brief Zynq-7000 Ethernet MAC interrupt service routine
+ * @param[in] interface Underlying network interface
+ **/
+
+void zynq7000EthIrqHandler(NetInterface *interface)
+{
+   bool_t flag;
+   volatile uint32_t isr;
+   volatile uint32_t tsr;
+   volatile uint32_t rsr;
+
+   //Enter interrupt service routine
+   osEnterIsr();
+
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Each time the software reads XEMACPS_ISR, it has to check the
+   //contents of XEMACPS_TXSR, XEMACPS_RXSR
+   isr = XEMACPS_ISR;
+   tsr = XEMACPS_TXSR;
+   rsr = XEMACPS_RXSR;
+
+   //Clear interrupt flags
+   XEMACPS_ISR = isr;
+
+   //A packet has been transmitted?
+   if(tsr & (XEMACPS_TXSR_TXCOMPL_MASK | XEMACPS_TXSR_TXGO_MASK | XEMACPS_TXSR_ERROR_MASK))
+   {
+      //Only clear TSR flags that are currently set
+      XEMACPS_TXSR = tsr;
+
+      //Check whether the TX buffer is available for writing
+      if(txBufferDesc[txBufferIndex].status & XEMACPS_TX_USED)
+      {
+         //Notify the TCP/IP stack that the transmitter is ready to send
+         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
+      }
+   }
+
+   //A packet has been received?
+   if(rsr & (XEMACPS_RXSR_FRAMERX_MASK | XEMACPS_RXSR_ERROR_MASK))
+   {
+      //Set event flag
+      nicDriverInterface->nicEvent = TRUE;
+      //Notify the TCP/IP stack of the event
+      flag |= osSetEventFromIsr(&netEvent);
+   }
+
+   //Flush packet if the receive buffer not available
+   if (isr & XEMACPS_IXR_RXUSED_MASK)
+      XEMACPS_NWCTRL |= XEMACPS_NWCTRL_FLUSH_DPRAM_MASK;
+
+   //Leave interrupt service routine
+   osExitIsr(flag);
+}
+
+
+/**
+ * @brief Zynq-7000 Ethernet MAC event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void zynq7000EthEventHandler(NetInterface *interface)
+{
+   error_t error;
+   uint32_t rsr;
+
+   //Read receive status
+   rsr = XEMACPS_RXSR;
+
+   //Packet received?
+   if(rsr & (XEMACPS_RXSR_FRAMERX_MASK | XEMACPS_RXSR_ERROR_MASK))
+   {
+      //Only clear RSR flags that are currently set
+      XEMACPS_RXSR = rsr;
+
+      //Process all pending packets
+      do
+      {
+         //Read incoming packet
+         error = zynq7000EthReceivePacket(interface);
+
+         //No more data in the receive buffer?
+      } while(error != ERROR_BUFFER_EMPTY);
+   }
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t zynq7000EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   static uint8_t temp[ZYNQ7000_ETH_TX_BUFFER_SIZE];
+   size_t length;
+
+   //Retrieve the length of the packet
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the frame length
+   if(length > ZYNQ7000_ETH_TX_BUFFER_SIZE)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+      //Report an error
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Make sure the current buffer is available for writing
+   if(!(txBufferDesc[txBufferIndex].status & XEMACPS_TX_USED))
+      return ERROR_FAILURE;
+
+   //Copy user data to the transmit buffer
+   netBufferRead(temp, buffer, offset, length);
+   memcpy(txBuffer[txBufferIndex], temp, length);
+
+   //Set the necessary flags in the descriptor entry
+   if(txBufferIndex < (ZYNQ7000_ETH_TX_BUFFER_COUNT - 1))
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status =
+         XEMACPS_TX_LAST | (length & XEMACPS_TX_LENGTH);
+
+      //Point to the next buffer
+      txBufferIndex++;
+   }
+   else
+   {
+      //Write the status word
+      txBufferDesc[txBufferIndex].status = XEMACPS_TX_WRAP |
+         XEMACPS_TX_LAST | (length & XEMACPS_TX_LENGTH);
+
+      //Wrap around
+      txBufferIndex = 0;
+   }
+
+   //Set the STARTTX bit to initiate transmission
+   XEMACPS_NWCTRL |= XEMACPS_NWCTRL_STARTTX_MASK;
+
+   //Check whether the next buffer is available for writing
+   if(txBufferDesc[txBufferIndex].status & XEMACPS_TX_USED)
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t zynq7000EthReceivePacket(NetInterface *interface)
+{
+   static uint8_t temp[ETH_MAX_FRAME_SIZE];
+   error_t error;
+   uint_t i;
+   uint_t j;
+   uint_t sofIndex;
+   uint_t eofIndex;
+   size_t n;
+   size_t size;
+   size_t length;
+
+   //Initialize SOF and EOF indices
+   sofIndex = UINT_MAX;
+   eofIndex = UINT_MAX;
+
+   //Search for SOF and EOF flags
+   for(i = 0; i < ZYNQ7000_ETH_RX_BUFFER_COUNT; i++)
+   {
+      //Point to the current entry
+      j = rxBufferIndex + i;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(j >= ZYNQ7000_ETH_RX_BUFFER_COUNT)
+         j -= ZYNQ7000_ETH_RX_BUFFER_COUNT;
+
+      //No more entries to process?
+      if(!(rxBufferDesc[j].address & XEMACPS_RX_OWNERSHIP))
+      {
+         //Stop processing
+         break;
+      }
+      //A valid SOF has been found?
+      if(rxBufferDesc[j].status & XEMACPS_RX_SOF)
+      {
+         //Save the position of the SOF
+         sofIndex = i;
+      }
+      //A valid EOF has been found?
+      if((rxBufferDesc[j].status & XEMACPS_RX_EOF) && sofIndex != UINT_MAX)
+      {
+         //Save the position of the EOF
+         eofIndex = i;
+         //Retrieve the length of the frame
+         size = rxBufferDesc[j].status & XEMACPS_RX_LENGTH;
+         //Limit the number of data to read
+         size = MIN(size, ETH_MAX_FRAME_SIZE);
+         //Stop processing since we have reached the end of the frame
+         break;
+      }
+   }
+
+   //Determine the number of entries to process
+   if(eofIndex != UINT_MAX)
+      j = eofIndex + 1;
+   else if(sofIndex != UINT_MAX)
+      j = sofIndex;
+   else
+      j = i;
+
+   //Total number of bytes that have been copied from the receive buffer
+   length = 0;
+
+   //Process incoming frame
+   for(i = 0; i < j; i++)
+   {
+      //Any data to copy from current buffer?
+      if(eofIndex != UINT_MAX && i >= sofIndex && i <= eofIndex)
+      {
+         //Calculate the number of bytes to read at a time
+         n = MIN(size, ZYNQ7000_ETH_RX_BUFFER_SIZE);
+         //Copy data from receive buffer
+         memcpy(temp + length, rxBuffer[rxBufferIndex], n);
+         //Update byte counters
+         length += n;
+         size -= n;
+      }
+
+      //Mark the current buffer as free
+      rxBufferDesc[rxBufferIndex].address &= ~XEMACPS_RX_OWNERSHIP;
+
+      //Point to the following entry
+      rxBufferIndex++;
+
+      //Wrap around to the beginning of the buffer if necessary
+      if(rxBufferIndex >= ZYNQ7000_ETH_RX_BUFFER_COUNT)
+         rxBufferIndex = 0;
+   }
+
+   //Any packet to process?
+   if(length > 0)
+   {
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, temp, length);
+      //Valid packet received
+      error = NO_ERROR;
+   }
+   else
+   {
+      //No more data in the receive buffer
+      error = ERROR_BUFFER_EMPTY;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t zynq7000EthSetMulticastFilter(NetInterface *interface)
+{
+   uint_t i;
+   uint_t k;
+   uint8_t *p;
+   uint32_t hashTable[2];
+   MacFilterEntry *entry;
+
+   //Debug message
+   TRACE_DEBUG("Updating Zynq-7000 hash table...\r\n");
+
+   //Clear hash table
+   hashTable[0] = 0;
+   hashTable[1] = 0;
+
+   //The MAC filter table contains the multicast MAC addresses
+   //to accept when receiving an Ethernet frame
+   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->macMulticastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Point to the MAC address
+         p = entry->addr.b;
+
+         //Apply the hash function
+         k = (p[0] >> 6) ^ p[0];
+         k ^= (p[1] >> 4) ^ (p[1] << 2);
+         k ^= (p[2] >> 2) ^ (p[2] << 4);
+         k ^= (p[3] >> 6) ^ p[3];
+         k ^= (p[4] >> 4) ^ (p[4] << 2);
+         k ^= (p[5] >> 2) ^ (p[5] << 4);
+
+         //The hash value is reduced to a 6-bit index
+         k &= 0x3F;
+
+         //Update hash table contents
+         hashTable[k / 32] |= (1 << (k % 32));
+      }
+   }
+
+   //Write the hash table
+   XEMACPS_HASHL = hashTable[0];
+   XEMACPS_HASHH = hashTable[1];
+
+   //Debug message
+   TRACE_DEBUG("  HASHL = %08" PRIX32 "\r\n", XEMACPS_HASHL);
+   TRACE_DEBUG("  HASHH = %08" PRIX32 "\r\n", XEMACPS_HASHH);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Adjust MAC configuration parameters for proper operation
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t zynq7000EthUpdateMacConfig(NetInterface *interface)
+{
+   uint32_t config;
+   uint32_t clockCtrl;
+
+   //Read network configuration register
+   config = XEMACPS_NWCFG;
+
+   //Read clock control register
+   clockCtrl = XSLCR_GEM0_CLK_CTRL;
+   clockCtrl &= ~(XSLCR_GEM0_CLK_CTRL_DIV1_MASK | XSLCR_GEM0_CLK_CTRL_DIV0_MASK);
+
+   //1000BASE-T operation mode?
+   if(interface->linkSpeed == NIC_LINK_SPEED_1GBPS)
+   {
+     //Update network configuration
+      config |= XEMACPS_NWCFG_1000_MASK;
+      config &= ~XEMACPS_NWCFG_100_MASK;
+
+      //Update clock configuration
+      clockCtrl |= (XPAR_PS7_ETHERNET_0_ENET_SLCR_1000MBPS_DIV1 << 20) & XSLCR_GEM0_CLK_CTRL_DIV1_MASK;
+      clockCtrl |= (XPAR_PS7_ETHERNET_0_ENET_SLCR_1000MBPS_DIV0 << 8) & XSLCR_GEM0_CLK_CTRL_DIV0_MASK;
+   }
+   //100BASE-TX operation mode?
+   else if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
+   {
+      //Update network configuration
+      config &= ~XEMACPS_NWCFG_1000_MASK;
+      config |= XEMACPS_NWCFG_100_MASK;
+
+      //Update clock configuration
+      clockCtrl |= (XPAR_PS7_ETHERNET_0_ENET_SLCR_100MBPS_DIV1 << 20) & XSLCR_GEM0_CLK_CTRL_DIV1_MASK;
+      clockCtrl |= (XPAR_PS7_ETHERNET_0_ENET_SLCR_100MBPS_DIV0 << 8) & XSLCR_GEM0_CLK_CTRL_DIV0_MASK;
+   }
+   //10BASE-T operation mode?
+   else
+   {
+      //Update network configuration
+      config &= ~XEMACPS_NWCFG_1000_MASK;
+      config &= ~XEMACPS_NWCFG_100_MASK;
+
+      //Update clock configuration
+      clockCtrl |= (XPAR_PS7_ETHERNET_0_ENET_SLCR_10MBPS_DIV1 << 20) & XSLCR_GEM0_CLK_CTRL_DIV1_MASK;
+      clockCtrl |= (XPAR_PS7_ETHERNET_0_ENET_SLCR_10MBPS_DIV0 << 8) & XSLCR_GEM0_CLK_CTRL_DIV0_MASK;
+   }
+
+   //Half-duplex or full-duplex mode?
+   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
+      config |= XEMACPS_NWCFG_FDEN_MASK;
+   else
+      config &= ~XEMACPS_NWCFG_FDEN_MASK;
+
+   //Write network configuration register
+   XEMACPS_NWCFG = config;
+
+   //Unlock SLCR
+   XSLCR_UNLOCK = XSLCR_UNLOCK_KEY_VALUE;
+   //Write clock control register
+   XSLCR_GEM0_CLK_CTRL = clockCtrl;
+   //Lock SLCR
+   XSLCR_LOCK = XSLCR_LOCK_KEY_VALUE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @param[in] data Register value
+ **/
+
+void zynq7000EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
+{
+   uint32_t value;
+
+   //Set up a write operation
+   value = XEMACPS_PHYMNTNC_OP_MASK | XEMACPS_PHYMNTNC_OP_W_MASK;
+   //PHY address
+   value |= (phyAddr << 23) & XEMACPS_PHYMNTNC_ADDR_MASK;
+   //Register address
+   value |= (regAddr << 18) & XEMACPS_PHYMNTNC_REG_MASK;
+   //Register value
+   value |= data & XEMACPS_PHYMNTNC_DATA_MASK;
+
+   //Start a write operation
+   XEMACPS_PHYMNTNC = value;
+   //Wait for the write to complete
+   while(!(XEMACPS_NWSR & XEMACPS_NWSR_MDIOIDLE_MASK));
+}
+
+
+/**
+ * @brief Read PHY register
+ * @param[in] phyAddr PHY address
+ * @param[in] regAddr Register address
+ * @return Register value
+ **/
+
+uint16_t zynq7000EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
+{
+   uint32_t value;
+
+   //Set up a read operation
+   value = XEMACPS_PHYMNTNC_OP_MASK | XEMACPS_PHYMNTNC_OP_R_MASK;
+   //PHY address
+   value |= (phyAddr << 23) & XEMACPS_PHYMNTNC_ADDR_MASK;
+   //Register address
+   value |= (regAddr << 18) & XEMACPS_PHYMNTNC_REG_MASK;
+
+   //Start a read operation
+   XEMACPS_PHYMNTNC = value;
+   //Wait for the read to complete
+   while(!(XEMACPS_NWSR & XEMACPS_NWSR_MDIOIDLE_MASK));
+
+   //Return PHY register contents
+   return XEMACPS_PHYMNTNC & XEMACPS_PHYMNTNC_DATA_MASK;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/drivers/zynq7000_eth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,279 @@
+/**
+ * @file zynq7000_eth.h
+ * @brief Zynq-7000 Ethernet MAC controller
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _ZYNQ7000_ETH_H
+#define _ZYNQ7000_ETH_H
+
+//Number of TX buffers
+#ifndef ZYNQ7000_ETH_TX_BUFFER_COUNT
+   #define ZYNQ7000_ETH_TX_BUFFER_COUNT 16
+#elif (ZYNQ7000_ETH_TX_BUFFER_COUNT < 1)
+   #error ZYNQ7000_ETH_TX_BUFFER_COUNT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef ZYNQ7000_ETH_TX_BUFFER_SIZE
+   #define ZYNQ7000_ETH_TX_BUFFER_SIZE 1536
+#elif (ZYNQ7000_ETH_TX_BUFFER_SIZE != 1536)
+   #error ZYNQ7000_ETH_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Number of RX buffers
+#ifndef ZYNQ7000_ETH_RX_BUFFER_COUNT
+   #define ZYNQ7000_ETH_RX_BUFFER_COUNT 16
+#elif (ZYNQ7000_ETH_RX_BUFFER_COUNT < 1)
+   #error ZYNQ7000_ETH_RX_BUFFER_COUNT parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef ZYNQ7000_ETH_RX_BUFFER_SIZE
+   #define ZYNQ7000_ETH_RX_BUFFER_SIZE 1536
+#elif (ZYNQ7000_ETH_RX_BUFFER_SIZE != 1536)
+   #error ZYNQ7000_ETH_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Ethernet interrupt priority
+#ifndef ZYNQ7000_ETH_IRQ_PRIORITY
+   #define ZYNQ7000_ETH_IRQ_PRIORITY 160
+#elif (ZYNQ7000_ETH_IRQ_PRIORITY < 0)
+   #error ZYNQ7000_ETH_IRQ_PRIORITY parameter is not valid
+#endif
+
+//Macro for hardware access
+#define _HW_REG(address) *((volatile uint32_t *) (address))
+
+//XEMACPS registers
+#define XSLCR_LOCK             _HW_REG(XSLCR_UNLOCK_ADDR - 4)
+#define XSLCR_UNLOCK           _HW_REG(XSLCR_UNLOCK_ADDR)
+#define XSLCR_GEM0_RCLK_CTRL   _HW_REG(XSLCR_GEM0_RCLK_CTRL_ADDR)
+#define XSLCR_GEM0_CLK_CTRL    _HW_REG(XSLCR_GEM0_CLK_CTRL_ADDR)
+#define XEMACPS_NWCTRL         _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_NWCTRL_OFFSET)
+#define XEMACPS_NWCFG          _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_NWCFG_OFFSET)
+#define XEMACPS_NWSR           _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_NWSR_OFFSET)
+#define XEMACPS_DMACR          _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_DMACR_OFFSET)
+#define XEMACPS_TXSR           _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_TXSR_OFFSET)
+#define XEMACPS_RXQBASE        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXQBASE_OFFSET)
+#define XEMACPS_TXQBASE        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_TXQBASE_OFFSET)
+#define XEMACPS_RXSR           _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXSR_OFFSET)
+#define XEMACPS_ISR            _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_ISR_OFFSET)
+#define XEMACPS_IER            _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_IER_OFFSET)
+#define XEMACPS_IDR            _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_IDR_OFFSET)
+#define XEMACPS_IMR            _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_IMR_OFFSET)
+#define XEMACPS_PHYMNTNC       _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_PHYMNTNC_OFFSET)
+#define XEMACPS_RXPAUSE        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXPAUSE_OFFSET)
+#define XEMACPS_TXPAUSE        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_TXPAUSE_OFFSET)
+#define XEMACPS_JUMBOMAXLEN    _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_JUMBOMAXLEN_OFFSET)
+#define XEMACPS_HASHL          _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_HASHL_OFFSET)
+#define XEMACPS_HASHH          _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_HASHH_OFFSET)
+#define XEMACPS_LADDR1L        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_LADDR1L_OFFSET)
+#define XEMACPS_LADDR1H        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_LADDR1H_OFFSET)
+#define XEMACPS_LADDR2L        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_LADDR2L_OFFSET)
+#define XEMACPS_LADDR2H        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_LADDR2H_OFFSET)
+#define XEMACPS_LADDR3L        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_LADDR3L_OFFSET)
+#define XEMACPS_LADDR3H        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_LADDR3H_OFFSET)
+#define XEMACPS_LADDR4L        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_LADDR4L_OFFSET)
+#define XEMACPS_LADDR4H        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_LADDR4H_OFFSET)
+#define XEMACPS_MATCH1         _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_MATCH1_OFFSET)
+#define XEMACPS_MATCH2         _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_MATCH2_OFFSET)
+#define XEMACPS_MATCH3         _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_MATCH3_OFFSET)
+#define XEMACPS_MATCH4         _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_MATCH4_OFFSET)
+#define XEMACPS_STRETCH        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_STRETCH_OFFSET)
+#define XEMACPS_OCTTXL         _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_OCTTXL_OFFSET)
+#define XEMACPS_OCTTXH         _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_OCTTXH_OFFSET)
+#define XEMACPS_TXCNT          _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_TXCNT_OFFSET)
+#define XEMACPS_TXBCCNT        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_TXBCCNT_OFFSET)
+#define XEMACPS_TXMCCNT        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_TXMCCNT_OFFSET)
+#define XEMACPS_TXPAUSECNT     _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_TXPAUSECNT_OFFSET)
+#define XEMACPS_TX64CNT        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_TX64CNT_OFFSET)
+#define XEMACPS_TX65CNT        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_TX65CNT_OFFSET)
+#define XEMACPS_TX128CNT       _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_TX128CNT_OFFSET)
+#define XEMACPS_TX256CNT       _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_TX256CNT_OFFSET)
+#define XEMACPS_TX512CNT       _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_TX512CNT_OFFSET)
+#define XEMACPS_TX1024CNT      _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_TX1024CNT_OFFSET)
+#define XEMACPS_TX1519CNT      _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_TX1519CNT_OFFSET)
+#define XEMACPS_TXURUNCNT      _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_TXURUNCNT_OFFSET)
+#define XEMACPS_SNGLCOLLCNT    _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_SNGLCOLLCNT_OFFSET)
+#define XEMACPS_MULTICOLLCNT   _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_MULTICOLLCNT_OFFSET)
+#define XEMACPS_EXCESSCOLLCNT  _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_EXCESSCOLLCNT_OFFSET)
+#define XEMACPS_LATECOLLCNT    _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_LATECOLLCNT_OFFSET)
+#define XEMACPS_TXDEFERCNT     _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_TXDEFERCNT_OFFSET)
+#define XEMACPS_TXCSENSECNT    _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_TXCSENSECNT_OFFSET)
+#define XEMACPS_OCTRXL         _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_OCTRXL_OFFSET)
+#define XEMACPS_OCTRXH         _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_OCTRXH_OFFSET)
+#define XEMACPS_RXCNT          _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXCNT_OFFSET)
+#define XEMACPS_RXBROADCNT     _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXBROADCNT_OFFSET)
+#define XEMACPS_RXMULTICNT     _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXMULTICNT_OFFSET)
+#define XEMACPS_RXPAUSECNT     _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXPAUSECNT_OFFSET)
+#define XEMACPS_RX64CNT        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RX64CNT_OFFSET)
+#define XEMACPS_RX65CNT        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RX65CNT_OFFSET)
+#define XEMACPS_RX128CNT       _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RX128CNT_OFFSET)
+#define XEMACPS_RX256CNT       _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RX256CNT_OFFSET)
+#define XEMACPS_RX512CNT       _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RX512CNT_OFFSET)
+#define XEMACPS_RX1024CNT      _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RX1024CNT_OFFSET)
+#define XEMACPS_RX1519CNT      _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RX1519CNT_OFFSET)
+#define XEMACPS_RXUNDRCNT      _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXUNDRCNT_OFFSET)
+#define XEMACPS_RXOVRCNT       _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXOVRCNT_OFFSET)
+#define XEMACPS_RXJABCNT       _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXJABCNT_OFFSET)
+#define XEMACPS_RXFCSCNT       _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXFCSCNT_OFFSET)
+#define XEMACPS_RXLENGTHCNT    _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXLENGTHCNT_OFFSET)
+#define XEMACPS_RXSYMBCNT      _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXSYMBCNT_OFFSET)
+#define XEMACPS_RXALIGNCNT     _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXALIGNCNT_OFFSET)
+#define XEMACPS_RXRESERRCNT    _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXRESERRCNT_OFFSET)
+#define XEMACPS_RXORCNT        _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXORCNT_OFFSET)
+#define XEMACPS_RXIPCCNT       _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXIPCCNT_OFFSET)
+#define XEMACPS_RXTCPCCNT      _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXTCPCCNT_OFFSET)
+#define XEMACPS_RXUDPCCNT      _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXUDPCCNT_OFFSET)
+#define XEMACPS_LAST           _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_LAST_OFFSET)
+#define XEMACPS_1588_SEC       _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_1588_SEC_OFFSET)
+#define XEMACPS_1588_NANOSEC   _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_1588_NANOSEC_OFFSET)
+#define XEMACPS_1588_ADJ       _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_1588_ADJ_OFFSET)
+#define XEMACPS_1588_INC       _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_1588_INC_OFFSET)
+#define XEMACPS_PTP_TXSEC      _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_PTP_TXSEC_OFFSET)
+#define XEMACPS_PTP_TXNANOSEC  _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_PTP_TXNANOSEC_OFFSET)
+#define XEMACPS_PTP_RXSEC      _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_PTP_RXSEC_OFFSET)
+#define XEMACPS_PTP_RXNANOSEC  _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_PTP_RXNANOSEC_OFFSET)
+#define XEMACPS_PTPP_TXSEC     _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_PTPP_TXSEC_OFFSET)
+#define XEMACPS_PTPP_TXNANOSEC _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_PTPP_TXNANOSEC_OFFSET)
+#define XEMACPS_PTPP_RXSEC     _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_PTPP_RXSEC_OFFSET)
+#define XEMACPS_PTPP_RXNANOSEC _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_PTPP_RXNANOSEC_OFFSET)
+#define XEMACPS_INTQ1_STS      _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_INTQ1_STS_OFFSET)
+#define XEMACPS_TXQ1BASE       _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_TXQ1BASE_OFFSET)
+#define XEMACPS_RXQ1BASE       _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_RXQ1BASE_OFFSET)
+#define XEMACPS_MSBBUF_TXQBASE _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_MSBBUF_TXQBASE_OFFSET)
+#define XEMACPS_MSBBUF_RXQBASE _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_MSBBUF_RXQBASE_OFFSET)
+#define XEMACPS_INTQ1_IER      _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_INTQ1_IER_OFFSET)
+#define XEMACPS_INTQ1_IDR      _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_INTQ1_IDR_OFFSET)
+#define XEMACPS_INTQ1_IMR      _HW_REG(XPAR_XEMACPS_0_BASEADDR + XEMACPS_INTQ1_IMR_OFFSET)
+
+//SLCR_LOCK register
+#define XSLCR_LOCK_KEY_VALUE             0x0000767B;
+
+//SLCR_UNLOCK register
+#define XSLCR_UNLOCK_KEY_VALUE           0x0000DF0D;
+
+//SLCR_GEM0_RCLK_CTRL register
+#define XSLCR_GEM0_RCLK_CTRL_SRCSEL_MASK 0x00000010
+#define XSLCR_GEM0_RCLK_CTRL_CLKACT_MASK 0x00000001
+
+//SLCR_GEM0_CLK_CTRL register
+#define XSLCR_GEM0_CLK_CTRL_DIV1_MASK    0x03F00000
+#define XSLCR_GEM0_CLK_CTRL_DIV0_MASK    0x00003F00
+#define XSLCR_GEM0_CLK_CTRL_SRCSEL_MASK  0x00000070
+#define XSLCR_GEM0_CLK_CTRL_CLKACT_MASK  0x00000001
+
+//PHYMNTNC register
+#ifdef XEMACPS_PHYMNTNC_DATA_MASK
+   #undef XEMACPS_PHYMNTNC_DATA_MASK
+   #define XEMACPS_PHYMNTNC_DATA_MASK    0x0000FFFF
+#endif
+
+//TX buffer descriptor flags
+#define XEMACPS_TX_USED                  0x80000000
+#define XEMACPS_TX_WRAP                  0x40000000
+#define XEMACPS_TX_RLE_ERROR             0x20000000
+#define XEMACPS_TX_UNDERRUN_ERROR        0x10000000
+#define XEMACPS_TX_AHB_ERROR             0x08000000
+#define XEMACPS_TX_LATE_COL_ERROR        0x04000000
+#define XEMACPS_TX_CHECKSUM_ERROR        0x00700000
+#define XEMACPS_TX_NO_CRC                0x00010000
+#define XEMACPS_TX_LAST                  0x00008000
+#define XEMACPS_TX_LENGTH                0x00003FFF
+
+//RX buffer descriptor flags
+#define XEMACPS_RX_ADDRESS               0xFFFFFFFC
+#define XEMACPS_RX_WRAP                  0x00000002
+#define XEMACPS_RX_OWNERSHIP             0x00000001
+#define XEMACPS_RX_BROADCAST             0x80000000
+#define XEMACPS_RX_MULTICAST_HASH        0x40000000
+#define XEMACPS_RX_UNICAST_HASH          0x20000000
+#define XEMACPS_RX_SAR                   0x08000000
+#define XEMACPS_RX_SAR_MASK              0x06000000
+#define XEMACPS_RX_TYPE_ID               0x01000000
+#define XEMACPS_RX_SNAP                  0x01000000
+#define XEMACPS_RX_TYPE_ID_MASK          0x00C00000
+#define XEMACPS_RX_CHECKSUM_VALID        0x00C00000
+#define XEMACPS_RX_VLAN_TAG              0x00200000
+#define XEMACPS_RX_PRIORITY_TAG          0x00100000
+#define XEMACPS_RX_VLAN_PRIORITY         0x000E0000
+#define XEMACPS_RX_CFI                   0x00010000
+#define XEMACPS_RX_EOF                   0x00008000
+#define XEMACPS_RX_SOF                   0x00004000
+#define XEMACPS_RX_LENGTH_MSB            0x00002000
+#define XEMACPS_RX_BAD_FCS               0x00002000
+#define XEMACPS_RX_LENGTH                0x00001FFF
+
+
+/**
+ * @brief Transmit buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Zynq7000TxBufferDesc;
+
+
+/**
+ * @brief Receive buffer descriptor
+ **/
+
+typedef struct
+{
+   uint32_t address;
+   uint32_t status;
+} Zynq7000RxBufferDesc;
+
+
+//Zynq-7000 Ethernet MAC driver
+extern const NicDriver zynq7000EthDriver;
+
+//Zynq-7000 Ethernet MAC related functions
+error_t zynq7000EthInit(NetInterface *interface);
+void zynq7000EthInitBufferDesc(NetInterface *interface);
+
+void zynq7000EthTick(NetInterface *interface);
+
+void zynq7000EthEnableIrq(NetInterface *interface);
+void zynq7000EthDisableIrq(NetInterface *interface);
+void zynq7000EthIrqHandler(NetInterface *interface);
+void zynq7000EthEventHandler(NetInterface *interface);
+
+error_t zynq7000EthSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t zynq7000EthReceivePacket(NetInterface *interface);
+
+error_t zynq7000EthSetMulticastFilter(NetInterface *interface);
+error_t zynq7000EthUpdateMacConfig(NetInterface *interface);
+
+void zynq7000EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data);
+uint16_t zynq7000EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ftp/ftp_client.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1574 @@
+/**
+ * @file ftp_client.c
+ * @brief FTP client (File Transfer Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * File Transfer Protocol (FTP) is a standard network protocol used to
+ * transfer files from one host to another host over a TCP-based network.
+ * Refer to the following RFCs for complete details:
+ * - RFC 959: File Transfer Protocol (FTP)
+ * - RFC 2428: FTP Extensions for IPv6 and NATs
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL FTP_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include <ctype.h>
+#include "ftp/ftp_client.h"
+#include "str.h"
+#include "error.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (FTP_CLIENT_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Set the port to be used in data connection
+ * @param[in] context Pointer to the FTP client context
+ * @param[in] ipAddr Host address
+ * @param[in] port Port number
+ * @return Error code
+ **/
+
+error_t ftpSetPort(FtpClientContext *context, const IpAddr *ipAddr, uint16_t port)
+{
+   error_t error;
+   uint_t replyCode;
+   char_t *p;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //IPv4 FTP client?
+   if(ipAddr->length == sizeof(Ipv4Addr))
+   {
+      //Format the PORT command
+      strcpy(context->buffer, "PORT ");
+
+      //Append host address
+      ipv4AddrToString(ipAddr->ipv4Addr, context->buffer + 5);
+
+      //Parse the resulting string
+      for(p = context->buffer; *p != '\0'; p++)
+      {
+         //Change dots to commas
+         if(*p == '.') *p = ',';
+      }
+
+      //Append port number
+      sprintf(p, ",%" PRIu8 ",%" PRIu8 "\r\n", MSB(port), LSB(port));
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 FTP client?
+   if(ipAddr->length == sizeof(Ipv6Addr))
+   {
+      //Format the EPRT command
+      strcpy(context->buffer, "EPRT |2|");
+
+      //Append host address
+      ipv6AddrToString(&ipAddr->ipv6Addr, context->buffer + 8);
+
+      //Point to the end of the resulting string
+      p = context->buffer + strlen(context->buffer);
+      //Append port number
+      sprintf(p, "|%" PRIu16 "|\r\n", port);
+   }
+   else
+#endif
+   //Invalid IP address?
+   {
+      //Report an error
+      return ERROR_INVALID_ADDRESS;
+   }
+
+   //Send the command to the server
+   error = ftpSendCommand(context, context->buffer, &replyCode);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check FTP response code
+   if(!FTP_REPLY_CODE_2YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Enter passive mode
+ * @param[in] context Pointer to the FTP client context
+ * @param[out] port The port number the server is listening on
+ * @return Error code
+ **/
+
+error_t ftpSetPassiveMode(FtpClientContext *context, uint16_t *port)
+{
+   error_t error;
+   uint_t replyCode;
+   char_t delimiter;
+   char_t *p;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //IPv4 FTP server?
+   if(context->serverIpAddr.length == sizeof(Ipv4Addr))
+   {
+      //Send the command to the server
+      error = ftpSendCommand(context, "PASV\r\n", &replyCode);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Check FTP response code
+      if(!FTP_REPLY_CODE_2YZ(replyCode))
+         return ERROR_UNEXPECTED_RESPONSE;
+
+      //Delimiter character
+      delimiter = ',';
+
+      //Retrieve the low byte of the port number
+      p = strrchr(context->buffer, delimiter);
+      //Failed to parse the response?
+      if(!p)
+         return ERROR_INVALID_SYNTAX;
+
+      //Convert the resulting string
+      *port = atoi(p + 1);
+      //Split the string
+      *p = '\0';
+
+      //Retrieve the high byte of the port number
+      p = strrchr(context->buffer, delimiter);
+      //Failed to parse the response?
+      if(!p)
+         return ERROR_INVALID_SYNTAX;
+
+      //Convert the resulting string
+      *port |= atoi(p + 1) << 8;
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 FTP server?
+   if(context->serverIpAddr.length == sizeof(Ipv6Addr))
+   {
+      //Send the command to the server
+      error = ftpSendCommand(context, "EPSV\r\n", &replyCode);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Check FTP response code
+      if(!FTP_REPLY_CODE_2YZ(replyCode))
+         return ERROR_UNEXPECTED_RESPONSE;
+
+      //Search for the opening parenthesis
+      p = strrchr(context->buffer, '(');
+      //Failed to parse the response?
+      if(!p || p[1] == '\0')
+         return ERROR_INVALID_SYNTAX;
+
+      //Retrieve the delimiter character
+      delimiter = p[1];
+
+      //Search for the last delimiter character
+      p = strrchr(context->buffer, delimiter);
+      //Failed to parse the response?
+      if(!p)
+         return ERROR_INVALID_SYNTAX;
+
+      //Split the string
+      *p = '\0';
+
+      //Search for the last but one delimiter character
+      p = strrchr(context->buffer, delimiter);
+      //Failed to parse the response?
+      if(!p)
+         return ERROR_INVALID_SYNTAX;
+
+      //Convert the resulting string
+      *port = atoi(p + 1);
+   }
+   else
+#endif
+   //Invalid IP address?
+   {
+      //Report an error
+      return ERROR_INVALID_ADDRESS;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set representation type
+ * @param[in] context Pointer to the FTP client context
+ * @param[in] type Single character identifying the desired type
+ * @return Error code
+ **/
+
+error_t ftpSetType(FtpClientContext *context, char_t type)
+{
+   error_t error;
+   uint_t replyCode;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Format the TYPE command
+   sprintf(context->buffer, "TYPE %c\r\n", type);
+
+   //Send the command to the server
+   error = ftpSendCommand(context, context->buffer, &replyCode);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check FTP response code
+   if(!FTP_REPLY_CODE_2YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set protection buffer size
+ * @param[in] context Pointer to the FTP client context
+ * @param[in] size Protection buffer size
+ * @return Error code
+ **/
+
+error_t ftpSetProtectionBufferSize(FtpClientContext *context, uint32_t size)
+{
+   error_t error;
+   uint_t replyCode;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Format the PBSZ command
+   sprintf(context->buffer, "PBSZ %" PRIu32 "\r\n", size);
+
+   //Send the command to the server
+   error = ftpSendCommand(context, context->buffer, &replyCode);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check FTP response code
+   if(!FTP_REPLY_CODE_2YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set data channel protection level
+ * @param[in] context Pointer to the FTP client context
+ * @param[in] level Character specifying the data channel protection level
+ * @return Error code
+ **/
+
+error_t ftpSetDataChannelProtectionLevel(FtpClientContext *context, char_t level)
+{
+   error_t error;
+   uint_t replyCode;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Format the PROT command
+   sprintf(context->buffer, "PROT %c\r\n", level);
+
+   //Send the command to the server
+   error = ftpSendCommand(context, context->buffer, &replyCode);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check FTP response code
+   if(!FTP_REPLY_CODE_2YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Establish a connection with the specified FTP server
+ * @param[in] context Pointer to the FTP client context
+ * @param[in] interface Underlying network interface (optional parameter)
+ * @param[in] serverIpAddr IP address of the FTP server
+ * @param[in] serverPort Port number
+ * @param[in] flags Connection options
+ * @return Error code
+ **/
+
+error_t ftpConnect(FtpClientContext *context, NetInterface *interface,
+   const IpAddr *serverIpAddr, uint16_t serverPort, uint_t flags)
+{
+   error_t error;
+   uint_t replyCode;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Initialize context
+   context->passiveMode = FALSE;
+   context->controlSocket = NULL;
+   context->dataSocket = NULL;
+
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+   context->controlTlsContext = NULL;
+   context->dataTlsContext = NULL;
+#endif
+
+   //Underlying network interface
+   context->interface = interface;
+   //Save the IP address of the FTP server
+   context->serverIpAddr = *serverIpAddr;
+
+   //Use passive mode?
+   if(flags & FTP_PASSIVE_MODE)
+      context->passiveMode = TRUE;
+
+   //Open control socket
+   context->controlSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
+   //Failed to open socket?
+   if(!context->controlSocket)
+      return ERROR_OPEN_FAILED;
+
+   //Start of exception handling block
+   do
+   {
+      //Bind the socket to a particular network interface?
+      if(context->interface != NULL)
+      {
+         //Associate the socket with the relevant interface
+         error = socketBindToInterface(context->controlSocket, context->interface);
+         //Any error to report?
+         if(error)
+            break;
+      }
+
+      //Set timeout for blocking operations
+      error = socketSetTimeout(context->controlSocket, FTP_CLIENT_DEFAULT_TIMEOUT);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Specify the size of the send buffer
+      error = socketSetTxBufferSize(context->controlSocket,
+         FTP_CLIENT_SOCKET_MIN_TX_BUFFER_SIZE);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Specify the size of the receive buffer
+      error = socketSetRxBufferSize(context->controlSocket,
+         FTP_CLIENT_SOCKET_MIN_RX_BUFFER_SIZE);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Connect to the FTP server
+      error = socketConnect(context->controlSocket, serverIpAddr, serverPort);
+      //Connection to server failed?
+      if(error)
+         break;
+
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+   //Use implicit FTPS?
+   if(flags & FTP_IMPLICIT_SECURITY)
+   {
+      //SSL initialization
+      error = ftpInitControlTlsContext(context);
+      //Any error to report?
+      if(error)
+         break;
+   }
+#endif
+
+   //Wait for the connection greeting reply
+   error = ftpSendCommand(context, NULL, &replyCode);
+   //Any communication error to report?
+   if(error)
+      break;
+
+   //Check FTP response code
+   if(!FTP_REPLY_CODE_2YZ(replyCode))
+      error = ERROR_UNEXPECTED_RESPONSE;
+
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+   //Use explicit FTPS?
+   if(flags & FTP_EXPLICIT_SECURITY)
+   {
+      //Sanity check
+      if(context->controlTlsContext == NULL)
+      {
+         //The client issues an AUTH TLS command
+         error = ftpAuth(context);
+         //Any error to report?
+         if(error)
+            break;
+
+         //SSL initialization
+         error = ftpInitControlTlsContext(context);
+         //Any error to report?
+         if(error)
+            break;
+      }
+   }
+#endif
+
+      //End of exception handling block
+   } while(0);
+
+   //Any error to report?
+   if(error)
+   {
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+      if(context->controlTlsContext != NULL)
+      {
+         //Release SSL context
+         tlsFree(context->controlTlsContext);
+         context->controlTlsContext = NULL;
+      }
+#endif
+
+      //Close socket
+      socketClose(context->controlSocket);
+      context->controlSocket = NULL;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Request authentication
+ * @param[in] context Pointer to the FTP client context
+ * @return Error code
+ **/
+
+error_t ftpAuth(FtpClientContext *context)
+{
+   error_t error;
+   uint_t replyCode;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Issue the AUTH command
+   error = ftpSendCommand(context, "AUTH TLS\r\n", &replyCode);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check FTP response code
+   if(!FTP_REPLY_CODE_2YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Login to the FTP server using the provided username and password
+ * @param[in] context Pointer to the FTP client context
+ * @param[in] username The username to login under
+ * @param[in] password The password to use
+ * @param[in] account Account name
+ * @return Error code
+ **/
+
+error_t ftpLogin(FtpClientContext *context, const char_t *username,
+   const char_t *password, const char_t *account)
+{
+   error_t error;
+   uint_t replyCode;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Format the USER command
+   sprintf(context->buffer, "USER %s\r\n", username);
+
+   //Send the command to the server
+   error = ftpSendCommand(context, context->buffer, &replyCode);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check FTP response code
+   if(FTP_REPLY_CODE_2YZ(replyCode))
+      return NO_ERROR;
+   else if(!FTP_REPLY_CODE_3YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Format the PASS command
+   sprintf(context->buffer, "PASS %s\r\n", password);
+
+   //Send the command to the server
+   error = ftpSendCommand(context, context->buffer, &replyCode);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check FTP response code
+   if(FTP_REPLY_CODE_2YZ(replyCode))
+      return NO_ERROR;
+   else if(!FTP_REPLY_CODE_3YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Format the ACCT command
+   sprintf(context->buffer, "ACCT %s\r\n", account);
+
+   //Send the command to the server
+   error = ftpSendCommand(context, context->buffer, &replyCode);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check FTP response code
+   if(!FTP_REPLY_CODE_2YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Get the working directory from the FTP server
+ * @param[in] context Pointer to the FTP client context
+ * @param[out] path Output buffer where to store the current directory
+ * @param[in] size Size of the output buffer
+ * @return Error code
+ **/
+
+error_t ftpGetWorkingDir(FtpClientContext *context, char_t *path, size_t size)
+{
+   error_t error;
+   size_t length;
+   uint_t replyCode;
+   char_t *p;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Check parameters
+   if(path == NULL || size == 0)
+      return ERROR_INVALID_PARAMETER;
+
+   //Send the command to the server
+   error = ftpSendCommand(context, "PWD\r\n", &replyCode);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check FTP response code
+   if(!FTP_REPLY_CODE_2YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Search for the last double quote
+   p = strrchr(context->buffer, '\"');
+   //Failed to parse the response?
+   if(!p)
+      return ERROR_INVALID_SYNTAX;
+
+   //Split the string
+   *p = '\0';
+
+   //Search for the first double quote
+   p = strchr(context->buffer, '\"');
+   //Failed to parse the response?
+   if(!p)
+      return ERROR_INVALID_SYNTAX;
+
+   //Retrieve the length of the working directory
+   length = strlen(p + 1);
+   //Limit the number of characters to copy
+   length = MIN(length, size - 1);
+
+   //Copy the string
+   strncpy(path, p + 1, length);
+   //Properly terminate the string with a NULL character
+   path[length] = '\0';
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Change the current working directory of the FTP session
+ * @param[in] context Pointer to the FTP client context
+ * @param[in] path The new current working directory
+ * @return Error code
+ **/
+
+error_t ftpChangeWorkingDir(FtpClientContext *context, const char_t *path)
+{
+   error_t error;
+   uint_t replyCode;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Format the CWD command
+   sprintf(context->buffer, "CWD %s\r\n", path);
+
+   //Send the command to the server
+   error = ftpSendCommand(context, context->buffer, &replyCode);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check FTP response code
+   if(!FTP_REPLY_CODE_2YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Change the current working directory to the parent directory
+ * @param[in] context Pointer to the FTP client context
+ * @return Error code
+ **/
+
+error_t ftpChangeToParentDir(FtpClientContext *context)
+{
+   error_t error;
+   uint_t replyCode;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Send the command to the server
+   error = ftpSendCommand(context, "CDUP\r\n", &replyCode);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check FTP response code
+   if(!FTP_REPLY_CODE_2YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Create a new directory
+ * @param[in] context Pointer to the FTP client context
+ * @param[in] path The name of the new directory
+ * @return Error code
+ **/
+
+error_t ftpMakeDir(FtpClientContext *context, const char_t *path)
+{
+   error_t error;
+   uint_t replyCode;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Format the MKD command
+   sprintf(context->buffer, "MKD %s\r\n", path);
+
+   //Send the command to the server
+   error = ftpSendCommand(context, context->buffer, &replyCode);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check FTP response code
+   if(!FTP_REPLY_CODE_2YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Remove a directory on the FTP server
+ * @param[in] context Pointer to the FTP client context
+ * @param[in] path Path to the directory to be removed
+ * @return Error code
+ **/
+
+error_t ftpRemoveDir(FtpClientContext *context, const char_t *path)
+{
+   error_t error;
+   uint_t replyCode;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Format the RMD command
+   sprintf(context->buffer, "RMD %s\r\n", path);
+
+   //Send the command to the server
+   error = ftpSendCommand(context, context->buffer, &replyCode);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check FTP response code
+   if(!FTP_REPLY_CODE_2YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Open a file for reading, writing, or appending
+ * @param[in] context Pointer to the FTP client context
+ * @param[in] path Path to the file to be be opened
+ * @param[in] flags Access mode
+ * @return Error code
+ **/
+
+error_t ftpOpenFile(FtpClientContext *context, const char_t *path, uint_t flags)
+{
+   error_t error;
+   uint_t replyCode;
+   IpAddr ipAddr;
+   uint16_t port;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Open data socket
+   context->dataSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
+   //Failed to open socket?
+   if(!context->dataSocket)
+      return ERROR_OPEN_FAILED;
+
+   //Start of exception handling block
+   do
+   {
+      //Bind the socket to a particular network interface?
+      if(context->interface != NULL)
+      {
+         //Associate the socket with the relevant interface
+         error = socketBindToInterface(context->dataSocket, context->interface);
+         //Any error to report?
+         if(error)
+            break;
+      }
+
+      //Set timeout for blocking operations
+      error = socketSetTimeout(context->dataSocket, FTP_CLIENT_DEFAULT_TIMEOUT);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Check data transfer direction
+      if(flags & (FTP_FOR_WRITING | FTP_FOR_APPENDING))
+      {
+         //Maximize transmission throughput by using a large buffer
+         error = socketSetTxBufferSize(context->dataSocket,
+            FTP_CLIENT_SOCKET_MAX_TX_BUFFER_SIZE);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Use a small buffer for the reception path
+         error = socketSetRxBufferSize(context->dataSocket,
+            FTP_CLIENT_SOCKET_MIN_RX_BUFFER_SIZE);
+         //Any error to report?
+         if(error)
+            break;
+      }
+      else
+      {
+         //Use a small buffer for the transmission path
+         error = socketSetTxBufferSize(context->dataSocket,
+            FTP_CLIENT_SOCKET_MIN_TX_BUFFER_SIZE);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Maximize reception throughput by using a large buffer
+         error = socketSetRxBufferSize(context->dataSocket,
+            FTP_CLIENT_SOCKET_MAX_RX_BUFFER_SIZE);
+         //Any error to report?
+         if(error)
+            break;
+      }
+
+      //Set representation type
+      if(flags & FTP_TEXT_TYPE)
+      {
+         //Use ASCII type
+         error = ftpSetType(context, 'A');
+         //Any error to report?
+         if(error)
+            break;
+      }
+      else
+      {
+         //Use image type
+         error = ftpSetType(context, 'I');
+         //Any error to report?
+         if(error)
+            break;
+      }
+
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+      //Use SSL?
+      if(context->controlTlsContext != NULL)
+      {
+         //A PBSZ command must be issued, but must have a parameter of '0' to
+         //indicate that no buffering is taking place and the data connection
+         //should not be encapsulated
+         error = ftpSetProtectionBufferSize(context, 0);
+         //Any communication error to report?
+         if(error)
+            break;
+
+         //If the data connection security level is 'Private', then an SSL
+         //negotiation must take place on the data connection
+         error = ftpSetDataChannelProtectionLevel(context, 'P');
+         //Any communication error to report?
+         if(error)
+            break;
+      }
+#endif
+
+      //Check transfer mode
+      if(!context->passiveMode)
+      {
+         //Place the data socket in the listening state
+         error = socketListen(context->dataSocket, 1);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Retrieve local IP address
+         error = socketGetLocalAddr(context->controlSocket, &ipAddr, NULL);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Retrieve local port number
+         error = socketGetLocalAddr(context->dataSocket, NULL, &port);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Set the port to be used in data connection
+         error = ftpSetPort(context, &ipAddr, port);
+         //Any error to report?
+         if(error)
+            break;
+      }
+      else
+      {
+         //Enter passive mode
+         error = ftpSetPassiveMode(context, &port);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Establish data connection
+         error = socketConnect(context->dataSocket, &context->serverIpAddr, port);
+         //Connection to server failed?
+         if(error)
+            break;
+      }
+
+      //Format the command
+      if(flags & FTP_FOR_WRITING)
+         sprintf(context->buffer, "STOR %s\r\n", path);
+      else if(flags & FTP_FOR_APPENDING)
+         sprintf(context->buffer, "APPE %s\r\n", path);
+      else
+         sprintf(context->buffer, "RETR %s\r\n", path);
+
+      //Send the command to the server
+      error = ftpSendCommand(context, context->buffer, &replyCode);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Check FTP response code
+      if(!FTP_REPLY_CODE_1YZ(replyCode))
+      {
+         //Report an error
+         error = ERROR_UNEXPECTED_RESPONSE;
+         break;
+      }
+
+      //Check transfer mode
+      if(!context->passiveMode)
+      {
+         //Wait for the server to connect back to the client's data port
+         Socket *socket = socketAccept(context->dataSocket, NULL, NULL);
+
+         //No connection request?
+         if(socket == NULL)
+         {
+            //Report an error
+            error = ERROR_FAILURE;
+            break;
+         }
+
+         //Close the listening socket
+         socketClose(context->dataSocket);
+         //Save socket handle
+         context->dataSocket = socket;
+
+         //Set timeout for blocking operations
+         error = socketSetTimeout(context->dataSocket, FTP_CLIENT_DEFAULT_TIMEOUT);
+         //Any error to report?
+         if(error)
+            break;
+      }
+
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+      //Use SSL?
+      if(context->controlTlsContext != NULL)
+      {
+         //SSL initialization
+         error = ftpInitDataTlsContext(context);
+         //Any error to report?
+         if(error)
+            break;
+      }
+#endif
+
+      //End of exception handling block
+   } while(0);
+
+   //Any error to report?
+   if(error)
+   {
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+      if(context->dataTlsContext != NULL)
+      {
+         //Release SSL context
+         tlsFree(context->dataTlsContext);
+         context->dataTlsContext = NULL;
+      }
+#endif
+
+      //Close socket
+      socketClose(context->dataSocket);
+      context->dataSocket = NULL;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Write to a remote file
+ * @param[in] context Pointer to the FTP client context
+ * @param[in] data Pointer to a buffer containing the data to be written
+ * @param[in] length Number of data bytes to write
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t ftpWriteFile(FtpClientContext *context,
+   const void *data, size_t length, uint_t flags)
+{
+   error_t error;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+   if(context->dataTlsContext != NULL)
+   {
+      //Transmit data to the FTP server
+      error = tlsWrite(context->dataTlsContext, data, length, NULL, flags);
+   }
+   else
+#endif
+   {
+      //Transmit data to the FTP server
+      error = socketSend(context->dataSocket, data, length, NULL, flags);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Read from a remote file
+ * @param[in] context Pointer to the FTP client context
+ * @param[out] data Buffer where to store the incoming data
+ * @param[in] size Maximum number of bytes that can be read
+ * @param[out] length Actual number of bytes that have been read
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t ftpReadFile(FtpClientContext *context,
+   void *data, size_t size, size_t *length, uint_t flags)
+{
+   error_t error;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+   if(context->dataTlsContext != NULL)
+   {
+      //Receive data from the FTP server
+      error = tlsRead(context->dataTlsContext, data, size, length, flags);
+   }
+   else
+#endif
+   {
+      //Receive data from the FTP server
+      error = socketReceive(context->dataSocket, data, size, length, flags);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Close file
+ * @param[in] context Pointer to the FTP client context
+ * @return Error code
+ **/
+
+error_t ftpCloseFile(FtpClientContext *context)
+{
+   error_t error;
+   uint_t replyCode;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+   if(context->dataTlsContext != NULL)
+   {
+      //Gracefully close the data connection
+      tlsShutdown(context->dataTlsContext);
+
+      //Release SSL context (data connection)
+      tlsFree(context->dataTlsContext);
+      context->dataTlsContext = NULL;
+   }
+#endif
+
+   //Graceful shutdown
+   socketShutdown(context->dataSocket, SOCKET_SD_BOTH);
+
+   //Close the data socket
+   socketClose(context->dataSocket);
+   context->dataSocket = NULL;
+
+   //Check the transfer status
+   error = ftpSendCommand(context, NULL, &replyCode);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check FTP response code
+   if(!FTP_REPLY_CODE_2YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Rename a remote file
+ * @param[in] context Pointer to the FTP client context
+ * @param[in] oldName The name of the remote file to rename
+ * @param[in] newName The new name of the remote file
+ * @return Error code
+ **/
+
+error_t ftpRenameFile(FtpClientContext *context,
+   const char_t *oldName, const char_t *newName)
+{
+   error_t error;
+   uint_t replyCode;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Format the RNFR command
+   sprintf(context->buffer, "RNFR %s\r\n", oldName);
+
+   //Send the command to the server
+   error = ftpSendCommand(context, context->buffer, &replyCode);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check FTP response code
+   if(!FTP_REPLY_CODE_3YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Format the RNTO command
+   sprintf(context->buffer, "RNTO %s\r\n", newName);
+
+   //Send the command to the server
+   error = ftpSendCommand(context, context->buffer, &replyCode);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check FTP response code
+   if(!FTP_REPLY_CODE_2YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Delete a file
+ * @param[in] context Pointer to the FTP client context
+ * @param[in] path Path to the file to be be deleted
+ * @return Error code
+ **/
+
+error_t ftpDeleteFile(FtpClientContext *context, const char_t *path)
+{
+   error_t error;
+   uint_t replyCode;
+
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Format the DELE command
+   sprintf(context->buffer, "DELE %s\r\n", path);
+
+   //Send the command to the server
+   error = ftpSendCommand(context, context->buffer, &replyCode);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check FTP response code
+   if(!FTP_REPLY_CODE_2YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Close the connection with the FTP server
+ * @param[in] context Pointer to the FTP client context
+ * @return Error code
+ **/
+
+error_t ftpClose(FtpClientContext *context)
+{
+   //Invalid context?
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+   if(context->dataTlsContext != NULL)
+   {
+      //Gracefully close the data connection
+      tlsShutdown(context->dataTlsContext);
+
+      //Release SSL context (data connection)
+      tlsFree(context->dataTlsContext);
+      context->dataTlsContext = NULL;
+   }
+#endif
+
+   //Close data socket
+   if(context->dataSocket)
+   {
+      socketClose(context->dataSocket);
+      context->dataSocket = NULL;
+   }
+
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+   if(context->controlTlsContext != NULL)
+   {
+      //Gracefully close the control connection
+      tlsShutdown(context->controlTlsContext);
+
+      //Release SSL context (control connection)
+      tlsFree(context->controlTlsContext);
+      context->controlTlsContext = NULL;
+   }
+#endif
+
+   //Close control socket
+   if(context->controlSocket)
+   {
+      socketClose(context->controlSocket);
+      context->controlSocket = NULL;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Send FTP command and wait for a reply
+ * @param[in] context Pointer to the FTP client context
+ * @param[in] command Command line
+ * @param[out] replyCode Response code from the FTP server
+ * @return Error code
+ **/
+
+error_t ftpSendCommand(FtpClientContext *context,
+   const char_t *command, uint_t *replyCode)
+{
+   error_t error;
+   size_t length;
+   char_t *p;
+
+   //Any command line to send?
+   if(command)
+   {
+      //Debug message
+      TRACE_DEBUG("FTP client: %s", command);
+
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+      if(context->controlTlsContext != NULL)
+      {
+         //Send the command to the FTP server
+         error = tlsWrite(context->controlTlsContext, command,
+            strlen(command), NULL, SOCKET_FLAG_WAIT_ACK);
+      }
+      else
+#endif
+      {
+         //Send the command to the FTP server
+         error = socketSend(context->controlSocket, command,
+            strlen(command), NULL, SOCKET_FLAG_WAIT_ACK);
+      }
+
+      //Failed to send command?
+      if(error)
+         return error;
+   }
+
+   //Multiline replies are allowed for any command
+   while(1)
+   {
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+      if(context->controlTlsContext != NULL)
+      {
+         //Wait for a response from the server
+         error = tlsRead(context->controlTlsContext, context->buffer,
+            FTP_CLIENT_BUFFER_SIZE - 1, &length, SOCKET_FLAG_BREAK_CRLF);
+      }
+      else
+#endif
+      {
+         //Wait for a response from the server
+         error = socketReceive(context->controlSocket, context->buffer,
+            FTP_CLIENT_BUFFER_SIZE - 1, &length, SOCKET_FLAG_BREAK_CRLF);
+      }
+
+      //The remote server did not respond as expected?
+      if(error)
+         return error;
+
+      //Point to the beginning of the buffer
+      p = context->buffer;
+      //Properly terminate the string with a NULL character
+      p[length] = '\0';
+
+      //Remove trailing whitespace from the response
+      strRemoveTrailingSpace(p);
+
+      //Debug message
+      TRACE_DEBUG("FTP server: %s\r\n", p);
+
+      //Check the length of the response
+      if(strlen(p) >= 3)
+      {
+         //All replies begin with a three digit numeric code
+         if(isdigit((uint8_t) p[0]) && isdigit((uint8_t) p[1]) && isdigit((uint8_t) p[2]))
+         {
+            //A space character follows the response code for the last line
+            if(p[3] == ' ' || p[3] == '\0')
+            {
+               //Get the server response code
+               *replyCode = strtoul(p, NULL, 10);
+               //Exit immediately
+               break;
+            }
+         }
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+
+/**
+ * @brief Register SSL initialization callback function
+ * @param[in] context Pointer to the FTP client context
+ * @param[in] callback SSL initialization callback function
+ * @return Error code
+ **/
+
+error_t ftpRegisterTlsInitCallback(FtpClientContext *context,
+   FtpClientTlsInitCallback callback)
+{
+   //Check parameters
+   if(context == NULL || callback == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save callback function
+   context->tlsInitCallback = callback;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief SSL initialization (control connection)
+ * @param[in] context Pointer to the FTP client context
+ * @return Error code
+ **/
+
+error_t ftpInitControlTlsContext(FtpClientContext *context)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_DEBUG("FTP Client: Initializing SSL session (control)...\r\n");
+
+   //Allocate SSL context
+   context->controlTlsContext = tlsInit();
+
+   //Initialization failed?
+   if(context->controlTlsContext == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Start of exception handling block
+   do
+   {
+      //Select the relevant operation mode
+      error = tlsSetConnectionEnd(context->controlTlsContext, TLS_CONNECTION_END_CLIENT);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Bind SSL to the relevant socket
+      error = tlsSetSocket(context->controlTlsContext, context->controlSocket);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Check whether the SSL initialization callback has been
+      //properly registered?
+      if(context->tlsInitCallback != NULL)
+      {
+         //Perform SSL related initialization
+         error = context->tlsInitCallback(context, context->controlTlsContext);
+         //Any error to report?
+         if(error)
+            break;
+      }
+      else
+      {
+         //No callback function registered
+         error = ERROR_NOT_CONFIGURED;
+         break;
+      }
+
+      //Establish a secure session
+      error = tlsConnect(context->controlTlsContext);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Save SSL session
+      error = tlsSaveSession(context->controlTlsContext, &context->tlsSession);
+      //Any error to report?
+      if(error)
+         break;
+
+      //End of exception handling block
+   } while(0);
+
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects
+      tlsFree(context->controlTlsContext);
+      context->controlTlsContext = NULL;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief SSL initialization (data connection)
+ * @param[in] context Pointer to the FTP client context
+ * @return Error code
+ **/
+
+error_t ftpInitDataTlsContext(FtpClientContext *context)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_DEBUG("FTP Client: Initializing SSL session (data)...\r\n");
+
+   //Allocate SSL context
+   context->dataTlsContext = tlsInit();
+
+   //Initialization failed?
+   if(context->dataTlsContext == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Start of exception handling block
+   do
+   {
+      //Select the relevant operation mode
+      error = tlsSetConnectionEnd(context->dataTlsContext, TLS_CONNECTION_END_CLIENT);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Bind SSL to the relevant socket
+      error = tlsSetSocket(context->dataTlsContext, context->dataSocket);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Check whether the SSL initialization callback has been
+      //properly registered?
+      if(context->tlsInitCallback != NULL)
+      {
+         //Perform SSL related initialization
+         error = context->tlsInitCallback(context, context->dataTlsContext);
+         //Any error to report?
+         if(error)
+            break;
+      }
+      else
+      {
+         //No callback function registered
+         error = ERROR_NOT_CONFIGURED;
+         break;
+      }
+
+      //Restore SSL session
+      error = tlsRestoreSession(context->dataTlsContext, &context->tlsSession);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Establish a secure session
+      error = tlsConnect(context->dataTlsContext);
+      //Any error to report?
+      if(error)
+         break;
+
+      //End of exception handling block
+   } while(0);
+
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects
+      tlsFree(context->dataTlsContext);
+      context->dataTlsContext = NULL;
+   }
+
+   //Return status code
+   return error;
+}
+
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ftp/ftp_client.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,233 @@
+/**
+ * @file ftp_client.h
+ * @brief FTP client (File Transfer Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _FTP_CLIENT_H
+#define _FTP_CLIENT_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/socket.h"
+
+//FTP client support
+#ifndef FTP_CLIENT_SUPPORT
+   #define FTP_CLIENT_SUPPORT ENABLED
+#elif (FTP_CLIENT_SUPPORT != ENABLED && FTP_CLIENT_SUPPORT != DISABLED)
+   #error FTP_CLIENT_SUPPORT parameter is not valid
+#endif
+
+//FTP over SSL/TLS
+#ifndef FTP_CLIENT_TLS_SUPPORT
+   #define FTP_CLIENT_TLS_SUPPORT DISABLED
+#elif (FTP_CLIENT_TLS_SUPPORT != ENABLED && FTP_CLIENT_TLS_SUPPORT != DISABLED)
+   #error FTP_CLIENT_TLS_SUPPORT parameter is not valid
+#endif
+
+//Default timeout
+#ifndef FTP_CLIENT_DEFAULT_TIMEOUT
+   #define FTP_CLIENT_DEFAULT_TIMEOUT 20000
+#elif (FTP_CLIENT_DEFAULT_TIMEOUT < 1000)
+   #error FTP_CLIENT_DEFAULT_TIMEOUT parameter is not valid
+#endif
+
+//Size of the buffer for input/output operations
+#ifndef FTP_CLIENT_BUFFER_SIZE
+   #define FTP_CLIENT_BUFFER_SIZE 512
+#elif (FTP_CLIENT_BUFFER_SIZE < 64)
+   #error FTP_CLIENT_BUFFER_SIZE parameter is not valid
+#endif
+
+//Minimum TX buffer size for FTP sockets
+#ifndef FTP_CLIENT_SOCKET_MIN_TX_BUFFER_SIZE
+   #define FTP_CLIENT_SOCKET_MIN_TX_BUFFER_SIZE 1430
+#elif (FTP_CLIENT_SOCKET_MIN_TX_BUFFER_SIZE < 1)
+   #error FTP_CLIENT_SOCKET_MIN_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Minimum RX buffer size for FTP sockets
+#ifndef FTP_CLIENT_SOCKET_MIN_RX_BUFFER_SIZE
+   #define FTP_CLIENT_SOCKET_MIN_RX_BUFFER_SIZE 1430
+#elif (FTP_CLIENT_SOCKET_MIN_RX_BUFFER_SIZE < 1)
+   #error FTP_CLIENT_SOCKET_MIN_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Maximum TX buffer size for FTP sockets
+#ifndef FTP_CLIENT_SOCKET_MAX_TX_BUFFER_SIZE
+   #define FTP_CLIENT_SOCKET_MAX_TX_BUFFER_SIZE 2860
+#elif (FTP_CLIENT_SOCKET_MAX_TX_BUFFER_SIZE < 1)
+   #error FTP_CLIENT_SOCKET_MAX_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Maximum RX buffer size for FTP sockets
+#ifndef FTP_CLIENT_SOCKET_MAX_RX_BUFFER_SIZE
+   #define FTP_CLIENT_SOCKET_MAX_RX_BUFFER_SIZE 2860
+#elif (FTP_CLIENT_SOCKET_MAX_RX_BUFFER_SIZE < 1)
+   #error FTP_CLIENT_SOCKET_MAX_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//SSL/TLS supported?
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+   #include "crypto.h"
+   #include "tls.h"
+#endif
+
+//Test macros for FTP response codes
+#define FTP_REPLY_CODE_1YZ(code) ((code) >= 100 && (code) < 200)
+#define FTP_REPLY_CODE_2YZ(code) ((code) >= 200 && (code) < 300)
+#define FTP_REPLY_CODE_3YZ(code) ((code) >= 300 && (code) < 400)
+#define FTP_REPLY_CODE_4YZ(code) ((code) >= 400 && (code) < 500)
+#define FTP_REPLY_CODE_5YZ(code) ((code) >= 500 && (code) < 600)
+
+//Forward declaration of FtpClientContext structure
+struct _FtpClientContext;
+#define FtpClientContext struct _FtpClientContext
+
+
+/**
+ * @brief Connection options
+ **/
+
+typedef enum
+{
+   FTP_NO_SECURITY       = 0,
+   FTP_IMPLICIT_SECURITY = 1,
+   FTP_EXPLICIT_SECURITY = 2,
+   FTP_ACTIVE_MODE       = 0,
+   FTP_PASSIVE_MODE      = 4
+} FtpConnectionFlags;
+
+
+/**
+ * @brief File opening options
+ **/
+
+typedef enum
+{
+   FTP_FOR_READING   = 0,
+   FTP_FOR_WRITING   = 1,
+   FTP_FOR_APPENDING = 2,
+   FTP_BINARY_TYPE   = 0,
+   FTP_TEXT_TYPE     = 4
+} FtpFileOpeningFlags;
+
+
+/**
+ * @brief Flags used by I/O functions
+ **/
+
+typedef enum
+{
+   FTP_FLAG_PEEK       = 0x0200,
+   FTP_FLAG_WAIT_ALL   = 0x0800,
+   FTP_FLAG_BREAK_CHAR = 0x1000,
+   FTP_FLAG_BREAK_CRLF = 0x100A,
+   FTP_FLAG_WAIT_ACK   = 0x2000
+} FtpFlags;
+
+
+//SSL/TLS supported?
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+
+/**
+ * @brief SSL initialization callback function
+ **/
+
+typedef error_t (*FtpClientTlsInitCallback)(FtpClientContext *context,
+   TlsContext *tlsContext);
+
+#endif
+
+
+/**
+ * @brief FTP client context
+ **/
+
+struct _FtpClientContext
+{
+   NetInterface *interface;                  ///<Underlying network interface
+   IpAddr serverIpAddr;                      ///<IP address of the FTP server
+   bool_t passiveMode;                       ///<Passive mode
+   Socket *controlSocket;                    ///<Control connection socket
+   Socket *dataSocket;                       ///<Data connection socket
+   char_t buffer[FTP_CLIENT_BUFFER_SIZE];    ///<Memory buffer for input/output operations
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+   TlsContext *controlTlsContext;            ///<SSL context (control connection)
+   TlsContext *dataTlsContext;               ///<SSL context (data connection)
+   TlsSession tlsSession;                    ///<SSL session
+   FtpClientTlsInitCallback tlsInitCallback; ///<SSL initialization callback function
+#endif
+};
+
+
+//FTP client related functions
+error_t ftpConnect(FtpClientContext *context, NetInterface *interface,
+   const IpAddr *serverIpAddr, uint16_t serverPort, uint_t flags);
+
+error_t ftpAuth(FtpClientContext *context);
+
+error_t ftpLogin(FtpClientContext *context, const char_t *username,
+   const char_t *password, const char_t *account);
+
+error_t ftpGetWorkingDir(FtpClientContext *context, char_t *path, size_t size);
+error_t ftpChangeWorkingDir(FtpClientContext *context, const char_t *path);
+error_t ftpChangeToParentDir(FtpClientContext *context);
+
+error_t ftpMakeDir(FtpClientContext *context, const char_t *path);
+error_t ftpRemoveDir(FtpClientContext *context, const char_t *path);
+
+error_t ftpOpenFile(FtpClientContext *context, const char_t *path, uint_t flags);
+
+error_t ftpWriteFile(FtpClientContext *context,
+   const void *data, size_t length, uint_t flags);
+
+error_t ftpReadFile(FtpClientContext *context,
+   void *data, size_t size, size_t *length, uint_t flags);
+
+error_t ftpCloseFile(FtpClientContext *context);
+
+error_t ftpRenameFile(FtpClientContext *context,
+   const char_t *oldName, const char_t *newName);
+
+error_t ftpDeleteFile(FtpClientContext *context, const char_t *path);
+
+error_t ftpClose(FtpClientContext *context);
+
+error_t ftpSendCommand(FtpClientContext *context,
+   const char_t *command, uint_t *replyCode);
+
+#if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
+
+error_t ftpRegisterTlsInitCallback(FtpClientContext *context,
+   FtpClientTlsInitCallback callback);
+
+error_t ftpInitControlTlsContext(FtpClientContext *context);
+error_t ftpInitDataTlsContext(FtpClientContext *context);
+
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ftp/ftp_server.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,443 @@
+/**
+ * @file ftp_server.c
+ * @brief FTP server (File Transfer Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * File Transfer Protocol (FTP) is a standard network protocol used to
+ * transfer files from one host to another host over a TCP-based network.
+ * Refer to the following RFCs for complete details:
+ * - RFC 959: File Transfer Protocol (FTP)
+ * - RFC 3659: Extensions to FTP
+ * - RFC 2428: FTP Extensions for IPv6 and NATs
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL FTP_TRACE_LEVEL
+
+//Dependencies
+#include "ftp/ftp_server.h"
+#include "ftp/ftp_server_events.h"
+#include "ftp/ftp_server_commands.h"
+#include "ftp/ftp_server_misc.h"
+#include "str.h"
+#include "path.h"
+#include "error.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (FTP_SERVER_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Initialize settings with default values
+ * @param[out] settings Structure that contains FTP server settings
+ **/
+
+void ftpServerGetDefaultSettings(FtpServerSettings *settings)
+{
+   //The FTP server is not bound to any interface
+   settings->interface = NULL;
+
+   //FTP command port number
+   settings->port = FTP_PORT;
+   //FTP data port number
+   settings->dataPort = FTP_DATA_PORT;
+   //Passive port range
+   settings->passivePortMin = FTP_SERVER_PASSIVE_PORT_MIN;
+   settings->passivePortMax = FTP_SERVER_PASSIVE_PORT_MAX;
+   //Set root directory
+   strcpy(settings->rootDir, "/");
+   //User verification callback function
+   settings->checkUserCallback = NULL;
+   //Password verification callback function
+   settings->checkPasswordCallback = NULL;
+   //Callback used to retrieve file permissions
+   settings->getFilePermCallback = NULL;
+   //Unknown command callback function
+   settings->unknownCommandCallback = NULL;
+}
+
+
+/**
+ * @brief FTP server initialization
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] settings FTP server specific settings
+ * @return Error code
+ **/
+
+error_t ftpServerInit(FtpServerContext *context, const FtpServerSettings *settings)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing FTP server...\r\n");
+
+   //Ensure the parameters are valid
+   if(context == NULL || settings == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Check passive port range
+   if(settings->passivePortMax <= settings->passivePortMin)
+      return ERROR_INVALID_PARAMETER;
+
+   //Clear the FTP server context
+   memset(context, 0, sizeof(FtpServerContext));
+
+   //Save user settings
+   context->settings = *settings;
+
+   //Clean the root directory path
+   pathCanonicalize(context->settings.rootDir);
+   pathRemoveSlash(context->settings.rootDir);
+
+   //Create an event object to poll the state of sockets
+   if(!osCreateEvent(&context->event))
+   {
+      //Failed to create event
+      return ERROR_OUT_OF_RESOURCES;
+   }
+
+   //Start of exception handling block
+   do
+   {
+      //Open a TCP socket
+      context->socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
+      //Failed to open socket?
+      if(!context->socket)
+      {
+         //Report an error
+         error = ERROR_OPEN_FAILED;
+         //Exit immediately
+         break;
+      }
+
+      //Set timeout for blocking functions
+      error = socketSetTimeout(context->socket, INFINITE_DELAY);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Change the size of the TX buffer
+      error = socketSetTxBufferSize(context->socket,
+         FTP_SERVER_CTRL_SOCKET_BUFFER_SIZE);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Change the size of the RX buffer
+      error = socketSetRxBufferSize(context->socket,
+         FTP_SERVER_CTRL_SOCKET_BUFFER_SIZE);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Associate the socket with the relevant interface
+      error = socketBindToInterface(context->socket, settings->interface);
+      //Unable to bind the socket to the desired interface?
+      if(error)
+         break;
+
+      //Bind newly created socket to port 21
+      error = socketBind(context->socket, &IP_ADDR_ANY, settings->port);
+      //Failed to bind socket to port 21?
+      if(error)
+         break;
+
+      //Place socket in listening state
+      error = socketListen(context->socket, FTP_SERVER_BACKLOG);
+      //Any failure to report?
+      if(error)
+         break;
+
+      //End of exception handling block
+   } while(0);
+
+   //Did we encounter an error?
+   if(error)
+   {
+      //Free previously allocated resources
+      osDeleteEvent(&context->event);
+      //Close socket
+      socketClose(context->socket);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Start FTP server
+ * @param[in] context Pointer to the FTP server context
+ * @return Error code
+ **/
+
+error_t ftpServerStart(FtpServerContext *context)
+{
+   OsTask *task;
+
+   //Debug message
+   TRACE_INFO("Starting FTP server...\r\n");
+
+   //Make sure the FTP server context is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Create the FTP server task
+   task = osCreateTask("FTP Server", (OsTaskCode) ftpServerTask,
+      context, FTP_SERVER_STACK_SIZE, FTP_SERVER_PRIORITY);
+
+   //Unable to create the task?
+   if(task == OS_INVALID_HANDLE)
+      return ERROR_OUT_OF_RESOURCES;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set home directory
+ * @param[in] connection Pointer to the client connection
+ * @param[in] homeDir NULL-terminated string specifying the home directory
+ * @return Error code
+ **/
+
+error_t ftpServerSetHomeDir(FtpClientConnection *connection, const char_t *homeDir)
+{
+   //Ensure the parameters are valid
+   if(connection == NULL || homeDir == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Set home directory
+   pathCombine(connection->homeDir, homeDir, FTP_SERVER_MAX_HOME_DIR_LEN);
+   //Clean the resulting path
+   pathCanonicalize(connection->homeDir);
+   pathRemoveSlash(connection->homeDir);
+
+   //Set current directory
+   strcpy(connection->currentDir, connection->homeDir);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief FTP server task
+ * @param[in] context Pointer to the FTP server context
+ **/
+
+void ftpServerTask(FtpServerContext *context)
+{
+   error_t error;
+   uint_t i;
+   systime_t time;
+   FtpClientConnection *connection;
+
+#if (NET_RTOS_SUPPORT == ENABLED)
+   //Process events
+   while(1)
+   {
+#endif
+      //Clear event descriptor set
+      memset(context->eventDesc, 0, sizeof(context->eventDesc));
+
+      //Specify the events the application is interested in
+      for(i = 0; i < FTP_SERVER_MAX_CONNECTIONS; i++)
+      {
+         //Point to the structure describing the current connection
+         connection = context->connection[i];
+
+         //Loop through active connections only
+         if(connection != NULL)
+         {
+            //Ensure the control connection is opened
+            if(connection->controlSocket != NULL)
+            {
+               //Check the state of the control connection
+               if(connection->responseLength > 0)
+               {
+                  //Wait until there is more room in the send buffer
+                  context->eventDesc[2 * i].socket = connection->controlSocket;
+                  context->eventDesc[2 * i].eventMask = SOCKET_EVENT_TX_READY;
+               }
+               else if(connection->controlState == FTP_CONTROL_STATE_WAIT_ACK)
+               {
+                  //Wait for all the data to be transmitted and acknowledged
+                  context->eventDesc[2 * i].socket = connection->controlSocket;
+                  context->eventDesc[2 * i].eventMask = SOCKET_EVENT_TX_ACKED;
+               }
+               else if(connection->controlState == FTP_CONTROL_STATE_SHUTDOWN_TX)
+               {
+                  //Wait for the FIN to be acknowledged
+                  context->eventDesc[2 * i].socket = connection->controlSocket;
+                  context->eventDesc[2 * i].eventMask = SOCKET_EVENT_TX_SHUTDOWN;
+               }
+               else if(connection->controlState == FTP_CONTROL_STATE_SHUTDOWN_RX)
+               {
+                  //Wait for a FIN to be received
+                  context->eventDesc[2 * i].socket = connection->controlSocket;
+                  context->eventDesc[2 * i].eventMask = SOCKET_EVENT_RX_SHUTDOWN;
+               }
+               else
+               {
+                  //Wait for data to be available for reading
+                  context->eventDesc[2 * i].socket = connection->controlSocket;
+                  context->eventDesc[2 * i].eventMask = SOCKET_EVENT_RX_READY;
+               }
+            }
+
+            //Ensure the data connection is opened
+            if(connection->dataSocket != NULL)
+            {
+               //Check the state of the data connection
+               if(connection->dataState == FTP_DATA_STATE_LISTEN ||
+                  connection->dataState == FTP_DATA_STATE_RECEIVE)
+               {
+                  //Wait for data to be available for reading
+                  context->eventDesc[2 * i + 1].socket = connection->dataSocket;
+                  context->eventDesc[2 * i + 1].eventMask = SOCKET_EVENT_RX_READY;
+               }
+               else if(connection->dataState == FTP_DATA_STATE_SEND)
+               {
+                  //Wait until there is more room in the send buffer
+                  context->eventDesc[2 * i + 1].socket = connection->dataSocket;
+                  context->eventDesc[2 * i + 1].eventMask = SOCKET_EVENT_TX_READY;
+               }
+
+               else if(connection->dataState == FTP_DATA_STATE_WAIT_ACK)
+               {
+                  //Wait for all the data to be transmitted and acknowledged
+                  context->eventDesc[2 * i + 1].socket = connection->dataSocket;
+                  context->eventDesc[2 * i + 1].eventMask = SOCKET_EVENT_TX_ACKED;
+               }
+               else if(connection->dataState == FTP_DATA_STATE_SHUTDOWN_TX)
+               {
+                  //Wait for the FIN to be acknowledged
+                  context->eventDesc[2 * i + 1].socket = connection->dataSocket;
+                  context->eventDesc[2 * i + 1].eventMask = SOCKET_EVENT_TX_SHUTDOWN;
+               }
+               else if(connection->dataState == FTP_DATA_STATE_SHUTDOWN_RX)
+               {
+                  //Wait for a FIN to be received
+                  context->eventDesc[2 * i + 1].socket = connection->dataSocket;
+                  context->eventDesc[2 * i + 1].eventMask = SOCKET_EVENT_RX_SHUTDOWN;
+               }
+            }
+         }
+      }
+
+      //Accept connection request events
+      context->eventDesc[2 * i].socket = context->socket;
+      context->eventDesc[2 * i].eventMask = SOCKET_EVENT_RX_READY;
+
+      //Wait for one of the set of sockets to become ready to perform I/O
+      error = socketPoll(context->eventDesc, 2 * FTP_SERVER_MAX_CONNECTIONS + 1,
+         &context->event, FTP_SERVER_SOCKET_POLLING_TIMEOUT);
+
+      //Get current time
+      time = osGetSystemTime();
+
+      //Verify status code
+      if(!error)
+      {
+         //Event-driven processing
+         for(i = 0; i < FTP_SERVER_MAX_CONNECTIONS; i++)
+         {
+            //Point to the structure describing the current connection
+            connection = context->connection[i];
+
+            //Make sure the control connection is still active
+            if(connection != NULL && connection->controlSocket != NULL)
+            {
+               //Check whether the control socket is to ready to perform I/O
+               if(context->eventDesc[2 * i].eventFlags)
+               {
+                  //Update time stamp
+                  connection->timestamp = time;
+                  //Control connection event handler
+                  ftpServerControlEventHandler(context, connection,
+                     context->eventDesc[2 * i].eventFlags);
+               }
+            }
+
+            //The connection may have been closed...
+            connection = context->connection[i];
+
+            //Make sure the data connection is still active
+            if(connection != NULL && connection->dataSocket != NULL)
+            {
+               //Check whether the data socket to is ready to perform I/O
+               if(context->eventDesc[2 * i + 1].eventFlags)
+               {
+                  //Update time stamp
+                  connection->timestamp = time;
+                  //Data connection event handler
+                  ftpServerDataEventHandler(context, connection,
+                     context->eventDesc[2 * i + 1].eventFlags);
+               }
+            }
+         }
+
+         //Check the state of the listening socket
+         if(context->eventDesc[2 * i].eventFlags & SOCKET_EVENT_RX_READY)
+         {
+            //Process incoming connection request
+            connection = ftpServerAcceptControlConnection(context);
+            //Initialize time stamp
+            if(connection != NULL)
+               connection->timestamp = time;
+         }
+      }
+
+      //Manage timeouts
+      for(i = 0; i < FTP_SERVER_MAX_CONNECTIONS; i++)
+      {
+         //Point to the structure describing the current connection
+         connection = context->connection[i];
+
+         //Any client connected?
+         if(connection != NULL)
+         {
+            //Disconnect inactive client after idle timeout
+            if((time - connection->timestamp) >= FTP_SERVER_TIMEOUT)
+            {
+               //Debug message
+               TRACE_INFO("FTP server: Closing inactive connection...\r\n");
+               //Close connection with the client
+               ftpServerCloseConnection(context, connection);
+            }
+         }
+      }
+#if (NET_RTOS_SUPPORT == ENABLED)
+   }
+#endif
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ftp/ftp_server.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,335 @@
+/**
+ * @file ftp_server.h
+ * @brief FTP server (File Transfer Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _FTP_SERVER_H
+#define _FTP_SERVER_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/socket.h"
+#include "fs_port.h"
+
+//FTP server support
+#ifndef FTP_SERVER_SUPPORT
+   #define FTP_SERVER_SUPPORT ENABLED
+#elif (FTP_SERVER_SUPPORT != ENABLED && FTP_SERVER_SUPPORT != DISABLED)
+   #error FTP_SERVER_SUPPORT parameter is not valid
+#endif
+
+//Stack size required to run the FTP server
+#ifndef FTP_SERVER_STACK_SIZE
+   #define FTP_SERVER_STACK_SIZE 650
+#elif (FTP_SERVER_STACK_SIZE < 1)
+   #error FTP_SERVER_STACK_SIZE parameter is not valid
+#endif
+
+//Priority at which the FTP server should run
+#ifndef FTP_SERVER_PRIORITY
+   #define FTP_SERVER_PRIORITY OS_TASK_PRIORITY_NORMAL
+#endif
+
+//Maximum number of simultaneous connections
+#ifndef FTP_SERVER_MAX_CONNECTIONS
+   #define FTP_SERVER_MAX_CONNECTIONS 4
+#elif (FTP_SERVER_MAX_CONNECTIONS < 1)
+   #error FTP_SERVER_MAX_CONNECTIONS parameter is not valid
+#endif
+
+//Maximum time the server will wait before closing the connection
+#ifndef FTP_SERVER_TIMEOUT
+   #define FTP_SERVER_TIMEOUT 60000
+#elif (FTP_SERVER_TIMEOUT < 1000)
+   #error FTP_SERVER_TIMEOUT parameter is not valid
+#endif
+
+//Socket polling timeout
+#ifndef FTP_SERVER_SOCKET_POLLING_TIMEOUT
+   #define FTP_SERVER_SOCKET_POLLING_TIMEOUT 2000
+#elif (FTP_SERVER_SOCKET_POLLING_TIMEOUT < 1000)
+   #error FTP_SERVER_SOCKET_POLLING_TIMEOUT parameter is not valid
+#endif
+
+//Maximum length of the pending connection queue
+#ifndef FTP_SERVER_BACKLOG
+   #define FTP_SERVER_BACKLOG 4
+#elif (FTP_SERVER_BACKLOG < 1)
+   #error FTP_SERVER_BACKLOG parameter is not valid
+#endif
+
+//Maximum line length
+#ifndef FTP_SERVER_MAX_LINE_LEN
+   #define FTP_SERVER_MAX_LINE_LEN 255
+#elif (FTP_SERVER_MAX_LINE_LEN < 64)
+   #error FTP_SERVER_MAX_LINE_LEN parameter is not valid
+#endif
+
+//Size of buffer used for input/output operations
+#ifndef FTP_SERVER_BUFFER_SIZE
+   #define FTP_SERVER_BUFFER_SIZE 1536
+#elif (FTP_SERVER_BUFFER_SIZE < 128)
+   #error FTP_SERVER_BUFFER_SIZE parameter is not valid
+#endif
+
+//Maximum size of root directory
+#ifndef FTP_SERVER_MAX_ROOT_DIR_LEN
+   #define FTP_SERVER_MAX_ROOT_DIR_LEN 63
+#elif (FTP_SERVER_MAX_ROOT_DIR_LEN < 7)
+   #error FTP_SERVER_MAX_ROOT_DIR_LEN parameter is not valid
+#endif
+
+//Maximum size of home directory
+#ifndef FTP_SERVER_MAX_HOME_DIR_LEN
+   #define FTP_SERVER_MAX_HOME_DIR_LEN 63
+#elif (FTP_SERVER_MAX_HOME_DIR_LEN < 7)
+   #error FTP_SERVER_MAX_HOME_DIR_LEN parameter is not valid
+#endif
+
+//Maximum user name length
+#ifndef FTP_SERVER_MAX_USERNAME_LEN
+   #define FTP_SERVER_MAX_USERNAME_LEN 63
+#elif (FTP_SERVER_MAX_USERNAME_LEN < 7)
+   #error FTP_SERVER_MAX_USERNAME_LEN parameter is not valid
+#endif
+
+//Maximum path length
+#ifndef FTP_SERVER_MAX_PATH_LEN
+   #define FTP_SERVER_MAX_PATH_LEN 255
+#elif (FTP_SERVER_MAX_PATH_LEN < 7)
+   #error FTP_SERVER_MAX_PATH_LEN parameter is not valid
+#endif
+
+//Socket buffer size (control connection)
+#ifndef FTP_SERVER_CTRL_SOCKET_BUFFER_SIZE
+   #define FTP_SERVER_CTRL_SOCKET_BUFFER_SIZE 1430
+#elif (FTP_SERVER_CTRL_SOCKET_BUFFER_SIZE < 1)
+   #error FTP_SERVER_CTRL_SOCKET_BUFFER_SIZE parameter is not valid
+#endif
+
+//Socket buffer size (data connection)
+#ifndef FTP_SERVER_DATA_SOCKET_BUFFER_SIZE
+   #define FTP_SERVER_DATA_SOCKET_BUFFER_SIZE 2860
+#elif (FTP_SERVER_DATA_SOCKET_BUFFER_SIZE < 1)
+   #error FTP_SERVER_DATA_SOCKET_BUFFER_SIZE parameter is not valid
+#endif
+
+//Passive port range (lower limit)
+#ifndef FTP_SERVER_PASSIVE_PORT_MIN
+   #define FTP_SERVER_PASSIVE_PORT_MIN 48128
+#elif (FTP_SERVER_PASSIVE_PORT_MIN < 1024)
+   #error FTP_SERVER_PASSIVE_PORT_MIN parameter is not valid
+#endif
+
+//Passive port range (upper limit)
+#ifndef FTP_SERVER_PASSIVE_PORT_MAX
+   #define FTP_SERVER_PASSIVE_PORT_MAX 49151
+#elif (FTP_SERVER_PASSIVE_PORT_MAX <= FTP_SERVER_PASSIVE_PORT_MIN || FTP_SERVER_PASSIVE_PORT_MAX > 65535)
+   #error FTP_SERVER_PASSIVE_PORT_MAX parameter is not valid
+#endif
+
+//FTP port number
+#define FTP_PORT 21
+//FTP data port number
+#define FTP_DATA_PORT 20
+
+//FTPS port number (implicit mode)
+#define FTPS_PORT 990
+//FTPS data port number (implicit mode)
+#define FTPS_DATA_PORT 989
+
+
+/**
+ * @brief Control connection state
+ **/
+
+typedef enum
+{
+   FTP_CONTROL_STATE_CLOSED      = 0,
+   FTP_CONTROL_STATE_IDLE        = 1,
+   FTP_CONTROL_STATE_DISCARD     = 2,
+   FTP_CONTROL_STATE_USER        = 3,
+   FTP_CONTROL_STATE_LIST        = 4,
+   FTP_CONTROL_STATE_RETR        = 5,
+   FTP_CONTROL_STATE_STOR        = 6,
+   FTP_CONTROL_STATE_APPE        = 7,
+   FTP_CONTROL_STATE_RNFR        = 8,
+   FTP_CONTROL_STATE_WAIT_ACK    = 9,
+   FTP_CONTROL_STATE_SHUTDOWN_TX = 10,
+   FTP_CONTROL_STATE_SHUTDOWN_RX = 11
+} FtpControlConnState;
+
+
+/**
+ * @brief Data connection state
+ **/
+
+typedef enum
+{
+   FTP_DATA_STATE_CLOSED      = 0,
+   FTP_DATA_STATE_LISTEN      = 1,
+   FTP_DATA_STATE_IDLE        = 2,
+   FTP_DATA_STATE_SEND        = 3,
+   FTP_DATA_STATE_RECEIVE     = 4,
+   FTP_DATA_STATE_WAIT_ACK    = 5,
+   FTP_DATA_STATE_SHUTDOWN_TX = 6,
+   FTP_DATA_STATE_SHUTDOWN_RX = 7
+} FtpDataConnState;
+
+
+/**
+ * @brief FTP server access status
+ **/
+
+typedef enum
+{
+   FTP_ACCESS_DENIED     = 0,
+   FTP_ACCESS_ALLOWED    = 1,
+   FTP_PASSWORD_REQUIRED = 2
+} FtpAccessStatus;
+
+
+/**
+ * @brief File permissions
+ **/
+
+typedef enum
+{
+   FTP_FILE_PERM_LIST  = 0x01,
+   FTP_FILE_PERM_READ  = 0x02,
+   FTP_FILE_PERM_WRITE = 0x04
+} FtpFilePerm;
+
+
+/**
+ * @brief FTP client connection
+ **/
+
+typedef struct
+{
+   NetInterface *interface;                         ///<Underlying network interface
+   bool_t userLoggedIn;                             ///<This flag tells whether the user is logged in
+   systime_t timestamp;                             ///<Time stamp to manage timeout
+   FtpControlConnState controlState;                ///<Control connection state
+   Socket *controlSocket;                           ///<Control connection socket
+   FtpDataConnState dataState;                      ///<Data connection state
+   Socket *dataSocket;                              ///<Data connection socket
+   FsFile *file;                                    ///<File pointer
+   FsDir *dir;                                      ///<Directory pointer
+   bool_t passiveMode;                              ///<Passive data transfer
+   IpAddr remoteIpAddr;                             ///<Remote IP address
+   uint16_t remotePort;                             ///<Remote port number
+   char_t user[FTP_SERVER_MAX_USERNAME_LEN + 1];    ///<User name
+   char_t homeDir[FTP_SERVER_MAX_HOME_DIR_LEN + 1]; ///<Home directory
+   char_t currentDir[FTP_SERVER_MAX_PATH_LEN + 1];  ///<Current directory
+   char_t path[FTP_SERVER_MAX_PATH_LEN + 1];        ///<Pathname
+   char_t command[FTP_SERVER_MAX_LINE_LEN + 1];     ///<Incoming command
+   size_t commandLength;                            ///<Number of bytes available in the command buffer
+   char_t response[FTP_SERVER_MAX_LINE_LEN + 1];    ///<Response buffer
+   size_t responseLength;                           ///<Number of bytes available in the response buffer
+   size_t responsePos;                              ///<Current position in the response buffer
+   char_t *buffer;                                  ///<Memory buffer for I/O operations
+   size_t bufferLength;                             ///<Number of bytes available in the I/O buffer
+   size_t bufferPos;                                ///<Current position in the I/O buffer
+} FtpClientConnection;
+
+
+/**
+ * @brief User verification callback function
+ **/
+
+typedef uint_t (*FtpCheckUserCallback)(FtpClientConnection *connection,
+   const char_t *user);
+
+
+/**
+ * @brief Password verification callback function
+ **/
+
+typedef uint_t (*FtpCheckPasswordCallback)(FtpClientConnection *connection,
+   const char_t *user, const char_t *password);
+
+
+/**
+ * @brief Callback used to retrieve file permissions
+ **/
+
+typedef uint_t (*FtpGetFilePermCallback)(FtpClientConnection *connection,
+   const char_t *user, const char_t *path);
+
+
+/**
+ * @brief Unknown command callback function
+ **/
+
+typedef error_t (*FtpUnknownCommandCallback)(FtpClientConnection *connection,
+   const char_t *command, const char_t *param);
+
+
+/**
+ * @brief FTP server settings
+ **/
+
+typedef struct
+{
+   NetInterface *interface;                          ///<Underlying network interface
+   uint16_t port;                                    ///<FTP command port number
+   uint16_t dataPort;                                ///<FTP data port number
+   uint16_t passivePortMin;                          ///<Passive port range (lower value)
+   uint16_t passivePortMax;                          ///<Passive port range (upper value)
+   char_t rootDir[FTP_SERVER_MAX_ROOT_DIR_LEN + 1];  ///<Root directory
+   FtpCheckUserCallback checkUserCallback;           ///<User verification callback function
+   FtpCheckPasswordCallback checkPasswordCallback;   ///<Password verification callback function
+   FtpGetFilePermCallback getFilePermCallback;       ///<Callback used to retrieve file permissions
+   FtpUnknownCommandCallback unknownCommandCallback; ///<Unknown command callback function
+} FtpServerSettings;
+
+
+/**
+ * @brief FTP server context
+ **/
+
+typedef struct
+{
+   FtpServerSettings settings;                                    ///<User settings
+   OsEvent event;                                                 ///<Event object used to poll the sockets
+   Socket *socket;                                                ///<Listening socket
+   uint16_t passivePort;                                          ///<Current passive port number
+   FtpClientConnection *connection[FTP_SERVER_MAX_CONNECTIONS];   ///<Client connections
+   SocketEventDesc eventDesc[2 * FTP_SERVER_MAX_CONNECTIONS + 1]; ///<The events the application is interested in
+} FtpServerContext;
+
+
+//FTP server related functions
+void ftpServerGetDefaultSettings(FtpServerSettings *settings);
+error_t ftpServerInit(FtpServerContext *context, const FtpServerSettings *settings);
+error_t ftpServerStart(FtpServerContext *context);
+error_t ftpServerSetHomeDir(FtpClientConnection *connection, const char_t *homeDir);
+
+void ftpServerTask(FtpServerContext *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ftp/ftp_server_commands.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,2326 @@
+/**
+ * @file ftp_server_commands.c
+ * @brief FTP server (command processing)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL FTP_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include "ftp/ftp_server.h"
+#include "ftp/ftp_server_events.h"
+#include "ftp/ftp_server_commands.h"
+#include "ftp/ftp_server_misc.h"
+#include "str.h"
+#include "path.h"
+#include "error.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (FTP_SERVER_SUPPORT == ENABLED)
+
+
+/**
+ * @brief FTP command processing
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ **/
+
+void ftpServerProcessCmd(FtpServerContext *context,
+   FtpClientConnection *connection)
+{
+   size_t n;
+   char_t *p;
+
+   //The <CRLF> sequence should be used to terminate the command line
+   for(n = 0; n < connection->commandLength; n++)
+   {
+      if(connection->command[n] == '\n')
+         break;
+   }
+
+   //Any command to process?
+   if(n < connection->commandLength)
+   {
+      //Properly terminate the string with a NULL character
+      connection->command[n] = '\0';
+      //Remove trailing whitespace from the command line
+      strRemoveTrailingSpace(connection->command);
+
+      //Debug message
+      TRACE_DEBUG("FTP client: %s\r\n", connection->command);
+
+      //Command line too long?
+      if(connection->controlState == FTP_CONTROL_STATE_DISCARD)
+      {
+         //Switch to idle state
+         connection->controlState = FTP_CONTROL_STATE_IDLE;
+         //Format response message
+         strcpy(connection->response, "500 Command line too long\r\n");
+      }
+      else
+      {
+         //The command name and the arguments are separated by one or more spaces
+         for(p = connection->command; *p != '\0' && *p != ' '; p++);
+
+         //Space character found?
+         if(*p == ' ')
+         {
+            //Split the string at the first occurrence of the space character
+            *(p++) = '\0';
+            //Skip extra whitespace
+            while(*p == ' ') p++;
+         }
+
+         //NOOP command received
+         if(!strcasecmp(connection->command, "NOOP"))
+            ftpServerProcessNoop(context, connection, p);
+         //SYST command received
+         else if(!strcasecmp(connection->command, "SYST"))
+            ftpServerProcessSyst(context, connection, p);
+         //FEAT command received?
+         else if(!strcasecmp(connection->command, "FEAT"))
+            ftpServerProcessFeat(context, connection, p);
+         //TYPE command received?
+         else if(!strcasecmp(connection->command, "TYPE"))
+            ftpServerProcessType(context, connection, p);
+         //STRU command received?
+         else if(!strcasecmp(connection->command, "STRU"))
+            ftpServerProcessStru(context, connection, p);
+         //MODE command received?
+         else if(!strcasecmp(connection->command, "MODE"))
+            ftpServerProcessMode(context, connection, p);
+         //USER command received?
+         else if(!strcasecmp(connection->command, "USER"))
+            ftpServerProcessUser(context, connection, p);
+         //PASS command received?
+         else if(!strcasecmp(connection->command, "PASS"))
+            ftpServerProcessPass(context, connection, p);
+         //REIN command received?
+         else if(!strcasecmp(connection->command, "REIN"))
+            ftpServerProcessRein(context, connection, p);
+         //QUIT command received?
+         else if(!strcasecmp(connection->command, "QUIT"))
+            ftpServerProcessQuit(context, connection, p);
+         //PORT command received?
+         else if(!strcasecmp(connection->command, "PORT"))
+            ftpServerProcessPort(context, connection, p);
+         //EPRT command received?
+         else if(!strcasecmp(connection->command, "EPRT"))
+            ftpServerProcessEprt(context, connection, p);
+         //PASV command received?
+         else if(!strcasecmp(connection->command, "PASV"))
+            ftpServerProcessPasv(context, connection, p);
+         //EPSV command received?
+         else if(!strcasecmp(connection->command, "EPSV"))
+            ftpServerProcessEpsv(context, connection, p);
+         //ABOR command received?
+         else if(!strcasecmp(connection->command, "ABOR"))
+            ftpServerProcessAbor(context, connection, p);
+         //PWD command received?
+         else if(!strcasecmp(connection->command, "PWD"))
+            ftpServerProcessPwd(context, connection, p);
+         //LIST command received?
+         else if(!strcasecmp(connection->command, "LIST"))
+            ftpServerProcessList(context, connection, p);
+         //CWD command received?
+         else if(!strcasecmp(connection->command, "CWD"))
+            ftpServerProcessCwd(context, connection, p);
+         //CDUP command received?
+         else if(!strcasecmp(connection->command, "CDUP"))
+            ftpServerProcessCdup(context, connection, p);
+         //MKD command received?
+         else if(!strcasecmp(connection->command, "MKD"))
+            ftpServerProcessMkd(context, connection, p);
+         //RMD command received?
+         else if(!strcasecmp(connection->command, "RMD"))
+            ftpServerProcessRmd(context, connection, p);
+         //SIZE command received?
+         else if(!strcasecmp(connection->command, "SIZE"))
+            ftpServerProcessSize(context, connection, p);
+         //RETR command received?
+         else if(!strcasecmp(connection->command, "RETR"))
+            ftpServerProcessRetr(context, connection, p);
+         //STOR command received?
+         else if(!strcasecmp(connection->command, "STOR"))
+            ftpServerProcessStor(context, connection, p);
+         //APPE command received?
+         else if(!strcasecmp(connection->command, "APPE"))
+            ftpServerProcessAppe(context, connection, p);
+         //RNFR command received?
+         else if(!strcasecmp(connection->command, "RNFR"))
+            ftpServerProcessRnfr(context, connection, p);
+         //RNTO command received?
+         else if(!strcasecmp(connection->command, "RNTO"))
+            ftpServerProcessRnto(context, connection, p);
+         //DELE command received?
+         else if(!strcasecmp(connection->command, "DELE"))
+            ftpServerProcessDele(context, connection, p);
+         //Unknown command received?
+         else
+            ftpServerProcessUnknownCmd(context, connection, p);
+      }
+
+      //Debug message
+      TRACE_DEBUG("FTP server: %s", connection->response);
+
+      //Number of bytes in the response buffer
+      connection->responseLength = strlen(connection->response);
+      connection->responsePos = 0;
+
+      //Clear command line
+      connection->commandLength = 0;
+   }
+   else if(connection->commandLength >= FTP_SERVER_MAX_LINE_LEN)
+   {
+      //The command line is too long...
+      connection->controlState = FTP_CONTROL_STATE_DISCARD;
+      //Drop incoming data
+      connection->commandLength = 0;
+   }
+}
+
+
+/**
+ * @brief Unknown command processing
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessUnknownCmd(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   error_t error;
+
+   //Invoke user-defined callback, if any
+   if(context->settings.unknownCommandCallback != NULL)
+   {
+      //Custom command processing
+      error = context->settings.unknownCommandCallback(connection,
+         connection->command, param);
+   }
+   else
+   {
+      //Report an error
+      error = ERROR_INVALID_COMMAND;
+   }
+
+   //Invalid command received?
+   if(error == ERROR_INVALID_COMMAND)
+   {
+      //Format response message
+      strcpy(connection->response, "500 Command unrecognized\r\n");
+   }
+}
+
+
+/**
+ * @brief NOOP command processing
+ *
+ * The NOOP command does not affect any parameters or previously entered
+ * commands. It specifies no action other than that the server send an OK reply
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessNoop(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   //Send an OK reply
+   strcpy(connection->response, "200 Command okay\r\n");
+}
+
+
+/**
+ * @brief SYST command processing
+ *
+ * The SYST command is used to find out the type of operating system
+ * at the server side
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessSyst(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   //Format the response to the SYST command
+   strcpy(connection->response, "215 UNIX Type: L8\r\n");
+}
+
+
+/**
+ * @brief FEAT command processing
+ *
+ * The FEAT command allows a client to discover which optional
+ * commands a server supports
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessFeat(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   //Format the response to the FEAT command
+   strcpy(connection->response, "211-Features supported:\r\n");
+   strcat(connection->response, " SIZE\r\n");
+   strcat(connection->response, " EPRT\r\n");
+   strcat(connection->response, " EPSV\r\n");
+   strcat(connection->response, "211 End\r\n");
+}
+
+
+/**
+ * @brief TYPE command processing
+ *
+ * The TYPE command specifies the representation type
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessType(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   //The argument specifies the representation type
+   if(*param != '\0')
+   {
+      //ASCII type?
+      if(!strcasecmp(param, "A"))
+      {
+         //Format the response to the TYPE command
+         strcpy(connection->response, "200 Type set to A\r\n");
+      }
+      //Image type?
+      else if(!strcasecmp(param, "I"))
+      {
+         //Format the response to the TYPE command
+         strcpy(connection->response, "200 Type set to I\r\n");
+      }
+      //Unknown type?
+      else
+      {
+         //Report an error
+         strcpy(connection->response, "504 Unknown type\r\n");
+      }
+   }
+   else
+   {
+      //The argument is missing...
+      strcpy(connection->response, "501 Missing parameter\r\n");
+   }
+}
+
+
+/**
+ * @brief STRU command processing
+ *
+ * The STRU command specifies the file structure
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessStru(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   //The argument specifies the file structure
+   if(*param != '\0')
+   {
+      //No record structure?
+      if(!strcasecmp(param, "F"))
+      {
+         //Format the response to the STRU command
+         strcpy(connection->response, "200 Structure set to F\r\n");
+      }
+      //Unknown file structure?
+      else
+      {
+         //Report an error
+         strcpy(connection->response, "504 Unknown structure\r\n");
+      }
+   }
+   else
+   {
+      //The argument is missing...
+      strcpy(connection->response, "501 Missing parameter\r\n");
+   }
+}
+
+
+/**
+ * @brief MODE command processing
+ *
+ * The MODE command specifies the data transfer mode
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessMode(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   //The argument specifies the data transfer mode
+   if(*param != '\0')
+   {
+      //Stream mode?
+      if(!strcasecmp(param, "S"))
+      {
+         //Format the response to the MODE command
+         strcpy(connection->response, "200 Mode set to S\r\n");
+      }
+      //Unknown data transfer mode?
+      else
+      {
+         //Report an error
+         strcpy(connection->response, "504 Unknown mode\r\n");
+      }
+   }
+   else
+   {
+      //The argument is missing...
+      strcpy(connection->response, "501 Missing parameter\r\n");
+   }
+}
+
+
+/**
+ * @brief USER command processing
+ *
+ * The USER command is used to identify the user
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessUser(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   uint_t status;
+
+   //The argument specifies the user name
+   if(*param == '\0')
+   {
+      //The argument is missing...
+      strcpy(connection->response, "501 Missing parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Check the length of the user name
+   if(strlen(param) > FTP_SERVER_MAX_USERNAME_LEN)
+   {
+      //The specified user name is not valid...
+      strcpy(connection->response, "501 Invalid parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Save user name
+   strcpy(connection->user, param);
+   //Log out the user
+   connection->userLoggedIn = FALSE;
+   //Set home directory
+   strcpy(connection->homeDir, context->settings.rootDir);
+   //Set current directory
+   strcpy(connection->currentDir, context->settings.rootDir);
+
+   //Invoke user-defined callback, if any
+   if(context->settings.checkUserCallback != NULL)
+      status = context->settings.checkUserCallback(connection, param);
+   else
+      status = FTP_ACCESS_ALLOWED;
+
+   //Access allowed?
+   if(status == FTP_ACCESS_ALLOWED)
+   {
+      //The user is now logged in
+      connection->userLoggedIn = TRUE;
+      //Format response message
+      strcpy(connection->response, "230 User logged in, proceed\r\n");
+   }
+   //Password required?
+   else if(status == FTP_PASSWORD_REQUIRED)
+   {
+      //This command must be immediately followed by a PASS command
+      connection->controlState = FTP_CONTROL_STATE_USER;
+      //Format response message
+      strcpy(connection->response, "331 User name okay, need password\r\n");
+   }
+   //Access denied?
+   else
+   {
+      //Format response message
+      strcpy(connection->response, "530 Login authentication failed\r\n");
+   }
+}
+
+
+/**
+ * @brief PASS command processing
+ *
+ * The USER command specifies the user's password
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessPass(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   uint_t status;
+
+   //This command must immediately follow a USER command
+   if(connection->controlState != FTP_CONTROL_STATE_USER)
+   {
+      //Switch to idle state
+      connection->controlState = FTP_CONTROL_STATE_IDLE;
+      //Report an error
+      strcpy(connection->response, "503 Bad sequence of commands\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Switch to idle state
+   connection->controlState = FTP_CONTROL_STATE_IDLE;
+
+   //The argument specifies the password
+   if(*param == '\0')
+   {
+      //The argument is missing...
+      strcpy(connection->response, "501 Missing parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Invoke user-defined callback, if any
+   if(context->settings.checkPasswordCallback != NULL)
+      status = context->settings.checkPasswordCallback(connection, connection->user, param);
+   else
+      status = FTP_ACCESS_ALLOWED;
+
+   //Access allowed?
+   if(status == FTP_ACCESS_ALLOWED)
+   {
+      //The user is now logged in
+      connection->userLoggedIn = TRUE;
+      //Format response message
+      strcpy(connection->response, "230 User logged in, proceed\r\n");
+   }
+   //Access denied?
+   else
+   {
+      //Format response message
+      strcpy(connection->response, "530 Login authentication failed\r\n");
+   }
+}
+
+
+/**
+ * @brief REIN command processing
+ *
+ * The REIN command is used to reinitialize a user session
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessRein(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   //Close data connection
+   ftpServerCloseDataConnection(connection);
+
+   //Release previously allocated resources
+   if(connection->file != NULL)
+   {
+      fsCloseFile(connection->file);
+      connection->file = NULL;
+   }
+   if(connection->dir != NULL)
+   {
+      fsCloseDir(connection->dir);
+      connection->dir = NULL;
+   }
+
+   //Clear account information
+   connection->userLoggedIn = FALSE;
+
+   //Format response message
+   strcpy(connection->response, "220 Service ready for new user\r\n");
+}
+
+
+/**
+ * @brief QUIT command processing
+ *
+ * The QUIT command is used to terminate a user session
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessQuit(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   //There are two cases to consider upon receipt of this command
+   if(connection->dataState == FTP_DATA_STATE_CLOSED)
+   {
+      //If the FTP service command was already completed, the server closes
+      //the data connection (if it is open)...
+      ftpServerCloseDataConnection(connection);
+
+      //...and responds with a 221 reply
+      strcpy(connection->response, "221 Service closing control connection\r\n");
+   }
+   else
+   {
+      //If the FTP service command is still in progress, the server aborts
+      //the FTP service in progress and closes the data connection...
+      ftpServerCloseDataConnection(connection);
+
+      //...returning a 426 reply to indicate that the service request
+      //terminated abnormally
+      strcpy(connection->response, "426 Connection closed; transfer aborted\r\n");
+
+      //The server then sends a 221 reply
+      strcat(connection->response, "221 Service closing control connection\r\n");
+   }
+
+   //Release previously allocated resources
+   if(connection->file != NULL)
+   {
+      fsCloseFile(connection->file);
+      connection->file = NULL;
+   }
+   if(connection->dir != NULL)
+   {
+      fsCloseDir(connection->dir);
+      connection->dir = NULL;
+   }
+
+   //Clear account information
+   connection->userLoggedIn = FALSE;
+   //Gracefully disconnect from the remote host
+   connection->controlState = FTP_CONTROL_STATE_WAIT_ACK;
+}
+
+
+/**
+ * @brief PORT command processing
+ *
+ * The PORT command specifies the data port to be used for the data connection
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessPort(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   error_t error;
+   size_t i;
+   size_t j;
+   char_t *p;
+   char_t *token;
+   char_t *end;
+
+   //Ensure the user is logged in
+   if(!connection->userLoggedIn)
+   {
+      //Format response message
+      strcpy(connection->response, "530 Not logged in\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //The argument is the concatenation of the IP address and the 16-bit port number
+   if(*param == '\0')
+   {
+      //The argument is missing...
+      strcpy(connection->response, "501 Missing parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Close the data connection, if any
+   ftpServerCloseDataConnection(connection);
+
+   //Start of exception handling block
+   do
+   {
+      //Assume an error condition...
+      error = ERROR_INVALID_SYNTAX;
+
+      //Parse the string
+      for(i = 0, j = 1; param[i] != '\0'; i++)
+      {
+         //Change commas to dots
+         if(param[i] == ',' && j < sizeof(Ipv4Addr))
+         {
+            param[i] = '.';
+            j++;
+         }
+      }
+
+      //Get the IP address to be used
+      token = strtok_r(param, ",", &p);
+      //Syntax error?
+      if(token == NULL)
+         break;
+
+      //Convert the dot-decimal string to a binary IP address
+      error = ipStringToAddr(token, &connection->remoteIpAddr);
+      //Invalid IP address?
+      if(error)
+         break;
+
+      //Assume an error condition...
+      error = ERROR_INVALID_SYNTAX;
+
+      //Get the most significant byte of the port number
+      token = strtok_r(NULL, ",", &p);
+      //Syntax error?
+      if(token == NULL)
+         break;
+
+      //Convert the string representation to integer
+      connection->remotePort = strtoul(token, &end, 10) << 8;
+      //Syntax error?
+      if(*end != '\0')
+         break;
+
+      //Get the least significant byte of the port number
+      token = strtok_r(NULL, ",", &p);
+      //Syntax error?
+      if(token == NULL)
+         break;
+
+      //Convert the string representation to integer
+      connection->remotePort |= strtoul(token, &end, 10) & 0xFF;
+      //Syntax error?
+      if(*end != '\0')
+         break;
+
+      //Successful processing
+      error = NO_ERROR;
+
+      //End of exception handling block
+   } while(0);
+
+   //Any error to report?
+   if(error)
+   {
+      //Re initialize data connection
+      connection->passiveMode = FALSE;
+      connection->remotePort = 0;
+
+      //Format response message
+      strcpy(connection->response, "501 Syntax error in parameters or arguments\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Successful processing
+   strcpy(connection->response, "200 Command okay\r\n");
+}
+
+
+/**
+ * @brief EPRT command processing
+ *
+ * The EPRT command allows for the specification of an extended address
+ * for the data connection
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessEprt(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   error_t error;
+   uint_t protocol;
+   char_t *p;
+   char_t *token;
+   char_t *end;
+   char_t delimiter[2];
+
+   //Ensure the user is logged in
+   if(!connection->userLoggedIn)
+   {
+      //Format response message
+      strcpy(connection->response, "530 Not logged in\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //The extended address must consist of the network protocol
+   //as well as the IP address and the 16-bit port number
+   if(*param == '\0')
+   {
+      //The argument is missing...
+      strcpy(connection->response, "501 Missing parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Close the data connection, if any
+   ftpServerCloseDataConnection(connection);
+
+   //Start of exception handling block
+   do
+   {
+      //A delimiter character must be specified
+      delimiter[0] = param[0];
+      delimiter[1] = '\0';
+      //Skip delimiter character
+      param++;
+
+      //Assume an error condition...
+      error = ERROR_INVALID_SYNTAX;
+
+      //Retrieve the network protocol to be used
+      token = strtok_r(param, delimiter, &p);
+      //Syntax error?
+      if(token == NULL)
+         break;
+
+      //Convert the string representation to integer
+      protocol = strtoul(token, &end, 10);
+      //Syntax error?
+      if(*end != '\0')
+         break;
+
+      //Get the IP address to be used
+      token = strtok_r(NULL, delimiter, &p);
+      //Syntax error?
+      if(token == NULL)
+         break;
+
+#if (IPV4_SUPPORT == ENABLED)
+      //IPv4 address family?
+      if(protocol == 1)
+      {
+         //IPv4 addresses are 4-byte long
+         connection->remoteIpAddr.length = sizeof(Ipv4Addr);
+         //Convert the string to IPv4 address
+         error = ipv4StringToAddr(token, &connection->remoteIpAddr.ipv4Addr);
+         //Invalid IP address?
+         if(error)
+            break;
+      }
+      else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+      //IPv6 address family?
+      if(protocol == 2)
+      {
+         //IPv6 addresses are 16-byte long
+         connection->remoteIpAddr.length = sizeof(Ipv6Addr);
+         //Convert the string to IPv6 address
+         error = ipv6StringToAddr(token, &connection->remoteIpAddr.ipv6Addr);
+         //Invalid IP address?
+         if(error)
+            break;
+      }
+      else
+#endif
+      //Unknown address family?
+      {
+         //Report an error
+         error = ERROR_INVALID_ADDRESS;
+         //Exit immediately
+         break;
+      }
+
+      //Assume an error condition...
+      error = ERROR_INVALID_SYNTAX;
+
+      //Get the port number to be used
+      token = strtok_r(NULL, delimiter, &p);
+      //Syntax error?
+      if(token == NULL)
+         break;
+
+      //Convert the string representation to integer
+      connection->remotePort = strtoul(token, &end, 10);
+      //Syntax error?
+      if(*end != '\0')
+         break;
+
+      //Successful processing
+      error = NO_ERROR;
+
+      //End of exception handling block
+   } while(0);
+
+   //Any error to report?
+   if(error)
+   {
+      //Re initialize data connection
+      connection->passiveMode = FALSE;
+      connection->remotePort = 0;
+
+      //Format response message
+      strcpy(connection->response, "501 Syntax error in parameters or arguments\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Successful processing
+   strcpy(connection->response, "200 Command okay\r\n");
+}
+
+
+/**
+ * @brief PASV command processing
+ *
+ * The PASV command requests the server to listen on a data port and
+ * to wait for a connection rather than initiate one upon receipt of
+ * a transfer command
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessPasv(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   error_t error;
+   size_t n;
+   IpAddr ipAddr;
+   uint16_t port;
+
+   //Ensure the user is logged in
+   if(!connection->userLoggedIn)
+   {
+      //Format response message
+      strcpy(connection->response, "530 Not logged in\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Close the data connection, if any
+   ftpServerCloseDataConnection(connection);
+
+   //Get the next passive port number to be used
+   port = ftpServerGetPassivePort(context);
+
+   //Start of exception handling block
+   do
+   {
+      //Open data socket
+      connection->dataSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
+      //Failed to open socket?
+      if(!connection->dataSocket)
+      {
+         //Report an error
+         error = ERROR_OPEN_FAILED;
+         //Exit immediately
+         break;
+      }
+
+      //Force the socket to operate in non-blocking mode
+      error = socketSetTimeout(connection->dataSocket, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Change the size of the TX buffer
+      error = socketSetTxBufferSize(connection->dataSocket,
+         FTP_SERVER_DATA_SOCKET_BUFFER_SIZE);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Change the size of the RX buffer
+      error = socketSetRxBufferSize(connection->dataSocket,
+         FTP_SERVER_DATA_SOCKET_BUFFER_SIZE);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Associate the socket with the relevant interface
+      error = socketBindToInterface(connection->dataSocket, connection->interface);
+      //Unable to bind the socket to the desired interface?
+      if(error)
+         break;
+
+      //Bind the socket to the passive port number
+      error = socketBind(connection->dataSocket, &IP_ADDR_ANY, port);
+      //Failed to bind the socket to the desired port?
+      if(error)
+         break;
+
+      //Place the data socket in the listening state
+      error = socketListen(connection->dataSocket, 1);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Retrieve local IP address
+      error = socketGetLocalAddr(connection->controlSocket, &ipAddr, NULL);
+      //Any error to report?
+      if(error)
+         break;
+
+      //The local IP address must be a valid IPv4 address
+      if(ipAddr.length != sizeof(Ipv4Addr))
+      {
+         //PASV command cannot be used on IPv6 connections
+         error = ERROR_INVALID_ADDRESS;
+         //Exit immediately
+         break;
+      }
+
+      //End of exception handling block
+   } while(0);
+
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects
+      ftpServerCloseDataConnection(connection);
+      //Format response message
+      strcpy(connection->response, "425 Can't enter passive mode\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Use passive data transfer
+   connection->passiveMode = TRUE;
+   //Update data connection state
+   connection->dataState = FTP_DATA_STATE_LISTEN;
+
+#if defined(FTP_SERVER_PASV_HOOK)
+   FTP_SERVER_PASV_HOOK(connection, ipAddr);
+#endif
+
+   //Format response message
+   n = sprintf(connection->response, "227 Entering passive mode (");
+   //Append host address
+   ipAddrToString(&ipAddr, connection->response + n);
+
+   //Parse the resulting string
+   for(n = 0; connection->response[n] != '\0'; n++)
+   {
+      //Change dots to commas
+      if(connection->response[n] == '.')
+         connection->response[n] = ',';
+   }
+
+   //Append port number
+   sprintf(connection->response + n, ",%" PRIu8 ",%" PRIu8 ")\r\n", MSB(port), LSB(port));
+}
+
+
+/**
+ * @brief EPSV command processing
+ *
+ * The EPSV command requests that a server listen on a data port and
+ * wait for a connection
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessEpsv(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   error_t error;
+   uint16_t port;
+
+   //Ensure the user is logged in
+   if(!connection->userLoggedIn)
+   {
+      //Format response message
+      strcpy(connection->response, "530 Not logged in\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Close the data connection, if any
+   ftpServerCloseDataConnection(connection);
+
+   //Get the next passive port number to be used
+   port = ftpServerGetPassivePort(context);
+
+   //Start of exception handling block
+   do
+   {
+      //Open data socket
+      connection->dataSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
+      //Failed to open socket?
+      if(!connection->dataSocket)
+      {
+         //Report an error
+         error = ERROR_OPEN_FAILED;
+         //Exit immediately
+         break;
+      }
+
+      //Force the socket to operate in non-blocking mode
+      error = socketSetTimeout(connection->dataSocket, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Change the size of the TX buffer
+      error = socketSetTxBufferSize(connection->dataSocket,
+         FTP_SERVER_DATA_SOCKET_BUFFER_SIZE);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Change the size of the RX buffer
+      error = socketSetRxBufferSize(connection->dataSocket,
+         FTP_SERVER_DATA_SOCKET_BUFFER_SIZE);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Associate the socket with the relevant interface
+      error = socketBindToInterface(connection->dataSocket, connection->interface);
+      //Unable to bind the socket to the desired interface?
+      if(error)
+         break;
+
+      //Bind the socket to the passive port number
+      error = socketBind(connection->dataSocket, &IP_ADDR_ANY, port);
+      //Failed to bind the socket to the desired port?
+      if(error)
+         break;
+
+      //Place the data socket in the listening state
+      error = socketListen(connection->dataSocket, 1);
+      //Any error to report?
+      if(error)
+         break;
+
+      //End of exception handling block
+   } while(0);
+
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects
+      ftpServerCloseDataConnection(connection);
+      //Format response message
+      strcpy(connection->response, "425 Can't enter passive mode\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Use passive data transfer
+   connection->passiveMode = TRUE;
+   //Update data connection state
+   connection->dataState = FTP_DATA_STATE_LISTEN;
+
+   //The response code for entering passive mode using an extended address must be 229
+   sprintf(connection->response, "229 Entering extended passive mode (|||%" PRIu16 "|)\r\n",
+      port);
+}
+
+
+/**
+ * @brief ABOR command processing
+ *
+ * The ABOR command tells the server to abort the previous FTP
+ * service command and any associated transfer of data
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessAbor(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   //There are two cases to consider upon receipt of this command
+   if(connection->dataState == FTP_DATA_STATE_CLOSED)
+   {
+      //If the FTP service command was already completed, the server closes
+      //the data connection (if it is open)...
+      ftpServerCloseDataConnection(connection);
+
+      //...and responds with a 226 reply, indicating that the abort command
+      //was successfully processed
+      strcpy(connection->response, "226 Abort command successful\r\n");
+   }
+   else
+   {
+      //If the FTP service command is still in progress, the server aborts
+      //the FTP service in progress and closes the data connection...
+      ftpServerCloseDataConnection(connection);
+
+      //...returning a 426 reply to indicate that the service request
+      //terminated abnormally
+      strcpy(connection->response, "426 Connection closed; transfer aborted\r\n");
+
+      //The server then sends a 226 reply, indicating that the abort command
+      //was successfully processed
+      strcat(connection->response, "226 Abort command successful\r\n");
+   }
+
+   //Release previously allocated resources
+   if(connection->file != NULL)
+   {
+      fsCloseFile(connection->file);
+      connection->file = NULL;
+   }
+   if(connection->dir != NULL)
+   {
+      fsCloseDir(connection->dir);
+      connection->dir = NULL;
+   }
+}
+
+
+/**
+ * @brief PWD command processing
+ *
+ * The PWD command causes the name of the current working
+ * directory to be returned in the reply
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessPwd(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   //Ensure the user is logged in
+   if(!connection->userLoggedIn)
+   {
+      //Format response message
+      strcpy(connection->response, "530 Not logged in\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //A successful PWD command uses the 257 reply code
+   sprintf(connection->response, "257 \"%s\" is current directory\r\n",
+      ftpServerStripHomeDir(connection, connection->currentDir));
+}
+
+
+/**
+ * @brief CWD command processing
+ *
+ * The CWD command allows the user to work with a different
+ * directory
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessCwd(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   error_t error;
+   uint_t perm;
+
+   //Ensure the user is logged in
+   if(!connection->userLoggedIn)
+   {
+      //Format response message
+      strcpy(connection->response, "530 Not logged in\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //The argument specifies the pathname
+   if(*param == '\0')
+   {
+      //The argument is missing...
+      strcpy(connection->response, "501 Missing parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve the full pathname
+   error = ftpServerGetPath(connection, param,
+      connection->path, FTP_SERVER_MAX_PATH_LEN);
+
+   //Make sure the pathname is valid
+   if(error)
+   {
+      //Report an error
+      strcpy(connection->response, "501 Invalid parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve permissions for the specified directory
+   perm = ftpServerGetFilePermissions(context, connection, connection->path);
+
+   //Insufficient access rights?
+   if(!(perm & FTP_FILE_PERM_READ))
+   {
+      //Report an error
+      strcpy(connection->response, "550 Access denied\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Make sure the specified directory exists
+   if(!fsDirExists(connection->path))
+   {
+      //Report an error
+      strcpy(connection->response, "550 Directory not found\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Change current working directory
+   strcpy(connection->currentDir, connection->path);
+
+   //A successful PWD command uses the 250 reply code
+   sprintf(connection->response, "250 Directory changed to %s\r\n",
+      ftpServerStripHomeDir(connection, connection->currentDir));
+}
+
+
+/**
+ * @brief CDUP command processing
+ *
+ * The CDUP command allows the user to change to the parent directory
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessCdup(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   uint_t perm;
+
+   //Ensure the user is logged in
+   if(!connection->userLoggedIn)
+   {
+      //Format response message
+      strcpy(connection->response, "530 Not logged in\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Get current directory
+   strcpy(connection->path, connection->currentDir);
+
+   //Change to the parent directory
+   pathCombine(connection->path, "..", FTP_SERVER_MAX_PATH_LEN);
+   pathCanonicalize(connection->path);
+
+   //Retrieve permissions for the directory
+   perm = ftpServerGetFilePermissions(context, connection, connection->path);
+
+   //Check access rights
+   if(perm & FTP_FILE_PERM_READ)
+   {
+      //Update current directory
+      strcpy(connection->currentDir, connection->path);
+   }
+
+   //A successful PWD command uses the 250 reply code
+   sprintf(connection->response, "250 Directory changed to %s\r\n",
+      ftpServerStripHomeDir(connection, connection->currentDir));
+}
+
+
+/**
+ * @brief LIST command processing
+ *
+ * The LIST command is used to list the content of a directory
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessList(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   error_t error;
+   uint_t perm;
+
+   //Ensure the user is logged in
+   if(!connection->userLoggedIn)
+   {
+      //Format response message
+      strcpy(connection->response, "530 Not logged in\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Any option flags
+   while(*param == '-')
+   {
+      //Skip option flags
+      while(*param != ' ' && *param != '\0')
+         param++;
+      //Skip whitespace characters
+      while(*param == ' ')
+         param++;
+   }
+
+   //The pathname is optional
+   if(*param == '\0')
+   {
+      //Use current directory if no pathname is specified
+      strcpy(connection->path, connection->currentDir);
+   }
+   else
+   {
+      //Retrieve the full pathname
+      error = ftpServerGetPath(connection, param,
+         connection->path, FTP_SERVER_MAX_PATH_LEN);
+
+      //Any error to report?
+      if(error)
+      {
+         //The specified pathname is not valid...
+         strcpy(connection->response, "501 Invalid parameter\r\n");
+         //Exit immediately
+         return;
+      }
+   }
+
+   //Retrieve permissions for the specified directory
+   perm = ftpServerGetFilePermissions(context, connection, connection->path);
+
+   //Insufficient access rights?
+   if(!(perm & FTP_FILE_PERM_READ))
+   {
+      //Report an error
+      strcpy(connection->response, "550 Access denied\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Open specified directory for reading
+   connection->dir = fsOpenDir(connection->path);
+
+   //Failed to open the directory?
+   if(!connection->dir)
+   {
+      //Report an error
+      strcpy(connection->response, "550 Directory not found\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Check current data transfer mode
+   if(connection->passiveMode)
+   {
+      //Check whether the data connection is already opened
+      if(connection->dataState == FTP_DATA_STATE_IDLE)
+         connection->dataState = FTP_DATA_STATE_SEND;
+   }
+   else
+   {
+      //Open the data connection
+      error = ftpServerOpenDataConnection(context, connection);
+
+      //Any error to report?
+      if(error)
+      {
+         //Clean up side effects
+         fsCloseDir(connection->dir);
+         //Format response
+         strcpy(connection->response, "450 Can't open data connection\r\n");
+         //Exit immediately
+         return;
+      }
+
+      //The data connection is ready to send data
+      connection->dataState = FTP_DATA_STATE_SEND;
+   }
+
+   //Flush transmission buffer
+   connection->bufferLength = 0;
+   connection->bufferPos = 0;
+
+   //LIST command is being processed
+   connection->controlState = FTP_CONTROL_STATE_LIST;
+
+   //Format response message
+   strcpy(connection->response, "150 Opening data connection\r\n");
+}
+
+
+/**
+ * @brief MKD command processing
+ *
+ * The MKD command causes the directory specified in the pathname
+ * to be created as a directory
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessMkd(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   error_t error;
+   uint_t perm;
+
+   //Ensure the user is logged in
+   if(!connection->userLoggedIn)
+   {
+      //Format response message
+      strcpy(connection->response, "530 Not logged in\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //The argument specifies the pathname
+   if(*param == '\0')
+   {
+      //The argument is missing...
+      strcpy(connection->response, "501 Missing parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve the full pathname
+   error = ftpServerGetPath(connection, param,
+      connection->path, FTP_SERVER_MAX_PATH_LEN);
+
+   //Any error to report?
+   if(error)
+   {
+      //The specified pathname is not valid...
+      strcpy(connection->response, "501 Invalid parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve permissions for the specified directory
+   perm = ftpServerGetFilePermissions(context, connection, connection->path);
+
+   //Insufficient access rights?
+   if(!(perm & FTP_FILE_PERM_WRITE))
+   {
+      //Report an error
+      strcpy(connection->response, "550 Access denied\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Create the specified directory
+   error = fsCreateDir(connection->path);
+
+   //Any error to report?
+   if(error)
+   {
+      //The specified pathname is not valid...
+      strcpy(connection->response, "550 Can't create directory\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //The specified directory was successfully created
+   sprintf(connection->response, "257 \"%s\" created\r\n",
+      ftpServerStripHomeDir(connection, connection->path));
+}
+
+
+/**
+ * @brief RMD command processing
+ *
+ * The RMD command causes the directory specified in the pathname
+ * to be removed
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessRmd(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   error_t error;
+   uint_t perm;
+
+   //Ensure the user is logged in
+   if(!connection->userLoggedIn)
+   {
+      //Format response message
+      strcpy(connection->response, "530 Not logged in\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //The argument specifies the directory to be removed
+   if(*param == '\0')
+   {
+      //The argument is missing...
+      strcpy(connection->response, "501 Missing parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve the full pathname of the directory
+   error = ftpServerGetPath(connection, param,
+      connection->path, FTP_SERVER_MAX_PATH_LEN);
+
+   //Any error to report?
+   if(error)
+   {
+      //The specified pathname is not valid...
+      strcpy(connection->response, "501 Invalid parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve permissions for the specified directory
+   perm = ftpServerGetFilePermissions(context, connection, connection->path);
+
+   //Insufficient access rights?
+   if(!(perm & FTP_FILE_PERM_WRITE))
+   {
+      //Report an error
+      strcpy(connection->response, "550 Access denied\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Remove the specified directory
+   error = fsRemoveDir(connection->path);
+
+   //Any error to report?
+   if(error)
+   {
+      //The specified directory cannot be deleted...
+      strcpy(connection->response, "550 Can't remove directory\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //The specified directory was successfully removed
+   strcpy(connection->response, "250 Directory removed\r\n");
+}
+
+
+/**
+ * @brief SIZE command processing
+ *
+ * The SIZE command is used to obtain the transfer size of the specified file
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessSize(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   error_t error;
+   uint_t perm;
+   uint32_t size;
+
+   //Ensure the user is logged in
+   if(!connection->userLoggedIn)
+   {
+      //Format response message
+      strcpy(connection->response, "530 Not logged in\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //The argument specifies the pathname of the file
+   if(*param == '\0')
+   {
+      //The argument is missing...
+      strcpy(connection->response, "501 Missing parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve the full pathname
+   error = ftpServerGetPath(connection, param,
+      connection->path, FTP_SERVER_MAX_PATH_LEN);
+
+   //Any error to report?
+   if(error)
+   {
+      //The specified pathname is not valid...
+      strcpy(connection->response, "501 Invalid parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve permissions for the specified directory
+   perm = ftpServerGetFilePermissions(context, connection, connection->path);
+
+   //Insufficient access rights?
+   if(!(perm & FTP_FILE_PERM_LIST) && !(perm & FTP_FILE_PERM_READ))
+   {
+      //Report an error
+      strcpy(connection->response, "550 Access denied\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve the size of the specified file
+   error = fsGetFileSize(connection->path, &size);
+
+   //Any error to report?
+   if(error)
+   {
+      //Report an error
+      strcpy(connection->response, "550 File not found\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Format response message
+   sprintf(connection->response, "213 %" PRIu32 "\r\n", size);
+}
+
+
+/**
+ * @brief RETR command processing
+ *
+ * The RETR command is used to retrieve the content of the specified file
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessRetr(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   error_t error;
+   uint_t perm;
+
+   //Ensure the user is logged in
+   if(!connection->userLoggedIn)
+   {
+      //Format response message
+      strcpy(connection->response, "530 Not logged in\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //The argument specifies the pathname of the file to read
+   if(*param == '\0')
+   {
+      //The argument is missing...
+      strcpy(connection->response, "501 Missing parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve the full pathname
+   error = ftpServerGetPath(connection, param,
+      connection->path, FTP_SERVER_MAX_PATH_LEN);
+
+   //Any error to report?
+   if(error)
+   {
+      //The specified pathname is not valid...
+      strcpy(connection->response, "501 Invalid parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve permissions for the specified directory
+   perm = ftpServerGetFilePermissions(context, connection, connection->path);
+
+   //Insufficient access rights?
+   if(!(perm & FTP_FILE_PERM_READ))
+   {
+      //Report an error
+      strcpy(connection->response, "550 Access denied\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Open specified file for reading
+   connection->file = fsOpenFile(connection->path, FS_FILE_MODE_READ);
+
+   //Failed to open the file?
+   if(!connection->file)
+   {
+      //Report an error
+      strcpy(connection->response, "550 File not found\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Check current data transfer mode
+   if(connection->passiveMode)
+   {
+      //Check whether the data connection is already opened
+      if(connection->dataState == FTP_DATA_STATE_IDLE)
+         connection->dataState = FTP_DATA_STATE_SEND;
+   }
+   else
+   {
+      //Open the data connection
+      error = ftpServerOpenDataConnection(context, connection);
+
+      //Any error to report?
+      if(error)
+      {
+         //Clean up side effects
+         fsCloseFile(connection->file);
+         //Format response
+         strcpy(connection->response, "450 Can't open data connection\r\n");
+         //Exit immediately
+         return;
+      }
+
+      //The data connection is ready to send data
+      connection->dataState = FTP_DATA_STATE_SEND;
+   }
+
+   //Flush transmission buffer
+   connection->bufferLength = 0;
+   connection->bufferPos = 0;
+
+   //RETR command is being processed
+   connection->controlState = FTP_CONTROL_STATE_RETR;
+
+   //Format response message
+   strcpy(connection->response, "150 Opening data connection\r\n");
+}
+
+
+/**
+ * @brief STOR command processing
+ *
+ * The STOR command is used to store data to the specified file
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessStor(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   error_t error;
+   uint_t perm;
+
+   //Ensure the user is logged in
+   if(!connection->userLoggedIn)
+   {
+      //Format response message
+      strcpy(connection->response, "530 Not logged in\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //The argument specifies the pathname of the file to written
+   if(*param == '\0')
+   {
+      //The argument is missing...
+      strcpy(connection->response, "501 Missing parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve the full pathname
+   error = ftpServerGetPath(connection, param,
+      connection->path, FTP_SERVER_MAX_PATH_LEN);
+
+   //Any error to report?
+   if(error)
+   {
+      //The specified pathname is not valid...
+      strcpy(connection->response, "501 Invalid parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve permissions for the specified directory
+   perm = ftpServerGetFilePermissions(context, connection, connection->path);
+
+   //Insufficient access rights?
+   if(!(perm & FTP_FILE_PERM_WRITE))
+   {
+      //Report an error
+      strcpy(connection->response, "550 Access denied\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Open specified file for writing
+   connection->file = fsOpenFile(connection->path,
+      FS_FILE_MODE_WRITE | FS_FILE_MODE_CREATE | FS_FILE_MODE_TRUNC);
+
+   //Failed to open the file?
+   if(!connection->file)
+   {
+      //Report an error
+      strcpy(connection->response, "550 File not found\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Check current data transfer mode
+   if(connection->passiveMode)
+   {
+      //Check whether the data connection is already opened
+      if(connection->dataState == FTP_DATA_STATE_IDLE)
+         connection->dataState = FTP_DATA_STATE_RECEIVE;
+   }
+   else
+   {
+      //Open the data connection
+      error = ftpServerOpenDataConnection(context, connection);
+
+      //Any error to report?
+      if(error)
+      {
+         //Clean up side effects
+         fsCloseFile(connection->file);
+         //Format response
+         strcpy(connection->response, "450 Can't open data connection\r\n");
+         //Exit immediately
+         return;
+      }
+
+      //The data connection is ready to receive data
+      connection->dataState = FTP_DATA_STATE_RECEIVE;
+   }
+
+   //Flush reception buffer
+   connection->bufferLength = 0;
+   connection->bufferPos = 0;
+
+   //STOR command is being processed
+   connection->controlState = FTP_CONTROL_STATE_STOR;
+
+   //Format response message
+   strcpy(connection->response, "150 Opening data connection\r\n");
+}
+
+
+/**
+ * @brief APPE command processing
+ *
+ * The APPE command is used to append data to the specified file
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessAppe(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   error_t error;
+   uint_t perm;
+
+   //Ensure the user is logged in
+   if(!connection->userLoggedIn)
+   {
+      //Format response message
+      strcpy(connection->response, "530 Not logged in\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //The argument specifies the pathname of the file to written
+   if(*param == '\0')
+   {
+      //The argument is missing...
+      strcpy(connection->response, "501 Missing parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve the full pathname
+   error = ftpServerGetPath(connection, param,
+      connection->path, FTP_SERVER_MAX_PATH_LEN);
+
+   //Any error to report?
+   if(error)
+   {
+      //The specified pathname is not valid...
+      strcpy(connection->response, "501 Invalid parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve permissions for the specified directory
+   perm = ftpServerGetFilePermissions(context, connection, connection->path);
+
+   //Insufficient access rights?
+   if(!(perm & FTP_FILE_PERM_WRITE))
+   {
+      //Report an error
+      strcpy(connection->response, "550 Access denied\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Open specified file for writing
+   connection->file = fsOpenFile(connection->path,
+      FS_FILE_MODE_WRITE | FS_FILE_MODE_CREATE);
+
+   //Failed to open the file?
+   if(!connection->file)
+   {
+      //Report an error
+      strcpy(connection->response, "550 File not found\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Move to the end of the file
+   error = fsSeekFile(connection->file, 0, FS_SEEK_END);
+
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects
+      fsCloseFile(connection->file);
+      //Format response
+      strcpy(connection->response, "550 File unavailable\r\n");
+   }
+
+   //Check current data transfer mode
+   if(connection->passiveMode)
+   {
+      //Check whether the data connection is already opened
+      if(connection->dataState == FTP_DATA_STATE_IDLE)
+         connection->dataState = FTP_DATA_STATE_RECEIVE;
+   }
+   else
+   {
+      //Open the data connection
+      error = ftpServerOpenDataConnection(context, connection);
+
+      //Any error to report?
+      if(error)
+      {
+         //Clean up side effects
+         fsCloseFile(connection->file);
+         //Format response
+         strcpy(connection->response, "450 Can't open data connection\r\n");
+         //Exit immediately
+         return;
+      }
+
+      //The data connection is ready to receive data
+      connection->dataState = FTP_DATA_STATE_RECEIVE;
+   }
+
+   //Flush reception buffer
+   connection->bufferLength = 0;
+   connection->bufferPos = 0;
+
+   //APPE command is being processed
+   connection->controlState = FTP_CONTROL_STATE_APPE;
+
+   //Format response message
+   strcpy(connection->response, "150 Opening data connection\r\n");
+}
+
+
+/**
+ * @brief RNFR command processing
+ *
+ * The RNFR command specifies the old pathname of the file which is
+ * to be renamed
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessRnfr(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   error_t error;
+   uint_t perm;
+
+   //Ensure the user is logged in
+   if(!connection->userLoggedIn)
+   {
+      //Format response message
+      strcpy(connection->response, "530 Not logged in\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //The argument specifies the file to be renamed
+   if(*param == '\0')
+   {
+      //The argument is missing...
+      strcpy(connection->response, "501 Missing parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve the full pathname
+   error = ftpServerGetPath(connection, param,
+      connection->path, FTP_SERVER_MAX_PATH_LEN);
+
+   //Any error to report?
+   if(error)
+   {
+      //The specified pathname is not valid...
+      strcpy(connection->response, "501 Invalid parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve permissions for the specified directory
+   perm = ftpServerGetFilePermissions(context, connection, connection->path);
+
+   //Insufficient access rights?
+   if(!(perm & FTP_FILE_PERM_WRITE))
+   {
+      //Report an error
+      strcpy(connection->response, "550 Access denied\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Make sure the file exists
+   if(!fsFileExists(connection->path) && !fsDirExists(connection->path))
+   {
+      //No such file or directory...
+      strcpy(connection->response, "550 File not found\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //This command must be immediately followed by a RNTO command
+   connection->controlState = FTP_CONTROL_STATE_RNFR;
+   //Format the response message
+   strcpy(connection->response, "350 File exists, ready for destination name\r\n");
+}
+
+
+/**
+ * @brief RNTO command processing
+ *
+ * The RNTO command specifies the new pathname of the file specified
+ * in the immediately preceding RNFR command
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessRnto(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   error_t error;
+   uint_t perm;
+   char_t newPath[FTP_SERVER_MAX_PATH_LEN];
+
+   //Ensure the user is logged in
+   if(!connection->userLoggedIn)
+   {
+      //Format response message
+      strcpy(connection->response, "530 Not logged in\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //This command must immediately follow a RNFR command
+   if(connection->controlState != FTP_CONTROL_STATE_RNFR)
+   {
+      //Switch to idle state
+      connection->controlState = FTP_CONTROL_STATE_IDLE;
+      //Report an error
+      strcpy(connection->response, "503 Bad sequence of commands\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Switch to idle state
+   connection->controlState = FTP_CONTROL_STATE_IDLE;
+
+   //The argument specifies the new pathname
+   if(*param == '\0')
+   {
+      //The argument is missing...
+      strcpy(connection->response, "501 Missing parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve the full pathname
+   error = ftpServerGetPath(connection, param,
+      newPath, FTP_SERVER_MAX_PATH_LEN);
+
+   //Any error to report?
+   if(error)
+   {
+      //The specified pathname is not valid...
+      strcpy(connection->response, "501 Invalid parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve permissions for the specified directory
+   perm = ftpServerGetFilePermissions(context, connection, newPath);
+
+   //Insufficient access rights?
+   if(!(perm & FTP_FILE_PERM_WRITE))
+   {
+      //Report an error
+      strcpy(connection->response, "550 Access denied\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Check whether the file name already exists
+   if(fsFileExists(newPath) || fsDirExists(newPath))
+   {
+      //Report an error
+      strcpy(connection->response, "550 File already exists\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Rename the specified file
+   error = fsRenameFile(connection->path, newPath);
+
+   //Any error to report?
+   if(error)
+   {
+      //The specified file cannot be renamed
+      strcpy(connection->response, "550 Can't rename file\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //The specified file was successfully deleted
+   strcpy(connection->response, "250 File renamed\r\n");
+}
+
+
+/**
+ * @brief DELE command processing
+ *
+ * The DELE command causes the file specified in the pathname to be
+ * deleted at the server site
+ *
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] param Command line parameters
+ **/
+
+void ftpServerProcessDele(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param)
+{
+   error_t error;
+   uint_t perm;
+
+   //Ensure the user is logged in
+   if(!connection->userLoggedIn)
+   {
+      //Format response message
+      strcpy(connection->response, "530 Not logged in\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //The argument specifies the file to be deleted
+   if(*param == '\0')
+   {
+      //The argument is missing...
+      strcpy(connection->response, "501 Missing parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve the full pathname of the file
+   error = ftpServerGetPath(connection, param,
+      connection->path, FTP_SERVER_MAX_PATH_LEN);
+
+   //Any error to report?
+   if(error)
+   {
+      //The specified pathname is not valid...
+      strcpy(connection->response, "501 Invalid parameter\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Retrieve permissions for the specified directory
+   perm = ftpServerGetFilePermissions(context, connection, connection->path);
+
+   //Insufficient access rights?
+   if(!(perm & FTP_FILE_PERM_WRITE))
+   {
+      //Report an error
+      strcpy(connection->response, "550 Access denied\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Delete the specified file
+   error = fsDeleteFile(connection->path);
+
+   //Any error to report?
+   if(error)
+   {
+      //The specified file cannot be deleted...
+      strcpy(connection->response, "550 Can't delete file\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //The specified file was successfully deleted
+   strcpy(connection->response, "250 File deleted\r\n");
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ftp/ftp_server_commands.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,127 @@
+/**
+ * @file ftp_server_commands.h
+ * @brief FTP server (command processing)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _FTP_SERVER_COMMANDS_H
+#define _FTP_SERVER_COMMANDS_H
+
+//Dependencies
+#include "ftp/ftp_server.h"
+
+//FTP server related functions
+void ftpServerProcessCmd(FtpServerContext *context,
+   FtpClientConnection *connection);
+
+void ftpServerProcessUnknownCmd(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessNoop(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessSyst(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessFeat(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessType(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessStru(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessMode(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessUser(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessPass(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessRein(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessQuit(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessPort(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessEprt(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessPasv(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessEpsv(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessAbor(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessPwd(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessCwd(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessCdup(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessList(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessMkd(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessRmd(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessSize(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessRetr(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessStor(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessAppe(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessRnfr(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessRnto(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+void ftpServerProcessDele(FtpServerContext *context,
+   FtpClientConnection *connection, char_t *param);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ftp/ftp_server_events.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,515 @@
+/**
+ * @file ftp_server_events.c
+ * @brief FTP server (event handlers)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL FTP_TRACE_LEVEL
+
+//Dependencies
+#include "ftp/ftp_server.h"
+#include "ftp/ftp_server_events.h"
+#include "ftp/ftp_server_commands.h"
+#include "ftp/ftp_server_misc.h"
+#include "str.h"
+#include "path.h"
+#include "error.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (FTP_SERVER_SUPPORT == ENABLED)
+
+//Abbreviated months
+static const char months[13][4] =
+{
+   "   ",
+   "Jan",
+   "Feb",
+   "Mar",
+   "Apr",
+   "May",
+   "Jun",
+   "Jul",
+   "Aug",
+   "Sep",
+   "Oct",
+   "Nov",
+   "Dec"
+};
+
+
+/**
+ * @brief Control connection event handler
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] eventFlags Event to be processed
+ **/
+
+void ftpServerControlEventHandler(FtpServerContext *context,
+   FtpClientConnection * connection, uint_t eventFlags)
+{
+   error_t error;
+   size_t n;
+
+   //Send buffer is available for writing?
+   if(eventFlags == SOCKET_EVENT_TX_READY)
+   {
+      //Send data back to the client
+      error = socketSend(connection->controlSocket, connection->response +
+         connection->responsePos, connection->responseLength, &n, 0);
+
+      //Failed to send data?
+      if(error != NO_ERROR && error != ERROR_TIMEOUT)
+      {
+         //Close connection with the client
+         ftpServerCloseConnection(context, connection);
+         //Exit immediately
+         return;
+      }
+
+      //Advance data pointer
+      connection->responsePos += n;
+      //Number of bytes still available in the response buffer
+      connection->responseLength -= n;
+   }
+   //Data is pending in the receive buffer?
+   else if(eventFlags == SOCKET_EVENT_RX_READY)
+   {
+      //Read data from the client
+      error = socketReceive(connection->controlSocket,
+         connection->command + connection->commandLength,
+         FTP_SERVER_MAX_LINE_LEN - connection->commandLength, &n, 0);
+
+      //Failed to receive data?
+      if(error == ERROR_END_OF_STREAM)
+      {
+         //Gracefully disconnect from the remote host
+         connection->controlState = FTP_CONTROL_STATE_WAIT_ACK;
+         //Exit immediately
+         return;
+      }
+      else if(error)
+      {
+         //Close connection with the client
+         ftpServerCloseConnection(context, connection);
+         //Exit immediately
+         return;
+      }
+
+      //Number of bytes available in the command buffer
+      connection->commandLength += n;
+      //Process incoming command
+      ftpServerProcessCmd(context, connection);
+   }
+   //Data are transmitted and acknowledged?
+   else if(eventFlags == SOCKET_EVENT_TX_ACKED)
+   {
+      //Disable transmission
+      socketShutdown(connection->controlSocket, SOCKET_SD_SEND);
+      //Next state
+      connection->controlState = FTP_CONTROL_STATE_SHUTDOWN_TX;
+   }
+   //Transmission is shut down?
+   else if(eventFlags == SOCKET_EVENT_TX_SHUTDOWN)
+   {
+      //Disable reception
+      socketShutdown(connection->controlSocket, SOCKET_SD_RECEIVE);
+      //Next state
+      connection->controlState = FTP_CONTROL_STATE_SHUTDOWN_RX;
+   }
+   //Reception is shut down?
+   else if(eventFlags == SOCKET_EVENT_RX_SHUTDOWN)
+   {
+      //Properly close connection
+      ftpServerCloseConnection(context, connection);
+   }
+}
+
+
+/**
+ * @brief Data connection event handler
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] eventFlags Event to be processed
+ **/
+
+void ftpServerDataEventHandler(FtpServerContext *context,
+   FtpClientConnection * connection, uint_t eventFlags)
+{
+   //Any connection attempt?
+   if(connection->dataState == FTP_DATA_STATE_LISTEN)
+   {
+      //Accept data connection
+      ftpServerAcceptDataConnection(connection);
+   }
+   //Ready to send data?
+   else if(connection->dataState == FTP_DATA_STATE_SEND)
+   {
+      //Send more data to the remote host
+      ftpServerSendData(context, connection);
+   }
+   //Any data pending in the receive buffer?
+   else if(connection->dataState == FTP_DATA_STATE_RECEIVE)
+   {
+      //Process incoming data
+      ftpServerReceiveData(context, connection);
+   }
+   //Data are transmitted and acknowledged?
+   else if(connection->dataState == FTP_DATA_STATE_WAIT_ACK)
+   {
+      //Disable transmission
+      socketShutdown(connection->dataSocket, SOCKET_SD_SEND);
+      //Next state
+      connection->dataState = FTP_DATA_STATE_SHUTDOWN_TX;
+   }
+   //Transmission is shut down?
+   else if(connection->dataState == FTP_DATA_STATE_SHUTDOWN_TX)
+   {
+      //Disable reception
+      socketShutdown(connection->dataSocket, SOCKET_SD_RECEIVE);
+      //Next state
+      connection->dataState = FTP_DATA_STATE_SHUTDOWN_RX;
+   }
+   //Reception is shut down?
+   else if(connection->dataState == FTP_DATA_STATE_SHUTDOWN_RX)
+   {
+      //Close the data connection
+      ftpServerCloseDataConnection(connection);
+
+      //Back to idle state
+      connection->controlState = FTP_CONTROL_STATE_IDLE;
+
+      //Transfer status
+      strcpy(connection->response, "226 Transfer complete\r\n");
+      //Debug message
+      TRACE_DEBUG("FTP server: %s", connection->response);
+
+      //Number of bytes in the response buffer
+      connection->responseLength = strlen(connection->response);
+      connection->responsePos = 0;
+   }
+}
+
+
+/**
+ * @brief Send data on the data connection
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ **/
+
+void ftpServerSendData(FtpServerContext *context, FtpClientConnection *connection)
+{
+   error_t error;
+   size_t n;
+
+   //Any data waiting for transmission?
+   if(connection->bufferLength > 0)
+   {
+      //Send more data
+      error = socketSend(connection->dataSocket, connection->buffer +
+         connection->bufferPos, connection->bufferLength, &n, 0);
+
+      //Failed to send data?
+      if(error != NO_ERROR && error != ERROR_TIMEOUT)
+      {
+         //Close the data connection
+         ftpServerCloseDataConnection(connection);
+
+         //Release previously allocated resources
+         if(connection->file != NULL)
+         {
+            fsCloseFile(connection->file);
+            connection->file = NULL;
+         }
+         if(connection->dir != NULL)
+         {
+            fsCloseDir(connection->dir);
+            connection->dir = NULL;
+         }
+
+         //Back to idle state
+         connection->controlState = FTP_CONTROL_STATE_IDLE;
+
+         //Transfer status
+         strcpy(connection->response, "451 Transfer aborted\r\n");
+         //Debug message
+         TRACE_DEBUG("FTP server: %s", connection->response);
+
+         //Number of bytes in the response buffer
+         connection->responseLength = strlen(connection->response);
+         connection->responsePos = 0;
+
+         //Exit immediately
+         return;
+      }
+
+      //Advance data pointer
+      connection->bufferPos += n;
+      //Number of bytes still available in the buffer
+      connection->bufferLength -= n;
+   }
+
+   //Empty transmission buffer?
+   if(connection->bufferLength == 0)
+   {
+      //File transfer in progress?
+      if(connection->controlState == FTP_CONTROL_STATE_RETR)
+      {
+         //Read more data
+         error = fsReadFile(connection->file,
+            connection->buffer, FTP_SERVER_BUFFER_SIZE, &n);
+
+         //End of stream?
+         if(error)
+         {
+            //Close file
+            fsCloseFile(connection->file);
+            connection->file = NULL;
+
+            //Wait for all the data to be transmitted and acknowledged
+            connection->dataState = FTP_DATA_STATE_WAIT_ACK;
+
+            //Exit immediately
+            return;
+         }
+      }
+      //Directory listing in progress?
+      else if(connection->controlState == FTP_CONTROL_STATE_LIST)
+      {
+         uint_t perm;
+         time_t currentTime;
+         time_t modified;
+         char_t *path;
+         FsDirEntry dirEntry;
+
+         //Read a new entry in the directory
+         error = fsReadDir(connection->dir, &dirEntry);
+
+         //End of stream?
+         if(error)
+         {
+            //Close directory
+            fsCloseDir(connection->dir);
+            connection->dir = NULL;
+
+            //Wait for all the data to be transmitted and acknowledged
+            connection->dataState = FTP_DATA_STATE_WAIT_ACK;
+
+            //Exit immediately
+            return;
+         }
+
+         //Point to the scratch buffer
+         path = connection->buffer;
+
+         //Get the pathname of the directory being listed
+         strcpy(path, connection->path);
+         //Retrieve the full pathname
+         pathCombine(path, dirEntry.name, FTP_SERVER_MAX_PATH_LEN);
+         pathCanonicalize(path);
+
+         //Get permissions for the specified file
+         perm = ftpServerGetFilePermissions(context, connection, path);
+
+         //Enforce access rights
+         if(perm & FTP_FILE_PERM_LIST)
+         {
+            //Format links, owner, group and size fields
+            n = sprintf(connection->buffer, "----------   1 owner    group    %10" PRIu32,
+               dirEntry.size);
+
+            //Check whether the current entry is a directory
+            if(dirEntry.attributes & FS_FILE_ATTR_DIRECTORY)
+               connection->buffer[0] = 'd';
+
+            //Read access permitted?
+            if(perm & FTP_FILE_PERM_READ)
+            {
+               connection->buffer[1] = 'r';
+               connection->buffer[4] = 'r';
+               connection->buffer[7] = 'r';
+            }
+
+            //Write access permitted?
+            if(perm & FTP_FILE_PERM_WRITE)
+            {
+               //Make sure the file is not marked as read-only
+               if(!(dirEntry.attributes & FS_FILE_ATTR_READ_ONLY))
+               {
+                  connection->buffer[2] = 'w';
+                  connection->buffer[5] = 'w';
+                  connection->buffer[8] = 'w';
+               }
+            }
+
+            //Get current time
+            currentTime = getCurrentUnixTime();
+            //Get modification time
+            modified = convertDateToUnixTime(&dirEntry.modified);
+
+            //Check whether the modification time is within the previous 180 days
+            if(currentTime > modified && currentTime < (modified + FTP_SERVER_180_DAYS))
+            {
+               //The format of the date/time field is Mmm dd hh:mm
+               n += sprintf(connection->buffer + n, " %s %02" PRIu8 " %02" PRIu8 ":%02" PRIu8,
+                  months[MIN(dirEntry.modified.month, 12)], dirEntry.modified.day,
+                  dirEntry.modified.hours, dirEntry.modified.minutes);
+            }
+            else
+            {
+               //The format of the date/time field is Mmm dd  yyyy
+               n += sprintf(connection->buffer + n, " %s %02" PRIu8 "  %04" PRIu16,
+                  months[MIN(dirEntry.modified.month, 12)], dirEntry.modified.day,
+                  dirEntry.modified.year);
+            }
+
+            //Append filename
+            n += sprintf(connection->buffer + n, " %s\r\n", dirEntry.name);
+            //Debug message
+            TRACE_DEBUG("FTP server: %s", connection->buffer);
+         }
+         else
+         {
+            //Insufficient access rights
+            n = 0;
+         }
+      }
+      //Invalid state?
+      else
+      {
+         //The FTP server has encountered a critical error
+         ftpServerCloseConnection(context, connection);
+         //Exit immediately
+         return;
+      }
+
+      //Number of bytes in the buffer
+      connection->bufferPos = 0;
+      connection->bufferLength = n;
+   }
+}
+
+
+/**
+ * @brief Receive data on the data connection
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ **/
+
+void ftpServerReceiveData(FtpServerContext *context, FtpClientConnection *connection)
+{
+   error_t error;
+   bool_t eof;
+   size_t n;
+
+   //File transfer in progress?
+   if(connection->controlState == FTP_CONTROL_STATE_STOR ||
+      connection->controlState == FTP_CONTROL_STATE_APPE)
+   {
+      //Read incoming data
+      error = socketReceive(connection->dataSocket,
+         connection->buffer + connection->bufferPos,
+         FTP_SERVER_BUFFER_SIZE - connection->bufferLength, &n, 0);
+
+      //Any error to report?
+      if(error)
+      {
+         //Cannot read more data
+         eof = TRUE;
+      }
+      else
+      {
+         //Successful read operation
+         eof = FALSE;
+
+         //Advance data pointer
+         connection->bufferPos += n;
+         connection->bufferLength += n;
+      }
+
+      //Read data until the buffer is full or the end of the file is reached
+      if(eof || connection->bufferLength >= FTP_SERVER_BUFFER_SIZE)
+      {
+         //Any data to be written?
+         if(connection->bufferLength > 0)
+         {
+            //Write data to the specified file
+            error = fsWriteFile(connection->file,
+               connection->buffer, connection->bufferLength);
+
+            //Any error to report?
+            if(error)
+            {
+               //Close the data connection
+               ftpServerCloseDataConnection(connection);
+
+               //Release previously allocated resources
+               fsCloseFile(connection->file);
+               connection->file = NULL;
+
+               //Back to idle state
+               connection->controlState = FTP_CONTROL_STATE_IDLE;
+
+               //Transfer status
+               strcpy(connection->response, "451 Transfer aborted\r\n");
+               //Debug message
+               TRACE_DEBUG("FTP server: %s", connection->response);
+
+               //Number of bytes in the response buffer
+               connection->responseLength = strlen(connection->response);
+               connection->responsePos = 0;
+
+               //Exit immediately
+               return;
+            }
+         }
+
+         //Flush reception buffer
+         connection->bufferLength = 0;
+         connection->bufferPos = 0;
+      }
+
+      //End of stream?
+      if(eof)
+      {
+         //Close file
+         fsCloseFile(connection->file);
+         connection->file = NULL;
+
+         //Graceful shutdown sequence
+         connection->dataState = FTP_DATA_STATE_WAIT_ACK;
+      }
+   }
+   //Invalid state?
+   else
+   {
+      //The FTP server has encountered a critical error
+      ftpServerCloseConnection(context, connection);
+   }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ftp/ftp_server_events.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,49 @@
+/**
+ * @file ftp_server_events.h
+ * @brief FTP server (event handlers)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _FTP_SERVER_EVENTS_H
+#define _FTP_SERVER_EVENTS_H
+
+//Dependencies
+#include "ftp/ftp_server.h"
+
+//Time constant
+#define FTP_SERVER_180_DAYS (180 * 86400)
+
+//FTP server related functions
+void ftpServerControlEventHandler(FtpServerContext *context,
+   FtpClientConnection * connection, uint_t eventFlags);
+
+void ftpServerDataEventHandler(FtpServerContext *context,
+   FtpClientConnection * connection, uint_t eventFlags);
+
+void ftpServerSendData(FtpServerContext *context, FtpClientConnection *connection);
+void ftpServerReceiveData(FtpServerContext *context, FtpClientConnection *connection);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ftp/ftp_server_misc.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,616 @@
+/**
+ * @file ftp_server_misc.c
+ * @brief FTP server (miscellaneous functions)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL FTP_TRACE_LEVEL
+
+//Dependencies
+#include "ftp/ftp_server.h"
+#include "ftp/ftp_server_events.h"
+#include "ftp/ftp_server_commands.h"
+#include "ftp/ftp_server_misc.h"
+#include "str.h"
+#include "path.h"
+#include "error.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (FTP_SERVER_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Get a passive port number
+ * @param[in] context Pointer to the FTP server context
+ * @return Passive port number
+ **/
+
+uint16_t ftpServerGetPassivePort(FtpServerContext *context)
+{
+   uint_t port;
+
+   //Retrieve current passive port number
+   port = context->passivePort;
+
+   //Invalid port number?
+   if(port < context->settings.passivePortMin ||
+      port > context->settings.passivePortMax)
+   {
+      //Generate a random port number
+      port = context->settings.passivePortMin + netGetRand() %
+         (context->settings.passivePortMax - context->settings.passivePortMin + 1);
+   }
+
+   //Next passive port to use
+   if(port < context->settings.passivePortMax)
+   {
+      //Increment port number
+      context->passivePort = port + 1;
+   }
+   else
+   {
+      //Wrap around if necessary
+      context->passivePort = context->settings.passivePortMin;
+   }
+
+   //Return the passive port number
+   return port;
+}
+
+
+/**
+ * @brief Close client connection properly
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection to be closed
+ **/
+
+void ftpServerCloseConnection(FtpServerContext *context,
+   FtpClientConnection *connection)
+{
+   uint_t i;
+
+   //Make sure the connection is active
+   if(connection != NULL)
+   {
+      //Loop through client connection table
+      for(i = 0; i < FTP_SERVER_MAX_CONNECTIONS; i++)
+      {
+         //Search the table for the specified connection
+         if(context->connection[i] == connection)
+         {
+            //Close data connection
+            ftpServerCloseDataConnection(connection);
+            //Close control connection
+            ftpServerCloseControlConnection(connection);
+
+            //Release previously allocated resources
+            if(connection->file != NULL)
+               fsCloseFile(connection->file);
+            if(connection->dir != NULL)
+               fsCloseDir(connection->dir);
+
+            //Free memory
+            memPoolFree(connection->buffer);
+            memPoolFree(connection);
+
+            //Mark the entry as free
+            context->connection[i] = NULL;
+            //We are done
+            break;
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Accept control connection
+ * @param[in] context Pointer to the FTP server context
+ * @return Pointer to the connection
+ **/
+
+FtpClientConnection *ftpServerAcceptControlConnection(FtpServerContext *context)
+{
+   error_t error;
+   uint_t i;
+   Socket *socket;
+   IpAddr clientIpAddr;
+   uint16_t clientPort;
+   FtpClientConnection *connection;
+
+   //Accept incoming connection
+   socket = socketAccept(context->socket, &clientIpAddr, &clientPort);
+   //Failure detected?
+   if(socket == NULL)
+      return NULL;
+
+   //Force the socket to operate in non-blocking mode
+   error = socketSetTimeout(socket, 0);
+   //Any error to report?
+   if(error)
+   {
+      //Close socket
+      socketClose(socket);
+      //Exit immediately
+      return NULL;
+   }
+
+   //Loop through client connection table
+   for(i = 0; i < FTP_SERVER_MAX_CONNECTIONS; i++)
+   {
+      //Check whether the entry is currently in used or not
+      if(context->connection[i] == NULL)
+      {
+         //Allocate resources for the new connection
+         connection = memPoolAlloc(sizeof(FtpClientConnection));
+         //Failed to allocate memory?
+         if(connection == NULL)
+         {
+            //Debug message
+            TRACE_ERROR("FTP server: Failed to allocate memory!\r\n");
+            //Exit immediately
+            break;
+         }
+
+         //Clear the structure
+         memset(connection, 0, sizeof(FtpClientConnection));
+
+         //Allocate a memory buffer for I/O operations
+         connection->buffer = memPoolAlloc(FTP_SERVER_BUFFER_SIZE);
+         //Failed to allocate memory
+         if(connection->buffer == NULL)
+         {
+            //Clean up side effects
+            memPoolFree(connection);
+            //Debug message
+            TRACE_ERROR("FTP server: Failed to allocate memory!\r\n");
+            //Exit immediately
+            break;
+         }
+
+         //Debug message
+         TRACE_INFO("TCP server: Control connection established with client %s port %" PRIu16 "...\r\n",
+            ipAddrToString(&clientIpAddr, NULL), clientPort);
+
+         //Underlying network interface
+         connection->interface = socket->interface;
+         //Save socket handle
+         connection->controlSocket = socket;
+         //Set home directory
+         strcpy(connection->homeDir, context->settings.rootDir);
+         //Set current directory
+         strcpy(connection->currentDir, context->settings.rootDir);
+
+         //Format greeting message
+         strcpy(connection->response, "220 Service ready for new user\r\n");
+         //Debug message
+         TRACE_DEBUG("FTP server: %s", connection->response);
+
+         //Number of bytes in the response buffer
+         connection->responseLength = strlen(connection->response);
+         connection->responsePos = 0;
+
+         //The client connection is ready for use
+         context->connection[i] = connection;
+         //Successful processing
+         return connection;
+      }
+   }
+
+   //Debug message
+   TRACE_INFO("TCP server: Connection refused with client %s port %" PRIu16 "...\r\n",
+      ipAddrToString(&clientIpAddr, NULL), clientPort);
+
+   //Close socket
+   socketClose(socket);
+   //The FTP server cannot accept the incoming connection request
+   return NULL;
+}
+
+
+/**
+ * @brief Close control connection
+ * @param[in] connection Pointer to the client connection
+ **/
+
+void ftpServerCloseControlConnection(FtpClientConnection *connection)
+{
+   IpAddr clientIpAddr;
+   uint16_t clientPort;
+
+   //Any running control connection?
+   if(connection->controlSocket != NULL)
+   {
+      //Retrieve the address of the peer to which a socket is connected
+      socketGetRemoteAddr(connection->controlSocket, &clientIpAddr, &clientPort);
+
+      //Debug message
+      TRACE_INFO("FTP server: Closing control connection with client %s port %" PRIu16 "...\r\n",
+         ipAddrToString(&clientIpAddr, NULL), clientPort);
+
+      //Close control connection
+      socketClose(connection->controlSocket);
+      connection->controlSocket = NULL;
+
+      //Back to idle state
+      connection->controlState = FTP_CONTROL_STATE_IDLE;
+   }
+}
+
+
+/**
+ * @brief Open data connection
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @return Error code
+ **/
+
+error_t ftpServerOpenDataConnection(FtpServerContext *context,
+   FtpClientConnection *connection)
+{
+   error_t error;
+
+   //Release previously allocated resources
+   ftpServerCloseDataConnection(connection);
+
+   //No port specified?
+   if(!connection->remotePort)
+      return ERROR_FAILURE;
+
+   //Debug message
+   TRACE_INFO("FTP server: Opening data connection with client %s port %" PRIu16 "...\r\n",
+         ipAddrToString(&connection->remoteIpAddr, NULL), connection->remotePort);
+
+   //Open data socket
+   connection->dataSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
+   //Failed to open socket?
+   if(!connection->dataSocket)
+      return ERROR_OPEN_FAILED;
+
+   //Start of exception handling block
+   do
+   {
+      //Force the socket to operate in non-blocking mode
+      error = socketSetTimeout(connection->dataSocket, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Change the size of the TX buffer
+      error = socketSetTxBufferSize(connection->dataSocket,
+         FTP_SERVER_DATA_SOCKET_BUFFER_SIZE);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Change the size of the RX buffer
+      error = socketSetRxBufferSize(connection->dataSocket,
+         FTP_SERVER_DATA_SOCKET_BUFFER_SIZE);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Associate the socket with the relevant interface
+      error = socketBindToInterface(connection->dataSocket, connection->interface);
+      //Unable to bind the socket to the desired interface?
+      if(error)
+         break;
+
+      //The server initiates the data connection from port 20
+      error = socketBind(connection->dataSocket, &IP_ADDR_ANY,
+         context->settings.dataPort);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Establish data connection
+      error = socketConnect(connection->dataSocket,
+         &connection->remoteIpAddr, connection->remotePort);
+      //Any error to report?
+      if(error != NO_ERROR && error != ERROR_TIMEOUT)
+         break;
+
+      //Connection is being established
+      error = NO_ERROR;
+
+      //End of exception handling block
+   } while(0);
+
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects
+      ftpServerCloseDataConnection(connection);
+      //Exit immediately
+      return error;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Accept data connection
+ * @param[in] connection Pointer to the client connection
+ **/
+
+void ftpServerAcceptDataConnection(FtpClientConnection *connection)
+{
+   error_t error;
+   Socket *socket;
+   IpAddr clientIpAddr;
+   uint16_t clientPort;
+
+   //Accept incoming connection
+   socket = socketAccept(connection->dataSocket, &clientIpAddr, &clientPort);
+   //Failure detected?
+   if(socket == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("FTP server: Data connection established with client %s port %" PRIu16 "...\r\n",
+      ipAddrToString(&clientIpAddr, NULL), clientPort);
+
+   //Close the listening socket
+   socketClose(connection->dataSocket);
+   //Save socket handle
+   connection->dataSocket = socket;
+
+   //Force the socket to operate in non-blocking mode
+   error = socketSetTimeout(connection->dataSocket, 0);
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects
+      socketClose(connection->dataSocket);
+      //Exit immediately
+      return;
+   }
+
+   //Check current state
+   if(connection->controlState == FTP_CONTROL_STATE_LIST ||
+      connection->controlState == FTP_CONTROL_STATE_RETR)
+   {
+      //Prepare to send data
+      connection->dataState = FTP_DATA_STATE_SEND;
+   }
+   else if(connection->controlState == FTP_CONTROL_STATE_STOR ||
+      connection->controlState == FTP_CONTROL_STATE_APPE)
+   {
+      //Prepare to receive data
+      connection->dataState = FTP_DATA_STATE_RECEIVE;
+   }
+   else
+   {
+      //Data transfer direction is unknown...
+      connection->dataState = FTP_DATA_STATE_IDLE;
+   }
+}
+
+
+/**
+ * @brief Close data connection
+ * @param[in] connection Pointer to the client connection
+ **/
+
+void ftpServerCloseDataConnection(FtpClientConnection *connection)
+{
+   IpAddr clientIpAddr;
+   uint16_t clientPort;
+
+   //Any running data connection?
+   if(connection->dataSocket != NULL)
+   {
+      //Retrieve the address of the peer to which a socket is connected
+      socketGetRemoteAddr(connection->dataSocket, &clientIpAddr, &clientPort);
+
+      //Check whether the data connection is established
+      if(clientPort != 0)
+      {
+         //Debug message
+         TRACE_INFO("FTP server: Closing data connection with client %s port %" PRIu16 "...\r\n",
+            ipAddrToString(&clientIpAddr, NULL), clientPort);
+      }
+
+      //Close data connection
+      socketClose(connection->dataSocket);
+      connection->dataSocket = NULL;
+
+      //Re initialize data connection
+      connection->passiveMode = FALSE;
+      connection->remotePort = 0;
+
+      //Back to default state
+      connection->dataState = FTP_DATA_STATE_CLOSED;
+   }
+}
+
+
+/**
+ * @brief Retrieve the full pathname
+ * @param[in] connection Pointer to the client connection
+ * @param[in] inputPath Relative or absolute path
+ * @param[out] outputPath Resulting full path
+ * @param[in] maxLen Maximum acceptable path length
+ * @return Error code
+ **/
+
+error_t ftpServerGetPath(FtpClientConnection *connection,
+   const char_t *inputPath, char_t *outputPath, size_t maxLen)
+{
+   size_t n;
+
+   //Relative or absolute path?
+   if(pathIsRelative(inputPath))
+   {
+      //Sanity check
+      if(strlen(connection->currentDir) > maxLen)
+         return ERROR_FAILURE;
+
+      //Copy current directory
+      strcpy(outputPath, connection->currentDir);
+      //Append the specified path
+      pathCombine(outputPath, inputPath, maxLen);
+   }
+   else
+   {
+      //Sanity check
+      if(strlen(connection->homeDir) > maxLen)
+         return ERROR_FAILURE;
+
+      //Copy home directory
+      strcpy(outputPath, connection->homeDir);
+      //Append the specified path
+      pathCombine(outputPath, inputPath, maxLen);
+   }
+
+   //Clean the resulting path
+   pathCanonicalize(outputPath);
+   pathRemoveSlash(outputPath);
+
+   //Calculate the length of the home directory
+   n = strlen(connection->homeDir);
+
+   //Make sure the pathname is valid
+   if(strncmp(outputPath, connection->homeDir, n))
+      return ERROR_INVALID_PATH;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Get permissions for the specified file or directory
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] connection Pointer to the client connection
+ * @param[in] path Canonical path of the file
+ * @return Access rights for the specified file
+ **/
+
+uint_t ftpServerGetFilePermissions(FtpServerContext *context,
+   FtpClientConnection *connection, const char_t *path)
+{
+   size_t n;
+   uint_t perm;
+
+   //Calculate the length of the home directory
+   n = strlen(connection->homeDir);
+
+   //Make sure the pathname is valid
+   if(!strncmp(path, connection->homeDir, n))
+   {
+      //Strip root directory from the pathname
+      path = ftpServerStripRootDir(context, path);
+
+      //Invoke user-defined callback, if any
+      if(context->settings.getFilePermCallback != NULL)
+      {
+         //Retrieve access rights for the specified file
+         perm = context->settings.getFilePermCallback(connection, connection->user, path);
+      }
+      else
+      {
+         //Use default access rights
+         perm = FTP_FILE_PERM_LIST | FTP_FILE_PERM_READ | FTP_FILE_PERM_WRITE;
+      }
+   }
+   else
+   {
+      //The specified pathname is not valid
+      perm = 0;
+   }
+
+   //Return access rights
+   return perm;
+}
+
+
+/**
+ * @brief Strip root dir from specified pathname
+ * @param[in] context Pointer to the FTP server context
+ * @param[in] path input pathname
+ * @return Resulting pathname with root dir stripped
+ **/
+
+const char_t *ftpServerStripRootDir(FtpServerContext *context, const char_t *path)
+{
+   //Default directory
+   static const char_t defaultDir[] = "/";
+
+   //Local variables
+   size_t m;
+   size_t n;
+
+   //Retrieve the length of the root directory
+   n = strlen(context->settings.rootDir);
+   //Retrieve the length of the specified pathname
+   m = strlen(path);
+
+   //Strip the root dir from the specified pathname
+   if(n <= 1)
+      return path;
+   else if(n < m)
+      return path + n;
+   else
+      return defaultDir;
+}
+
+
+/**
+ * @brief Strip home directory from specified pathname
+ * @param[in] connection Pointer to the client connection
+ * @param[in] path input pathname
+ * @return Resulting pathname with home directory stripped
+ **/
+
+const char_t *ftpServerStripHomeDir(FtpClientConnection *connection, const char_t *path)
+{
+   //Default directory
+   static const char_t defaultDir[] = "/";
+
+   //Local variables
+   size_t m;
+   size_t n;
+
+   //Retrieve the length of the home directory
+   n = strlen(connection->homeDir);
+   //Retrieve the length of the specified pathname
+   m = strlen(path);
+
+   //Strip the home directory from the specified pathname
+   if(n <= 1)
+      return path;
+   else if(n < m)
+      return path + n;
+   else
+      return defaultDir;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ftp/ftp_server_misc.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,60 @@
+/**
+ * @file ftp_server_misc.h
+ * @brief FTP server (miscellaneous functions)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _FTP_SERVER_MISC_H
+#define _FTP_SERVER_MISC_H
+
+//Dependencies
+#include "ftp/ftp_server.h"
+
+//FTP server related functions
+uint16_t ftpServerGetPassivePort(FtpServerContext *context);
+
+void ftpServerCloseConnection(FtpServerContext *context,
+   FtpClientConnection *connection);
+
+FtpClientConnection *ftpServerAcceptControlConnection(FtpServerContext *context);
+void ftpServerCloseControlConnection(FtpClientConnection *connection);
+
+error_t ftpServerOpenDataConnection(FtpServerContext *context,
+   FtpClientConnection *connection);
+
+void ftpServerAcceptDataConnection(FtpClientConnection *connection);
+void ftpServerCloseDataConnection(FtpClientConnection *connection);
+
+error_t ftpServerGetPath(FtpClientConnection *connection,
+   const char_t *inputPath, char_t *outputPath, size_t maxLen);
+
+uint_t ftpServerGetFilePermissions(FtpServerContext *context,
+   FtpClientConnection *connection, const char_t *path);
+
+const char_t *ftpServerStripRootDir(FtpServerContext *context, const char_t *path);
+const char_t *ftpServerStripHomeDir(FtpClientConnection *connection, const char_t *path);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/http/http_server.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1164 @@
+/**
+ * @file http_server.c
+ * @brief HTTP server (HyperText Transfer Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * Using the HyperText Transfer Protocol, the HTTP server delivers web pages
+ * to browsers as well as other data files to web-based applications. Refers
+ * to the following RFCs for complete details:
+ * - RFC 1945: Hypertext Transfer Protocol - HTTP/1.0
+ * - RFC 2616: Hypertext Transfer Protocol - HTTP/1.1
+ * - RFC 2617: HTTP Authentication: Basic and Digest Access Authentication
+ * - RFC 2818: HTTP Over TLS
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL HTTP_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include "core/net.h"
+#include "http/http_server.h"
+#include "http/http_server_auth.h"
+#include "http/http_server_misc.h"
+#include "http/mime.h"
+#include "http/ssi.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (HTTP_SERVER_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Initialize settings with default values
+ * @param[out] settings Structure that contains HTTP server settings
+ **/
+
+void httpServerGetDefaultSettings(HttpServerSettings *settings)
+{
+   //The HTTP server is not bound to any interface
+   settings->interface = NULL;
+
+   //Listen to port 80
+   settings->port = HTTP_PORT;
+   //Maximum length of the pending connection queue
+   settings->backlog = HTTP_SERVER_BACKLOG;
+   //Client connections
+   settings->maxConnections = 0;
+   settings->connections = NULL;
+   //Specify the server's root directory
+   strcpy(settings->rootDirectory, "/");
+   //Set default home page
+   strcpy(settings->defaultDocument, "index.htm");
+
+#if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
+   //SSL/TLS initialization callback function
+   settings->tlsInitCallback = NULL;
+#endif
+
+#if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
+   //Random data generation callback function
+   settings->randCallback = NULL;
+   //HTTP authentication callback function
+   settings->authCallback = NULL;
+#endif
+
+   //CGI callback function
+   settings->cgiCallback = NULL;
+   //HTTP request callback function
+   settings->requestCallback = NULL;
+   //URI not found callback function
+   settings->uriNotFoundCallback = NULL;
+}
+
+
+/**
+ * @brief HTTP server initialization
+ * @param[in] context Pointer to the HTTP server context
+ * @param[in] settings HTTP server specific settings
+ * @return Error code
+ **/
+
+error_t httpServerInit(HttpServerContext *context, const HttpServerSettings *settings)
+{
+   error_t error;
+   uint_t i;
+   HttpConnection *connection;
+
+   //Debug message
+   TRACE_INFO("Initializing HTTP server...\r\n");
+
+   //Ensure the parameters are valid
+   if(context == NULL || settings == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Check user settings
+   if(settings->maxConnections == 0 || settings->connections == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Clear the HTTP server context
+   memset(context, 0, sizeof(HttpServerContext));
+
+   //Save user settings
+   context->settings = *settings;
+   //Client connections
+   context->connections = settings->connections;
+
+   //Create a semaphore to limit the number of simultaneous connections
+   if(!osCreateSemaphore(&context->semaphore, context->settings.maxConnections))
+      return ERROR_OUT_OF_RESOURCES;
+
+   //Loop through client connections
+   for(i = 0; i < context->settings.maxConnections; i++)
+   {
+      //Point to the structure representing the client connection
+      connection = &context->connections[i];
+
+      //Initialize the structure
+      memset(connection, 0, sizeof(HttpConnection));
+
+      //Create an event object to manage connection lifetime
+      if(!osCreateEvent(&connection->startEvent))
+         return ERROR_OUT_OF_RESOURCES;
+   }
+
+#if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
+   //Create a mutex to prevent simultaneous access to the nonce cache
+   if(!osCreateMutex(&context->nonceCacheMutex))
+      return ERROR_OUT_OF_RESOURCES;
+#endif
+
+   //Open a TCP socket
+   context->socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
+   //Failed to open socket?
+   if(!context->socket)
+      return ERROR_OPEN_FAILED;
+
+   //Set timeout for blocking functions
+   error = socketSetTimeout(context->socket, INFINITE_DELAY);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Associate the socket with the relevant interface
+   error = socketBindToInterface(context->socket, settings->interface);
+   //Unable to bind the socket to the desired interface?
+   if(error)
+      return error;
+
+   //Bind newly created socket to port 80
+   error = socketBind(context->socket, &IP_ADDR_ANY, settings->port);
+   //Failed to bind socket to port 80?
+   if(error)
+      return error;
+
+   //Place socket in listening state
+   error = socketListen(context->socket, settings->backlog);
+   //Any failure to report?
+   if(error)
+      return error;
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Start HTTP server
+ * @param[in] context Pointer to the HTTP server context
+ * @return Error code
+ **/
+
+error_t httpServerStart(HttpServerContext *context)
+{
+   uint_t i;
+
+   //Debug message
+   TRACE_INFO("Starting HTTP server...\r\n");
+
+   //Make sure the HTTP server context is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Loop through client connections
+   for(i = 0; i < context->settings.maxConnections; i++)
+   {
+      //Create a task to service a given HTTP client connection
+      context->connections[i].taskHandle = osCreateTask("HTTP Connection",
+         httpConnectionTask, &context->connections[i],
+         HTTP_SERVER_STACK_SIZE, HTTP_SERVER_PRIORITY);
+
+      //Unable to create the task?
+      if(context->connections[i].taskHandle == OS_INVALID_HANDLE)
+         return ERROR_OUT_OF_RESOURCES;
+   }
+
+   //Create the HTTP server listener task
+   context->taskHandle = osCreateTask("HTTP Listener", httpListenerTask,
+      context, HTTP_SERVER_STACK_SIZE, HTTP_SERVER_PRIORITY);
+
+   //Unable to create the task?
+   if(context->taskHandle == OS_INVALID_HANDLE)
+      return ERROR_OUT_OF_RESOURCES;
+
+   //The HTTP server has successfully started
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief HTTP server listener task
+ * @param[in] param Pointer to the HTTP server context
+ **/
+
+void httpListenerTask(void *param)
+{
+   uint_t i;
+   uint_t counter;
+   uint16_t clientPort;
+   IpAddr clientIpAddr;
+   HttpServerContext *context;
+   HttpConnection *connection;
+   Socket *socket;
+
+   //Retrieve the HTTP server context
+   context = (HttpServerContext *) param;
+
+   //Process incoming connections to the server
+   for(counter = 1; ; counter++)
+   {
+      //Debug message
+      TRACE_INFO("Ready to accept a new connection...\r\n");
+
+      //Limit the number of simultaneous connections to the HTTP server
+      osWaitForSemaphore(&context->semaphore, INFINITE_DELAY);
+
+      //Loop through available client connections
+      for(i = 0; i < context->settings.maxConnections; i++)
+      {
+         //Point to the current connection
+         connection = &context->connections[i];
+
+         //Ready to service the client request?
+         if(!connection->running)
+         {
+            //Accept an incoming connection
+            socket = socketAccept(context->socket, &clientIpAddr, &clientPort);
+
+            //Make sure the socket handle is valid
+            if(socket != NULL)
+            {
+               //Debug message
+               TRACE_INFO("Connection #%u established with client %s port %" PRIu16 "...\r\n",
+                  counter, ipAddrToString(&clientIpAddr, NULL), clientPort);
+
+               //Reference to the HTTP server settings
+               connection->settings = &context->settings;
+               //Reference to the HTTP server context
+               connection->serverContext = context;
+               //Reference to the new socket
+               connection->socket = socket;
+
+               //Set timeout for blocking functions
+               socketSetTimeout(connection->socket, HTTP_SERVER_TIMEOUT);
+
+               //The client connection task is now running...
+               connection->running = TRUE;
+               //Service the current connection request
+               osSetEvent(&connection->startEvent);
+
+               //We are done
+               break;
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Task that services requests from an active connection
+ * @param[in] param Structure representing an HTTP connection with a client
+ **/
+
+void httpConnectionTask(void *param)
+{
+   error_t error;
+   uint_t counter;
+   HttpConnection *connection;
+
+   //Point to the structure representing the HTTP connection
+   connection = (HttpConnection *) param;
+
+   //Endless loop
+   while(1)
+   {
+      //Wait for an incoming connection attempt
+      osWaitForEvent(&connection->startEvent, INFINITE_DELAY);
+
+      //Initialize status code
+      error = NO_ERROR;
+
+#if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
+      //Use SSL/TLS to secure the connection?
+      if(connection->settings->useTls)
+      {
+         //Debug message
+         TRACE_INFO("Initializing SSL/TLS session...\r\n");
+
+         //Start of exception handling block
+         do
+         {
+            //Allocate SSL/TLS context
+            connection->tlsContext = tlsInit();
+            //Initialization failed?
+            if(connection->tlsContext == NULL)
+            {
+               //Report an error
+               error = ERROR_OUT_OF_MEMORY;
+               //Exit immediately
+               break;
+            }
+
+            //Select server operation mode
+            error = tlsSetConnectionEnd(connection->tlsContext, TLS_CONNECTION_END_SERVER);
+            //Any error to report?
+            if(error)
+               break;
+
+            //Bind TLS to the relevant socket
+            error = tlsSetSocket(connection->tlsContext, connection->socket);
+            //Any error to report?
+            if(error)
+               break;
+
+            //Invoke user-defined callback, if any
+            if(connection->settings->tlsInitCallback != NULL)
+            {
+               //Perform SSL/TLS related initialization
+               error = connection->settings->tlsInitCallback(connection, connection->tlsContext);
+               //Any error to report?
+               if(error)
+                  break;
+            }
+
+            //Establish a secure session
+            error = tlsConnect(connection->tlsContext);
+            //Any error to report?
+            if(error)
+               break;
+
+            //End of exception handling block
+         } while(0);
+      }
+      else
+      {
+         //Do not use SSL/TLS
+         connection->tlsContext = NULL;
+      }
+#endif
+
+      //Check status code
+      if(!error)
+      {
+         //Process incoming requests
+         for(counter = 0; counter < HTTP_SERVER_MAX_REQUESTS; counter++)
+         {
+            //Debug message
+            TRACE_INFO("Waiting for request...\r\n");
+
+            //Clear request header
+            memset(&connection->request, 0, sizeof(HttpRequest));
+            //Clear response header
+            memset(&connection->response, 0, sizeof(HttpResponse));
+
+            //Read the HTTP request header and parse its contents
+            error = httpReadRequestHeader(connection);
+            //Any error to report?
+            if(error)
+            {
+               //Debug message
+               TRACE_INFO("No HTTP request received or parsing error...\r\n");
+               break;
+            }
+
+#if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
+            //No Authorization header found?
+            if(!connection->request.auth.found)
+            {
+               //Invoke user-defined callback, if any
+               if(connection->settings->authCallback != NULL)
+               {
+                  //Check whether the access to the specified URI is authorized
+                  connection->status = connection->settings->authCallback(connection,
+                     connection->request.auth.user, connection->request.uri);
+               }
+               else
+               {
+                  //Access to the specified URI is allowed
+                  connection->status = HTTP_ACCESS_ALLOWED;
+               }
+            }
+
+            //Check access status
+            if(connection->status == HTTP_ACCESS_ALLOWED)
+            {
+               //Access to the specified URI is allowed
+               error = NO_ERROR;
+            }
+            else if(connection->status == HTTP_ACCESS_BASIC_AUTH_REQUIRED)
+            {
+               //Basic access authentication is required
+               connection->response.auth.mode = HTTP_AUTH_MODE_BASIC;
+               //Report an error
+               error = ERROR_AUTH_REQUIRED;
+            }
+            else if(connection->status == HTTP_ACCESS_DIGEST_AUTH_REQUIRED)
+            {
+               //Digest access authentication is required
+               connection->response.auth.mode = HTTP_AUTH_MODE_DIGEST;
+               //Report an error
+               error = ERROR_AUTH_REQUIRED;
+            }
+            else
+            {
+               //Access to the specified URI is denied
+               error = ERROR_NOT_FOUND;
+            }
+#endif
+            //Debug message
+            TRACE_INFO("Sending HTTP response to the client...\r\n");
+
+            //Check status code
+            if(!error)
+            {
+               //Default HTTP header fields
+               httpInitResponseHeader(connection);
+
+               //Invoke user-defined callback, if any
+               if(connection->settings->requestCallback != NULL)
+               {
+                  error = connection->settings->requestCallback(connection,
+                     connection->request.uri);
+               }
+               else
+               {
+                  //Keep processing...
+                  error = ERROR_NOT_FOUND;
+               }
+
+               //Check status code
+               if(error == ERROR_NOT_FOUND)
+               {
+#if (HTTP_SERVER_SSI_SUPPORT == ENABLED)
+                  //Use server-side scripting to dynamically generate HTML code?
+                  if(httpCompExtension(connection->request.uri, ".stm") ||
+                     httpCompExtension(connection->request.uri, ".shtm") ||
+                     httpCompExtension(connection->request.uri, ".shtml"))
+                  {
+                     //SSI processing (Server Side Includes)
+                     error = ssiExecuteScript(connection, connection->request.uri, 0);
+                  }
+                  else
+#endif
+                  {
+                     //Set the maximum age for static resources
+                     connection->response.maxAge = HTTP_SERVER_MAX_AGE;
+
+                     //Send the contents of the requested page
+                     error = httpSendResponse(connection, connection->request.uri);
+                  }
+               }
+
+               //The requested resource is not available?
+               if(error == ERROR_NOT_FOUND)
+               {
+                  //Default HTTP header fields
+                  httpInitResponseHeader(connection);
+
+                  //Invoke user-defined callback, if any
+                  if(connection->settings->uriNotFoundCallback != NULL)
+                  {
+                     error = connection->settings->uriNotFoundCallback(connection,
+                        connection->request.uri);
+                  }
+               }
+            }
+
+            //Check status code
+            if(error)
+            {
+               //Default HTTP header fields
+               httpInitResponseHeader(connection);
+
+               //Bad request?
+               if(error == ERROR_INVALID_REQUEST)
+               {
+                  //Send an error 400 and close the connection immediately
+                  httpSendErrorResponse(connection, 400,
+                     "The request is badly formed");
+               }
+               //Authorization required?
+               else if(error == ERROR_AUTH_REQUIRED)
+               {
+                  //Send an error 401 and keep the connection alive
+                  error = httpSendErrorResponse(connection, 401,
+                     "Authorization required");
+               }
+               //Page not found?
+               else if(error == ERROR_NOT_FOUND)
+               {
+                  //Send an error 404 and keep the connection alive
+                  error = httpSendErrorResponse(connection, 404,
+                     "The requested page could not be found");
+               }
+            }
+
+            //Internal error?
+            if(error)
+            {
+               //Close the connection immediately
+               break;
+            }
+
+            //Check whether the connection is persistent or not
+            if(!connection->request.keepAlive || !connection->response.keepAlive)
+            {
+               //Close the connection immediately
+               break;
+            }
+         }
+      }
+
+#if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
+      //Valid SSL/TLS context?
+      if(connection->tlsContext != NULL)
+      {
+         //Debug message
+         TRACE_INFO("Closing SSL/TLS session...\r\n");
+
+         //Gracefully close SSL/TLS session
+         tlsShutdown(connection->tlsContext);
+         //Release context
+         tlsFree(connection->tlsContext);
+      }
+#endif
+
+      //Valid socket handle?
+      if(connection->socket != NULL)
+      {
+         //Debug message
+         TRACE_INFO("Graceful shutdown...\r\n");
+         //Graceful shutdown
+         socketShutdown(connection->socket, SOCKET_SD_BOTH);
+
+         //Debug message
+         TRACE_INFO("Closing socket...\r\n");
+         //Close socket
+         socketClose(connection->socket);
+      }
+
+      //Ready to serve the next connection request...
+      connection->running = FALSE;
+      //Release semaphore
+      osReleaseSemaphore(&connection->serverContext->semaphore);
+   }
+}
+
+
+/**
+ * @brief Send HTTP response header
+ * @param[in] connection Structure representing an HTTP connection
+ * @return Error code
+ **/
+
+error_t httpWriteHeader(HttpConnection *connection)
+{
+   error_t error;
+
+   //Format HTTP response header
+   error = httpFormatResponseHeader(connection, connection->buffer);
+
+   //Check status code
+   if(!error)
+   {
+      //Debug message
+      TRACE_DEBUG("HTTP response header:\r\n%s", connection->buffer);
+
+      //Send HTTP response header to the client
+      error = httpSend(connection, connection->buffer,
+         strlen(connection->buffer), HTTP_FLAG_DELAY);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Read data from client request
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[out] data Buffer where to store the incoming data
+ * @param[in] size Maximum number of bytes that can be received
+ * @param[out] received Number of bytes that have been received
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t httpReadStream(HttpConnection *connection,
+   void *data, size_t size, size_t *received, uint_t flags)
+{
+   error_t error;
+   size_t n;
+
+   //No data has been read yet
+   *received = 0;
+
+   //Chunked encoding transfer is used?
+   if(connection->request.chunkedEncoding)
+   {
+      //Point to the output buffer
+      char_t *p = data;
+
+      //Read as much data as possible
+      while(*received < size)
+      {
+         //End of HTTP request body?
+         if(connection->request.lastChunk)
+            return ERROR_END_OF_STREAM;
+
+         //Acquire a new chunk when the current chunk
+         //has been completely consumed
+         if(connection->request.byteCount == 0)
+         {
+            //The size of each chunk is sent right before the chunk itself
+            error = httpReadChunkSize(connection);
+            //Failed to decode the chunk-size field?
+            if(error)
+               return error;
+
+            //Any chunk whose size is zero terminates the data transfer
+            if(!connection->request.byteCount)
+            {
+               //The user must be satisfied with data already on hand
+               return (*received > 0) ? NO_ERROR : ERROR_END_OF_STREAM;
+            }
+         }
+
+         //Limit the number of bytes to read at a time
+         n = MIN(size - *received, connection->request.byteCount);
+
+         //Read data
+         error = httpReceive(connection, p, n, &n, flags);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //Total number of data that have been read
+         *received += n;
+         //Remaining data still available in the current chunk
+         connection->request.byteCount -= n;
+
+         //The HTTP_FLAG_BREAK_CHAR flag causes the function to stop reading
+         //data as soon as the specified break character is encountered
+         if(flags & HTTP_FLAG_BREAK_CRLF)
+         {
+            //Check whether a break character has been received
+            if(p[n - 1] == LSB(flags))
+               break;
+         }
+         //The HTTP_FLAG_WAIT_ALL flag causes the function to return
+         //only when the requested number of bytes have been read
+         else if(!(flags & HTTP_FLAG_WAIT_ALL))
+         {
+            break;
+         }
+
+         //Advance data pointer
+         p += n;
+      }
+   }
+   //Default encoding?
+   else
+   {
+      //Return immediately if the end of the request body has been reached
+      if(!connection->request.byteCount)
+         return ERROR_END_OF_STREAM;
+
+      //Limit the number of bytes to read
+      n = MIN(size, connection->request.byteCount);
+
+      //Read data
+      error = httpReceive(connection, data, n, received, flags);
+      //Any error to report
+      if(error)
+         return error;
+
+      //Decrement the count of remaining bytes to read
+      connection->request.byteCount -= *received;
+   }
+
+   //Successful read operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write data to the client
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[in] data Buffer containing the data to be transmitted
+ * @param[in] length Number of bytes to be transmitted
+ * @return Error code
+ **/
+
+error_t httpWriteStream(HttpConnection *connection,
+   const void *data, size_t length)
+{
+   error_t error;
+   uint_t n;
+
+   //Use chunked encoding transfer?
+   if(connection->response.chunkedEncoding)
+   {
+      //Any data to send?
+      if(length > 0)
+      {
+         char_t s[8];
+
+         //The chunk-size field is a string of hex digits
+         //indicating the size of the chunk
+         n = sprintf(s, "%X\r\n", length);
+
+         //Send the chunk-size field
+         error = httpSend(connection, s, n, HTTP_FLAG_DELAY);
+         //Failed to send data?
+         if(error)
+            return error;
+
+         //Send the chunk-data
+         error = httpSend(connection, data, length, HTTP_FLAG_DELAY);
+         //Failed to send data?
+         if(error)
+            return error;
+
+         //Terminate the chunk-data by CRLF
+         error = httpSend(connection, "\r\n", 2, HTTP_FLAG_DELAY);
+      }
+      else
+      {
+         //Any chunk whose size is zero may terminate the data
+         //transfer and must be discarded
+         error = NO_ERROR;
+      }
+   }
+   //Default encoding?
+   else
+   {
+      //The length of the body shall not exceed the value
+      //specified in the Content-Length field
+      length = MIN(length, connection->response.byteCount);
+
+      //Send user data
+      error = httpSend(connection, data, length, HTTP_FLAG_DELAY);
+
+      //Decrement the count of remaining bytes to be transferred
+      connection->response.byteCount -= length;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Close output stream
+ * @param[in] connection Structure representing an HTTP connection
+ * @return Error code
+ **/
+
+error_t httpCloseStream(HttpConnection *connection)
+{
+   error_t error;
+
+   //Use chunked encoding transfer?
+   if(connection->response.chunkedEncoding)
+   {
+      //The chunked encoding is ended by any chunk whose size is zero
+      error = httpSend(connection, "0\r\n\r\n", 5, HTTP_FLAG_NO_DELAY);
+   }
+   else
+   {
+      //Flush the send buffer
+      error = httpSend(connection, "", 0, HTTP_FLAG_NO_DELAY);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send HTTP response
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[in] uri NULL-terminated string containing the file to be sent in response
+ * @return Error code
+ **/
+
+error_t httpSendResponse(HttpConnection *connection, const char_t *uri)
+{
+#if (HTTP_SERVER_FS_SUPPORT == ENABLED)
+   error_t error;
+   uint32_t length;
+   size_t n;
+   FsFile *file;
+
+   //Retrieve the full pathname
+   httpGetAbsolutePath(connection, uri,
+      connection->buffer, HTTP_SERVER_BUFFER_SIZE);
+
+   //Retrieve the size of the specified file
+   error = fsGetFileSize(connection->buffer, &length);
+   //The specified URI cannot be found?
+   if(error)
+      return ERROR_NOT_FOUND;
+
+   //Open the file for reading
+   file = fsOpenFile(connection->buffer, FS_FILE_MODE_READ);
+   //Failed to open the file?
+   if(file == NULL)
+      return ERROR_NOT_FOUND;
+#else
+   error_t error;
+   size_t length;
+   uint8_t *data;
+
+   //Retrieve the full pathname
+   httpGetAbsolutePath(connection, uri,
+      connection->buffer, HTTP_SERVER_BUFFER_SIZE);
+
+   //Get the resource data associated with the URI
+   error = resGetData(connection->buffer, &data, &length);
+   //The specified URI cannot be found?
+   if(error)
+      return error;
+#endif
+
+   //Format HTTP response header
+   connection->response.statusCode = 200;
+   connection->response.contentType = mimeGetType(uri);
+   connection->response.chunkedEncoding = FALSE;
+   connection->response.contentLength = length;
+
+   //Send the header to the client
+   error = httpWriteHeader(connection);
+   //Any error to report?
+   if(error)
+   {
+#if (HTTP_SERVER_FS_SUPPORT == ENABLED)
+      //Close the file
+      fsCloseFile(file);
+#endif
+      //Return status code
+      return error;
+   }
+
+#if (HTTP_SERVER_FS_SUPPORT == ENABLED)
+   //Send response body
+   while(length > 0)
+   {
+      //Limit the number of bytes to read at a time
+      n = MIN(length, HTTP_SERVER_BUFFER_SIZE);
+
+      //Read data from the specified file
+      error = fsReadFile(file, connection->buffer, n, &n);
+      //End of input stream?
+      if(error)
+         break;
+
+      //Send data to the client
+      error = httpWriteStream(connection, connection->buffer, n);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Decrement the count of remaining bytes to be transferred
+      length -= n;
+   }
+
+   //Close the file
+   fsCloseFile(file);
+
+   //Successful file transfer?
+   if(error == NO_ERROR || error == ERROR_END_OF_FILE)
+   {
+      if(length == 0)
+      {
+         //Properly close the output stream
+         error = httpCloseStream(connection);
+      }
+   }
+#else
+   //Send response body
+   error = httpWriteStream(connection, data, length);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Properly close output stream
+   error = httpCloseStream(connection);
+#endif
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send error response to the client
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[in] statusCode HTTP status code
+ * @param[in] message User message
+ * @return Error code
+ **/
+
+error_t httpSendErrorResponse(HttpConnection *connection,
+   uint_t statusCode, const char_t *message)
+{
+   error_t error;
+   size_t length;
+
+   //HTML response template
+   static const char_t template[] =
+      "<!doctype html>\r\n"
+      "<html>\r\n"
+      "<head><title>Error %03d</title></head>\r\n"
+      "<body>\r\n"
+      "<h2>Error %03d</h2>\r\n"
+      "<p>%s</p>\r\n"
+      "</body>\r\n"
+      "</html>\r\n";
+
+   //Compute the length of the response
+   length = strlen(template) + strlen(message) - 4;
+
+   //Format HTTP response header
+   connection->response.statusCode = statusCode;
+   connection->response.contentType = mimeGetType(".htm");
+   connection->response.chunkedEncoding = FALSE;
+   connection->response.contentLength = length;
+
+   //Send the header to the client
+   error = httpWriteHeader(connection);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Format HTML response
+   sprintf(connection->buffer, template, statusCode, statusCode, message);
+
+   //Send response body
+   error = httpWriteStream(connection, connection->buffer, length);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Properly close output stream
+   error = httpCloseStream(connection);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send redirect response to the client
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[in] statusCode HTTP status code (301 for permanent redirects)
+ * @param[in] uri NULL-terminated string containing the redirect URI
+ * @return Error code
+ **/
+
+error_t httpSendRedirectResponse(HttpConnection *connection,
+   uint_t statusCode, const char_t *uri)
+{
+   error_t error;
+   size_t length;
+
+   //HTML response template
+   static const char_t template[] =
+      "<!doctype html>\r\n"
+      "<html>\r\n"
+      "<head><title>Moved</title></head>\r\n"
+      "<body>\r\n"
+      "<h2>Moved</h2>\r\n"
+      "<p>This page has moved to <a href=\"%s\">%s</a>.</p>"
+      "</body>\r\n"
+      "</html>\r\n";
+
+   //Compute the length of the response
+   length = strlen(template) + 2 * strlen(uri) - 4;
+
+   //Format HTTP response header
+   connection->response.statusCode = statusCode;
+   connection->response.location = uri;
+   connection->response.contentType = mimeGetType(".htm");
+   connection->response.chunkedEncoding = FALSE;
+   connection->response.contentLength = length;
+
+   //Send the header to the client
+   error = httpWriteHeader(connection);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Format HTML response
+   sprintf(connection->buffer, template, uri, uri);
+
+   //Send response body
+   error = httpWriteStream(connection, connection->buffer, length);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Properly close output stream
+   error = httpCloseStream(connection);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Check whether the client's handshake is valid
+ * @param[in] connection Structure representing an HTTP connection
+ * @return TRUE if the WebSocket handshake is valid, else FALSE
+ **/
+
+bool_t httpCheckWebSocketHandshake(HttpConnection *connection)
+{
+#if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED)
+   error_t error;
+   size_t n;
+
+   //The request must contain an Upgrade header field whose value
+   //must include the "websocket" keyword
+   if(!connection->request.upgradeWebSocket)
+      return FALSE;
+
+   //The request must contain a Connection header field whose value
+   //must include the "Upgrade" token
+   if(!connection->request.connectionUpgrade)
+      return FALSE;
+
+   //Retrieve the length of the client's key
+   n = strlen(connection->request.clientKey);
+
+   //The request must include a header field with the name Sec-WebSocket-Key
+   if(n == 0)
+      return FALSE;
+
+   //The value of the Sec-WebSocket-Key header field must be a 16-byte
+   //value that has been Base64-encoded
+   error = base64Decode(connection->request.clientKey, n, connection->buffer, &n);
+   //Decoding failed?
+   if(error)
+      return FALSE;
+
+   //Check the length of the resulting value
+   if(n != 16)
+      return FALSE;
+
+   //The client's handshake is valid
+   return TRUE;
+#else
+   //WebSocket are not supported
+   return FALSE;
+#endif
+}
+
+
+/**
+ * @brief Upgrade an existing HTTP connection to a WebSocket
+ * @param[in] connection Structure representing an HTTP connection
+ * @return Handle referencing the new WebSocket
+ **/
+
+WebSocket *httpUpgradeToWebSocket(HttpConnection *connection)
+{
+   WebSocket *webSocket;
+
+#if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED)
+#if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
+   //Check whether a secure connection is being used
+   if(connection->tlsContext != NULL)
+   {
+      //Upgrade the secure connection to a WebSocket
+      webSocket = webSocketUpgradeSecureSocket(connection->socket,
+         connection->tlsContext);
+   }
+   else
+#endif
+   {
+      //Upgrade the connection to a WebSocket
+      webSocket = webSocketUpgradeSocket(connection->socket);
+   }
+
+   //Succesful upgrade?
+   if(webSocket != NULL)
+   {
+      error_t error;
+
+      //Copy client's key
+      error = webSocketSetClientKey(webSocket, connection->request.clientKey);
+
+      //Check status code
+      if(!error)
+      {
+#if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
+         //Detach the SSL/TLS context from the HTTP connection
+         connection->tlsContext = NULL;
+#endif
+         //Detach the socket from the HTTP connection
+         connection->socket = NULL;
+      }
+      else
+      {
+         //Clean up side effects
+         webSocketClose(webSocket);
+         webSocket = NULL;
+      }
+   }
+#else
+   //WebSockets are not supported
+   webSocket = NULL;
+#endif
+
+   //Return a handle to the freshly created WebSocket
+   return webSocket;
+}
+
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/http/http_server.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,653 @@
+/**
+ * @file http_server.h
+ * @brief HTTP server (HyperText Transfer Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _HTTP_SERVER_H
+#define _HTTP_SERVER_H
+
+//Dependencies
+#include "os_port.h"
+#include "core/socket.h"
+#include "web_socket/web_socket.h"
+
+//HTTP server support
+#ifndef HTTP_SERVER_SUPPORT
+   #define HTTP_SERVER_SUPPORT ENABLED
+#elif (HTTP_SERVER_SUPPORT != ENABLED && HTTP_SERVER_SUPPORT != DISABLED)
+   #error HTTP_SERVER_SUPPORT parameter is not valid
+#endif
+
+//Support for persistent connections
+#ifndef HTTP_SERVER_PERSISTENT_CONN_SUPPORT
+   #define HTTP_SERVER_PERSISTENT_CONN_SUPPORT DISABLED
+#elif (HTTP_SERVER_PERSISTENT_CONN_SUPPORT != ENABLED && HTTP_SERVER_PERSISTENT_CONN_SUPPORT != DISABLED)
+   #error HTTP_SERVER_PERSISTENT_CONN_SUPPORT parameter is not valid
+#endif
+
+//File system support
+#ifndef HTTP_SERVER_FS_SUPPORT
+   #define HTTP_SERVER_FS_SUPPORT DISABLED
+#elif (HTTP_SERVER_FS_SUPPORT != ENABLED && HTTP_SERVER_FS_SUPPORT != DISABLED)
+   #error HTTP_SERVER_FS_SUPPORT parameter is not valid
+#endif
+
+//Server Side Includes support
+#ifndef HTTP_SERVER_SSI_SUPPORT
+   #define HTTP_SERVER_SSI_SUPPORT DISABLED
+#elif (HTTP_SERVER_SSI_SUPPORT != ENABLED && HTTP_SERVER_SSI_SUPPORT != DISABLED)
+   #error HTTP_SERVER_SSI_SUPPORT parameter is not valid
+#endif
+
+//HTTP over SSL/TLS
+#ifndef HTTP_SERVER_TLS_SUPPORT
+   #define HTTP_SERVER_TLS_SUPPORT DISABLED
+#elif (HTTP_SERVER_TLS_SUPPORT != ENABLED && HTTP_SERVER_TLS_SUPPORT != DISABLED)
+   #error HTTP_SERVER_TLS_SUPPORT parameter is not valid
+#endif
+
+//Basic access authentication support
+#ifndef HTTP_SERVER_BASIC_AUTH_SUPPORT
+   #define HTTP_SERVER_BASIC_AUTH_SUPPORT DISABLED
+#elif (HTTP_SERVER_BASIC_AUTH_SUPPORT != ENABLED && HTTP_SERVER_BASIC_AUTH_SUPPORT != DISABLED)
+   #error HTTP_SERVER_BASIC_AUTH_SUPPORT parameter is not valid
+#endif
+
+//Digest access authentication support
+#ifndef HTTP_SERVER_DIGEST_AUTH_SUPPORT
+   #define HTTP_SERVER_DIGEST_AUTH_SUPPORT DISABLED
+#elif (HTTP_SERVER_DIGEST_AUTH_SUPPORT != ENABLED && HTTP_SERVER_DIGEST_AUTH_SUPPORT != DISABLED)
+   #error HTTP_SERVER_DIGEST_AUTH_SUPPORT parameter is not valid
+#endif
+
+//WebSocket support
+#ifndef HTTP_SERVER_WEB_SOCKET_SUPPORT
+   #define HTTP_SERVER_WEB_SOCKET_SUPPORT DISABLED
+#elif (HTTP_SERVER_WEB_SOCKET_SUPPORT != ENABLED && HTTP_SERVER_WEB_SOCKET_SUPPORT != DISABLED)
+   #error HTTP_SERVER_WEB_SOCKET_SUPPORT parameter is not valid
+#endif
+
+//Multipart content type support
+#ifndef HTTP_SERVER_MULTIPART_TYPE_SUPPORT
+   #define HTTP_SERVER_MULTIPART_TYPE_SUPPORT DISABLED
+#elif (HTTP_SERVER_MULTIPART_TYPE_SUPPORT != ENABLED && HTTP_SERVER_MULTIPART_TYPE_SUPPORT != DISABLED)
+   #error HTTP_SERVER_MULTIPART_TYPE_SUPPORT parameter is not valid
+#endif
+
+//Stack size required to run the HTTP server
+#ifndef HTTP_SERVER_STACK_SIZE
+   #define HTTP_SERVER_STACK_SIZE 650
+#elif (HTTP_SERVER_STACK_SIZE < 1)
+   #error HTTP_SERVER_STACK_SIZE parameter is not valid
+#endif
+
+//Priority at which the HTTP server should run
+#ifndef HTTP_SERVER_PRIORITY
+   #define HTTP_SERVER_PRIORITY OS_TASK_PRIORITY_NORMAL
+#endif
+
+//HTTP connection timeout
+#ifndef HTTP_SERVER_TIMEOUT
+   #define HTTP_SERVER_TIMEOUT 10000
+#elif (HTTP_SERVER_TIMEOUT < 1000)
+   #error HTTP_SERVER_TIMEOUT parameter is not valid
+#endif
+
+//Maximum time the server will wait for a subsequent
+//request before closing the connection
+#ifndef HTTP_SERVER_IDLE_TIMEOUT
+   #define HTTP_SERVER_IDLE_TIMEOUT 5000
+#elif (HTTP_SERVER_IDLE_TIMEOUT < 1000)
+   #error HTTP_SERVER_IDLE_TIMEOUT parameter is not valid
+#endif
+
+//Maximum length of the pending connection queue
+#ifndef HTTP_SERVER_BACKLOG
+   #define HTTP_SERVER_BACKLOG 4
+#elif (HTTP_SERVER_BACKLOG < 1)
+   #error HTTP_SERVER_BACKLOG parameter is not valid
+#endif
+
+//Maximum number of requests per connection
+#ifndef HTTP_SERVER_MAX_REQUESTS
+   #define HTTP_SERVER_MAX_REQUESTS 1000
+#elif (HTTP_SERVER_MAX_REQUESTS < 1)
+   #error HTTP_SERVER_MAX_REQUESTS parameter is not valid
+#endif
+
+//Size of buffer used for input/output operations
+#ifndef HTTP_SERVER_BUFFER_SIZE
+   #define HTTP_SERVER_BUFFER_SIZE 1024
+#elif (HTTP_SERVER_BUFFER_SIZE < 128)
+   #error HTTP_SERVER_BUFFER_SIZE parameter is not valid
+#endif
+
+//Maximum size of root directory
+#ifndef HTTP_SERVER_ROOT_DIR_MAX_LEN
+   #define HTTP_SERVER_ROOT_DIR_MAX_LEN 31
+#elif (HTTP_SERVER_ROOT_DIR_MAX_LEN < 7)
+   #error HTTP_SERVER_ROOT_DIR_MAX_LEN parameter is not valid
+#endif
+
+//Maximum size of default index file
+#ifndef HTTP_SERVER_DEFAULT_DOC_MAX_LEN
+   #define HTTP_SERVER_DEFAULT_DOC_MAX_LEN 31
+#elif (HTTP_SERVER_DEFAULT_DOC_MAX_LEN < 7)
+   #error HTTP_SERVER_DEFAULT_DOC_MAX_LEN parameter is not valid
+#endif
+
+//Maximum length of HTTP method
+#ifndef HTTP_SERVER_METHOD_MAX_LEN
+   #define HTTP_SERVER_METHOD_MAX_LEN 7
+#elif (HTTP_SERVER_METHOD_MAX_LEN < 1)
+   #error HTTP_SERVER_METHOD_MAX_LEN parameter is not valid
+#endif
+
+//Maximum length of URI
+#ifndef HTTP_SERVER_URI_MAX_LEN
+   #define HTTP_SERVER_URI_MAX_LEN 255
+#elif (HTTP_SERVER_URI_MAX_LEN < 31)
+   #error HTTP_SERVER_URI_MAX_LEN parameter is not valid
+#endif
+
+//Maximum length of query strings
+#ifndef HTTP_SERVER_QUERY_STRING_MAX_LEN
+   #define HTTP_SERVER_QUERY_STRING_MAX_LEN 255
+#elif (HTTP_SERVER_QUERY_STRING_MAX_LEN < 7)
+   #error HTTP_SERVER_QUERY_STRING_MAX_LEN parameter is not valid
+#endif
+
+//Maximum host name length
+#ifndef HTTP_SERVER_HOST_MAX_LEN
+   #define HTTP_SERVER_HOST_MAX_LEN 31
+#elif (HTTP_SERVER_HOST_MAX_LEN < 7)
+   #error HTTP_SERVER_HOST_MAX_LEN parameter is not valid
+#endif
+
+//Maximum user name length
+#ifndef HTTP_SERVER_USERNAME_MAX_LEN
+   #define HTTP_SERVER_USERNAME_MAX_LEN 31
+#elif (HTTP_SERVER_USERNAME_MAX_LEN < 7)
+   #error HTTP_SERVER_USERNAME_MAX_LEN parameter is not valid
+#endif
+
+//Maximum length of CGI parameters
+#ifndef HTTP_SERVER_CGI_PARAM_MAX_LEN
+   #define HTTP_SERVER_CGI_PARAM_MAX_LEN 31
+#elif (HTTP_SERVER_CGI_PARAM_MAX_LEN < 7)
+   #error HTTP_SERVER_CGI_PARAM_MAX_LEN parameter is not valid
+#endif
+
+//Maximum recursion limit
+#ifndef HTTP_SERVER_SSI_MAX_RECURSION
+   #define HTTP_SERVER_SSI_MAX_RECURSION 3
+#elif (HTTP_SERVER_SSI_MAX_RECURSION < 1 || HTTP_SERVER_SSI_MAX_RECURSION > 8)
+   #error HTTP_SERVER_SSI_MAX_RECURSION parameter is not valid
+#endif
+
+//Maximum age for static resources
+#ifndef HTTP_SERVER_MAX_AGE
+   #define HTTP_SERVER_MAX_AGE 0
+#elif (HTTP_SERVER_MAX_AGE < 0)
+   #error HTTP_SERVER_MAX_AGE parameter is not valid
+#endif
+
+//Nonce cache size
+#ifndef HTTP_SERVER_NONCE_CACHE_SIZE
+   #define HTTP_SERVER_NONCE_CACHE_SIZE 8
+#elif (HTTP_SERVER_NONCE_CACHE_SIZE < 1)
+   #error HTTP_SERVER_NONCE_CACHE_SIZE parameter is not valid
+#endif
+
+//Lifetime of nonces
+#ifndef HTTP_SERVER_NONCE_LIFETIME
+   #define HTTP_SERVER_NONCE_LIFETIME 60000
+#elif (HTTP_SERVER_NONCE_LIFETIME < 1000)
+   #error HTTP_SERVER_NONCE_LIFETIME parameter is not valid
+#endif
+
+//Nonce size
+#ifndef HTTP_SERVER_NONCE_SIZE
+   #define HTTP_SERVER_NONCE_SIZE 16
+#elif (HTTP_SERVER_NONCE_SIZE < 1)
+   #error HTTP_SERVER_NONCE_SIZE parameter is not valid
+#endif
+
+//Maximum length for boundary string
+#ifndef HTTP_SERVER_BOUNDARY_MAX_LEN
+   #define HTTP_SERVER_BOUNDARY_MAX_LEN 70
+#elif (HTTP_SERVER_BOUNDARY_MAX_LEN < 1)
+   #error HTTP_SERVER_BOUNDARY_MAX_LEN parameter is not valid
+#endif
+
+//File system support?
+#if (HTTP_SERVER_FS_SUPPORT == ENABLED)
+   #include "fs_port.h"
+#else
+   #include "resource_manager.h"
+#endif
+
+//HTTP over SSL/TLS supported?
+#if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
+   #include "crypto.h"
+   #include "tls.h"
+#endif
+
+//Basic authentication supported?
+#if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED)
+   #include "crypto.h"
+   #include "base64.h"
+#endif
+
+//Digest authentication supported?
+#if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
+   #include "crypto.h"
+   #include "md5.h"
+#endif
+
+//WebSocket supported?
+#if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED)
+   #include "crypto.h"
+   #include "base64.h"
+#endif
+
+//HTTP port number
+#define HTTP_PORT 80
+//HTTPS port number (HTTP over SSL/TLS)
+#define HTTPS_PORT 443
+
+//Forward declaration of HttpServerContext structure
+struct _HttpServerContext;
+#define HttpServerContext struct _HttpServerContext
+
+//Forward declaration of HttpConnection structure
+struct _HttpConnection;
+#define HttpConnection struct _HttpConnection
+
+
+/**
+ * @brief HTTP version numbers
+ **/
+
+typedef enum
+{
+   HTTP_VERSION_0_9 = 0x0009,
+   HTTP_VERSION_1_0 = 0x0100,
+   HTTP_VERSION_1_1 = 0x0101
+} HttpVersion;
+
+
+/**
+ * @brief HTTP authentication schemes
+ **/
+
+typedef enum
+{
+   HTTP_AUTH_MODE_NONE   = 0,
+   HTTP_AUTH_MODE_BASIC  = 1,
+   HTTP_AUTH_MODE_DIGEST = 2
+} HttpAuthMode;
+
+
+/**
+ * @brief Access status
+ **/
+
+typedef enum
+{
+   HTTP_ACCESS_DENIED               = 0,
+   HTTP_ACCESS_ALLOWED              = 1,
+   HTTP_ACCESS_BASIC_AUTH_REQUIRED  = 2,
+   HTTP_ACCESS_DIGEST_AUTH_REQUIRED = 3
+} HttpAccessStatus;
+
+
+/**
+ * @brief Flags used by I/O functions
+ **/
+
+typedef enum
+{
+   HTTP_FLAG_WAIT_ALL   = 0x0800,
+   HTTP_FLAG_BREAK_CHAR = 0x1000,
+   HTTP_FLAG_BREAK_CRLF = 0x100A,
+   HTTP_FLAG_WAIT_ACK   = 0x2000,
+   HTTP_FLAG_NO_DELAY   = 0x4000,
+   HTTP_FLAG_DELAY      = 0x8000
+} HttpFlags;
+
+
+/**
+ * @brief HTTP connection states
+ **/
+
+typedef enum
+{
+   HTTP_CONN_STATE_IDLE        = 0,
+   HTTP_CONN_STATE_REQ_LINE    = 1,
+   HTTP_CONN_STATE_REQ_HEADER  = 2,
+   HTTP_CONN_STATE_REQ_BODY    = 3,
+   HTTP_CONN_STATE_RESP_HEADER = 4,
+   HTTP_CONN_STATE_RESP_BODY   = 5,
+   HTTP_CONN_STATE_SHUTDOWN    = 6,
+   HTTP_CONN_STATE_CLOSE       = 7
+} HttpConnState;
+
+
+//The HTTP_FLAG_BREAK macro causes the httpReadStream() function to stop
+//reading data whenever the specified break character is encountered
+#define HTTP_FLAG_BREAK(c) (HTTP_FLAG_BREAK_CHAR | LSB(c))
+
+
+//HTTP over SSL/TLS supported?
+#if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
+
+/**
+ * @brief SSL/TLS initialization callback function
+ **/
+
+typedef error_t (*TlsInitCallback)(HttpConnection *connection,
+   TlsContext *tlsContext);
+
+#endif
+
+
+/**
+ * @brief Random data generation callback function
+ **/
+
+typedef error_t (*HttpRandCallback)(uint8_t *data, size_t length);
+
+
+/**
+ * @brief HTTP authentication callback function
+ **/
+
+typedef HttpAccessStatus (*HttpAuthCallback)(HttpConnection *connection,
+   const char_t *user, const char_t *uri);
+
+
+/**
+ * @brief CGI callback function
+ **/
+
+typedef error_t (*HttpCgiCallback)(HttpConnection *connection,
+   const char_t *param);
+
+
+/**
+ * @brief HTTP request callback function
+ **/
+
+typedef error_t (*HttpRequestCallback)(HttpConnection *connection,
+   const char_t *uri);
+
+
+/**
+ * @brief URI not found callback function
+ **/
+
+typedef error_t (*HttpUriNotFoundCallback)(HttpConnection *connection,
+   const char_t *uri);
+
+
+/**
+ * @brief HTTP status code
+ **/
+
+typedef struct
+{
+   uint_t value;
+   const char_t message[28];
+} HttpStatusCodeDesc;
+
+
+/**
+ * @brief Authorization header
+ **/
+
+typedef struct
+{
+   bool_t found;                                  ///<The Authorization header has been found
+   HttpAuthMode mode;                             ///<Authentication scheme
+   char_t user[HTTP_SERVER_USERNAME_MAX_LEN + 1]; ///<User name
+#if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED)
+   const char_t *password;                        ///<Password
+#endif
+#if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
+   const char_t *realm;
+   const char_t *nonce;                           ///<Server nonce
+   const char_t *uri;                             ///<Digest URI
+   const char_t *qop;
+   const char_t *nc;                              ///<Nonce count
+   const char_t *cnonce;                          ///<Client nonce
+   const char_t *response;
+   const char_t *opaque;
+#endif
+} HttpAuthorizationHeader;
+
+
+/**
+ * @brief Authenticate header
+ **/
+
+typedef struct
+{
+   HttpAuthMode mode; ///<Authentication scheme
+#if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
+   bool_t stale;      ///<STALE flag
+#endif
+} HttpAuthenticateHeader;
+
+
+/**
+ * @brief HTTP request
+ **/
+
+typedef struct
+{
+   uint_t version;                                           ///<HTTP version number
+   char_t method[HTTP_SERVER_METHOD_MAX_LEN + 1];            ///<HTTP method
+   char_t uri[HTTP_SERVER_URI_MAX_LEN + 1];                  ///<Resource identifier
+   char_t queryString[HTTP_SERVER_QUERY_STRING_MAX_LEN + 1]; ///<Query string
+   char_t host[HTTP_SERVER_HOST_MAX_LEN + 1];                ///<Host name
+   bool_t keepAlive;
+   bool_t chunkedEncoding;
+   size_t contentLength;
+   size_t byteCount;
+   bool_t firstChunk;
+   bool_t lastChunk;
+#if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
+   HttpAuthorizationHeader auth;                             ///<Authorization header
+#endif
+#if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED)
+   bool_t upgradeWebSocket;
+   bool_t connectionUpgrade;
+   char_t clientKey[WEB_SOCKET_CLIENT_KEY_SIZE + 1];
+#endif
+#if (HTTP_SERVER_MULTIPART_TYPE_SUPPORT == ENABLED)
+   char_t boundary[HTTP_SERVER_BOUNDARY_MAX_LEN + 1];        ///<Boundary string
+   size_t boundaryLength;                                    ///<Boundary string length
+#endif
+} HttpRequest;
+
+
+/**
+ * @brief HTTP response
+ **/
+
+typedef struct
+{
+   uint_t version;              ///<HTTP version number
+   uint_t statusCode;           ///<HTTP status code
+   bool_t keepAlive;
+   bool_t noCache;
+   uint_t maxAge;
+   const char_t *location;
+   const char_t *contentType;
+   bool_t chunkedEncoding;
+   size_t contentLength;
+   size_t byteCount;
+#if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
+   HttpAuthenticateHeader auth; ///<Authenticate header
+#endif
+} HttpResponse;
+
+
+/**
+ * @brief HTTP server settings
+ **/
+
+typedef struct
+{
+   NetInterface *interface;                                     ///<Underlying network interface
+   uint16_t port;                                               ///<HTTP server port number
+   uint_t backlog;                                              ///<Maximum length of the pending connection queue
+   uint_t maxConnections;                                       ///<Maximum number of simultaneous connections
+   HttpConnection *connections;                                 ///<HTTP client connections
+   char_t rootDirectory[HTTP_SERVER_ROOT_DIR_MAX_LEN + 1];      ///<Web root directory
+   char_t defaultDocument[HTTP_SERVER_DEFAULT_DOC_MAX_LEN + 1]; ///<Default home page
+#if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
+   bool_t useTls;                                               ///<HTTP over SSL/TLS
+   TlsInitCallback tlsInitCallback;                             ///<SSL/TLS initialization callback function
+#endif
+#if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
+   HttpRandCallback randCallback;                               ///<Random data generation callback function
+   HttpAuthCallback authCallback;                               ///<HTTP authentication callback function
+#endif
+   HttpCgiCallback cgiCallback;                                 ///<CGI callback function
+   HttpRequestCallback requestCallback;                         ///<HTTP request callback function
+   HttpUriNotFoundCallback uriNotFoundCallback;                 ///<URI not found callback function
+} HttpServerSettings;
+
+
+/**
+ * @brief Nonce cache entry
+ **/
+
+typedef struct
+{
+   char_t nonce[HTTP_SERVER_NONCE_SIZE * 2 + 1]; ///<Nonce
+   uint32_t count;                               ///<Nonce count
+   systime_t timestamp;                          ///<Time stamp to manage entry lifetime
+} HttpNonceCacheEntry;
+
+
+/**
+ * @brief HTTP server context
+ **/
+
+struct _HttpServerContext
+{
+   HttpServerSettings settings;                                  ///<User settings
+   OsTask *taskHandle;                                           ///<Listener task handle
+   OsSemaphore semaphore;                                        ///<Semaphore limiting the number of connections
+   Socket *socket;                                               ///<Listening socket
+   HttpConnection *connections;                                  ///<HTTP client connections
+#if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
+   OsMutex nonceCacheMutex;                                      ///<Mutex preventing simultaneous access to the nonce cache
+   HttpNonceCacheEntry nonceCache[HTTP_SERVER_NONCE_CACHE_SIZE]; ///<Nonce cache
+#endif
+};
+
+
+/**
+ * @brief HTTP connection
+ *
+ * An HttpConnection instance represents one
+ * transaction with an HTTP client
+ *
+ **/
+
+struct _HttpConnection
+{
+   HttpServerSettings *settings;                       ///<Reference to the HTTP server settings
+   HttpServerContext *serverContext;                   ///<Reference to the HTTP server context
+   OsTask *taskHandle;                                 ///<Client task handle
+   OsEvent startEvent;
+   bool_t running;
+   Socket *socket;                                     ///<Socket
+#if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
+   TlsContext *tlsContext;                             ///<SSL/TLS context
+#endif
+   HttpRequest request;                                ///<Incoming HTTP request header
+   HttpResponse response;                              ///<HTTP response header
+   HttpAccessStatus status;                            ///<Access status
+   char_t cgiParam[HTTP_SERVER_CGI_PARAM_MAX_LEN + 1]; ///<CGI parameter
+   uint32_t dummy;                                     ///<Force alignment of the buffer on 32-bit boundaries
+   char_t buffer[HTTP_SERVER_BUFFER_SIZE];             ///<Memory buffer for input/output operations
+#if (NET_RTOS_SUPPORT == DISABLED)
+   HttpConnState state;                                ///<Connection state
+   systime_t timestamp;
+   size_t bufferPos;
+   size_t bufferLen;
+   uint8_t *bodyStart;
+   size_t bodyPos;
+   size_t bodyLen;
+#endif
+};
+
+
+//HTTP server related functions
+void httpServerGetDefaultSettings(HttpServerSettings *settings);
+error_t httpServerInit(HttpServerContext *context, const HttpServerSettings *settings);
+error_t httpServerStart(HttpServerContext *context);
+
+void httpListenerTask(void *param);
+void httpConnectionTask(void *param);
+
+error_t httpWriteHeader(HttpConnection *connection);
+
+error_t httpReadStream(HttpConnection *connection,
+   void *data, size_t size, size_t *received, uint_t flags);
+
+error_t httpWriteStream(HttpConnection *connection,
+   const void *data, size_t length);
+
+error_t httpCloseStream(HttpConnection *connection);
+
+error_t httpSendResponse(HttpConnection *connection, const char_t *uri);
+
+error_t httpSendErrorResponse(HttpConnection *connection,
+   uint_t statusCode, const char_t *message);
+
+error_t httpSendRedirectResponse(HttpConnection *connection,
+   uint_t statusCode, const char_t *uri);
+
+//HTTP authentication related functions
+bool_t httpCheckPassword(HttpConnection *connection,
+   const char_t *password, HttpAuthMode mode);
+
+//WebSocket related functions
+bool_t httpCheckWebSocketHandshake(HttpConnection *connection);
+WebSocket *httpUpgradeToWebSocket(HttpConnection *connection);
+
+//Miscellaneous functions
+error_t httpDecodePercentEncodedString(const char_t *input,
+   char_t *output, size_t outputSize);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/http/http_server_auth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,633 @@
+/**
+ * @file http_server_auth.c
+ * @brief HTTP authentication
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL HTTP_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include "core/net.h"
+#include "http/http_server.h"
+#include "http/http_server_auth.h"
+#include "http/http_server_misc.h"
+#include "str.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (HTTP_SERVER_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Password verification
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[in] password NULL-terminated string containing the password to be checked
+ * @param[in] mode HTTP authentication scheme to be used. Acceptable
+ *   values are HTTP_AUTH_MODE_BASIC or HTTP_AUTH_MODE_DIGEST
+ * @return TRUE if the password is valid, else FALSE
+ **/
+
+bool_t httpCheckPassword(HttpConnection *connection,
+   const char_t *password, HttpAuthMode mode)
+{
+   //This flag tells whether the password is valid
+   bool_t status = FALSE;
+
+   //Debug message
+   TRACE_DEBUG("HTTP password verification...\r\n");
+
+#if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED)
+   //Basic authentication scheme?
+   if(mode == HTTP_AUTH_MODE_BASIC)
+   {
+      //Point to the authentication credentials
+      HttpAuthorizationHeader *auth = &connection->request.auth;
+
+      //Make sure authentication credentials have been found
+      if(auth->found && auth->mode == HTTP_AUTH_MODE_BASIC)
+      {
+         //Sanity check
+         if(auth->password != NULL)
+         {
+            //Check whether the password is valid
+            if(!strcmp(password, auth->password))
+               status = TRUE;
+         }
+      }
+   }
+#endif
+#if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
+   //Digest authentication scheme?
+   if(mode == HTTP_AUTH_MODE_DIGEST)
+   {
+      //Point to the authentication credentials
+      HttpAuthorizationHeader *auth = &connection->request.auth;
+
+      //Make sure authentication credentials have been found
+      if(auth->found && auth->mode == HTTP_AUTH_MODE_DIGEST)
+      {
+         //Sanity check
+         if(auth->realm != NULL && auth->nonce != NULL &&
+            auth->uri != NULL && auth->qop != NULL &&
+            auth->nc != NULL && auth->cnonce != NULL &&
+            auth->response != NULL)
+         {
+            error_t error;
+            Md5Context *md5Context;
+            char_t ha1[2 * MD5_DIGEST_SIZE + 1];
+            char_t ha2[2 * MD5_DIGEST_SIZE + 1];
+
+            //Allocate a memory buffer to hold the MD5 context
+            md5Context = osAllocMem(sizeof(Md5Context));
+
+            //MD5 context successfully allocated?
+            if(md5Context != NULL)
+            {
+               //Compute HA1 = MD5(username : realm : password)
+               md5Init(md5Context);
+               md5Update(md5Context, auth->user, strlen(auth->user));
+               md5Update(md5Context, ":", 1);
+               md5Update(md5Context, auth->realm, strlen(auth->realm));
+               md5Update(md5Context, ":", 1);
+               md5Update(md5Context, password, strlen(password));
+               md5Final(md5Context, NULL);
+
+               //Convert MD5 hash to hex string
+               httpConvertArrayToHexString(md5Context->digest, MD5_DIGEST_SIZE, ha1);
+               //Debug message
+               TRACE_DEBUG("  HA1: %s\r\n", ha1);
+
+               //Compute HA2 = MD5(method : uri)
+               md5Init(md5Context);
+               md5Update(md5Context, connection->request.method, strlen(connection->request.method));
+               md5Update(md5Context, ":", 1);
+               md5Update(md5Context, auth->uri, strlen(auth->uri));
+               md5Final(md5Context, NULL);
+
+               //Convert MD5 hash to hex string
+               httpConvertArrayToHexString(md5Context->digest, MD5_DIGEST_SIZE, ha2);
+               //Debug message
+               TRACE_DEBUG("  HA2: %s\r\n", ha2);
+
+               //Compute MD5(HA1 : nonce : nc : cnonce : qop : HA1)
+               md5Init(md5Context);
+               md5Update(md5Context, ha1, strlen(ha1));
+               md5Update(md5Context, ":", 1);
+               md5Update(md5Context, auth->nonce, strlen(auth->nonce));
+               md5Update(md5Context, ":", 1);
+               md5Update(md5Context, auth->nc, strlen(auth->nc));
+               md5Update(md5Context, ":", 1);
+               md5Update(md5Context, auth->cnonce, strlen(auth->cnonce));
+               md5Update(md5Context, ":", 1);
+               md5Update(md5Context, auth->qop, strlen(auth->qop));
+               md5Update(md5Context, ":", 1);
+               md5Update(md5Context, ha2, strlen(ha2));
+               md5Final(md5Context, NULL);
+
+               //Convert MD5 hash to hex string
+               httpConvertArrayToHexString(md5Context->digest, MD5_DIGEST_SIZE, ha1);
+               //Debug message
+               TRACE_DEBUG("  response: %s\r\n", ha1);
+
+               //Release MD5 context
+               osFreeMem(md5Context);
+
+               //Check response
+               if(!strcasecmp(auth->response, ha1))
+               {
+                  //Perform nonce verification
+                  error = httpVerifyNonce(connection->serverContext, auth->nonce, auth->nc);
+
+                  //Valid nonce?
+                  if(!error)
+                  {
+                     //Access to the resource is granted
+                     status = TRUE;
+                  }
+                  else
+                  {
+                     //The client may wish to simply retry the request with a
+                     //new encrypted response, without re-prompting the user
+                     //for a new username and password
+                     connection->response.auth.stale = TRUE;
+                  }
+               }
+            }
+         }
+      }
+   }
+#endif
+
+   //Return TRUE is the password is valid, else FALSE
+   return status;
+}
+
+
+/**
+ * @brief Parse Authorization header field
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[in] value Authorization field value
+ **/
+
+void httpParseAuthorizationField(HttpConnection *connection, char_t *value)
+{
+   char_t *p;
+   char_t *token;
+
+   //Retrieve the authentication scheme
+   token = strtok_r(value, " \t", &p);
+
+   //Any parsing error?
+   if(token == NULL)
+   {
+      //Exit immediately
+      return;
+   }
+#if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED)
+   //Basic access authentication?
+   else if(!strcasecmp(token, "Basic"))
+   {
+      error_t error;
+      size_t n;
+      char_t *separator;
+
+      //Use the relevant authentication scheme
+      connection->request.auth.mode = HTTP_AUTH_MODE_BASIC;
+      //Retrieve the credentials
+      token = strtok_r(NULL, " \t", &p);
+
+      //Any parsing error?
+      if(token != NULL)
+      {
+         //Decrypt the Base64 encoded string
+         error = base64Decode(token, strlen(token), token, &n);
+
+         //Successful decoding?
+         if(!error)
+         {
+            //Properly terminate the string
+            token[n] = '\0';
+            //Check whether a separator is present
+            separator = strchr(token, ':');
+
+            //Separator found?
+            if(separator != NULL)
+            {
+               //Split the line
+               *separator = '\0';
+
+               //Save user name
+               strSafeCopy(connection->request.auth.user,
+                  token, HTTP_SERVER_USERNAME_MAX_LEN);
+
+               //Point to the password
+               token = separator + 1;
+               //Save password
+               connection->request.auth.password = token;
+            }
+         }
+      }
+
+      //Debug message
+      TRACE_DEBUG("Authorization header:\r\n");
+      TRACE_DEBUG("  username: %s\r\n", connection->request.auth.user);
+      TRACE_DEBUG("  password: %s\r\n", connection->request.auth.password);
+   }
+#endif
+#if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
+   //Digest access authentication?
+   else if(!strcasecmp(token, "Digest"))
+   {
+      size_t n;
+      char_t *separator;
+      char_t *name;
+
+      //Use the relevant authentication scheme
+      connection->request.auth.mode = HTTP_AUTH_MODE_DIGEST;
+      //Get the first parameter
+      token = strtok_r(NULL, ",", &p);
+
+      //Parse the Authorization header field
+      while(token != NULL)
+      {
+         //Check whether a separator is present
+         separator = strchr(token, '=');
+
+         //Separator found?
+         if(separator != NULL)
+         {
+            //Split the string
+            *separator = '\0';
+
+            //Get field name and value
+            name = strTrimWhitespace(token);
+            value = strTrimWhitespace(separator + 1);
+
+            //Retrieve the length of the value field
+            n = strlen(value);
+
+            //Discard the surrounding quotes
+            if(n > 0 && value[n - 1] == '\"')
+               value[n - 1] = '\0';
+            if(value[0] == '\"')
+               value++;
+
+            //Check parameter name
+            if(!strcasecmp(name, "username"))
+            {
+               //Save user name
+               strSafeCopy(connection->request.auth.user,
+                  value, HTTP_SERVER_USERNAME_MAX_LEN);
+            }
+            else if(!strcasecmp(name, "realm"))
+            {
+               //Save realm
+               connection->request.auth.realm = value;
+            }
+            else if(!strcasecmp(name, "nonce"))
+            {
+               //Save nonce parameter
+               connection->request.auth.nonce = value;
+            }
+            else if(!strcasecmp(name, "uri"))
+            {
+               //Save uri parameter
+               connection->request.auth.uri = value;
+            }
+            else if(!strcasecmp(name, "qop"))
+            {
+               //Save qop parameter
+               connection->request.auth.qop = value;
+            }
+            else if(!strcasecmp(name, "nc"))
+            {
+               //Save nc parameter
+               connection->request.auth.nc = value;
+            }
+            else if(!strcasecmp(name, "cnonce"))
+            {
+               //Save cnonce parameter
+               connection->request.auth.cnonce = value;
+            }
+            else if(!strcasecmp(name, "response"))
+            {
+               //Save response parameter
+               connection->request.auth.response = value;
+            }
+            else if(!strcasecmp(name, "opaque"))
+            {
+               //Save opaque parameter
+               connection->request.auth.opaque = value;
+            }
+
+            //Get next parameter
+            token = strtok_r(NULL, ",", &p);
+         }
+      }
+
+      //Debug message
+      TRACE_DEBUG("Authorization header:\r\n");
+      TRACE_DEBUG("  username: %s\r\n", connection->request.auth.user);
+      TRACE_DEBUG("  realm: %s\r\n", connection->request.auth.realm);
+      TRACE_DEBUG("  nonce: %s\r\n", connection->request.auth.nonce);
+      TRACE_DEBUG("  uri: %s\r\n", connection->request.auth.uri);
+      TRACE_DEBUG("  qop: %s\r\n", connection->request.auth.qop);
+      TRACE_DEBUG("  nc: %s\r\n", connection->request.auth.nc);
+      TRACE_DEBUG("  cnonce: %s\r\n", connection->request.auth.cnonce);
+      TRACE_DEBUG("  response: %s\r\n", connection->request.auth.response);
+      TRACE_DEBUG("  opaque: %s\r\n", connection->request.auth.opaque);
+   }
+#endif
+   else
+   {
+      //The specified authentication scheme is not supported
+      return;
+   }
+
+#if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
+   //The Authorization header has been found
+   connection->request.auth.found = TRUE;
+
+   //Invoke user-defined callback, if any
+   if(connection->settings->authCallback != NULL)
+   {
+      //Check whether the access to the specified URI is authorized
+      connection->status = connection->settings->authCallback(connection,
+         connection->request.auth.user, connection->request.uri);
+   }
+   else
+   {
+      //Access to the specified URI is allowed
+      connection->status = HTTP_ACCESS_ALLOWED;
+   }
+#endif
+}
+
+
+/**
+ * @brief Format WWW-Authenticate header field
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[out] output Buffer where to format the header field
+ * @return Total length of the header field
+ **/
+
+size_t httpAddAuthenticateField(HttpConnection *connection, char_t *output)
+{
+   size_t n;
+
+#if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED)
+   //Basic authentication scheme?
+   if(connection->response.auth.mode == HTTP_AUTH_MODE_BASIC)
+   {
+      //Set WWW-Authenticate field
+      n = sprintf(output, "WWW-Authenticate: Basic realm=\"Protected Area\"\r\n");
+   }
+   else
+#endif
+#if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
+   //Digest authentication scheme?
+   if(connection->response.auth.mode == HTTP_AUTH_MODE_DIGEST)
+   {
+      error_t error;
+      size_t k;
+      uint8_t opaque[16];
+
+      //Set WWW-Authenticate field
+      n = sprintf(output, "WWW-Authenticate: Digest\r\n");
+      n += sprintf(output + n, "  realm=\"Protected Area\",\r\n");
+      n += sprintf(output + n, "  qop=\"auth\",\r\n");
+      n += sprintf(output + n, "  nonce=\"");
+
+      //The nonce is a server-specified data string which should be uniquely
+      //generated each time a 401 response is made
+      error = httpGenerateNonce(connection->serverContext, output + n, &k);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Advance pointer
+      n += k;
+      //Properly terminate the nonce string
+      n += sprintf(output + n, "\",\r\n");
+
+      //Format opaque parameter
+      n += sprintf(output + n, "  opaque=\"");
+
+      //Generate a random value
+      if(connection->settings->randCallback != NULL)
+         error = connection->settings->randCallback(opaque, 16);
+      else
+         error = ERROR_FAILURE;
+
+      //Random number generation failed?
+      if(error)
+         return error;
+
+      //Convert the byte array to hex string
+      httpConvertArrayToHexString(opaque, 16, output + n);
+
+      //Advance pointer
+      n += 32;
+      //Properly terminate the opaque string
+      n += sprintf(output + n, "\"");
+
+      //The STALE flag indicates that the previous request from the client
+      //was rejected because the nonce value was stale
+      if(connection->response.auth.stale)
+         n += sprintf(output + n, ",\r\n  stale=TRUE");
+
+      //Properly terminate the WWW-Authenticate field
+      n += sprintf(output + n, "\r\n");
+   }
+   else
+#endif
+   //Unknown authentication scheme?
+   {
+      //No need to add the WWW-Authenticate header field
+      n = 0;
+   }
+
+   //Return the total length of the WWW-Authenticate header field
+   return n;
+}
+
+
+/**
+ * @brief Nonce generation
+ * @param[in] context Pointer to the HTTP server context
+ * @param[in] output NULL-terminated string containing the nonce
+ * @param[in] length NULL-terminated string containing the nonce count
+ * @return Error code
+ **/
+
+error_t httpGenerateNonce(HttpServerContext *context,
+   char_t *output, size_t *length)
+{
+#if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
+   error_t error;
+   uint_t i;
+   HttpNonceCacheEntry *entry;
+   HttpNonceCacheEntry *oldestEntry;
+   uint8_t nonce[HTTP_SERVER_NONCE_SIZE];
+
+   //Acquire exclusive access to the nonce cache
+   osAcquireMutex(&context->nonceCacheMutex);
+
+   //Keep track of the oldest entry
+   oldestEntry = &context->nonceCache[0];
+
+   //Loop through nonce cache entries
+   for(i = 0; i < HTTP_SERVER_NONCE_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &context->nonceCache[i];
+
+      //Check whether the entry is currently in used or not
+      if(!entry->count)
+         break;
+
+      //Keep track of the oldest entry in the table
+      if(timeCompare(entry->timestamp, oldestEntry->timestamp) < 0)
+         oldestEntry = entry;
+   }
+
+   //The oldest entry is removed whenever the table runs out of space
+   if(i >= HTTP_SERVER_NONCE_CACHE_SIZE)
+      entry = oldestEntry;
+
+   //Generate a new nonce
+   if(context->settings.randCallback != NULL)
+      error = context->settings.randCallback(nonce, HTTP_SERVER_NONCE_SIZE);
+   else
+      error = ERROR_FAILURE;
+
+   //Check status code
+   if(!error)
+   {
+      //Convert the byte array to hex string
+      httpConvertArrayToHexString(nonce, HTTP_SERVER_NONCE_SIZE, entry->nonce);
+      //Clear nonce count
+      entry->count = 1;
+      //Save the time at which the nonce was generated
+      entry->timestamp = osGetSystemTime();
+
+      //Copy the nonce to the output buffer
+      strcpy(output, entry->nonce);
+      //Return the length of the nonce excluding the NULL character
+      *length = HTTP_SERVER_NONCE_SIZE * 2;
+   }
+   else
+   {
+      //Random number generation failed
+      memset(entry, 0, sizeof(HttpNonceCacheEntry));
+   }
+
+   //Release exclusive access to the nonce cache
+   osReleaseMutex(&context->nonceCacheMutex);
+   //Return status code
+   return error;
+
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Nonce verification
+ * @param[in] context Pointer to the HTTP server context
+ * @param[in] nonce NULL-terminated string containing the nonce
+ * @param[in] nc NULL-terminated string containing the nonce count
+ * @return Error code
+ **/
+
+error_t httpVerifyNonce(HttpServerContext *context,
+   const char_t *nonce, const char_t *nc)
+{
+#if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
+   error_t error;
+   uint_t i;
+   uint32_t count;
+   systime_t time;
+   HttpNonceCacheEntry *entry;
+
+   //Check parameters
+   if(nonce == NULL || nc == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Convert the nonce count to integer
+   count = strtoul(nc, NULL, 16);
+   //Get current time
+   time = osGetSystemTime();
+
+   //Acquire exclusive access to the nonce cache
+   osAcquireMutex(&context->nonceCacheMutex);
+
+   //Loop through nonce cache entries
+   for(i = 0; i < HTTP_SERVER_NONCE_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &context->nonceCache[i];
+
+      //Check nonce value
+      if(!strcasecmp(entry->nonce, nonce))
+      {
+         //Make sure the nonce timestamp has not expired
+         if((time - entry->timestamp) < HTTP_SERVER_NONCE_LIFETIME)
+         {
+            //Check nonce count to prevent replay attacks
+            if(count >= entry->count)
+            {
+               //Update nonce count to the next expected value
+               entry->count = count + 1;
+               //We are done
+               break;
+            }
+         }
+      }
+   }
+
+   //Check whether the nonce is valid
+   if(i < HTTP_SERVER_NONCE_CACHE_SIZE)
+      error = NO_ERROR;
+   else
+      error = ERROR_NOT_FOUND;
+
+   //Release exclusive access to the nonce cache
+   osReleaseMutex(&context->nonceCacheMutex);
+   //Return status code
+   return error;
+
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/http/http_server_auth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,49 @@
+/**
+ * @file http_server_auth.h
+ * @brief HTTP authentication
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _HTTP_SERVER_AUTH_H
+#define _HTTP_SERVER_AUTH_H
+
+//Dependencies
+#include "http/http_server.h"
+
+//HTTP authentication related functions
+bool_t httpCheckPassword(HttpConnection *connection,
+   const char_t *password, HttpAuthMode mode);
+
+void httpParseAuthorizationField(HttpConnection *connection, char_t *value);
+size_t httpAddAuthenticateField(HttpConnection *connection, char_t *output);
+
+error_t httpGenerateNonce(HttpServerContext *context,
+   char_t *output, size_t *length);
+
+error_t httpVerifyNonce(HttpServerContext *context,
+   const char_t *nonce, const char_t *nc);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/http/http_server_misc.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1070 @@
+/**
+ * @file http_server_misc.c
+ * @brief HTTP server (miscellaneous functions)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL HTTP_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include <limits.h>
+#include "core/net.h"
+#include "http/http_server.h"
+#include "http/http_server_auth.h"
+#include "http/http_server_misc.h"
+#include "http/mime.h"
+#include "str.h"
+#include "path.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (HTTP_SERVER_SUPPORT == ENABLED)
+
+
+/**
+ * @brief HTTP status codes
+ **/
+
+static const HttpStatusCodeDesc statusCodeList[] =
+{
+   //Success
+   {200, "OK"},
+   {201, "Created"},
+   {202, "Accepted"},
+   {204, "No Content"},
+   //Redirection
+   {301, "Moved Permanently"},
+   {302, "Found"},
+   {304, "Not Modified"},
+   //Client error
+   {400, "Bad Request"},
+   {401, "Unauthorized"},
+   {403, "Forbidden"},
+   {404, "Not Found"},
+   //Server error
+   {500, "Internal Server Error"},
+   {501, "Not Implemented"},
+   {502, "Bad Gateway"},
+   {503, "Service Unavailable"}
+};
+
+
+/**
+ * @brief Read HTTP request header and parse its contents
+ * @param[in] connection Structure representing an HTTP connection
+ * @return Error code
+ **/
+
+error_t httpReadRequestHeader(HttpConnection *connection)
+{
+   error_t error;
+   size_t length;
+
+   //Set the maximum time the server will wait for an HTTP
+   //request before closing the connection
+   error = socketSetTimeout(connection->socket, HTTP_SERVER_IDLE_TIMEOUT);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Read the first line of the request
+   error = httpReceive(connection, connection->buffer,
+      HTTP_SERVER_BUFFER_SIZE - 1, &length, SOCKET_FLAG_BREAK_CRLF);
+   //Unable to read any data?
+   if(error)
+      return error;
+
+   //Revert to default timeout
+   error = socketSetTimeout(connection->socket, HTTP_SERVER_TIMEOUT);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Properly terminate the string with a NULL character
+   connection->buffer[length] = '\0';
+   //Debug message
+   TRACE_INFO("%s", connection->buffer);
+
+   //Parse the Request-Line
+   error = httpParseRequestLine(connection, connection->buffer);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Default value for properties
+   connection->request.chunkedEncoding = FALSE;
+   connection->request.contentLength = 0;
+#if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED)
+   connection->request.upgradeWebSocket = FALSE;
+   connection->request.connectionUpgrade = FALSE;
+   strcpy(connection->request.clientKey, "");
+#endif
+
+   //HTTP 0.9 does not support Full-Request
+   if(connection->request.version >= HTTP_VERSION_1_0)
+   {
+      //Local variables
+      char_t firstChar;
+      char_t *separator;
+      char_t *name;
+      char_t *value;
+
+      //This variable is used to decode header fields that span multiple lines
+      firstChar = '\0';
+
+      //Parse the header fields of the HTTP request
+      while(1)
+      {
+         //Decode multiple-line header field
+         error = httpReadHeaderField(connection, connection->buffer,
+            HTTP_SERVER_BUFFER_SIZE, &firstChar);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //Debug message
+         TRACE_DEBUG("%s", connection->buffer);
+
+         //An empty line indicates the end of the header fields
+         if(!strcmp(connection->buffer, "\r\n"))
+            break;
+
+         //Check whether a separator is present
+         separator = strchr(connection->buffer, ':');
+
+         //Separator found?
+         if(separator != NULL)
+         {
+            //Split the line
+            *separator = '\0';
+
+            //Trim whitespace characters
+            name = strTrimWhitespace(connection->buffer);
+            value = strTrimWhitespace(separator + 1);
+
+            //Parse HTTP header field
+            httpParseHeaderField(connection, name, value);
+         }
+      }
+   }
+
+   //Prepare to read the HTTP request body
+   if(connection->request.chunkedEncoding)
+   {
+      connection->request.byteCount = 0;
+      connection->request.firstChunk = TRUE;
+      connection->request.lastChunk = FALSE;
+   }
+   else
+   {
+      connection->request.byteCount = connection->request.contentLength;
+   }
+
+   //The request header has been successfully parsed
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse Request-Line
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[in] requestLine Pointer to the string that holds the Request-Line
+ * @return Error code
+ **/
+
+error_t httpParseRequestLine(HttpConnection *connection, char_t *requestLine)
+{
+   error_t error;
+   char_t *token;
+   char_t *p;
+   char_t *s;
+
+   //The Request-Line begins with a method token
+   token = strtok_r(requestLine, " \r\n", &p);
+   //Unable to retrieve the method?
+   if(token == NULL)
+      return ERROR_INVALID_REQUEST;
+
+   //The Method token indicates the method to be performed on the
+   //resource identified by the Request-URI
+   error = strSafeCopy(connection->request.method, token, HTTP_SERVER_METHOD_MAX_LEN);
+   //Any error to report?
+   if(error)
+      return ERROR_INVALID_REQUEST;
+
+   //The Request-URI is following the method token
+   token = strtok_r(NULL, " \r\n", &p);
+   //Unable to retrieve the Request-URI?
+   if(token == NULL)
+      return ERROR_INVALID_REQUEST;
+
+   //Check whether a query string is present
+   s = strchr(token, '?');
+
+   //Query string found?
+   if(s != NULL)
+   {
+      //Split the string
+      *s = '\0';
+
+      //Save the Request-URI
+      error = httpDecodePercentEncodedString(token,
+         connection->request.uri, HTTP_SERVER_URI_MAX_LEN);
+      //Any error to report?
+      if(error)
+         return ERROR_INVALID_REQUEST;
+
+      //Check the length of the query string
+      if(strlen(s + 1) > HTTP_SERVER_QUERY_STRING_MAX_LEN)
+         return ERROR_INVALID_REQUEST;
+
+      //Save the query string
+      strcpy(connection->request.queryString, s + 1);
+   }
+   else
+   {
+      //Save the Request-URI
+      error = httpDecodePercentEncodedString(token,
+         connection->request.uri, HTTP_SERVER_URI_MAX_LEN);
+      //Any error to report?
+      if(error)
+         return ERROR_INVALID_REQUEST;
+
+      //No query string
+      connection->request.queryString[0] = '\0';
+   }
+
+   //Redirect to the default home page if necessary
+   if(!strcasecmp(connection->request.uri, "/"))
+      strcpy(connection->request.uri, connection->settings->defaultDocument);
+
+   //Clean the resulting path
+   pathCanonicalize(connection->request.uri);
+
+   //The protocol version is following the Request-URI
+   token = strtok_r(NULL, " \r\n", &p);
+
+   //HTTP version 0.9?
+   if(token == NULL)
+   {
+      //Save version number
+      connection->request.version = HTTP_VERSION_0_9;
+      //Persistent connections are not supported
+      connection->request.keepAlive = FALSE;
+   }
+   //HTTP version 1.0?
+   else if(!strcasecmp(token, "HTTP/1.0"))
+   {
+      //Save version number
+      connection->request.version = HTTP_VERSION_1_0;
+      //By default connections are not persistent
+      connection->request.keepAlive = FALSE;
+   }
+   //HTTP version 1.1?
+   else if(!strcasecmp(token, "HTTP/1.1"))
+   {
+      //Save version number
+      connection->request.version = HTTP_VERSION_1_1;
+      //HTTP 1.1 makes persistent connections the default
+      connection->request.keepAlive = TRUE;
+   }
+   //HTTP version not supported?
+   else
+   {
+      //Report an error
+      return ERROR_INVALID_REQUEST;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Read multiple-line header field
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[out] buffer Buffer where to store the header field
+ * @param[in] size Size of the buffer, in bytes
+ * @param[in,out] firstChar Leading character of the header line
+ * @return Error code
+ **/
+
+error_t httpReadHeaderField(HttpConnection *connection,
+   char_t *buffer, size_t size, char_t *firstChar)
+{
+   error_t error;
+   size_t n;
+   size_t length;
+
+   //This is the actual length of the header field
+   length = 0;
+
+   //The process of moving from a multiple-line representation of a header
+   //field to its single line representation is called unfolding
+   do
+   {
+      //Check the length of the header field
+      if((length + 1) >= size)
+      {
+         //Report an error
+         error = ERROR_INVALID_REQUEST;
+         //Exit immediately
+         break;
+      }
+
+      //NULL character found?
+      if(*firstChar == '\0')
+      {
+         //Prepare to decode the first header field
+         length = 0;
+      }
+      //LWSP character found?
+      else if(*firstChar == ' ' || *firstChar == '\t')
+      {
+         //Unfolding is accomplished by regarding CRLF immediately
+         //followed by a LWSP as equivalent to the LWSP character
+         buffer[length] = *firstChar;
+         //The current header field spans multiple lines
+         length++;
+      }
+      //Any other character?
+      else
+      {
+         //Restore the very first character of the header field
+         buffer[0] = *firstChar;
+         //Prepare to decode a new header field
+         length = 1;
+      }
+
+      //Read data until a CLRF character is encountered
+      error = httpReceive(connection, buffer + length,
+         size - 1 - length, &n, SOCKET_FLAG_BREAK_CRLF);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Update the length of the header field
+      length += n;
+      //Properly terminate the string with a NULL character
+      buffer[length] = '\0';
+
+      //An empty line indicates the end of the header fields
+      if(!strcmp(buffer, "\r\n"))
+         break;
+
+      //Read the next character to detect if the CRLF is immediately
+      //followed by a LWSP character
+      error = httpReceive(connection, firstChar,
+         sizeof(char_t), &n, SOCKET_FLAG_WAIT_ALL);
+      //Any error to report?
+      if(error)
+         break;
+
+      //LWSP character found?
+      if(*firstChar == ' ' || *firstChar == '\t')
+      {
+         //CRLF immediately followed by LWSP as equivalent to the LWSP character
+         if(length >= 2)
+         {
+            if(buffer[length - 2] == '\r' || buffer[length - 1] == '\n')
+            {
+               //Remove trailing CRLF sequence
+               length -= 2;
+               //Properly terminate the string with a NULL character
+               buffer[length] = '\0';
+            }
+         }
+      }
+
+      //A header field may span multiple lines...
+   } while(*firstChar == ' ' || *firstChar == '\t');
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse HTTP header field
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[in] name Name of the header field
+ * @param[in] value Value of the header field
+ * @return Error code
+ **/
+
+void httpParseHeaderField(HttpConnection *connection,
+   const char_t *name, char_t *value)
+{
+   //Host header field?
+   if(!strcasecmp(name, "Host"))
+   {
+      //Save host name
+      strSafeCopy(connection->request.host, value,
+         HTTP_SERVER_HOST_MAX_LEN);
+   }
+   //Connection header field?
+   else if(!strcasecmp(name, "Connection"))
+   {
+      //Parse Connection header field
+      httpParseConnectionField(connection, value);
+   }
+   //Transfer-Encoding header field?
+   else if(!strcasecmp(name, "Transfer-Encoding"))
+   {
+      //Check whether chunked encoding is used
+      if(!strcasecmp(value, "chunked"))
+         connection->request.chunkedEncoding = TRUE;
+   }
+   //Content-Type field header?
+   else if(!strcasecmp(name, "Content-Type"))
+   {
+      //Parse Content-Type header field
+      httpParseContentTypeField(connection, value);
+   }
+   //Content-Length header field?
+   else if(!strcasecmp(name, "Content-Length"))
+   {
+      //Get the length of the body data
+      connection->request.contentLength = atoi(value);
+   }
+   //Authorization header field?
+   else if(!strcasecmp(name, "Authorization"))
+   {
+      //Parse Authorization header field
+      httpParseAuthorizationField(connection, value);
+   }
+#if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED)
+   //Upgrade header field?
+   else if(!strcasecmp(name, "Upgrade"))
+   {
+      //WebSocket support?
+      if(!strcasecmp(value, "websocket"))
+         connection->request.upgradeWebSocket = TRUE;
+   }
+   //Sec-WebSocket-Key header field?
+   else if(!strcasecmp(name, "Sec-WebSocket-Key"))
+   {
+      //Save the contents of the Sec-WebSocket-Key header field
+      strSafeCopy(connection->request.clientKey, value,
+         WEB_SOCKET_CLIENT_KEY_SIZE + 1);
+   }
+#endif
+}
+
+
+/**
+ * @brief Parse Connection header field
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[in] value Content-Type field value
+ **/
+
+void httpParseConnectionField(HttpConnection *connection,
+   char_t *value)
+{
+   char_t *p;
+   char_t *token;
+
+   //Get the first value of the list
+   token = strtok_r(value, ",", &p);
+
+   //Parse the comma-separated list
+   while(token != NULL)
+   {
+      //Trim whitespace characters
+      value = strTrimWhitespace(token);
+
+      //Check current value
+      if(!strcasecmp(value, "keep-alive"))
+      {
+         //The connection is persistent
+         connection->request.keepAlive = TRUE;
+      }
+      else if(!strcasecmp(value, "close"))
+      {
+         //The connection will be closed after completion of the response
+         connection->request.keepAlive = FALSE;
+      }
+#if (HTTP_SERVER_WEB_SOCKET_SUPPORT == ENABLED)
+      else if(!strcasecmp(value, "upgrade"))
+      {
+         //Upgrade the connection
+         connection->request.connectionUpgrade = TRUE;
+      }
+#endif
+
+      //Get next value
+      token = strtok_r(NULL, ",", &p);
+   }
+}
+
+
+/**
+ * @brief Parse Content-Type header field
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[in] value Content-Type field value
+ **/
+
+void httpParseContentTypeField(HttpConnection *connection,
+   char_t *value)
+{
+#if (HTTP_SERVER_MULTIPART_TYPE_SUPPORT == ENABLED)
+   size_t n;
+   char_t *p;
+   char_t *token;
+
+   //Retrieve type
+   token = strtok_r(value, "/", &p);
+   //Any parsing error?
+   if(token == NULL)
+      return;
+
+   //The boundary parameter makes sense only for the multipart content-type
+   if(!strcasecmp(token, "multipart"))
+   {
+      //Skip subtype
+      token = strtok_r(NULL, ";", &p);
+      //Any parsing error?
+      if(token == NULL)
+         return;
+
+      //Retrieve parameter name
+      token = strtok_r(NULL, "=", &p);
+      //Any parsing error?
+      if(token == NULL)
+         return;
+
+      //Trim whitespace characters
+      token = strTrimWhitespace(token);
+
+      //Check parameter name
+      if(!strcasecmp(token, "boundary"))
+      {
+         //Retrieve parameter value
+         token = strtok_r(NULL, ";", &p);
+         //Any parsing error?
+         if(token == NULL)
+            return;
+
+         //Trim whitespace characters
+         token = strTrimWhitespace(token);
+         //Get the length of the boundary string
+         n = strlen(token);
+
+         //Check the length of the boundary string
+         if(n < HTTP_SERVER_BOUNDARY_MAX_LEN)
+         {
+            //Copy the boundary string
+            strncpy(connection->request.boundary, token, n);
+            //Properly terminate the string
+            connection->request.boundary[n] = '\0';
+
+            //Save the length of the boundary string
+            connection->request.boundaryLength = n;
+         }
+      }
+   }
+#endif
+}
+
+
+/**
+ * @brief Read chunk-size field from the input stream
+ * @param[in] connection Structure representing an HTTP connection
+ **/
+
+error_t httpReadChunkSize(HttpConnection *connection)
+{
+   error_t error;
+   size_t n;
+   char_t *end;
+   char_t s[8];
+
+   //First chunk to be received?
+   if(connection->request.firstChunk)
+   {
+      //Clear the flag
+      connection->request.firstChunk = FALSE;
+   }
+   else
+   {
+      //Read the CRLF that follows the previous chunk-data field
+      error = httpReceive(connection, s, sizeof(s) - 1, &n, SOCKET_FLAG_BREAK_CRLF);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Properly terminate the string with a NULL character
+      s[n] = '\0';
+
+      //The chunk data must be terminated by CRLF
+      if(strcmp(s, "\r\n"))
+         return ERROR_WRONG_ENCODING;
+   }
+
+   //Read the chunk-size field
+   error = httpReceive(connection, s, sizeof(s) - 1, &n, SOCKET_FLAG_BREAK_CRLF);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Properly terminate the string with a NULL character
+   s[n] = '\0';
+   //Remove extra whitespaces
+   strRemoveTrailingSpace(s);
+
+   //Retrieve the size of the chunk
+   connection->request.byteCount = strtoul(s, &end, 16);
+
+   //No valid conversion could be performed?
+   if(end == s || *end != '\0')
+      return ERROR_WRONG_ENCODING;
+
+   //Any chunk whose size is zero terminates the data transfer
+   if(!connection->request.byteCount)
+   {
+      //The end of the HTTP request body has been reached
+      connection->request.lastChunk = TRUE;
+
+      //Skip the trailer
+      while(1)
+      {
+         //Read a complete line
+         error = httpReceive(connection, s, sizeof(s) - 1, &n, SOCKET_FLAG_BREAK_CRLF);
+         //Unable to read any data?
+         if(error)
+            return error;
+
+         //Properly terminate the string with a NULL character
+         s[n] = '\0';
+
+         //The trailer is terminated by an empty line
+         if(!strcmp(s, "\r\n"))
+            break;
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Initialize response header
+ * @param[in] connection Structure representing an HTTP connection
+ **/
+
+void httpInitResponseHeader(HttpConnection *connection)
+{
+   //Default HTTP header fields
+   connection->response.version = connection->request.version;
+   connection->response.statusCode = 200;
+   connection->response.noCache = FALSE;
+   connection->response.maxAge = 0;
+   connection->response.location = NULL;
+   connection->response.contentType = mimeGetType(connection->request.uri);
+   connection->response.chunkedEncoding = TRUE;
+
+#if (HTTP_SERVER_PERSISTENT_CONN_SUPPORT == ENABLED)
+   //Persistent connections are accepted
+   connection->response.keepAlive = connection->request.keepAlive;
+#else
+   //Connections are not persistent by default
+   connection->response.keepAlive = FALSE;
+#endif
+}
+
+
+/**
+ * @brief Format HTTP response header
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[out] buffer Pointer to the buffer where to format the HTTP header
+ * @return Error code
+ **/
+
+error_t httpFormatResponseHeader(HttpConnection *connection, char_t *buffer)
+{
+   uint_t i;
+   char_t *p;
+
+   //HTTP version 0.9?
+   if(connection->response.version == HTTP_VERSION_0_9)
+   {
+      //Enforce default parameters
+      connection->response.keepAlive = FALSE;
+      connection->response.chunkedEncoding = FALSE;
+      //The size of the response body is not limited
+      connection->response.byteCount = UINT_MAX;
+      //We are done since HTTP 0.9 does not support Full-Response format
+      return NO_ERROR;
+   }
+
+   //When generating dynamic web pages with HTTP 1.0, the only way to
+   //signal the end of the body is to close the connection
+   if(connection->response.version == HTTP_VERSION_1_0 &&
+      connection->response.chunkedEncoding)
+   {
+      //Make the connection non persistent
+      connection->response.keepAlive = FALSE;
+      connection->response.chunkedEncoding = FALSE;
+      //The size of the response body is not limited
+      connection->response.byteCount = UINT_MAX;
+   }
+   else
+   {
+      //Limit the size of the response body
+      connection->response.byteCount = connection->response.contentLength;
+   }
+
+   //Point to the beginning of the buffer
+   p = buffer;
+
+   //The first line of a response message is the Status-Line, consisting
+   //of the protocol version followed by a numeric status code and its
+   //associated textual phrase
+   p += sprintf(p, "HTTP/%u.%u %u ", MSB(connection->response.version),
+      LSB(connection->response.version), connection->response.statusCode);
+
+   //Retrieve the Reason-Phrase that corresponds to the Status-Code
+   for(i = 0; i < arraysize(statusCodeList); i++)
+   {
+      //Check the status code
+      if(statusCodeList[i].value == connection->response.statusCode)
+      {
+         //Append the textual phrase to the Status-Line
+         p += sprintf(p, statusCodeList[i].message);
+         //Break the loop and continue processing
+         break;
+      }
+   }
+
+   //Properly terminate the Status-Line
+   p += sprintf(p, "\r\n");
+   //The Server response-header field contains information about the
+   //software used by the origin server to handle the request
+   p += sprintf(p, "Server: Oryx Embedded HTTP Server\r\n");
+
+   //Valid location?
+   if(connection->response.location != NULL)
+   {
+      //Set Location field
+      p += sprintf(p, "Location: %s\r\n", connection->response.location);
+   }
+
+   //Persistent connection?
+   if(connection->response.keepAlive)
+   {
+      //Set Connection field
+      p += sprintf(p, "Connection: keep-alive\r\n");
+
+      //Set Keep-Alive field
+      p += sprintf(p, "Keep-Alive: timeout=%u, max=%u\r\n",
+         HTTP_SERVER_IDLE_TIMEOUT / 1000, HTTP_SERVER_MAX_REQUESTS);
+   }
+   else
+   {
+      //Set Connection field
+      p += sprintf(p, "Connection: close\r\n");
+   }
+
+   //Specify the caching policy
+   if(connection->response.noCache)
+   {
+      //Set Pragma field
+      p += sprintf(p, "Pragma: no-cache\r\n");
+      //Set Cache-Control field
+      p += sprintf(p, "Cache-Control: no-store, no-cache, must-revalidate\r\n");
+      p += sprintf(p, "Cache-Control: max-age=0, post-check=0, pre-check=0\r\n");
+   }
+   else if(connection->response.maxAge != 0)
+   {
+      //Set Cache-Control field
+      p += sprintf(p, "Cache-Control: max-age=%u\r\n", connection->response.maxAge);
+   }
+
+#if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
+   //Check whether authentication is required
+   if(connection->response.auth.mode != HTTP_AUTH_MODE_NONE)
+   {
+      //Add WWW-Authenticate header field
+      p += httpAddAuthenticateField(connection, p);
+   }
+#endif
+
+   //Valid content type?
+   if(connection->response.contentType != NULL)
+   {
+      //Content type
+      p += sprintf(p, "Content-Type: %s\r\n", connection->response.contentType);
+   }
+
+   //Use chunked encoding transfer?
+   if(connection->response.chunkedEncoding)
+   {
+      //Set Transfer-Encoding field
+      p += sprintf(p, "Transfer-Encoding: chunked\r\n");
+   }
+   //Persistent connection?
+   else if(connection->response.keepAlive)
+   {
+      //Set Content-Length field
+      p += sprintf(p, "Content-Length: %" PRIuSIZE "\r\n", connection->response.contentLength);
+   }
+
+   //Terminate the header with an empty line
+   p += sprintf(p, "\r\n");
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Send data to the client
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[in] data Pointer to a buffer containing the data to be transmitted
+ * @param[in] length Number of bytes to be transmitted
+ * @param[in] flags Set of flags that influences the behavior of this function
+ **/
+
+error_t httpSend(HttpConnection *connection,
+   const void *data, size_t length, uint_t flags)
+{
+#if (NET_RTOS_SUPPORT == ENABLED)
+      error_t error;
+
+#if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
+   //Check whether a secure connection is being used
+   if(connection->tlsContext != NULL)
+   {
+      //Use SSL/TLS to transmit data to the client
+      error = tlsWrite(connection->tlsContext, data, length, NULL, flags);
+   }
+   else
+#endif
+   {
+      //Transmit data to the client
+      error = socketSend(connection->socket, data, length, NULL, flags);
+   }
+
+   //Return status code
+   return error;
+#else
+   //Prevent buffer overflow
+   if((connection->bufferLen + length) > HTTP_SERVER_BUFFER_SIZE)
+      return ERROR_BUFFER_OVERFLOW;
+
+   //Copy user data
+   memcpy(connection->buffer + connection->bufferLen, data, length);
+   //Adjust the length of the buffer
+   connection->bufferLen += length;
+
+   //Successful processing
+   return NO_ERROR;
+#endif
+}
+
+
+/**
+ * @brief Receive data from the client
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[out] data Buffer into which received data will be placed
+ * @param[in] size Maximum number of bytes that can be received
+ * @param[out] received Actual number of bytes that have been received
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t httpReceive(HttpConnection *connection,
+   void *data, size_t size, size_t *received, uint_t flags)
+{
+   error_t error;
+
+#if (HTTP_SERVER_TLS_SUPPORT == ENABLED)
+   //Check whether a secure connection is being used
+   if(connection->tlsContext != NULL)
+   {
+      //Use SSL/TLS to receive data from the client
+      error = tlsRead(connection->tlsContext, data, size, received, flags);
+   }
+   else
+#endif
+   {
+      //Receive data from the client
+      error = socketReceive(connection->socket, data, size, received, flags);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Retrieve the full pathname to the specified resource
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[in] relative String containing the relative path to the resource
+ * @param[out] absolute Resulting string containing the absolute path
+ * @param[in] maxLen Maximum acceptable path length
+ **/
+
+void httpGetAbsolutePath(HttpConnection *connection,
+   const char_t *relative, char_t *absolute, size_t maxLen)
+{
+   //Copy the root directory
+   strcpy(absolute, connection->settings->rootDirectory);
+
+   //Append the specified path
+   pathCombine(absolute, relative, maxLen);
+
+   //Clean the resulting path
+   pathCanonicalize(absolute);
+}
+
+
+/**
+ * @brief Compare filename extension
+ * @param[in] filename Filename whose extension is to be checked
+ * @param[in] extension String defining the extension to be checked
+ * @return TRUE is the filename matches the given extension, else FALSE
+ **/
+
+bool_t httpCompExtension(const char_t *filename, const char_t *extension)
+{
+   uint_t n;
+   uint_t m;
+
+   //Get the length of the specified filename
+   n = strlen(filename);
+   //Get the length of the extension
+   m = strlen(extension);
+
+   //Check the length of the filename
+   if(n < m)
+      return FALSE;
+
+   //Compare extensions
+   if(!strncasecmp(filename + n - m, extension, m))
+      return TRUE;
+   else
+      return FALSE;
+}
+
+
+/**
+ * @brief Decode a percent-encoded string
+ * @param[in] input NULL-terminated string to be decoded
+ * @param[out] output NULL-terminated string resulting from the decoding process
+ * @param[in] outputSize Size of the output buffer in bytes
+ * @return Error code
+ **/
+
+error_t httpDecodePercentEncodedString(const char_t *input,
+   char_t *output, size_t outputSize)
+{
+   size_t i;
+   char_t buffer[3];
+
+   //Check parameters
+   if(input == NULL || output == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Decode the percent-encoded string
+   for(i = 0; *input != '\0' && i < outputSize; i++)
+   {
+      //Check current character
+      if(*input == '+')
+      {
+         //Replace '+' characters with spaces
+         output[i] = ' ';
+         //Advance data pointer
+         input++;
+      }
+      else if(input[0] == '%' && input[1] != '\0' && input[2] != '\0')
+      {
+         //Process percent-encoded characters
+         buffer[0] = input[1];
+         buffer[1] = input[2];
+         buffer[2] = '\0';
+         //String to integer conversion
+         output[i] = (uint8_t) strtoul(buffer, NULL, 16);
+         //Advance data pointer
+         input += 3;
+      }
+      else
+      {
+         //Copy any other characters
+         output[i] = *input;
+         //Advance data pointer
+         input++;
+      }
+   }
+
+   //Check whether the output buffer runs out of space
+   if(i >= outputSize)
+      return ERROR_FAILURE;
+
+   //Properly terminate the resulting string
+   output[i] = '\0';
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Convert byte array to hex string
+ * @param[in] input Point to the byte array
+ * @param[in] inputLength Length of the byte array
+ * @param[out] output NULL-terminated string resulting from the conversion
+ * @return Error code
+ **/
+
+void httpConvertArrayToHexString(const uint8_t *input,
+   size_t inputLength, char_t *output)
+{
+   size_t i;
+
+   //Hex conversion table
+   static const char_t hexDigit[] =
+   {
+      '0', '1', '2', '3', '4', '5', '6', '7',
+      '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+   };
+
+   //Process byte array
+   for(i = 0; i < inputLength; i++)
+   {
+      //Convert upper nibble
+      output[i * 2] = hexDigit[(input[i] >> 4) & 0x0F];
+      //Then convert lower nibble
+      output[i * 2 + 1] = hexDigit[input[i] & 0x0F];
+   }
+
+   //Properly terminate the string with a NULL character
+   output[i * 2] = '\0';
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/http/http_server_misc.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,74 @@
+/**
+ * @file http_server_misc.h
+ * @brief HTTP server (miscellaneous functions)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _HTTP_SERVER_MISC_H
+#define _HTTP_SERVER_MISC_H
+
+//Dependencies
+#include "http/http_server.h"
+
+//HTTP server related functions
+error_t httpReadRequestHeader(HttpConnection *connection);
+error_t httpParseRequestLine(HttpConnection *connection, char_t *requestLine);
+
+error_t httpReadHeaderField(HttpConnection *connection,
+   char_t *buffer, size_t size, char_t *firstChar);
+
+void httpParseHeaderField(HttpConnection *connection,
+   const char_t *name, char_t *value);
+
+void httpParseConnectionField(HttpConnection *connection,
+   char_t *value);
+
+void httpParseContentTypeField(HttpConnection *connection,
+   char_t *value);
+
+error_t httpReadChunkSize(HttpConnection *connection);
+
+void httpInitResponseHeader(HttpConnection *connection);
+error_t httpFormatResponseHeader(HttpConnection *connection, char_t *buffer);
+
+error_t httpSend(HttpConnection *connection,
+   const void *data, size_t length, uint_t flags);
+
+error_t httpReceive(HttpConnection *connection,
+   void *data, size_t size, size_t *received, uint_t flags);
+
+void httpGetAbsolutePath(HttpConnection *connection,
+   const char_t *relative, char_t *absolute, size_t maxLen);
+
+bool_t httpCompExtension(const char_t *filename, const char_t *extension);
+
+error_t httpDecodePercentEncodedString(const char_t *input,
+   char_t *output, size_t outputSize);
+
+void httpConvertArrayToHexString(const uint8_t *input,
+   size_t inputLength, char_t *output);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/http/mime.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,140 @@
+/**
+ * @file mime.c
+ * @brief MIME (Multipurpose Internet Mail Extensions)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL HTTP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "http/mime.h"
+#include "debug.h"
+
+//MIME type list
+static const MimeType mimeTypeList[] =
+{
+   //Custom MIME types
+   MIME_CUSTOM_TYPES
+
+   //Text MIME types
+   {".css",   "text/css"},
+   {".csv",   "text/csv"},
+   {".htc",   "text/x-component"},
+   {".htm",   "text/html"},
+   {".html",  "text/html"},
+   {".shtm",  "text/html"},
+   {".shtml", "text/html"},
+   {".stm",   "text/html"},
+   {".txt",   "text/plain"},
+   {".vcf",   "text/vcard"},
+   {".vcard", "text/vcard"},
+   {".xml",   "text/xml"},
+
+   //Image MIME types
+   {".gif",   "image/gif"},
+   {".ico",   "image/x-icon"},
+   {".jpg",   "image/jpeg"},
+   {".jpeg",  "image/jpeg"},
+   {".png",   "image/png"},
+   {".svg",   "image/svg+xml"},
+   {".tif",   "image/tiff"},
+
+   //Audio MIME types
+   {".aac",   "audio/x-aac"},
+   {".aif",   "audio/x-aiff"},
+   {".mp3",   "audio/mpeg"},
+   {".wav",   "audio/x-wav"},
+   {".wma",   "audio/x-ms-wma"},
+
+   //Video MIME types
+   {".avi",   "video/x-msvideo"},
+   {".flv",   "video/x-flv"},
+   {".mov",   "video/quicktime"},
+   {".mp4",   "video/mp4"},
+   {".mpg",   "video/mpeg"},
+   {".mpeg",  "video/mpeg"},
+   {".wmv",   "video/x-ms-wmv"},
+
+   //Application MIME types
+   {".doc",   "application/msword"},
+   {".gz",    "application/x-gzip"},
+   {".gzip",  "application/x-gzip"},
+   {".js",    "application/javascript"},
+   {".json",  "application/json"},
+   {".ogg",   "application/ogg"},
+   {".pdf",   "application/pdf"},
+   {".ppt",   "application/vnd.ms-powerpoint"},
+   {".rar",   "application/x-rar-compressed"},
+   {".rtf",   "application/rtf"},
+   {".tar",   "application/x-tar"},
+   {".tgz",   "application/x-gzip"},
+   {".xht",   "application/xhtml+xml"},
+   {".xhtml", "application/xhtml+xml"},
+   {".xls",   "application/vnd.ms-excel"},
+   {".zip",   "application/zip"}
+};
+
+
+/**
+ * @brief Get the MIME type from a given extension
+ *
+ * This function translates a filename or a file extension into a MIME type
+ *
+ * @param[in] filename Filename from which to extract the MIME type
+ * @return NULL-terminated string containing the associated MIME type
+ **/
+
+const char_t *mimeGetType(const char_t *filename)
+{
+   uint_t i;
+   uint_t n;
+   uint_t m;
+
+   //MIME type for unknown extensions
+   static const char_t defaultMimeType[] = "application/octet-stream";
+
+   //Valid filename?
+   if(filename != NULL)
+   {
+      //Get the length of the specified filename
+      n = strlen(filename);
+
+      //Search the MIME type that matches the specified extension
+      for(i = 0; i < arraysize(mimeTypeList); i++)
+      {
+         //Length of the extension
+         m = strlen(mimeTypeList[i].extension);
+         //Compare file extensions
+         if(m <= n && !strcasecmp(filename + n - m, mimeTypeList[i].extension))
+            return mimeTypeList[i].type;
+      }
+   }
+
+   //Return the default MIME type when an unknown extension is encountered
+   return defaultMimeType;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/http/mime.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,56 @@
+/**
+ * @file mime.h
+ * @brief MIME (Multipurpose Internet Mail Extensions)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MIME_H
+#define _MIME_H
+
+//Dependencies
+#include "core/net.h"
+
+//Custom MIME types
+#ifndef MIME_CUSTOM_TYPES
+   #define MIME_CUSTOM_TYPES
+#endif
+
+
+/**
+ * @brief MIME type
+ **/
+
+typedef struct
+{
+   const char_t *extension;
+   const char_t *type;
+} MimeType;
+
+
+//MIME related functions
+const char_t *mimeGetType(const char_t *filename);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/http/ssi.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,867 @@
+/**
+ * @file ssi.c
+ * @brief SSI (Server Side Includes)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * Server Side Includes (SSI) is a simple interpreted server-side scripting
+ * language used to generate dynamic content to web pages
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL HTTP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "http/http_server.h"
+#include "http/http_server_misc.h"
+#include "http/mime.h"
+#include "http/ssi.h"
+#include "str.h"
+#include "debug.h"
+
+//File system support?
+#if (HTTP_SERVER_FS_SUPPORT == ENABLED)
+   #include "fs_port.h"
+#else
+   #include "resource_manager.h"
+#endif
+
+//Check TCP/IP stack configuration
+#if (HTTP_SERVER_SUPPORT == ENABLED && HTTP_SERVER_SSI_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Execute SSI script
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[in] uri NULL-terminated string containing the file to process
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t ssiExecuteScript(HttpConnection *connection, const char_t *uri, uint_t level)
+{
+   error_t error;
+   size_t length;
+
+#if (HTTP_SERVER_FS_SUPPORT == ENABLED)
+   bool_t more;
+   uint_t pos;
+   uint_t n;
+   char_t *buffer;
+   FsFile *file;
+#else
+   uint_t i;
+   uint_t j;
+   char_t *data;
+#endif
+
+   //Recursion limit exceeded?
+   if(level >= HTTP_SERVER_SSI_MAX_RECURSION)
+      return NO_ERROR;
+
+   //Retrieve the full pathname
+   httpGetAbsolutePath(connection, uri,
+      connection->buffer, HTTP_SERVER_BUFFER_SIZE);
+
+#if (HTTP_SERVER_FS_SUPPORT == ENABLED)
+   //Open the file for reading
+   file = fsOpenFile(connection->buffer, FS_FILE_MODE_READ);
+   //Failed to open the file?
+   if(file == NULL)
+      return ERROR_NOT_FOUND;
+
+   //Allocate a memory buffer
+   buffer = osAllocMem(HTTP_SERVER_BUFFER_SIZE);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+   {
+      //Close the file
+      fsCloseFile(file);
+      //Report an error
+      return ERROR_OUT_OF_MEMORY;
+   }
+#else
+   //Get the resource data associated with the URI
+   error = resGetData(connection->buffer, (uint8_t **) &data, &length);
+   //The specified URI cannot be found?
+   if(error)
+      return error;
+#endif
+
+   //Send the HTTP response header before executing the script
+   if(!level)
+   {
+      //Format HTTP response header
+      connection->response.statusCode = 200;
+      connection->response.contentType = mimeGetType(uri);
+      connection->response.chunkedEncoding = TRUE;
+
+      //Send the header to the client
+      error = httpWriteHeader(connection);
+      //Any error to report?
+      if(error)
+      {
+#if (HTTP_SERVER_FS_SUPPORT == ENABLED)
+         //Close the file
+         fsCloseFile(file);
+         //Release memory buffer
+         osFreeMem(buffer);
+#endif
+         //Return status code
+         return error;
+      }
+   }
+
+#if (HTTP_SERVER_FS_SUPPORT == ENABLED)
+   //Point to the beginning of the buffer
+   pos = 0;
+   length = 0;
+
+   //This flag indicates whether data should be read
+   more = TRUE;
+
+   //Parse the specified file
+   while(1)
+   {
+      //Read more data if needed
+      if(more)
+      {
+         //Check whether the current position is aligned on 32-bit boundaries
+         n = 4 - ((pos + length) % 4);
+
+         //Maintain proper alignment
+         if(n != 4)
+         {
+            memmove(buffer + pos + n, buffer + pos, length);
+            pos += n;
+         }
+
+         //Read data from the specified file
+         error = fsReadFile(file, buffer + pos + length,
+            HTTP_SERVER_BUFFER_SIZE - (pos + length), &n);
+
+         //End of input stream?
+         if(error)
+         {
+            //Purge data buffer
+            error = httpWriteStream(connection, buffer + pos, length);
+            //Exit immediately
+            break;
+         }
+
+         //Adjust the length of the buffer
+         length += n;
+         //Clear flag
+         more = FALSE;
+      }
+
+      //Search for any SSI tags
+      error = ssiSearchTag(buffer + pos, length, "<!--#", 5, &n);
+
+      //Full match?
+      if(error == NO_ERROR)
+      {
+         //Send the part of the file that precedes the tag
+         error = httpWriteStream(connection, buffer + pos, n);
+         //Failed to send data?
+         if(error)
+            break;
+
+         //Advance data pointer
+         pos += n;
+         length -= n;
+
+         //Search for the comment terminator
+         error = ssiSearchTag(buffer + pos + 5, length - 5, "-->", 3, &n);
+
+         //Full match?
+         if(error == NO_ERROR)
+         {
+            //Advance data pointer over the opening identifier
+            pos += 5;
+            length -= 5;
+
+            //Process SSI directive
+            error = ssiProcessCommand(connection, buffer + pos, n, uri, level);
+            //Any error to report?
+            if(error)
+               break;
+
+            //Advance data pointer over the SSI tag
+            pos += n + 3;
+            length -= n + 3;
+         }
+         //No match or partial match?
+         else
+         {
+            if(pos > 0)
+            {
+               //Move the remaining bytes to the start of the buffer
+               memmove(buffer, buffer + pos, length);
+               //Rewind to the beginning of the buffer
+               pos = 0;
+               //More data are needed
+               more = TRUE;
+            }
+            else
+            {
+               //Send data to the client
+               error = httpWriteStream(connection, buffer + pos, length);
+               //Any error to report?
+               if(error)
+                  break;
+
+               //Rewind to the beginning of the buffer
+               pos = 0;
+               length = 0;
+               //More data are needed
+               more = TRUE;
+            }
+         }
+      }
+      //Partial match?
+      else if(error == ERROR_PARTIAL_MATCH)
+      {
+         //Send the part of the file that precedes the tag
+         error = httpWriteStream(connection, buffer + pos, n);
+         //Failed to send data?
+         if(error)
+            break;
+
+         //Advance data pointer
+         pos += n;
+         length -= n;
+
+         //Move the remaining bytes to the start of the buffer
+         memmove(buffer, buffer + pos, length);
+         //Rewind to the beginning of the buffer
+         pos = 0;
+         //More data are needed
+         more = TRUE;
+      }
+      //No match?
+      else
+      {
+         //Send data to the client
+         error = httpWriteStream(connection, buffer + pos, length);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Rewind to the beginning of the buffer
+         pos = 0;
+         length = 0;
+         //More data are needed
+         more = TRUE;
+      }
+   }
+
+   //Close the file
+   fsCloseFile(file);
+   //Release memory buffer
+   osFreeMem(buffer);
+
+   //Properly close the output stream
+   if(!level && error == NO_ERROR)
+      error = httpCloseStream(connection);
+#else
+   //Parse the specified file
+   while(length > 0)
+   {
+      //Search for any SSI tags
+      error = ssiSearchTag(data, length, "<!--#", 5, &i);
+
+      //Opening identifier found?
+      if(!error)
+      {
+         //Search for the comment terminator
+         error = ssiSearchTag(data + i + 5, length - i - 5, "-->", 3, &j);
+      }
+
+      //Check whether a valid SSI tag has been found?
+      if(!error)
+      {
+         //Send the part of the file that precedes the tag
+         error = httpWriteStream(connection, data, i);
+         //Failed to send data?
+         if(error)
+            return error;
+
+         //Advance data pointer over the opening identifier
+         data += i + 5;
+         length -= i + 5;
+
+         //Process SSI directive
+         error = ssiProcessCommand(connection, data, j, uri, level);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //Advance data pointer over the SSI tag
+         data += j + 3;
+         length -= j + 3;
+      }
+      else
+      {
+         //Send the rest of the file
+         error = httpWriteStream(connection, data, length);
+         //Failed to send data?
+         if(error)
+            return error;
+
+         //Advance data pointer
+         data += length;
+         length = 0;
+      }
+   }
+
+   //Properly close the output stream
+   if(!level)
+      error = httpCloseStream(connection);
+#endif
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process SSI directive
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[in] tag Pointer to the SSI tag
+ * @param[in] length Total length of the SSI tag
+ * @param[in] uri NULL-terminated string containing the file being processed
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t ssiProcessCommand(HttpConnection *connection,
+   const char_t *tag, size_t length, const char_t *uri, uint_t level)
+{
+   error_t error;
+
+   //Include command found?
+   if(length > 7 && !strncasecmp(tag, "include", 7))
+   {
+      //Process SSI include directive
+      error = ssiProcessIncludeCommand(connection, tag, length, uri, level);
+   }
+   //Echo command found?
+   else if(length > 4 && !strncasecmp(tag, "echo", 4))
+   {
+      //Process SSI echo directive
+      error = ssiProcessEchoCommand(connection, tag, length);
+   }
+   //Exec command found?
+   else if(length > 4 && !strncasecmp(tag, "exec", 4))
+   {
+      //Process SSI exec directive
+      error = ssiProcessExecCommand(connection, tag, length);
+   }
+   //Unknown command?
+   else
+   {
+      //The server is unable to decode the SSI tag
+      error = ERROR_INVALID_TAG;
+   }
+
+   //Invalid SSI directive?
+   if(error == ERROR_INVALID_TAG)
+   {
+      //Report a warning to the user
+      error = httpWriteStream(connection, "Warning: Invalid SSI Tag", 24);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process SSI include directive
+ *
+ * This include directive allows the content of one document to be included
+ * in another. The file parameter defines the included file as relative to
+ * the document path. The virtual parameter defines the included file as
+ * relative to the document root
+ *
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[in] tag Pointer to the SSI tag
+ * @param[in] length Total length of the SSI tag
+ * @param[in] uri NULL-terminated string containing the file being processed
+ * @param[in] level Current level of recursion
+ * @return Error code
+ **/
+
+error_t ssiProcessIncludeCommand(HttpConnection *connection,
+   const char_t *tag, size_t length, const char_t *uri, uint_t level)
+{
+   error_t error;
+   char_t *separator;
+   char_t *attribute;
+   char_t *value;
+   char_t *path;
+   char_t *p;
+
+   //Discard invalid SSI directives
+   if(length < 7 || length >= HTTP_SERVER_BUFFER_SIZE)
+      return ERROR_INVALID_TAG;
+
+   //Skip the SSI include command (7 bytes)
+   memcpy(connection->buffer, tag + 7, length - 7);
+   //Ensure the resulting string is NULL-terminated
+   connection->buffer[length - 7] = '\0';
+
+   //Check whether a separator is present
+   separator = strchr(connection->buffer, '=');
+   //Separator not found?
+   if(!separator)
+      return ERROR_INVALID_TAG;
+
+   //Split the tag
+   *separator = '\0';
+
+   //Get attribute name and value
+   attribute = strTrimWhitespace(connection->buffer);
+   value = strTrimWhitespace(separator + 1);
+
+   //Remove leading simple or double quote
+   if(value[0] == '\'' || value[0] == '\"')
+      value++;
+
+   //Get the length of the attribute value
+   length = strlen(value);
+
+   //Remove trailing simple or double quote
+   if(length > 0)
+   {
+      if(value[length - 1] == '\'' || value[length - 1] == '\"')
+         value[length - 1] = '\0';
+   }
+
+   //Check the length of the filename
+   if(strlen(value) > HTTP_SERVER_URI_MAX_LEN)
+      return ERROR_INVALID_TAG;
+
+   //The file parameter defines the included file as relative to the document path
+   if(!strcasecmp(attribute, "file"))
+   {
+      //Allocate a buffer to hold the path to the file to be included
+      path = osAllocMem(strlen(uri) + strlen(value) + 1);
+      //Failed to allocate memory?
+      if(path == NULL)
+         return ERROR_OUT_OF_MEMORY;
+
+      //Copy the path identifying the script file being processed
+      strcpy(path, uri);
+      //Search for the last slash character
+      p = strrchr(path, '/');
+
+      //Remove the filename from the path if applicable
+      if(p)
+         strcpy(p + 1, value);
+      else
+         strcpy(path, value);
+   }
+   //The virtual parameter defines the included file as relative to the document root
+   else if(!strcasecmp(attribute, "virtual"))
+   {
+      //Copy the absolute path
+      path = strDuplicate(value);
+      //Failed to duplicate the string?
+      if(path == NULL)
+         return ERROR_OUT_OF_MEMORY;
+   }
+   //Unknown parameter...
+   else
+   {
+      //Report an error
+      return ERROR_INVALID_TAG;
+   }
+
+   //Use server-side scripting to dynamically generate HTML code?
+   if(httpCompExtension(value, ".stm") ||
+      httpCompExtension(value, ".shtm") ||
+      httpCompExtension(value, ".shtml"))
+   {
+      //SSI processing (Server Side Includes)
+      error = ssiExecuteScript(connection, path, level + 1);
+   }
+   else
+   {
+#if (HTTP_SERVER_FS_SUPPORT == ENABLED)
+      FsFile *file;
+
+      //Retrieve the full pathname
+      httpGetAbsolutePath(connection, path,
+         connection->buffer, HTTP_SERVER_BUFFER_SIZE);
+
+      //Open the file for reading
+      file = fsOpenFile(connection->buffer, FS_FILE_MODE_READ);
+
+      //Successful operation?
+      if(file)
+      {
+         //Send the contents of the requested file
+         while(1)
+         {
+            //Read data from the specified file
+            error = fsReadFile(file, connection->buffer, HTTP_SERVER_BUFFER_SIZE, &length);
+            //End of input stream?
+            if(error)
+               break;
+
+            //Send data to the client
+            error = httpWriteStream(connection, connection->buffer, length);
+            //Any error to report?
+            if(error)
+               break;
+         }
+
+         //Close the file
+         fsCloseFile(file);
+
+         //Successful file transfer?
+         if(error == ERROR_END_OF_FILE)
+            error = NO_ERROR;
+      }
+      else
+      {
+         //The specified URI cannot be found
+         error = ERROR_NOT_FOUND;
+      }
+#else
+      uint8_t *data;
+
+      //Retrieve the full pathname
+      httpGetAbsolutePath(connection, path,
+         connection->buffer, HTTP_SERVER_BUFFER_SIZE);
+
+      //Get the resource data associated with the file
+      error = resGetData(connection->buffer, &data, &length);
+
+      //Send the contents of the requested file
+      if(!error)
+         error = httpWriteStream(connection, data, length);
+#endif
+   }
+
+   //Cannot found the specified resource?
+   if(error == ERROR_NOT_FOUND)
+      error = ERROR_INVALID_TAG;
+
+   //Release previously allocated memory
+   osFreeMem(path);
+   //return status code
+   return error;
+}
+
+
+/**
+ * @brief Process SSI echo directive
+ *
+ * This echo directive displays the contents of a specified
+ * HTTP environment variable
+ *
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[in] tag Pointer to the SSI tag
+ * @param[in] length Total length of the SSI tag
+ * @return Error code
+ **/
+
+error_t ssiProcessEchoCommand(HttpConnection *connection, const char_t *tag, size_t length)
+{
+   error_t error;
+   char_t *separator;
+   char_t *attribute;
+   char_t *value;
+
+   //Discard invalid SSI directives
+   if(length < 4 || length >= HTTP_SERVER_BUFFER_SIZE)
+      return ERROR_INVALID_TAG;
+
+   //Skip the SSI echo command (4 bytes)
+   memcpy(connection->buffer, tag + 4, length - 4);
+   //Ensure the resulting string is NULL-terminated
+   connection->buffer[length - 4] = '\0';
+
+   //Check whether a separator is present
+   separator = strchr(connection->buffer, '=');
+   //Separator not found?
+   if(!separator)
+      return ERROR_INVALID_TAG;
+
+   //Split the tag
+   *separator = '\0';
+
+   //Get attribute name and value
+   attribute = strTrimWhitespace(connection->buffer);
+   value = strTrimWhitespace(separator + 1);
+
+   //Remove leading simple or double quote
+   if(value[0] == '\'' || value[0] == '\"')
+      value++;
+
+   //Get the length of the attribute value
+   length = strlen(value);
+
+   //Remove trailing simple or double quote
+   if(length > 0)
+   {
+      if(value[length - 1] == '\'' || value[length - 1] == '\"')
+         value[length - 1] = '\0';
+   }
+
+   //Enforce attribute name
+   if(strcasecmp(attribute, "var"))
+      return ERROR_INVALID_TAG;
+
+   //Remote address?
+   if(!strcasecmp(value, "REMOTE_ADDR"))
+   {
+      //The IP address of the host making this request
+      ipAddrToString(&connection->socket->remoteIpAddr, connection->buffer);
+   }
+   //Remote port?
+   else if(!strcasecmp(value, "REMOTE_PORT"))
+   {
+      //The port number used by the remote host when making this request
+      sprintf(connection->buffer, "%" PRIu16, connection->socket->remotePort);
+   }
+   //Server address?
+   else if(!strcasecmp(value, "SERVER_ADDR"))
+   {
+      //The IP address of the server for this URL
+      ipAddrToString(&connection->socket->localIpAddr, connection->buffer);
+   }
+   //Server port?
+   else if(!strcasecmp(value, "SERVER_PORT"))
+   {
+      //The port number on this server to which this request was directed
+      sprintf(connection->buffer, "%" PRIu16, connection->socket->localPort);
+   }
+   //Request method?
+   else if(!strcasecmp(value, "REQUEST_METHOD"))
+   {
+      //The method used for this HTTP request
+      strcpy(connection->buffer, connection->request.method);
+   }
+   //Document root?
+   else if(!strcasecmp(value, "DOCUMENT_ROOT"))
+   {
+      //The root directory
+      strcpy(connection->buffer, connection->settings->rootDirectory);
+   }
+   //Document URI?
+   else if(!strcasecmp(value, "DOCUMENT_URI"))
+   {
+      //The URI for this request relative to the root directory
+      strcpy(connection->buffer, connection->request.uri);
+   }
+   //Document name?
+   else if(!strcasecmp(value, "DOCUMENT_NAME"))
+   {
+      //The full physical path and filename of the document requested
+      httpGetAbsolutePath(connection, connection->request.uri,
+         connection->buffer, HTTP_SERVER_BUFFER_SIZE);
+   }
+   //Query string?
+   else if(!strcasecmp(value, "QUERY_STRING"))
+   {
+      //The information following the "?" in the URL for this request
+      strcpy(connection->buffer, connection->request.queryString);
+   }
+   //User name?
+   else if(!strcasecmp(value, "AUTH_USER"))
+   {
+#if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED)
+      //The username provided by the user to the server
+      strcpy(connection->buffer, connection->request.auth.user);
+#else
+      //Basic access authentication is not supported
+      connection->buffer[0] = '\0';
+#endif
+   }
+   //GMT time?
+   else if(!strcasecmp(value, "DATE_GMT"))
+   {
+      //The current date and time in Greenwich Mean Time
+      connection->buffer[0] = '\0';
+   }
+   //Local time?
+   else if(!strcasecmp(value, "DATE_LOCAL"))
+   {
+      //The current date and time in the local timezone
+      connection->buffer[0] = '\0';
+   }
+   //Unknown variable?
+   else
+   {
+      //Report an error
+      return ERROR_INVALID_TAG;
+   }
+
+   //Get the length of the resulting string
+   length = strlen(connection->buffer);
+
+   //Send the contents of the specified environment variable
+   error = httpWriteStream(connection, connection->buffer, length);
+   //Failed to send data?
+   if(error)
+      return error;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process SSI exec directive
+ *
+ * This exec directive executes a program, script, or shell command on
+ * the server. The cmd parameter specifies a server-side command. The
+ * cgi parameter specifies the path to a CGI script
+ *
+ * @param[in] connection Structure representing an HTTP connection
+ * @param[in] tag Pointer to the SSI tag
+ * @param[in] length Total length of the SSI tag
+ * @return Error code
+ **/
+
+error_t ssiProcessExecCommand(HttpConnection *connection, const char_t *tag, size_t length)
+{
+   char_t *separator;
+   char_t *attribute;
+   char_t *value;
+
+   //First, check whether CGI is supported by the server
+   if(connection->settings->cgiCallback == NULL)
+      return ERROR_INVALID_TAG;
+
+   //Discard invalid SSI directives
+   if(length < 4 || length >= HTTP_SERVER_BUFFER_SIZE)
+      return ERROR_INVALID_TAG;
+
+   //Skip the SSI exec command (4 bytes)
+   memcpy(connection->buffer, tag + 4, length - 4);
+   //Ensure the resulting string is NULL-terminated
+   connection->buffer[length - 4] = '\0';
+
+   //Check whether a separator is present
+   separator = strchr(connection->buffer, '=');
+   //Separator not found?
+   if(!separator)
+      return ERROR_INVALID_TAG;
+
+   //Split the tag
+   *separator = '\0';
+
+   //Get attribute name and value
+   attribute = strTrimWhitespace(connection->buffer);
+   value = strTrimWhitespace(separator + 1);
+
+   //Remove leading simple or double quote
+   if(value[0] == '\'' || value[0] == '\"')
+      value++;
+
+   //Get the length of the attribute value
+   length = strlen(value);
+
+   //Remove trailing simple or double quote
+   if(length > 0)
+   {
+      if(value[length - 1] == '\'' || value[length - 1] == '\"')
+         value[length - 1] = '\0';
+   }
+
+   //Enforce attribute name
+   if(strcasecmp(attribute, "cgi") && strcasecmp(attribute, "cmd") && strcasecmp(attribute, "cmd_argument"))
+      return ERROR_INVALID_TAG;
+   //Check the length of the CGI parameter
+   if(strlen(value) > HTTP_SERVER_CGI_PARAM_MAX_LEN)
+      return ERROR_INVALID_TAG;
+
+   //The scratch buffer may be altered by the user-defined callback.
+   //So the CGI parameter must be copied prior to function invocation
+   strcpy(connection->cgiParam, value);
+
+   //Invoke user-defined callback
+   return connection->settings->cgiCallback(connection, connection->cgiParam);
+}
+
+
+/**
+ * @brief Search a string for a given tag
+ * @param[in] s String to search
+ * @param[in] sLen Length of the string to search
+ * @param[in] tag String containing the tag to search for
+ * @param[in] tagLen Length of the tag
+ * @param[out] pos The index of the first occurrence of the tag in the string,
+ * @retval NO_ERROR if the specified tag has been found
+ * @retval ERROR_PARTIAL_MATCH if a partial match occurs
+ * @retval ERROR_NO_MATCH if the tag does not appear in the string
+ **/
+
+error_t ssiSearchTag(const char_t *s, size_t sLen, const char_t *tag, size_t tagLen, uint_t *pos)
+{
+   uint_t i;
+   uint_t j;
+
+   //Parse the input string
+   for(i = 0; i <= sLen; i++)
+   {
+      //Compare current substring with the given tag
+      for(j = 0; (i + j) < sLen && j < tagLen; j++)
+      {
+         if(s[i + j] != tag[j])
+            break;
+      }
+
+      //Check whether a full match occurred
+      if(j == tagLen)
+      {
+         //Save the position of the first character
+         *pos = i;
+         //The specified tag has been found
+         return NO_ERROR;
+      }
+      //Check whether a partial match occurred
+      else if((i + j) == sLen && j > 0)
+      {
+         //Save the position of the first character
+         *pos = i;
+         //The beginning of the tag matches the end of the string
+         return ERROR_PARTIAL_MATCH;
+      }
+   }
+
+   //The tag does not appear in the string
+   return ERROR_NO_MATCH;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/http/ssi.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,50 @@
+/**
+ * @file ssi.h
+ * @brief SSI (Server Side Includes)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SSI_H
+#define _SSI_H
+
+//Dependencies
+#include "http/http_server.h"
+
+//SSI related functions
+error_t ssiExecuteScript(HttpConnection *connection, const char_t *uri, uint_t level);
+
+error_t ssiProcessCommand(HttpConnection *connection,
+   const char_t *tag, size_t length, const char_t *uri, uint_t level);
+
+error_t ssiProcessIncludeCommand(HttpConnection *connection,
+   const char_t *tag, size_t length, const char_t *uri, uint_t level);
+
+error_t ssiProcessEchoCommand(HttpConnection *connection, const char_t *tag, size_t length);
+error_t ssiProcessExecCommand(HttpConnection *connection, const char_t *tag, size_t length);
+
+error_t ssiSearchTag(const char_t *s, size_t sLen, const char_t *tag, size_t tagLen, uint_t *pos);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/icecast/icecast_client.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,677 @@
+/**
+ * @file icecast_client.c
+ * @brief Icecast client
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL ICECAST_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include "icecast/icecast_client.h"
+#include "str.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (ICECAST_CLIENT_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Initialize settings with default values
+ * @param[out] settings Structure that contains Icecast client settings
+ **/
+
+void icecastClientGetDefaultSettings(IcecastClientSettings *settings)
+{
+   //Use default interface
+   settings->interface = netGetDefaultInterface();
+
+   //Icecast server name
+   strcpy(settings->serverName, "");
+   //Icecast server port
+   settings->serverPort = 8000;
+   //Requested resource
+   strcpy(settings->resource, "/stream");
+
+   //Streaming buffer size
+   settings->bufferSize = 80000;
+}
+
+
+/**
+ * @brief Icecast client initialization
+ * @param[in] context Pointer to the Icecast client context
+ * @param[in] settings Icecast client specific settings
+ * @return Error code
+ **/
+
+error_t icecastClientInit(IcecastClientContext *context,
+   const IcecastClientSettings *settings)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing Icecast client...\r\n");
+
+   //Ensure the parameters are valid
+   if(!context || !settings)
+      return ERROR_INVALID_PARAMETER;
+
+   //Clear the Icecast client context
+   memset(context, 0, sizeof(IcecastClientContext));
+
+   //Save user settings
+   context->settings = *settings;
+   //Get the size of the circular buffer
+   context->bufferSize = settings->bufferSize;
+
+   //Start of exception handling block
+   do
+   {
+      //Allocate a memory block to hold the circular buffer
+      context->streamBuffer = osAllocMem(context->bufferSize);
+      //Failed to allocate memory?
+      if(!context->streamBuffer)
+      {
+         //Report an error to the calling function
+         error = ERROR_OUT_OF_MEMORY;
+         break;
+      }
+
+      //Create mutex object to protect critical sections
+      if(!osCreateMutex(&context->mutex))
+      {
+         //Failed to create mutex
+         error = ERROR_OUT_OF_RESOURCES;
+         break;
+      }
+
+      //Create event to get notified when the buffer is writable
+      if(!osCreateEvent(&context->writeEvent))
+      {
+         //Failed to create event object
+         error = ERROR_OUT_OF_RESOURCES;
+         break;
+      }
+
+      //The buffer is available for writing
+      osSetEvent(&context->writeEvent);
+
+      //Create event to get notified when the buffer is readable
+      if(!osCreateEvent(&context->readEvent))
+      {
+         //Failed to create event object
+         error = ERROR_OUT_OF_RESOURCES;
+         break;
+      }
+
+      //Successful initialization
+      error = NO_ERROR;
+
+      //End of exception handling block
+   } while(0);
+
+   //Check whether an error occurred
+   if(error)
+   {
+      //Clean up side effects...
+      osFreeMem(context->streamBuffer);
+      osDeleteMutex(&context->mutex);
+      osDeleteEvent(&context->writeEvent);
+      osDeleteEvent(&context->readEvent);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Start Icecast client
+ * @param[in] context Pointer to the Icecast client context
+ * @return Error code
+ **/
+
+error_t icecastClientStart(IcecastClientContext *context)
+{
+   OsTask *task;
+
+   //Debug message
+   TRACE_INFO("Starting Icecast client...\r\n");
+
+   //Create the Icecast client task
+   task = osCreateTask("Icecast client", icecastClientTask,
+      context, ICECAST_CLIENT_STACK_SIZE, ICECAST_CLIENT_PRIORITY);
+
+   //Unable to create the task?
+   if(task == OS_INVALID_HANDLE)
+      return ERROR_OUT_OF_RESOURCES;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Copy data from input stream
+ * @param[in] context Pointer to the Icecast client context
+ * @param[out] data Pointer to the user buffer
+ * @param[in] size Maximum number of bytes that can be read
+ * @param[out] length Number of bytes that have been read
+ * @param[in] timeout Maximum time to wait before returning
+ * @return Error code
+ **/
+
+error_t icecastClientReadStream(IcecastClientContext *context,
+   uint8_t *data, size_t size, size_t *length, systime_t timeout)
+{
+   bool_t status;
+
+   //Ensure the parameters are valid
+   if(!context || !data)
+      return ERROR_INVALID_PARAMETER;
+
+   //Wait for the buffer to be available for reading
+   status = osWaitForEvent(&context->readEvent, timeout);
+   //Timeout error?
+   if(!status)
+      return ERROR_TIMEOUT;
+
+   //Enter critical section
+   osAcquireMutex(&context->mutex);
+   //Compute the number of bytes to read at a time
+   *length = MIN(size, context->bufferLength);
+   //Leave critical section
+   osReleaseMutex(&context->mutex);
+
+   //Check whether the specified data crosses buffer boundaries
+   if((context->readIndex + *length) <= context->bufferSize)
+   {
+      //Copy the data
+      memcpy(data, context->streamBuffer + context->readIndex, *length);
+   }
+   else
+   {
+      //Copy the first part of the data
+      memcpy(data, context->streamBuffer + context->readIndex,
+         context->bufferSize - context->readIndex);
+      //Wrap around to the beginning of the circular buffer
+      memcpy(data + context->bufferSize - context->readIndex, context->streamBuffer,
+         *length - context->bufferSize + context->readIndex);
+   }
+
+   //Enter critical section
+   osAcquireMutex(&context->mutex);
+
+   //Increment read index
+   context->readIndex += *length;
+   //Wrap around if necessary
+   if(context->readIndex >= context->bufferSize)
+      context->readIndex -= context->bufferSize;
+
+   //Update buffer length
+   context->bufferLength -= *length;
+   //Check whether the buffer is available for writing
+   if(context->bufferLength < context->bufferSize)
+      osSetEvent(&context->writeEvent);
+   //Check whether the buffer is available for reading
+   if(context->bufferLength > 0)
+      osSetEvent(&context->readEvent);
+
+   //Leave critical section
+   osReleaseMutex(&context->mutex);
+
+   //Successful read operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Copy metadata from input stream
+ * @param[in] context Pointer to the Icecast client context
+ * @param[out] metadata Pointer to the user buffer
+ * @param[in] size Maximum number of bytes that can be read
+ * @param[out] length Number of bytes that have been read
+ * @return Error code
+ **/
+
+error_t icecastClientReadMetadata(IcecastClientContext *context,
+   char_t *metadata, size_t size, size_t *length)
+{
+   //Ensure the parameters are valid
+   if(!context || !metadata)
+      return ERROR_INVALID_PARAMETER;
+
+   //Enter critical section
+   osAcquireMutex(&context->mutex);
+
+   //Limit the number of data to read
+   *length = MIN(size, context->metadataLength);
+   //Save metadata information
+   memcpy(metadata, context->metadata, *length);
+
+   //Leave critical section
+   osReleaseMutex(&context->mutex);
+
+   //Successful read operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Icecast client task
+ * @param[in] param Pointer to the Icecast client context
+ **/
+
+void icecastClientTask(void *param)
+{
+   error_t error;
+   bool_t end;
+   size_t n;
+   size_t length;
+   size_t received;
+   IcecastClientContext *context;
+
+   //Retrieve the Icecast client context
+   context = (IcecastClientContext *) param;
+
+   //Main loop
+   while(1)
+   {
+      //Debug message
+      TRACE_INFO("Icecast client: Connecting to server %s port %" PRIu16 "\r\n",
+         context->settings.serverName, context->settings.serverPort);
+
+      //Initiate a connection to the Icecast server
+      error = icecastClientConnect(context);
+
+      //Connection to server failed?
+      if(error)
+      {
+         //Debug message
+         TRACE_ERROR("Icecast client: Connection to server failed!\r\n");
+         //Recovery delay
+         osDelayTask(ICECAST_RECOVERY_DELAY);
+         //Try to reconnect...
+         continue;
+      }
+
+      //Debug message
+      TRACE_INFO("Block size = %" PRIuSIZE "\r\n", context->blockSize);
+
+      //Check block size
+      if(!context->blockSize)
+      {
+         //Close socket
+         socketClose(context->socket);
+         //Recovery delay
+         osDelayTask(ICECAST_RECOVERY_DELAY);
+         //Try to reconnect...
+         continue;
+      }
+
+      //Initialize loop condition variable
+      end = FALSE;
+
+      //Read as much data as possible...
+      while(!end)
+      {
+         //Process the stream block by block
+         length = context->blockSize;
+
+         //Read current block
+         while(!end && length > 0)
+         {
+            //Wait for the buffer to be available for writing
+            osWaitForEvent(&context->writeEvent, INFINITE_DELAY);
+
+            //Enter critical section
+            osAcquireMutex(&context->mutex);
+            //Compute the number of bytes to read at a time
+            n = MIN(length, context->bufferSize - context->bufferLength);
+            //Leave critical section
+            osReleaseMutex(&context->mutex);
+
+            //Check whether the specified data crosses buffer boundaries
+            if((context->writeIndex + n) > context->bufferSize)
+               n = context->bufferSize - context->writeIndex;
+
+            //Receive data
+            error = socketReceive(context->socket, context->streamBuffer +
+               context->writeIndex, n, &received, SOCKET_FLAG_WAIT_ALL);
+
+            //Any error to report?
+            if(error)
+            {
+               //Stop streaming data
+               end = TRUE;
+            }
+            else
+            {
+               //Enter critical section
+               osAcquireMutex(&context->mutex);
+
+               //Increment write index
+               context->writeIndex += n;
+               //Wrap around if necessary
+               if(context->writeIndex >= context->bufferSize)
+                  context->writeIndex -= context->bufferSize;
+
+               //Update buffer length
+               context->bufferLength += n;
+               //Check whether the buffer is available for writing
+               if(context->bufferLength < context->bufferSize)
+                  osSetEvent(&context->writeEvent);
+               //Check whether the buffer is available for reading
+               if(context->bufferLength > 0)
+                  osSetEvent(&context->readEvent);
+
+               //Leave critical section
+               osReleaseMutex(&context->mutex);
+
+               //Update the total number of bytes that have been received
+               context->totalLength += n;
+               //Number of remaining data to read
+               length -= n;
+            }
+         }
+
+         //Debug message
+         TRACE_DEBUG("Icecast client: Total bytes received = %" PRIuSIZE "\r\n", context->totalLength);
+
+         //Check whether the metadata block should be read
+         if(!end)
+         {
+            //Process the metadata block
+            error = icecastClientProcessMetadata(context);
+            //Any error to report?
+            if(error) end = TRUE;
+         }
+      }
+
+      //Close connection
+      socketClose(context->socket);
+   }
+}
+
+
+/**
+ * @brief Connect to the specified Icecast server
+ * @param[in] context Pointer to the Icecast client context
+ **/
+
+error_t icecastClientConnect(IcecastClientContext *context)
+{
+   error_t error;
+   size_t length;
+   uint16_t serverPort;
+   IpAddr serverIpAddr;
+   NetInterface *interface;
+
+   //Underlying network interface
+   interface = context->settings.interface;
+
+   //Force traffic to go through a proxy server?
+   if(strcmp(interface->proxyName, ""))
+   {
+      //Icecast request template
+      const char_t requestTemplate[] =
+         "GET http://%s:%" PRIu16 "%s HTTP/1.1\r\n"
+         "Host: %s:%" PRIu16 "\r\n"
+         "User-agent: UserAgent\r\n"
+         "Icy-MetaData: 1\r\n"
+         "Connection: close\r\n"
+         "\r\n";
+
+      //Format Icecast request
+      length = sprintf(context->buffer, requestTemplate,
+         context->settings.serverName, context->settings.serverPort,
+         context->settings.resource, context->settings.serverName,
+         context->settings.serverPort);
+
+      //The specified proxy server can be either an IP or a host name
+      error = getHostByName(interface, interface->proxyName, &serverIpAddr, 0);
+      //Unable to resolve server name?
+      if(error)
+         return error;
+
+      //Proxy server port
+      serverPort = interface->proxyPort;
+   }
+   else
+   {
+      //Icecast request template
+      const char_t requestTemplate[] =
+         "GET %s HTTP/1.1\r\n"
+         "Host: %s\r\n"
+         "User-agent: UserAgent\r\n"
+         "Icy-MetaData: 1\r\n"
+         "Connection: close\r\n"
+         "\r\n";
+
+      //Format Icecast request
+      length = sprintf(context->buffer, requestTemplate,
+         context->settings.resource, context->settings.serverName);
+
+      //The specified Icecast server can be either an IP or a host name
+      error = getHostByName(interface, context->settings.serverName, &serverIpAddr, 0);
+      //Unable to resolve server name?
+      if(error)
+         return error;
+
+      //Icecast server port
+      serverPort = context->settings.serverPort;
+   }
+
+   //Open a TCP socket
+   context->socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
+   //Failed to open socket?
+   if(!context->socket)
+      return ERROR_OUT_OF_RESOURCES;
+
+   //Start of exception handling block
+   do
+   {
+      //Associate the socket with the relevant interface
+      error = socketBindToInterface(context->socket, interface);
+      //Unable to bind the socket to the desired interface?
+      if(error)
+         break;
+
+      //Adjust receive timeout
+      error = socketSetTimeout(context->socket, ICECAST_CLIENT_TIMEOUT);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Connect to the server
+      error = socketConnect(context->socket, &serverIpAddr, serverPort);
+      //Connection with server failed?
+      if(error)
+         break;
+
+      //Display Icecast request for debugging purpose
+      TRACE_DEBUG(context->buffer);
+
+      //Send request to the server
+      error = socketSend(context->socket, context->buffer,
+         length, NULL, SOCKET_FLAG_WAIT_ACK);
+      //Failed to send the request?
+      if(error)
+         break;
+
+      //Parse response header
+      while(1)
+      {
+         char_t *separator;
+         char_t *property;
+         char_t *value;
+
+         //Read a line from the response header
+         error = socketReceive(context->socket, context->buffer,
+            ICECAST_CLIENT_METADATA_MAX_SIZE, &length, SOCKET_FLAG_BREAK_CRLF);
+         //Failed to read data?
+         if(error)
+            break;
+
+         //Properly terminate the string with a NULL character
+         context->buffer[length] = '\0';
+
+         //The end of the header has been reached?
+         if(!strcmp(context->buffer, "\r\n"))
+            break;
+
+         //Check whether a separator is present
+         separator = strchr(context->buffer, ':');
+
+         //Separator found?
+         if(separator)
+         {
+            //Split the line
+            *separator = '\0';
+
+            //Get property name and value
+            property = strTrimWhitespace(context->buffer);
+            value = strTrimWhitespace(separator + 1);
+
+            //Debug message
+            TRACE_INFO("<%s>=<%s>\r\n", property, value);
+
+            //Icy-Metaint property found?
+            if(!strcasecmp(property, "Icy-Metaint"))
+            {
+               //Retrieve the block size used by the Icecast server
+               context->blockSize = atoi(value);
+            }
+         }
+      }
+
+      //End of exception handling block
+   } while(0);
+
+   //Check whether an error occurred
+   if(error)
+   {
+      //Clean up side effects
+      socketClose(context->socket);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Decode metadata block
+ * @param[in] context Pointer to the Icecast client context
+ **/
+
+error_t icecastClientProcessMetadata(IcecastClientContext *context)
+{
+   error_t error;
+   size_t n;
+   size_t length;
+   size_t metadataLength;
+
+   //The metadata block begins with a single byte which indicates
+   //how many 16-byte segments need to be read
+   error = socketReceive(context->socket, context->buffer,
+      sizeof(uint8_t), &length, SOCKET_FLAG_WAIT_ALL);
+
+   //Any error to report?
+   if(error)
+      return error;
+   //Make sure the expected number of bytes have been received
+   if(length != sizeof(uint8_t))
+      return ERROR_INVALID_METADATA;
+
+   //Compute the length of the following metadata block
+   metadataLength = context->buffer[0] * 16;
+   //Limit the number of bytes to read
+   n = MIN(metadataLength, ICECAST_CLIENT_METADATA_MAX_SIZE - 1);
+
+   //Read the metadata information
+   error = socketReceive(context->socket, context->buffer,
+      n, &length, SOCKET_FLAG_WAIT_ALL);
+
+   //Any error to report?
+   if(error)
+      return error;
+   //Make sure the expected number of bytes have been received
+   if(length != n)
+      return ERROR_INVALID_METADATA;
+
+   //Enter critical section
+   osAcquireMutex(&context->mutex);
+
+   //Save metadata information
+   memcpy(context->metadata, context->buffer, n);
+   //Terminate the string properly
+   context->metadata[n] = '\0';
+   //Record the length of the metadata
+   context->metadataLength = n;
+
+   //Leave critical section
+   osReleaseMutex(&context->mutex);
+
+   //Any metadata information received?
+   if(n > 0)
+   {
+      //Debug message
+      TRACE_DEBUG("Icecast client: Metadata = <%s>\r\n", context->metadata);
+   }
+
+   //Compute the number of bytes that have not been processed
+   metadataLength -= n;
+
+   //Read the complete metadata
+   while(metadataLength > 0)
+   {
+      //Compute the number of data to read at a time
+      n = MIN(metadataLength, ICECAST_CLIENT_METADATA_MAX_SIZE);
+
+      //Drop incoming data...
+      error = socketReceive(context->socket, context->buffer,
+         n, &length, SOCKET_FLAG_WAIT_ALL);
+
+      //Any error to report?
+      if(error)
+         return error;
+      //Make sure the expected number of bytes have been received
+      if(length != n)
+         return ERROR_INVALID_METADATA;
+
+      //Update byte counter
+      metadataLength -= n;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+#endif
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/icecast/icecast_client.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,149 @@
+/**
+ * @file icecast_client.h
+ * @brief Icecast client
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _ICECAST_CLIENT_H
+#define _ICECAST_CLIENT_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/socket.h"
+
+//Icecast client support
+#ifndef ICECAST_CLIENT_SUPPORT
+   #define ICECAST_CLIENT_SUPPORT ENABLED
+#elif (ICECAST_CLIENT_SUPPORT != ENABLED && ICECAST_CLIENT_SUPPORT != DISABLED)
+   #error ICECAST_CLIENT_SUPPORT parameter is not valid
+#endif
+
+//Stack size required to run the Icecast client
+#ifndef ICECAST_CLIENT_STACK_SIZE
+   #define ICECAST_CLIENT_STACK_SIZE 650
+#elif (ICECAST_CLIENT_STACK_SIZE < 1)
+   #error ICECAST_CLIENT_STACK_SIZE parameter is not valid
+#endif
+
+//Priority at which the Icecast client should run
+#ifndef ICECAST_CLIENT_PRIORITY
+   #define ICECAST_CLIENT_PRIORITY OS_TASK_PRIORITY_NORMAL
+#endif
+
+//Maximum time the Icecast client will wait before closing the connection
+#ifndef ICECAST_CLIENT_TIMEOUT
+   #define ICECAST_CLIENT_TIMEOUT 10000
+#elif (ICECAST_CLIENT_TIMEOUT < 1000)
+   #error ICECAST_CLIENT_TIMEOUT parameter is not valid
+#endif
+
+//Recovery delay
+#ifndef ICECAST_RECOVERY_DELAY
+   #define ICECAST_RECOVERY_DELAY 5000
+#elif (ICECAST_RECOVERY_DELAY < 1000)
+   #error ICECAST_RECOVERY_DELAY parameter is not valid
+#endif
+
+//Maximum length of the server name
+#ifndef ICECAST_SERVER_NAME_MAX_LEN
+   #define ICECAST_SERVER_NAME_MAX_LEN 48
+#elif (ICECAST_SERVER_NAME_MAX_LEN < 1)
+   #error ICECAST_SERVER_NAME_MAX_LEN parameter is not valid
+#endif
+
+//Maxmimum length of the requested resource
+#ifndef ICECAST_RESOURCE_MAX_LEN
+   #define ICECAST_RESOURCE_MAX_LEN 32
+#elif (ICECAST_RESOURCE_MAX_LEN < 1)
+   #error ICECAST_RESOURCE_MAX_LEN parameter is not valid
+#endif
+
+//Maximum size of metadata blocks
+#ifndef ICECAST_CLIENT_METADATA_MAX_SIZE
+   #define ICECAST_CLIENT_METADATA_MAX_SIZE 512
+#elif (ICECAST_CLIENT_METADATA_MAX_SIZE < 128)
+   #error ICECAST_CLIENT_METADATA_MAX_SIZE parameter is not valid
+#endif
+
+
+/**
+ * @brief Icecast client settings
+ **/
+
+typedef struct
+{
+   NetInterface *interface;                        ///<Underlying network interface
+   char_t serverName[ICECAST_SERVER_NAME_MAX_LEN]; ///<Icecast server name
+   uint16_t serverPort;                            ///<Icecast server port
+   char_t resource[ICECAST_RESOURCE_MAX_LEN];      ///<Requested resource
+   size_t bufferSize;                              ///<Streaming buffer size
+} IcecastClientSettings;
+
+
+/**
+ * @brief Icecast client context
+ **/
+
+typedef struct
+{
+   IcecastClientSettings settings;                    ///<User settings
+   OsMutex mutex;                                     ///<Mutex protecting critical sections
+   OsEvent writeEvent;                                ///<This event tells whether the buffer is writable
+   OsEvent readEvent;                                 ///<This event tells whether the buffer is readable
+   Socket *socket;                                    ///<Underlying socket
+   size_t blockSize;                                  ///<Number of data bytes between subsequent metadata blocks
+   uint8_t *streamBuffer;                             ///<Streaming buffer
+   size_t bufferSize;                                 ///<Streaming buffer size
+   size_t bufferLength;                               ///<Streaming buffer length
+   size_t writeIndex;                                 ///<Current write index within the buffer
+   size_t readIndex;                                  ///<Current read index within the buffer
+   size_t totalLength;                                ///<Total number of bytes that have been received
+   char_t buffer[ICECAST_CLIENT_METADATA_MAX_SIZE];   ///<Memory buffer for input/output operations
+   char_t metadata[ICECAST_CLIENT_METADATA_MAX_SIZE]; ///<Metadata information
+   size_t metadataLength;                             ///<Length of the metadata
+} IcecastClientContext;
+
+
+//Icecast related functions
+void icecastClientGetDefaultSettings(IcecastClientSettings *settings);
+
+error_t icecastClientInit(IcecastClientContext *context,
+   const IcecastClientSettings *settings);
+
+error_t icecastClientStart(IcecastClientContext *context);
+
+error_t icecastClientReadStream(IcecastClientContext *context,
+   uint8_t *data, size_t size, size_t *length, systime_t timeout);
+
+error_t icecastClientReadMetadata(IcecastClientContext *context,
+   char_t *metadata, size_t size, size_t *length);
+
+void icecastClientTask(void *param);
+
+error_t icecastClientConnect(IcecastClientContext *context);
+error_t icecastClientProcessMetadata(IcecastClientContext *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv4/arp.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,908 @@
+/**
+ * @file arp.c
+ * @brief ARP (Address Resolution Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * Address Resolution Protocol is used to determine the hardware address of
+ * a specific host when only its IPv4 address is known. Refer to RFC 826
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL ARP_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "core/net.h"
+#include "core/ethernet.h"
+#include "ipv4/arp.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV4_SUPPORT == ENABLED && ETH_SUPPORT == ENABLED)
+
+//Tick counter to handle periodic operations
+systime_t arpTickCounter;
+
+
+/**
+ * @brief ARP cache initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t arpInit(NetInterface *interface)
+{
+   //Initialize the ARP cache
+   memset(interface->arpCache, 0, sizeof(interface->arpCache));
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Flush ARP cache
+ * @param[in] interface Underlying network interface
+ **/
+
+void arpFlushCache(NetInterface *interface)
+{
+   uint_t i;
+   ArpCacheEntry *entry;
+
+   //Loop through ARP cache entries
+   for(i = 0; i < ARP_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->arpCache[i];
+
+      //Drop packets that are waiting for address resolution
+      arpFlushQueuedPackets(interface, entry);
+      //Release ARP entry
+      entry->state = ARP_STATE_NONE;
+   }
+}
+
+
+/**
+ * @brief Create a new entry in the ARP cache
+ * @param[in] interface Underlying network interface
+ * @return Pointer to the newly created entry
+ **/
+
+ArpCacheEntry *arpCreateEntry(NetInterface *interface)
+{
+   uint_t i;
+   ArpCacheEntry *entry;
+   ArpCacheEntry *oldestEntry;
+
+   //Keep track of the oldest entry
+   oldestEntry = &interface->arpCache[0];
+
+   //Loop through ARP cache entries
+   for(i = 0; i < ARP_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->arpCache[i];
+
+      //Check whether the entry is currently in used or not
+      if(entry->state == ARP_STATE_NONE)
+      {
+         //Erase contents
+         memset(entry, 0, sizeof(ArpCacheEntry));
+         //Return a pointer to the ARP entry
+         return entry;
+      }
+
+      //Keep track of the oldest entry in the table
+      if(timeCompare(entry->timestamp, oldestEntry->timestamp) < 0)
+         oldestEntry = entry;
+   }
+
+   //Drop any pending packets
+   arpFlushQueuedPackets(interface, oldestEntry);
+   //The oldest entry is removed whenever the table runs out of space
+   memset(oldestEntry, 0, sizeof(ArpCacheEntry));
+   //Return a pointer to the ARP entry
+   return oldestEntry;
+}
+
+
+/**
+ * @brief Search the ARP cache for a given IPv4 address
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr IPv4 address
+ * @return A pointer to the matching ARP entry is returned. NULL is returned
+ *   if the specified IPv4 address could not be found in ARP cache
+ **/
+
+ArpCacheEntry *arpFindEntry(NetInterface *interface, Ipv4Addr ipAddr)
+{
+   uint_t i;
+   ArpCacheEntry *entry;
+
+   //Loop through ARP cache entries
+   for(i = 0; i < ARP_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->arpCache[i];
+
+      //Check whether the entry is currently in used
+      if(entry->state != ARP_STATE_NONE)
+      {
+         //Current entry matches the specified address?
+         if(entry->ipAddr == ipAddr)
+            return entry;
+      }
+   }
+
+   //No matching entry in ARP cache...
+   return NULL;
+}
+
+
+/**
+ * @brief Send packets that are waiting for address resolution
+ * @param[in] interface Underlying network interface
+ * @param[in] entry Pointer to a ARP cache entry
+ **/
+
+void arpSendQueuedPackets(NetInterface *interface, ArpCacheEntry *entry)
+{
+   uint_t i;
+   ArpQueueItem *item;
+
+   //Check current state
+   if(entry->state == ARP_STATE_INCOMPLETE)
+   {
+      //Loop through the queued packets
+      for(i = 0; i < entry->queueSize; i++)
+      {
+         //Point to the current queue item
+         item = &entry->queue[i];
+
+         //Send the IPv4 packet
+         ethSendFrame(interface, &entry->macAddr,
+            item->buffer, item->offset, ETH_TYPE_IPV4);
+
+         //Release memory buffer
+         netBufferFree(item->buffer);
+      }
+   }
+
+   //The queue is now empty
+   entry->queueSize = 0;
+}
+
+
+/**
+ * @brief Flush packet queue
+ * @param[in] interface Underlying network interface
+ * @param[in] entry Pointer to a ARP cache entry
+ **/
+
+void arpFlushQueuedPackets(NetInterface *interface, ArpCacheEntry *entry)
+{
+   uint_t i;
+
+   //Check current state
+   if(entry->state == ARP_STATE_INCOMPLETE)
+   {
+      //Drop packets that are waiting for address resolution
+      for(i = 0; i < entry->queueSize; i++)
+      {
+         //Release memory buffer
+         netBufferFree(entry->queue[i].buffer);
+      }
+   }
+
+   //The queue is now empty
+   entry->queueSize = 0;
+}
+
+
+/**
+ * @brief Address resolution using ARP protocol
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr IPv4 address
+ * @param[in] macAddr Physical address matching the specified IPv4 address
+ * @return Error code
+ **/
+
+error_t arpResolve(NetInterface *interface, Ipv4Addr ipAddr, MacAddr *macAddr)
+{
+   error_t error;
+   ArpCacheEntry *entry;
+
+   //Search the ARP cache for the specified IPv4 address
+   entry = arpFindEntry(interface, ipAddr);
+
+   //Check whether a matching entry has been found
+   if(entry != NULL)
+   {
+      //Check the state of the ARP entry
+      if(entry->state == ARP_STATE_INCOMPLETE)
+      {
+         //The address resolution is already in progress
+         error = ERROR_IN_PROGRESS;
+      }
+      else if(entry->state == ARP_STATE_STALE)
+      {
+         //Copy the MAC address associated with the specified IPv4 address
+         *macAddr = entry->macAddr;
+
+         //Start delay timer
+         entry->timestamp = osGetSystemTime();
+         //Delay before sending the first probe
+         entry->timeout = ARP_DELAY_FIRST_PROBE_TIME;
+         //Switch to the DELAY state
+         entry->state = ARP_STATE_DELAY;
+
+         //Successful address resolution
+         error = NO_ERROR;
+      }
+      else
+      {
+         //Copy the MAC address associated with the specified IPv4 address
+         *macAddr = entry->macAddr;
+
+         //Successful address resolution
+         error = NO_ERROR;
+      }
+   }
+   else
+   {
+      //If no entry exists, then create a new one
+      entry = arpCreateEntry(interface);
+
+      //ARP cache entry successfully created?
+      if(entry != NULL)
+      {
+         //Record the IPv4 address whose MAC address is unknown
+         entry->ipAddr = ipAddr;
+
+         //Reset retransmission counter
+         entry->retransmitCount = 0;
+         //No packet are pending in the transmit queue
+         entry->queueSize = 0;
+
+         //Send an ARP request
+         arpSendRequest(interface, entry->ipAddr, &MAC_BROADCAST_ADDR);
+
+         //Save the time at which the packet was sent
+         entry->timestamp = osGetSystemTime();
+         //Set timeout value
+         entry->timeout = ARP_REQUEST_TIMEOUT;
+         //Enter INCOMPLETE state
+         entry->state = ARP_STATE_INCOMPLETE;
+
+         //The address resolution is in progress
+         error = ERROR_IN_PROGRESS;
+      }
+      else
+      {
+         //Failed to create ARP cache entry...
+         error = ERROR_OUT_OF_RESOURCES;
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Enqueue an IPv4 packet waiting for address resolution
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr IPv4 address of the destination host
+ * @param[in] buffer Multi-part buffer containing the packet to be enqueued
+ * @param[in] offset Offset to the first byte of the packet
+ * @return Error code
+ **/
+
+error_t arpEnqueuePacket(NetInterface *interface,
+   Ipv4Addr ipAddr, NetBuffer *buffer, size_t offset)
+{
+   error_t error;
+   uint_t i;
+   size_t length;
+   ArpCacheEntry *entry;
+
+   //Retrieve the length of the multi-part buffer
+   length = netBufferGetLength(buffer);
+
+   //Search the ARP cache for the specified IPv4 address
+   entry = arpFindEntry(interface, ipAddr);
+
+   //Check whether a matching entry exists
+   if(entry != NULL)
+   {
+      //Check current state
+      if(entry->state == ARP_STATE_INCOMPLETE)
+      {
+         //Check whether the packet queue is full
+         if(entry->queueSize >= ARP_MAX_PENDING_PACKETS)
+         {
+            //When the queue overflows, the new arrival should replace the oldest entry
+            netBufferFree(entry->queue[0].buffer);
+
+            //Make room for the new packet
+            for(i = 1; i < ARP_MAX_PENDING_PACKETS; i++)
+               entry->queue[i - 1] = entry->queue[i];
+
+            //Adjust the number of pending packets
+            entry->queueSize--;
+         }
+
+         //Index of the entry to be filled in
+         i = entry->queueSize;
+         //Allocate a memory buffer to store the packet
+         entry->queue[i].buffer = netBufferAlloc(length);
+
+         //Successful memory allocation?
+         if(entry->queue[i].buffer != NULL)
+         {
+            //Copy the contents of the IPv4 packet
+            netBufferCopy(entry->queue[i].buffer, 0, buffer, 0, length);
+            //Offset to the first byte of the IPv4 header
+            entry->queue[i].offset = offset;
+
+            //Increment the number of queued packets
+            entry->queueSize++;
+            //The packet was successfully enqueued
+            error = NO_ERROR;
+         }
+         else
+         {
+            //Failed to allocate memory
+            error = ERROR_OUT_OF_MEMORY;
+         }
+      }
+      else
+      {
+         //The address is already resolved
+         error = ERROR_UNEXPECTED_STATE;
+      }
+   }
+   else
+   {
+      //No matching entry in ARP cache
+      error = ERROR_NOT_FOUND;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief ARP timer handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * manage ARP cache
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void arpTick(NetInterface *interface)
+{
+   uint_t i;
+   systime_t time;
+   ArpCacheEntry *entry;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Go through ARP cache
+   for(i = 0; i < ARP_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->arpCache[i];
+
+      //INCOMPLETE state?
+      if(entry->state == ARP_STATE_INCOMPLETE)
+      {
+         //The request timed out?
+         if(timeCompare(time, entry->timestamp + entry->timeout) >= 0)
+         {
+            //Increment retransmission counter
+            entry->retransmitCount++;
+
+            //Check whether the maximum number of retransmissions has been exceeded
+            if(entry->retransmitCount < ARP_MAX_REQUESTS)
+            {
+               //Retransmit ARP request
+               arpSendRequest(interface, entry->ipAddr, &MAC_BROADCAST_ADDR);
+
+               //Save the time at which the packet was sent
+               entry->timestamp = time;
+               //Set timeout value
+               entry->timeout = ARP_REQUEST_TIMEOUT;
+            }
+            else
+            {
+               //Drop packets that are waiting for address resolution
+               arpFlushQueuedPackets(interface, entry);
+               //The entry should be deleted since address resolution has failed
+               entry->state = ARP_STATE_NONE;
+            }
+         }
+      }
+      //REACHABLE state?
+      else if(entry->state == ARP_STATE_REACHABLE)
+      {
+         //Periodically time out ARP cache entries
+         if(timeCompare(time, entry->timestamp + entry->timeout) >= 0)
+         {
+            //Save current time
+            entry->timestamp = osGetSystemTime();
+            //Enter STALE state
+            entry->state = ARP_STATE_STALE;
+         }
+      }
+      //DELAY state?
+      else if(entry->state == ARP_STATE_DELAY)
+      {
+         //Wait for the specified delay before sending the first probe
+         if(timeCompare(time, entry->timestamp + entry->timeout) >= 0)
+         {
+            //Send a point-to-point ARP request to the host
+            arpSendRequest(interface, entry->ipAddr, &entry->macAddr);
+
+            //Save the time at which the packet was sent
+            entry->timestamp = time;
+            //Set timeout value
+            entry->timeout = ARP_PROBE_TIMEOUT;
+            //Switch to the PROBE state
+            entry->state = ARP_STATE_PROBE;
+         }
+      }
+      //PROBE state?
+      else if(entry->state == ARP_STATE_PROBE)
+      {
+         //The request timed out?
+         if(timeCompare(time, entry->timestamp + entry->timeout) >= 0)
+         {
+            //Increment retransmission counter
+            entry->retransmitCount++;
+
+            //Check whether the maximum number of retransmissions has been exceeded
+            if(entry->retransmitCount < ARP_MAX_PROBES)
+            {
+               //Send a point-to-point ARP request to the host
+               arpSendRequest(interface, entry->ipAddr, &entry->macAddr);
+
+               //Save the time at which the packet was sent
+               entry->timestamp = time;
+               //Set timeout value
+               entry->timeout = ARP_PROBE_TIMEOUT;
+            }
+            else
+            {
+               //The entry should be deleted since the host is not reachable anymore
+               entry->state = ARP_STATE_NONE;
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Incoming ARP packet processing
+ * @param[in] interface Underlying network interface
+ * @param[in] arpPacket Incoming ARP packet
+ * @param[in] length Packet length
+ **/
+
+void arpProcessPacket(NetInterface *interface, ArpPacket *arpPacket, size_t length)
+{
+   //Discard invalid ARP packets
+   if(length < sizeof(ArpPacket))
+      return;
+
+   //Debug message
+   TRACE_INFO("ARP packet received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump ARP packet contents for debugging purpose
+   arpDumpPacket(arpPacket);
+
+   //Make sure the hardware type is valid
+   if(arpPacket->hrd != HTONS(ARP_HARDWARE_TYPE_ETH))
+      return;
+   //Make sure the protocol type is valid
+   if(arpPacket->pro != HTONS(ARP_PROTOCOL_TYPE_IPV4))
+      return;
+   //Check the length of the hardware address
+   if(arpPacket->hln != sizeof(MacAddr))
+      return;
+   //Check the length of the protocol address
+   if(arpPacket->pln != sizeof(Ipv4Addr))
+      return;
+
+   //Check the state of the host address
+   if(interface->ipv4Context.addrState == IPV4_ADDR_STATE_TENTATIVE)
+   {
+      //If the host receives any ARP packet where the sender IP address is
+      //the address being probed for, then this is a conflicting ARP packet
+      if(arpPacket->spa == interface->ipv4Context.addr)
+      {
+         //An address conflict has been detected...
+         interface->ipv4Context.addrConflict = TRUE;
+         //Exit immediately
+         return;
+      }
+   }
+   else if(interface->ipv4Context.addrState == IPV4_ADDR_STATE_VALID)
+   {
+      //Check whether the sender protocol address matches the IP
+      //address assigned to the interface
+      if(arpPacket->spa == interface->ipv4Context.addr)
+      {
+         //If the sender hardware address does not match the hardware
+         //address of that interface, then this is a conflicting ARP packet
+         if(!macCompAddr(&arpPacket->sha, &interface->macAddr))
+         {
+            //An address conflict has been detected...
+            interface->ipv4Context.addrConflict = TRUE;
+            //Exit immediately
+            return;
+         }
+      }
+   }
+
+   //Check whether the target protocol address matches the IP
+   //address assigned to the interface
+   if(arpPacket->tpa != interface->ipv4Context.addr)
+      return;
+
+   //Check operation code
+   switch(ntohs(arpPacket->op))
+   {
+   //ARP request?
+   case ARP_OPCODE_ARP_REQUEST:
+      //Process incoming ARP request
+      arpProcessRequest(interface, arpPacket);
+      break;
+   //ARP reply?
+   case ARP_OPCODE_ARP_REPLY:
+      //Process incoming ARP reply
+      arpProcessReply(interface, arpPacket);
+      break;
+   //Unknown operation code?
+   default:
+      //Debug message
+      TRACE_INFO("Unknown operation code!\r\n");
+      //Discard incoming packet
+      break;
+   }
+}
+
+
+/**
+ * @brief Incoming ARP request processing
+ * @param[in] interface Underlying network interface
+ * @param[in] arpRequest Incoming ARP request
+ **/
+
+void arpProcessRequest(NetInterface *interface, ArpPacket *arpRequest)
+{
+   //Debug message
+   TRACE_INFO("ARP Request received...\r\n");
+
+   //Check sender protocol address
+   if(ipv4IsBroadcastAddr(interface, arpRequest->spa))
+      return;
+   if(ipv4IsMulticastAddr(arpRequest->spa))
+      return;
+
+   //Check whether the target IP address is an address being probed for
+   if(ipv4IsTentativeAddr(interface, arpRequest->tpa))
+   {
+      //ARP probe received?
+      if(arpRequest->spa == IPV4_UNSPECIFIED_ADDR)
+      {
+         //If the sender hardware address does not match the hardware
+         //address of that interface, then this is a conflicting ARP packet
+         if(!macCompAddr(&arpRequest->sha, &interface->macAddr))
+         {
+            //An address conflict has been detected...
+            interface->ipv4Context.addrConflict = TRUE;
+         }
+      }
+
+      //In all cases, the host must not respond to an ARP request for an
+      //address being probed for
+      return;
+   }
+
+   //Send ARP reply
+   arpSendReply(interface, arpRequest->spa, &arpRequest->sha, &arpRequest->sha);
+}
+
+
+/**
+ * @brief Incoming ARP reply processing
+ * @param[in] interface Underlying network interface
+ * @param[in] arpReply Incoming ARP reply
+ **/
+
+void arpProcessReply(NetInterface *interface, ArpPacket *arpReply)
+{
+   ArpCacheEntry *entry;
+
+   //Debug message
+   TRACE_INFO("ARP Reply received...\r\n");
+
+   //Check sender protocol address
+   if(arpReply->spa == IPV4_UNSPECIFIED_ADDR)
+      return;
+   if(ipv4IsBroadcastAddr(interface, arpReply->spa))
+      return;
+   if(ipv4IsMulticastAddr(arpReply->spa))
+      return;
+
+   //Check sender hardware address
+   if(macCompAddr(&arpReply->sha, &MAC_UNSPECIFIED_ADDR))
+      return;
+   if(macCompAddr(&arpReply->sha, &MAC_BROADCAST_ADDR))
+      return;
+   if(macIsMulticastAddr(&arpReply->sha))
+      return;
+
+   //Check whether the target IP address is an address being probed for
+   if(ipv4IsTentativeAddr(interface, arpReply->tpa))
+      return;
+
+   //Search the ARP cache for the specified IPv4 address
+   entry = arpFindEntry(interface, arpReply->spa);
+
+   //Check whether a matching entry has been found
+   if(entry != NULL)
+   {
+      //Check current state
+      if(entry->state == ARP_STATE_INCOMPLETE)
+      {
+         //Record the corresponding MAC address
+         entry->macAddr = arpReply->sha;
+
+         //Send all the packets that are pending for transmission
+         arpSendQueuedPackets(interface, entry);
+
+         //Save current time
+         entry->timestamp = osGetSystemTime();
+         //The validity of the ARP entry is limited in time
+         entry->timeout = ARP_REACHABLE_TIME;
+         //Switch to the REACHABLE state
+         entry->state = ARP_STATE_REACHABLE;
+      }
+      else if(entry->state == ARP_STATE_REACHABLE)
+      {
+         //Different link-layer address than cached?
+         if(!macCompAddr(&arpReply->sha, &entry->macAddr))
+         {
+            //Enter STALE state
+            entry->state = ARP_STATE_STALE;
+         }
+      }
+      else if(entry->state == ARP_STATE_PROBE)
+      {
+         //Record IPv4/MAC address pair
+         entry->ipAddr = arpReply->spa;
+         entry->macAddr = arpReply->sha;
+
+         //Save current time
+         entry->timestamp = osGetSystemTime();
+         //The validity of the ARP entry is limited in time
+         entry->timeout = ARP_REACHABLE_TIME;
+         //Switch to the REACHABLE state
+         entry->state = ARP_STATE_REACHABLE;
+      }
+   }
+}
+
+
+/**
+ * @brief Send ARP probe
+ * @param[in] interface Underlying network interface
+ * @param[in] targetIpAddr Target IPv4 address
+ * @return Error code
+ **/
+
+error_t arpSendProbe(NetInterface *interface, Ipv4Addr targetIpAddr)
+{
+   error_t error;
+   size_t offset;
+   NetBuffer *buffer;
+   ArpPacket *arpRequest;
+
+   //Allocate a memory buffer to hold an ARP packet
+   buffer = ethAllocBuffer(sizeof(ArpPacket), &offset);
+   //Failed to allocate buffer?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the ARP packet
+   arpRequest = netBufferAt(buffer, offset);
+
+   //Format ARP request
+   arpRequest->hrd = htons(ARP_HARDWARE_TYPE_ETH);
+   arpRequest->pro = htons(ARP_PROTOCOL_TYPE_IPV4);
+   arpRequest->hln = sizeof(MacAddr);
+   arpRequest->pln = sizeof(Ipv4Addr);
+   arpRequest->op = htons(ARP_OPCODE_ARP_REQUEST);
+   arpRequest->sha = interface->macAddr;
+   arpRequest->spa = IPV4_UNSPECIFIED_ADDR;
+   arpRequest->tha = MAC_UNSPECIFIED_ADDR;
+   arpRequest->tpa = targetIpAddr;
+
+   //Debug message
+   TRACE_INFO("Sending ARP Probe (%" PRIuSIZE " bytes)...\r\n", sizeof(ArpPacket));
+   //Dump ARP packet contents for debugging purpose
+   arpDumpPacket(arpRequest);
+
+   //Send ARP request
+   error = ethSendFrame(interface, &MAC_BROADCAST_ADDR,
+      buffer, offset, ETH_TYPE_ARP);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send ARP request
+ * @param[in] interface Underlying network interface
+ * @param[in] targetIpAddr Target IPv4 address
+ * @param[in] destMacAddr Destination MAC address
+ * @return Error code
+ **/
+
+error_t arpSendRequest(NetInterface *interface,
+   Ipv4Addr targetIpAddr, const MacAddr *destMacAddr)
+{
+   error_t error;
+   size_t offset;
+   NetBuffer *buffer;
+   ArpPacket *arpRequest;
+   Ipv4Addr senderIpAddr;
+
+   //Select the most appropriate sender IP address to be used
+   error = ipv4SelectSourceAddr(&interface, targetIpAddr, &senderIpAddr);
+   //No address assigned to the interface?
+   if(error)
+      return error;
+
+   //Allocate a memory buffer to hold an ARP packet
+   buffer = ethAllocBuffer(sizeof(ArpPacket), &offset);
+   //Failed to allocate buffer?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the ARP packet
+   arpRequest = netBufferAt(buffer, offset);
+
+   //Format ARP request
+   arpRequest->hrd = htons(ARP_HARDWARE_TYPE_ETH);
+   arpRequest->pro = htons(ARP_PROTOCOL_TYPE_IPV4);
+   arpRequest->hln = sizeof(MacAddr);
+   arpRequest->pln = sizeof(Ipv4Addr);
+   arpRequest->op = htons(ARP_OPCODE_ARP_REQUEST);
+   arpRequest->sha = interface->macAddr;
+   arpRequest->spa = senderIpAddr;
+   arpRequest->tha = MAC_UNSPECIFIED_ADDR;
+   arpRequest->tpa = targetIpAddr;
+
+   //Debug message
+   TRACE_INFO("Sending ARP Request (%" PRIuSIZE " bytes)...\r\n", sizeof(ArpPacket));
+   //Dump ARP packet contents for debugging purpose
+   arpDumpPacket(arpRequest);
+
+   //Send ARP request
+   error = ethSendFrame(interface, destMacAddr, buffer, offset, ETH_TYPE_ARP);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send ARP reply
+ * @param[in] interface Underlying network interface
+ * @param[in] targetIpAddr Target IPv4 address
+ * @param[in] targetMacAddr Target MAC address
+ * @param[in] destMacAddr Destination MAC address
+ * @return Error code
+ **/
+
+error_t arpSendReply(NetInterface *interface, Ipv4Addr targetIpAddr,
+   const MacAddr *targetMacAddr, const MacAddr *destMacAddr)
+{
+   error_t error;
+   size_t offset;
+   NetBuffer *buffer;
+   ArpPacket *arpReply;
+
+   //Allocate a memory buffer to hold an ARP packet
+   buffer = ethAllocBuffer(sizeof(ArpPacket), &offset);
+   //Failed to allocate buffer?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the ARP packet
+   arpReply = netBufferAt(buffer, offset);
+
+   //Format ARP reply
+   arpReply->hrd = htons(ARP_HARDWARE_TYPE_ETH);
+   arpReply->pro = htons(ETH_TYPE_IPV4);
+   arpReply->hln = sizeof(MacAddr);
+   arpReply->pln = sizeof(Ipv4Addr);
+   arpReply->op = htons(ARP_OPCODE_ARP_REPLY);
+   arpReply->sha = interface->macAddr;
+   arpReply->spa = interface->ipv4Context.addr;
+   arpReply->tha = *targetMacAddr;
+   arpReply->tpa = targetIpAddr;
+
+   //Debug message
+   TRACE_INFO("Sending ARP Reply (%" PRIuSIZE " bytes)...\r\n", sizeof(ArpPacket));
+   //Dump ARP packet contents for debugging purpose
+   arpDumpPacket(arpReply);
+
+   //Send ARP reply
+   error = ethSendFrame(interface, destMacAddr, buffer, offset, ETH_TYPE_ARP);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Dump ARP packet for debugging purpose
+ * @param[in] arpPacket ARP header
+ **/
+
+void arpDumpPacket(const ArpPacket *arpPacket)
+{
+   //Dump ARP packet contents
+   TRACE_DEBUG("  Hardware Type (hrd) = 0x%04" PRIX16 "\r\n", ntohs(arpPacket->hrd));
+   TRACE_DEBUG("  Protocol Type (pro) = 0x%04" PRIX16 "\r\n", ntohs(arpPacket->pro));
+   TRACE_DEBUG("  Hardware Address Length (hln) = %" PRIu8 "\r\n", arpPacket->hln);
+   TRACE_DEBUG("  Protocol Address Length (pln) = %" PRIu8 "\r\n", arpPacket->pln);
+   TRACE_DEBUG("  Opcode (op) = %" PRIu16 "\r\n", ntohs(arpPacket->op));
+   TRACE_DEBUG("  Sender Hardware Address (sha)= %s\r\n", macAddrToString(&arpPacket->sha, NULL));
+   TRACE_DEBUG("  Sender Protocol Address (spa) = %s\r\n", ipv4AddrToString(arpPacket->spa, NULL));
+   TRACE_DEBUG("  Target Hardware Address (tha)= %s\r\n", macAddrToString(&arpPacket->tha, NULL));
+   TRACE_DEBUG("  Target Protocol Address (tpa) = %s\r\n", ipv4AddrToString(arpPacket->tpa, NULL));
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv4/arp.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,224 @@
+/**
+ * @file arp.h
+ * @brief ARP (Address Resolution Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _ARP_H
+#define _ARP_H
+
+//Dependencies
+#include "core/net.h"
+
+//ARP tick interval
+#ifndef ARP_TICK_INTERVAL
+   #define ARP_TICK_INTERVAL 200
+#elif (ARP_TICK_INTERVAL < 10)
+   #error ARP_TICK_INTERVAL parameter is not valid
+#endif
+
+//Size of ARP cache
+#ifndef ARP_CACHE_SIZE
+   #define ARP_CACHE_SIZE 8
+#elif (ARP_CACHE_SIZE < 4)
+   #error ARP_CACHE_SIZE parameter is not valid
+#endif
+
+//Maximum number of packets waiting for address resolution to complete
+#ifndef ARP_MAX_PENDING_PACKETS
+   #define ARP_MAX_PENDING_PACKETS 2
+#elif (ARP_MAX_PENDING_PACKETS < 1)
+   #error ARP_MAX_PENDING_PACKETS parameter is not valid
+#endif
+
+//Maximum number of times that an ARP request will be retransmitted
+#ifndef ARP_MAX_REQUESTS
+   #define ARP_MAX_REQUESTS 3
+#elif (ARP_MAX_REQUESTS < 1)
+   #error ARP_MAX_REQUESTS parameter is not valid
+#endif
+
+//Time interval between subsequent retransmissions of ARP requests
+#ifndef ARP_REQUEST_TIMEOUT
+   #define ARP_REQUEST_TIMEOUT 1000
+#elif (ARP_REQUEST_TIMEOUT < 100)
+   #error ARP_REQUEST_TIMEOUT parameter is not valid
+#endif
+
+//Maximum number of times that a probe will be retransmitted
+#ifndef ARP_MAX_PROBES
+   #define ARP_MAX_PROBES 2
+#elif (ARP_MAX_PROBES < 1)
+   #error ARP_MAX_PROBES parameter is not valid
+#endif
+
+//time interval between subsequent retransmissions of probes
+#ifndef ARP_PROBE_TIMEOUT
+   #define ARP_PROBE_TIMEOUT 60000
+#elif (ARP_PROBE_TIMEOUT < 1000)
+   #error ARP_PROBE_TIMEOUT parameter is not valid
+#endif
+
+//The time a host is considered reachable after receiving a reachability confirmation
+#ifndef ARP_REACHABLE_TIME
+   #define ARP_REACHABLE_TIME 60000
+#elif (ARP_REACHABLE_TIME < 1000)
+   #error ARP_REACHABLE_TIME parameter is not valid
+#endif
+
+//Delay before sending the first probe
+#ifndef ARP_DELAY_FIRST_PROBE_TIME
+   #define ARP_DELAY_FIRST_PROBE_TIME 5000
+#elif (ARP_DELAY_FIRST_PROBE_TIME < 1000)
+   #error ARP_DELAY_FIRST_PROBE_TIME parameter is not valid
+#endif
+
+//Hardware type
+#define ARP_HARDWARE_TYPE_ETH 0x0001
+//Protocol type
+#define ARP_PROTOCOL_TYPE_IPV4 0x0800
+
+
+/**
+ * @brief ARP opcodes
+ **/
+
+typedef enum
+{
+   ARP_OPCODE_ARP_REQUEST = 1,
+   ARP_OPCODE_ARP_REPLY   = 2
+} ArpOpcode;
+
+
+/**
+ * @brief ARP cache entry states
+ **/
+
+typedef enum
+{
+   ARP_STATE_NONE       = 0,
+   ARP_STATE_INCOMPLETE = 1,
+   ARP_STATE_REACHABLE  = 2,
+   ARP_STATE_STALE      = 3,
+   ARP_STATE_DELAY      = 4,
+   ARP_STATE_PROBE      = 5,
+   ARP_STATE_PERMANENT  = 6
+} ArpState;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief ARP packet
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t hrd; //0-1
+   uint16_t pro; //2-3
+   uint8_t hln;  //4
+   uint8_t pln;  //5
+   uint16_t op;  //6-7
+   MacAddr sha;  //8-13
+   Ipv4Addr spa; //14-17
+   MacAddr tha;  //18-23
+   Ipv4Addr tpa; //24-27
+} __end_packed ArpPacket;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+/**
+ * @brief ARP queue item
+ **/
+
+typedef struct
+{
+   NetBuffer *buffer; //Packet waiting for address resolution
+   size_t offset;         //Offset to the first byte of the packet
+} ArpQueueItem;
+
+
+/**
+ * @brief ARP cache entry
+ **/
+
+typedef struct
+{
+   ArpState state;                              //Reachability state
+   Ipv4Addr ipAddr;                             //Unicast IPv4 address
+   MacAddr macAddr;                             //Link layer address associated with the IPv4 address
+   systime_t timestamp;                         //Time stamp to manage entry lifetime
+   systime_t timeout;                           //Timeout value
+   uint_t retransmitCount;                      //Retransmission counter
+   ArpQueueItem queue[ARP_MAX_PENDING_PACKETS]; //Packets waiting for address resolution to complete
+   uint_t queueSize;                            //Number of queued packets
+} ArpCacheEntry;
+
+
+//Tick counter to handle periodic operations
+extern systime_t arpTickCounter;
+
+//ARP related functions
+error_t arpInit(NetInterface *interface);
+void arpFlushCache(NetInterface *interface);
+
+ArpCacheEntry *arpCreateEntry(NetInterface *interface);
+ArpCacheEntry *arpFindEntry(NetInterface *interface, Ipv4Addr ipAddr);
+
+void arpSendQueuedPackets(NetInterface *interface, ArpCacheEntry *entry);
+void arpFlushQueuedPackets(NetInterface *interface, ArpCacheEntry *entry);
+
+error_t arpResolve(NetInterface *interface, Ipv4Addr ipAddr, MacAddr *macAddr);
+
+error_t arpEnqueuePacket(NetInterface *interface,
+   Ipv4Addr ipAddr, NetBuffer *buffer, size_t offset);
+
+void arpTick(NetInterface *interface);
+
+void arpProcessPacket(NetInterface *interface, ArpPacket *arpPacket, size_t length);
+void arpProcessRequest(NetInterface *interface, ArpPacket *arpRequest);
+void arpProcessReply(NetInterface *interface, ArpPacket *arpResponse);
+
+error_t arpSendProbe(NetInterface *interface, Ipv4Addr targetIpAddr);
+
+error_t arpSendRequest(NetInterface *interface,
+   Ipv4Addr targetIpAddr, const MacAddr *destMacAddr);
+
+error_t arpSendReply(NetInterface *interface, Ipv4Addr targetIpAddr,
+   const MacAddr *targetMacAddr, const MacAddr *destMacAddr);
+
+void arpDumpPacket(const ArpPacket *arpPacket);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv4/auto_ip.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,562 @@
+/**
+ * @file auto_ip.c
+ * @brief Auto-IP (Dynamic Configuration of IPv4 Link-Local Addresses)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * Auto-IP describes a method by which a host may automatically configure an
+ * interface with an IPv4 address in the 169.254/16 prefix that is valid for
+ * Link-Local communication on that interface. This is especially valuable in
+ * environments where no other configuration mechanism is available. Refer to
+ * the following RFCs for complete details:
+ * - RFC 3927: Dynamic Configuration of IPv4 Link-Local Addresses
+ * - RFC 5227: IPv4 Address Conflict Detection
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL AUTO_IP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "core/ethernet.h"
+#include "ipv4/arp.h"
+#include "ipv4/auto_ip.h"
+#include "mdns/mdns_responder.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV4_SUPPORT == ENABLED && AUTO_IP_SUPPORT == ENABLED)
+
+//Tick counter to handle periodic operations
+systime_t autoIpTickCounter;
+
+
+/**
+ * @brief Initialize settings with default values
+ * @param[out] settings Structure that contains Auto-IP settings
+ **/
+
+void autoIpGetDefaultSettings(AutoIpSettings *settings)
+{
+   //Use default interface
+   settings->interface = netGetDefaultInterface();
+
+   //Initial link-local address to be used
+   settings->linkLocalAddr = IPV4_UNSPECIFIED_ADDR;
+   //Link state change event
+   settings->linkChangeEvent = NULL;
+   //FSM state change event
+   settings->stateChangeEvent = NULL;
+}
+
+
+/**
+ * @brief Auto-IP initialization
+ * @param[in] context Pointer to the Auto-IP context
+ * @param[in] settings Auto-IP specific settings
+ * @return Error code
+ **/
+
+error_t autoIpInit(AutoIpContext *context, const AutoIpSettings *settings)
+{
+   NetInterface *interface;
+
+   //Debug message
+   TRACE_INFO("Initializing Auto-IP...\r\n");
+
+   //Ensure the parameters are valid
+   if(context == NULL || settings == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //A valid pointer to the interface being configured is required
+   if(settings->interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Point to the underlying network interface
+   interface = settings->interface;
+
+   //Clear the Auto-IP context
+   memset(context, 0, sizeof(AutoIpContext));
+   //Save user settings
+   context->settings = *settings;
+
+   //Use default link-local address
+   context->linkLocalAddr = settings->linkLocalAddr;
+   //Reset conflict counter
+   context->conflictCount = 0;
+
+   //Auto-IP operation is currently suspended
+   context->running = FALSE;
+   //Initialize state machine
+   context->state = AUTO_IP_STATE_INIT;
+
+   //Attach the Auto-IP context to the network interface
+   interface->autoIpContext = context;
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Start Auto-IP process
+ * @param[in] context Pointer to the Auto-IP context
+ * @return Error code
+ **/
+
+error_t autoIpStart(AutoIpContext *context)
+{
+   //Check parameter
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Starting Auto-IP...\r\n");
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Start Auto-IP operation
+   context->running = TRUE;
+   //Initialize state machine
+   context->state = AUTO_IP_STATE_INIT;
+   //Reset conflict counter
+   context->conflictCount = 0;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Stop Auto-IP process
+ * @param[in] context Pointer to the Auto-IP context
+ * @return Error code
+ **/
+
+error_t autoIpStop(AutoIpContext *context)
+{
+   //Check parameter
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Stopping Auto-IP...\r\n");
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Suspend Auto-IP operation
+   context->running = FALSE;
+   //Reinitialize state machine
+   context->state = AUTO_IP_STATE_INIT;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Retrieve current state
+ * @param[in] context Pointer to the Auto-IP context
+ * @return Current Auto-IP state
+ **/
+
+AutoIpState autoIpGetState(AutoIpContext *context)
+{
+   AutoIpState state;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Get current state
+   state = context->state;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return current state
+   return state;
+}
+
+
+/**
+ * @brief Auto-IP timer handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * manage Auto-IP operation
+ *
+ * @param[in] context Pointer to the Auto-IP context
+ **/
+
+void autoIpTick(AutoIpContext *context)
+{
+   systime_t time;
+   systime_t delay;
+   NetInterface *interface;
+
+   //Make sure Auto-IP has been properly instantiated
+   if(context == NULL)
+      return;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check current state
+   if(context->state == AUTO_IP_STATE_INIT)
+   {
+      //Wait for the link to be up before starting Auto-IP
+      if(context->running && interface->linkState)
+      {
+         //Configure subnet mask
+         interface->ipv4Context.subnetMask = AUTO_IP_MASK;
+
+         //The address must be in the range from 169.54.1.0 to 169.254.254.255
+         if(ntohl(context->linkLocalAddr) < ntohl(AUTO_IP_ADDR_MIN) ||
+            ntohl(context->linkLocalAddr) > ntohl(AUTO_IP_ADDR_MAX))
+         {
+            //Generate a random link-local address
+            autoIpGenerateAddr(&context->linkLocalAddr);
+         }
+
+         //Use the link-local address as a tentative address
+         interface->ipv4Context.addr = context->linkLocalAddr;
+         interface->ipv4Context.addrState = IPV4_ADDR_STATE_TENTATIVE;
+
+         //Clear conflict flag
+         interface->ipv4Context.addrConflict = FALSE;
+
+         //Initial random delay
+         delay = netGetRandRange(0, AUTO_IP_PROBE_WAIT);
+
+         //The number of conflicts exceeds the maximum acceptable value?
+         if(context->conflictCount >= AUTO_IP_MAX_CONFLICTS)
+         {
+            //The host must limit the rate at which it probes for new addresses
+            delay += AUTO_IP_RATE_LIMIT_INTERVAL;
+         }
+
+         //Verify the uniqueness of the link-local address
+         autoIpChangeState(context, AUTO_IP_STATE_PROBING, delay);
+      }
+   }
+   else if(context->state == AUTO_IP_STATE_PROBING)
+   {
+      //Any conflict detected?
+      if(interface->ipv4Context.addrConflict)
+      {
+         //The address is already in use by some other host and
+         //must not be assigned to the interface
+         interface->ipv4Context.addr = IPV4_UNSPECIFIED_ADDR;
+         interface->ipv4Context.addrState = IPV4_ADDR_STATE_INVALID;
+
+         //The host should maintain a counter of the number of address
+         //conflicts it has experienced
+         context->conflictCount++;
+
+         //The host must pick a new random address...
+         autoIpGenerateAddr(&context->linkLocalAddr);
+         //...and repeat the process
+         autoIpChangeState(context, AUTO_IP_STATE_INIT, 0);
+      }
+      else
+      {
+         //Check current time
+         if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+         {
+            //Address Conflict Detection is on-going?
+            if(context->retransmitCount < AUTO_IP_PROBE_NUM)
+            {
+               //Conflict detection is done using ARP probes
+               arpSendProbe(interface, context->linkLocalAddr);
+
+               //Save the time at which the packet was sent
+               context->timestamp = time;
+               //Increment retransmission counter
+               context->retransmitCount++;
+
+               //Last probe packet sent?
+               if(context->retransmitCount == AUTO_IP_PROBE_NUM)
+               {
+                  //Delay before announcing
+                  context->timeout = AUTO_IP_ANNOUNCE_WAIT;
+               }
+               else
+               {
+                  //Maximum delay till repeated probe
+                  context->timeout = netGetRandRange(AUTO_IP_PROBE_MIN,
+                     AUTO_IP_PROBE_MAX);
+               }
+            }
+            else
+            {
+               //The use of the IPv4 address is now unrestricted
+               interface->ipv4Context.addrState = IPV4_ADDR_STATE_VALID;
+
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+               //Restart mDNS probing process
+               mdnsResponderStartProbing(interface->mdnsResponderContext);
+#endif
+               //The host must then announce its claimed address
+               autoIpChangeState(context, AUTO_IP_STATE_ANNOUNCING, 0);
+            }
+         }
+      }
+   }
+   else if(context->state == AUTO_IP_STATE_ANNOUNCING)
+   {
+      //Check current time
+      if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+      {
+         //An ARP announcement is identical to an ARP probe, except that
+         //now the sender and target IP addresses are both set to the
+         //host's newly selected IPv4 address
+         arpSendRequest(interface, context->linkLocalAddr, &MAC_BROADCAST_ADDR);
+
+         //Save the time at which the packet was sent
+         context->timestamp = time;
+         //Time interval between announcement packets
+         context->timeout = AUTO_IP_ANNOUNCE_INTERVAL;
+         //Increment retransmission counter
+         context->retransmitCount++;
+
+         //Announcing is complete?
+         if(context->retransmitCount >= AUTO_IP_ANNOUNCE_NUM)
+         {
+            //Successful address autoconfiguration
+            autoIpChangeState(context, AUTO_IP_STATE_CONFIGURED, 0);
+            //Reset conflict counter
+            context->conflictCount = 0;
+
+            //Dump current IPv4 configuration for debugging purpose
+            autoIpDumpConfig(context);
+         }
+      }
+   }
+   else if(context->state == AUTO_IP_STATE_CONFIGURED)
+   {
+      //Address Conflict Detection is an ongoing process that is in effect
+      //for as long as a host is using an IPv4 link-local address
+      if(interface->ipv4Context.addrConflict)
+      {
+         //The host may elect to attempt to defend its address by recording
+         //the time that the conflicting ARP packet was received, and then
+         //broadcasting one single ARP announcement, giving its own IP and
+         //hardware addresses as the sender addresses of the ARP
+#if (AUTO_IP_BCT_SUPPORT == ENABLED)
+         arpSendProbe(interface, context->linkLocalAddr);
+#else
+         arpSendRequest(interface, context->linkLocalAddr, &MAC_BROADCAST_ADDR);
+#endif
+         //Clear conflict flag
+         interface->ipv4Context.addrConflict = FALSE;
+
+         //The host can then continue to use the address normally without
+         //any further special action
+         autoIpChangeState(context, AUTO_IP_STATE_DEFENDING, 0);
+      }
+   }
+   else if(context->state == AUTO_IP_STATE_DEFENDING)
+   {
+      //if this is not the first conflicting ARP packet the host has seen, and
+      //the time recorded for the previous conflicting ARP packet is recent,
+      //within DEFEND_INTERVAL seconds, then the host must immediately cease
+      //using this address
+      if(interface->ipv4Context.addrConflict)
+      {
+         //The link-local address cannot be used anymore
+         interface->ipv4Context.addr = IPV4_UNSPECIFIED_ADDR;
+         interface->ipv4Context.addrState = IPV4_ADDR_STATE_INVALID;
+
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+         //Restart mDNS probing process
+         mdnsResponderStartProbing(interface->mdnsResponderContext);
+#endif
+         //The host must pick a new random address...
+         autoIpGenerateAddr(&context->linkLocalAddr);
+         //...and probes/announces again
+         autoIpChangeState(context, AUTO_IP_STATE_INIT, 0);
+      }
+      else
+      {
+         //Check whether the DEFEND_INTERVAL has elapsed
+         if(timeCompare(time, context->timestamp + AUTO_IP_DEFEND_INTERVAL) >= 0)
+         {
+            //The host can continue to use its link-local address
+            autoIpChangeState(context, AUTO_IP_STATE_CONFIGURED, 0);
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Callback function for link change event
+ * @param[in] context Pointer to the Auto-IP context
+ **/
+
+void autoIpLinkChangeEvent(AutoIpContext *context)
+{
+   NetInterface *interface;
+
+   //Make sure Auto-IP has been properly instantiated
+   if(context == NULL)
+      return;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Check whether Auto-IP is enabled
+   if(context->running)
+   {
+      //The host address is not longer valid
+      interface->ipv4Context.addr = IPV4_UNSPECIFIED_ADDR;
+      interface->ipv4Context.addrState = IPV4_ADDR_STATE_INVALID;
+
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+      //Restart mDNS probing process
+      mdnsResponderStartProbing(interface->mdnsResponderContext);
+#endif
+
+      //Clear subnet mask
+      interface->ipv4Context.subnetMask = IPV4_UNSPECIFIED_ADDR;
+   }
+
+   //Reinitialize state machine
+   context->state = AUTO_IP_STATE_INIT;
+   //Reset conflict counter
+   context->conflictCount = 0;
+
+   //Any registered callback?
+   if(context->settings.linkChangeEvent != NULL)
+   {
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+      //Invoke user callback function
+      context->settings.linkChangeEvent(context, interface, interface->linkState);
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+   }
+}
+
+
+/**
+ * @brief Update Auto-IP FSM state
+ * @param[in] context Pointer to the Auto-IP context
+ * @param[in] newState New Auto-IP state to switch to
+ * @param[in] delay Initial delay
+ **/
+
+void autoIpChangeState(AutoIpContext *context,
+   AutoIpState newState, systime_t delay)
+{
+   NetInterface *interface;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Set time stamp
+   context->timestamp = osGetSystemTime();
+   //Set initial delay
+   context->timeout = delay;
+   //Reset retransmission counter
+   context->retransmitCount = 0;
+   //Switch to the new state
+   context->state = newState;
+
+   //Any registered callback?
+   if(context->settings.stateChangeEvent != NULL)
+   {
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+      //Invoke user callback function
+      context->settings.stateChangeEvent(context, interface, newState);
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+   }
+}
+
+
+/**
+ * @brief Generate a random link-local address
+ * @param[out] ipAddr Random link-local address
+ **/
+
+void autoIpGenerateAddr(Ipv4Addr *ipAddr)
+{
+   uint32_t n;
+
+   //Generate a random address in the range from 169.254.1.0 to 169.254.254.255
+   n = netGetRand() % ntohl(AUTO_IP_ADDR_MAX - AUTO_IP_ADDR_MIN);
+   n += ntohl(AUTO_IP_ADDR_MIN);
+
+   //Convert the resulting address to network byte order
+   *ipAddr = htonl(n);
+}
+
+
+/**
+ * @brief Dump Auto-IP configuration for debugging purpose
+ * @param[in] context Pointer to the Auto-IP context
+ **/
+
+void autoIpDumpConfig(AutoIpContext *context)
+{
+#if (AUTO_IP_TRACE_LEVEL >= TRACE_LEVEL_INFO)
+   NetInterface *interface;
+   Ipv4Context *ipv4Context;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+   //Point to the IPv4 context
+   ipv4Context = &interface->ipv4Context;
+
+   //Debug message
+   TRACE_INFO("\r\n");
+   TRACE_INFO("Auto-IP configuration:\r\n");
+
+   //Link-local address
+   TRACE_INFO("  Link-local Address = %s\r\n",
+      ipv4AddrToString(ipv4Context->addr, NULL));
+
+   //Subnet mask
+   TRACE_INFO("  Subnet Mask = %s\r\n",
+      ipv4AddrToString(ipv4Context->subnetMask, NULL));
+
+   //Debug message
+   TRACE_INFO("\r\n");
+#endif
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv4/auto_ip.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,221 @@
+/**
+ * @file auto_ip.h
+ * @brief Auto-IP (Dynamic Configuration of IPv4 Link-Local Addresses)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _AUTO_IP_H
+#define _AUTO_IP_H
+
+//Dependencies
+#include "core/net.h"
+
+//Auto-IP support
+#ifndef AUTO_IP_SUPPORT
+   #define AUTO_IP_SUPPORT DISABLED
+#elif (AUTO_IP_SUPPORT != ENABLED && AUTO_IP_SUPPORT != DISABLED)
+   #error AUTO_IP_SUPPORT parameter is not valid
+#endif
+
+//Bonjour Conformance Test support
+#ifndef AUTO_IP_BCT_SUPPORT
+   #define AUTO_IP_BCT_SUPPORT DISABLED
+#elif (AUTO_IP_BCT_SUPPORT != ENABLED && AUTO_IP_BCT_SUPPORT != DISABLED)
+   #error AUTO_IP_BCT_SUPPORT parameter is not valid
+#endif
+
+//Auto-IP tick interval
+#ifndef AUTO_IP_TICK_INTERVAL
+   #define AUTO_IP_TICK_INTERVAL 200
+#elif (AUTO_IP_TICK_INTERVAL < 10)
+   #error AUTO_IP_TICK_INTERVAL parameter is not valid
+#endif
+
+//Initial random delay
+#ifndef AUTO_IP_PROBE_WAIT
+   #define AUTO_IP_PROBE_WAIT 1000
+#elif (AUTO_IP_PROBE_WAIT < 0)
+   #error AUTO_IP_PROBE_WAIT parameter is not valid
+#endif
+
+//Number of probe packets
+#ifndef AUTO_IP_PROBE_NUM
+   #define AUTO_IP_PROBE_NUM 3
+#elif (AUTO_IP_PROBE_NUM < 1)
+   #error AUTO_IP_PROBE_NUM parameter is not valid
+#endif
+
+//Minimum delay till repeated probe
+#ifndef AUTO_IP_PROBE_MIN
+   #define AUTO_IP_PROBE_MIN 1000
+#elif (AUTO_IP_PROBE_MIN < 100)
+   #error AUTO_IP_PROBE_MIN parameter is not valid
+#endif
+
+//Maximum delay till repeated probe
+#ifndef AUTO_IP_PROBE_MAX
+   #define AUTO_IP_PROBE_MAX 2000
+#elif (AUTO_IP_PROBE_MAX < AUTO_IP_PROBE_MIN)
+   #error AUTO_IP_PROBE_MAX parameter is not valid
+#endif
+
+//Delay before announcing
+#ifndef AUTO_IP_ANNOUNCE_WAIT
+   #define AUTO_IP_ANNOUNCE_WAIT 2000
+#elif (AUTO_IP_ANNOUNCE_WAIT < 100)
+   #error AUTO_IP_ANNOUNCE_WAIT parameter is not valid
+#endif
+
+//Number of announcement packets
+#ifndef AUTO_IP_ANNOUNCE_NUM
+   #define AUTO_IP_ANNOUNCE_NUM 2
+#elif (AUTO_IP_ANNOUNCE_NUM < 1)
+   #error AUTO_IP_ANNOUNCE_NUM parameter is not valid
+#endif
+
+//Time between announcement packets
+#ifndef AUTO_IP_ANNOUNCE_INTERVAL
+   #define AUTO_IP_ANNOUNCE_INTERVAL 2000
+#elif (AUTO_IP_ANNOUNCE_INTERVAL < 100)
+   #error AUTO_IP_ANNOUNCE_INTERVAL parameter is not valid
+#endif
+
+//Max conflicts before rate limiting
+#ifndef AUTO_IP_MAX_CONFLICTS
+   #define AUTO_IP_MAX_CONFLICTS 10
+#elif (AUTO_IP_MAX_CONFLICTS < 1)
+   #error AUTO_IP_MAX_CONFLICTS parameter is not valid
+#endif
+
+//Delay between successive attempts
+#ifndef AUTO_IP_RATE_LIMIT_INTERVAL
+   #define AUTO_IP_RATE_LIMIT_INTERVAL 60000
+#elif (AUTO_IP_RATE_LIMIT_INTERVAL < 1000)
+   #error AUTO_IP_RATE_LIMIT_INTERVAL parameter is not valid
+#endif
+
+//Minimum interval between defensive
+#ifndef AUTO_IP_DEFEND_INTERVAL
+   #define AUTO_IP_DEFEND_INTERVAL 10000
+#elif (AUTO_IP_DEFEND_INTERVAL < 1000)
+   #error AUTO_IP_DEFEND_INTERVAL parameter is not valid
+#endif
+
+//Auto-IP address prefix
+#define AUTO_IP_PREFIX IPV4_ADDR(169, 254, 0, 0)
+//Auto-IP subnet mask
+#define AUTO_IP_MASK IPV4_ADDR(255, 255, 0, 0)
+
+//Auto-IP address range
+#define AUTO_IP_ADDR_MIN IPV4_ADDR(169, 254, 1, 0)
+#define AUTO_IP_ADDR_MAX IPV4_ADDR(169, 254, 254, 255)
+
+//Forward declaration of AutoIpContext structure
+struct _AutoIpContext;
+#define AutoIpContext struct _AutoIpContext
+
+
+/**
+ * @brief Auto-IP FSM states
+ **/
+
+typedef enum
+{
+   AUTO_IP_STATE_INIT,
+   AUTO_IP_STATE_PROBING,
+   AUTO_IP_STATE_ANNOUNCING,
+   AUTO_IP_STATE_CONFIGURED,
+   AUTO_IP_STATE_DEFENDING
+} AutoIpState;
+
+
+/**
+ * @brief Link state change callback
+ **/
+
+typedef void (*AutoIpLinkChangeCallback)(AutoIpContext *context,
+   NetInterface *interface, bool_t linkState);
+
+
+/**
+ * @brief FSM state change callback
+ **/
+
+typedef void (*AutoIpStateChangeCallback)(AutoIpContext *context,
+   NetInterface *interface, AutoIpState state);
+
+
+/**
+ * @brief Auto-IP settings
+ **/
+
+typedef struct
+{
+   NetInterface *interface;                    ///<Network interface to configure
+   Ipv4Addr linkLocalAddr;                     ///<Initial link-local address to be used
+   AutoIpLinkChangeCallback linkChangeEvent;   ///<Link state change event
+   AutoIpStateChangeCallback stateChangeEvent; ///<FSM state change event
+} AutoIpSettings;
+
+
+/**
+ * @brief Auto-IP context
+ **/
+
+struct _AutoIpContext
+{
+   AutoIpSettings settings; ///<Auto-IP settings
+   bool_t running;          ///<Auto-IP is currently running
+   AutoIpState state;       ///<Current state of the FSM
+   Ipv4Addr linkLocalAddr;  ///<Link-local address
+   systime_t timestamp;     ///<Timestamp to manage retransmissions
+   systime_t timeout;       ///<Timeout value
+   uint_t retransmitCount;  ///<Retransmission counter
+   uint_t conflictCount;    ///<Number of conflicts
+};
+
+
+//Tick counter to handle periodic operations
+extern systime_t autoIpTickCounter;
+
+//Auto-IP related functions
+void autoIpGetDefaultSettings(AutoIpSettings *settings);
+error_t autoIpInit(AutoIpContext *context, const AutoIpSettings *settings);
+error_t autoIpStart(AutoIpContext *context);
+error_t autoIpStop(AutoIpContext *context);
+AutoIpState autoIpGetState(AutoIpContext *context);
+
+void autoIpTick(AutoIpContext *context);
+void autoIpLinkChangeEvent(AutoIpContext *context);
+
+void autoIpChangeState(AutoIpContext *context,
+   AutoIpState newState, systime_t delay);
+
+void autoIpGenerateAddr(Ipv4Addr *ipAddr);
+
+void autoIpDumpConfig(AutoIpContext *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv4/icmp.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,387 @@
+/**
+ * @file icmp.c
+ * @brief ICMP (Internet Control Message Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL ICMP_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "core/net.h"
+#include "core/ip.h"
+#include "ipv4/ipv4.h"
+#include "ipv4/icmp.h"
+#include "mibs/mib2_module.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV4_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Incoming ICMP message processing
+ * @param[in] interface Underlying network interface
+ * @param[in] srcIpAddr Source IPv4 address
+ * @param[in] buffer Multi-part buffer containing the incoming ICMP message
+ * @param[in] offset Offset to the first byte of the ICMP message
+ **/
+
+void icmpProcessMessage(NetInterface *interface,
+   Ipv4Addr srcIpAddr, const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+   IcmpHeader *header;
+
+   //Total number of ICMP messages which the entity received
+   MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpInMsgs, 1);
+
+   //Retrieve the length of the ICMP message
+   length = netBufferGetLength(buffer) - offset;
+
+   //Ensure the message length is correct
+   if(length < sizeof(IcmpHeader))
+   {
+      //Number of ICMP messages which the entity received but determined
+      //as having ICMP-specific errors
+      MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpInErrors, 1);
+      //Silently discard incoming message
+      return;
+   }
+
+   //Point to the ICMP message header
+   header = netBufferAt(buffer, offset);
+   //Sanity check
+   if(header == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("ICMP message received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message contents for debugging purpose
+   icmpDumpMessage(header);
+
+   //Verify checksum value
+   if(ipCalcChecksumEx(buffer, offset, length) != 0x0000)
+   {
+      //Debug message
+      TRACE_WARNING("Wrong ICMP header checksum!\r\n");
+      //Number of ICMP messages which the entity received but determined
+      //as having ICMP-specific errors
+      MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpInErrors, 1);
+      //Drop incoming message
+      return;
+   }
+
+   //Check the type of ICMP message
+   switch(header->type)
+   {
+   //Echo Request?
+   case ICMP_TYPE_ECHO_REQUEST:
+      //Process Echo Request message
+      icmpProcessEchoRequest(interface, srcIpAddr, buffer, offset);
+      break;
+   //Unknown type?
+   default:
+      //Debug message
+      TRACE_WARNING("Unknown ICMP message type!\r\n");
+      //Discard incoming ICMP message
+      break;
+   }
+}
+
+
+/**
+ * @brief Echo Request message processing
+ * @param[in] interface Underlying network interface
+ * @param[in] srcIpAddr Source IPv4 address
+ * @param[in] request Multi-part buffer containing the incoming Echo Request message
+ * @param[in] requestOffset Offset to the first byte of the Echo Request message
+ **/
+
+void icmpProcessEchoRequest(NetInterface *interface,
+   Ipv4Addr srcIpAddr, const NetBuffer *request, size_t requestOffset)
+{
+   error_t error;
+   size_t requestLength;
+   size_t replyOffset;
+   size_t replyLength;
+   NetBuffer *reply;
+   IcmpEchoMessage *requestHeader;
+   IcmpEchoMessage *replyHeader;
+   Ipv4PseudoHeader pseudoHeader;
+
+   //Number of ICMP Echo Request messages received
+   MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpInEchos, 1);
+
+   //Retrieve the length of the Echo Request message
+   requestLength = netBufferGetLength(request) - requestOffset;
+
+   //Ensure the packet length is correct
+   if(requestLength < sizeof(IcmpEchoMessage))
+      return;
+
+   //Point to the Echo Request header
+   requestHeader = netBufferAt(request, requestOffset);
+   //Sanity check
+   if(requestHeader == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("ICMP Echo Request message received (%" PRIuSIZE " bytes)...\r\n", requestLength);
+   //Dump message contents for debugging purpose
+   icmpDumpEchoMessage(requestHeader);
+
+   //Allocate memory to hold the Echo Reply message
+   reply = ipAllocBuffer(sizeof(IcmpEchoMessage), &replyOffset);
+   //Failed to allocate memory?
+   if(reply == NULL)
+      return;
+
+   //Point to the Echo Reply header
+   replyHeader = netBufferAt(reply, replyOffset);
+
+   //Format Echo Reply header
+   replyHeader->type = ICMP_TYPE_ECHO_REPLY;
+   replyHeader->code = 0;
+   replyHeader->checksum = 0;
+   replyHeader->identifier = requestHeader->identifier;
+   replyHeader->sequenceNumber = requestHeader->sequenceNumber;
+
+   //Point to the first data byte
+   requestOffset += sizeof(IcmpEchoMessage);
+   requestLength -= sizeof(IcmpEchoMessage);
+
+   //Copy data
+   error = netBufferConcat(reply, request, requestOffset, requestLength);
+
+   //Check status code
+   if(!error)
+   {
+      //Get the length of the resulting message
+      replyLength = netBufferGetLength(reply) - replyOffset;
+      //Calculate ICMP header checksum
+      replyHeader->checksum = ipCalcChecksumEx(reply, replyOffset, replyLength);
+
+      //Format IPv4 pseudo header
+      pseudoHeader.srcAddr = interface->ipv4Context.addr;
+      pseudoHeader.destAddr = srcIpAddr;
+      pseudoHeader.reserved = 0;
+      pseudoHeader.protocol = IPV4_PROTOCOL_ICMP;
+      pseudoHeader.length = htons(replyLength);
+
+      //Total number of ICMP messages which this entity attempted to send
+      MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpOutMsgs, 1);
+      //Number of ICMP Echo Reply messages sent
+      MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpOutEchoReps, 1);
+
+      //Debug message
+      TRACE_INFO("Sending ICMP Echo Reply message (%" PRIuSIZE " bytes)...\r\n", replyLength);
+      //Dump message contents for debugging purpose
+      icmpDumpEchoMessage(replyHeader);
+
+      //Send Echo Reply message
+      ipv4SendDatagram(interface, &pseudoHeader, reply, replyOffset, IPV4_DEFAULT_TTL);
+   }
+
+   //Free previously allocated memory block
+   netBufferFree(reply);
+}
+
+
+/**
+ * @brief Send an ICMP Error message
+ * @param[in] interface Underlying network interface
+ * @param[in] type Message type
+ * @param[in] code Specific message code
+ * @param[in] parameter Specific message parameter
+ * @param[in] ipPacket Multi-part buffer that holds the invoking IPv4 packet
+ * @param[in] ipPacketOffset Offset to the first byte of the IPv4 packet
+ * @return Error code
+ **/
+
+error_t icmpSendErrorMessage(NetInterface *interface, uint8_t type, uint8_t code,
+   uint8_t parameter, const NetBuffer *ipPacket, size_t ipPacketOffset)
+{
+   error_t error;
+   size_t offset;
+   size_t length;
+   Ipv4Header *ipHeader;
+   NetBuffer *icmpMessage;
+   IcmpErrorMessage *icmpHeader;
+   Ipv4PseudoHeader pseudoHeader;
+
+   //Retrieve the length of the invoking IPv4 packet
+   length = netBufferGetLength(ipPacket) - ipPacketOffset;
+
+   //Check the length of the IPv4 packet
+   if(length < sizeof(Ipv4Header))
+      return ERROR_INVALID_LENGTH;
+
+   //Point to the header of the invoking packet
+   ipHeader = netBufferAt(ipPacket, ipPacketOffset);
+   //Sanity check
+   if(ipHeader == NULL)
+      return ERROR_FAILURE;
+
+   //Never respond to a packet destined to a broadcast or a multicast address
+   if(ipv4IsBroadcastAddr(interface, ipHeader->destAddr) ||
+      ipv4IsMulticastAddr(ipHeader->destAddr))
+   {
+      //Report an error
+      return ERROR_INVALID_ADDRESS;
+   }
+
+   //Length of the data that will be returned along with the ICMP header
+   length = MIN(length, (size_t) ipHeader->headerLength * 4 + 8);
+
+   //Allocate a memory buffer to hold the ICMP message
+   icmpMessage = ipAllocBuffer(sizeof(IcmpErrorMessage), &offset);
+   //Failed to allocate memory?
+   if(icmpMessage == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the ICMP header
+   icmpHeader = netBufferAt(icmpMessage, offset);
+
+   //Format ICMP message
+   icmpHeader->type = type;
+   icmpHeader->code = code;
+   icmpHeader->checksum = 0;
+   icmpHeader->parameter = parameter;
+   icmpHeader->unused = 0;
+
+   //Copy the IP header and the first 8 bytes of the original datagram data
+   error = netBufferConcat(icmpMessage, ipPacket, ipPacketOffset, length);
+
+   //Check status code
+   if(!error)
+   {
+      //Get the length of the resulting message
+      length = netBufferGetLength(icmpMessage) - offset;
+      //Message checksum calculation
+      icmpHeader->checksum = ipCalcChecksumEx(icmpMessage, offset, length);
+
+      //Format IPv4 pseudo header
+      pseudoHeader.srcAddr = ipHeader->destAddr;
+      pseudoHeader.destAddr = ipHeader->srcAddr;
+      pseudoHeader.reserved = 0;
+      pseudoHeader.protocol = IPV4_PROTOCOL_ICMP;
+      pseudoHeader.length = htons(length);
+
+      //Total number of ICMP messages which this entity attempted to send
+      MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpOutMsgs, 1);
+
+      //Check ICMP message type
+      if(code == ICMP_TYPE_DEST_UNREACHABLE)
+      {
+         //Number of ICMP Destination Unreachable messages sent
+         MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpOutDestUnreachs, 1);
+      }
+      else if(code == ICMP_TYPE_SOURCE_QUENCH)
+      {
+         //Number of ICMP Source Quench messages sent
+         MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpOutSrcQuenchs, 1);
+      }
+      else if(code == ICMP_TYPE_REDIRECT)
+      {
+         //Number of ICMP Redirect messages sent
+         MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpOutRedirects, 1);
+      }
+      else if(code == ICMP_TYPE_TIME_EXCEEDED)
+      {
+         //Number of ICMP Time Exceeded messages sent
+         MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpOutTimeExcds, 1);
+      }
+      else if(code == ICMP_TYPE_PARAM_PROBLEM)
+      {
+         //Number of ICMP Parameter Problem messages sent
+         MIB2_INC_COUNTER32(mib2Base.icmpGroup.icmpOutParmProbs, 1);
+      }
+
+      //Debug message
+      TRACE_INFO("Sending ICMP Error message (%" PRIuSIZE " bytes)...\r\n", length);
+      //Dump message contents for debugging purpose
+      icmpDumpErrorMessage(icmpHeader);
+
+      //Send ICMP Error message
+      error = ipv4SendDatagram(interface, &pseudoHeader,
+         icmpMessage, offset, IPV4_DEFAULT_TTL);
+   }
+
+   //Free previously allocated memory
+   netBufferFree(icmpMessage);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Dump ICMP message for debugging purpose
+ * @param[in] message Pointer to the ICMP message
+ **/
+
+void icmpDumpMessage(const IcmpHeader *message)
+{
+   //Dump ICMP message
+   TRACE_DEBUG("  Type = %" PRIu8 "\r\n", message->type);
+   TRACE_DEBUG("  Code = %" PRIu8 "\r\n", message->code);
+   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
+}
+
+
+/**
+ * @brief Dump ICMP Echo Request or Echo Reply message
+ * @param[in] message Pointer to the ICMP message
+ **/
+
+void icmpDumpEchoMessage(const IcmpEchoMessage *message)
+{
+   //Dump ICMP message
+   TRACE_DEBUG("  Type = %" PRIu8 "\r\n", message->type);
+   TRACE_DEBUG("  Code = %" PRIu8 "\r\n", message->code);
+   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
+   TRACE_DEBUG("  Identifier = 0x%04" PRIX16 "\r\n", ntohs(message->identifier));
+   TRACE_DEBUG("  Sequence Number = 0x%04" PRIX16 "\r\n", ntohs(message->sequenceNumber));
+}
+
+
+/**
+ * @brief Dump generic ICMP Error message
+ * @param[in] message Pointer to the ICMP message
+ **/
+
+void icmpDumpErrorMessage(const IcmpErrorMessage *message)
+{
+   //Dump ICMP message
+   TRACE_DEBUG("  Type = %" PRIu8 "\r\n", message->type);
+   TRACE_DEBUG("  Code = %" PRIu8 "\r\n", message->code);
+   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
+   TRACE_DEBUG("  Parameter = %" PRIu8 "\r\n", message->parameter);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv4/icmp.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,199 @@
+/**
+ * @file icmp.h
+ * @brief ICMP (Internet Control Message Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _ICMP_H
+#define _ICMP_H
+
+//Dependencies
+#include "core/net.h"
+
+
+/**
+ * @brief ICMP message type
+ *
+ * The type field indicates the type of the message. Its
+ * value determines the format of the remaining data
+ *
+ **/
+
+typedef enum
+{
+   ICMP_TYPE_ECHO_REPLY        = 0,
+   ICMP_TYPE_DEST_UNREACHABLE  = 3,
+   ICMP_TYPE_SOURCE_QUENCH     = 4,
+   ICMP_TYPE_REDIRECT          = 5,
+   ICMP_TYPE_ECHO_REQUEST      = 8,
+   ICMP_TYPE_TIME_EXCEEDED     = 11,
+   ICMP_TYPE_PARAM_PROBLEM     = 12,
+   ICMP_TYPE_TIMESTAMP_REQUEST = 13,
+   ICMP_TYPE_TIMESTAMP_REPLY   = 14,
+   ICMP_TYPE_INFO_REQUEST      = 15,
+   ICMP_TYPE_INFO_REPLY        = 16
+} IcmpType;
+
+
+/**
+ * @brief Destination Unreachable message codes
+ **/
+
+typedef enum
+{
+   ICMP_CODE_NET_UNREACHABLE        = 0,
+   ICMP_CODE_HOST_UNREACHABLE       = 1,
+   ICMP_CODE_PROTOCOL_UNREACHABLE   = 2,
+   ICMP_CODE_PORT_UNREACHABLE       = 3,
+   ICMP_CODE_FRAG_NEEDED_AND_DF_SET = 4,
+   ICMP_CODE_SOURCE_ROUTE_FAILED    = 5
+} IcmpDestUnreachableCode;
+
+
+/**
+ * @brief Time Exceeded message codes
+ **/
+
+typedef enum
+{
+   ICMP_CODE_TTL_EXCEEDED             = 0,
+   ICMP_CODE_REASSEMBLY_TIME_EXCEEDED = 1
+} IcmpTimeExceededCode;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief ICMP header
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;      //0
+   uint8_t code;      //1
+   uint16_t checksum; //2-3
+   uint8_t data[];    //4
+} __end_packed IcmpHeader;
+
+
+/**
+ * @brief ICMP Error message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;           //0
+   uint8_t code;           //1
+   uint16_t checksum;      //2-3
+   uint32_t parameter : 8; //4
+   uint32_t unused : 24;   //5-7
+   uint8_t data[];         //8
+} __end_packed IcmpErrorMessage;
+
+
+/**
+ * @brief ICMP Destination Unreachable message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;      //0
+   uint8_t code;      //1
+   uint16_t checksum; //2-3
+   uint32_t unused;   //4-7
+   uint8_t data[];    //8
+} __end_packed IcmpDestUnreachableMessage;
+
+
+/**
+ * @brief ICMP Time Exceeded message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;      //0
+   uint8_t code;      //1
+   uint16_t checksum; //2-3
+   uint32_t unused;   //4-7
+   uint8_t data[];    //8
+} __end_packed IcmpTimeExceededMessage;
+
+
+/**
+ * @brief ICMP Parameter Problem message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;         //0
+   uint8_t code;         //1
+   uint16_t checksum;    //2-3
+   uint32_t pointer : 8; //4
+   uint32_t unused : 24; //5-7
+   uint8_t data[];       //8
+} __end_packed IcmpParamProblemMessage;
+
+
+/**
+ * @brief ICMP Echo Request and Echo Reply messages
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;            //0
+   uint8_t code;            //1
+   uint16_t checksum;       //2-3
+   uint16_t identifier;     //4-5
+   uint16_t sequenceNumber; //6-7
+   uint8_t data[];          //8
+} __end_packed IcmpEchoMessage;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+//ICMP related functions
+void icmpProcessMessage(NetInterface *interface,
+   Ipv4Addr srcIpAddr, const NetBuffer *buffer, size_t offset);
+
+void icmpProcessEchoRequest(NetInterface *interface,
+   Ipv4Addr srcIpAddr, const NetBuffer *request, size_t requestOffset);
+
+error_t icmpSendErrorMessage(NetInterface *interface, uint8_t type, uint8_t code,
+   uint8_t parameter, const NetBuffer *ipPacket, size_t ipPacketOffset);
+
+void icmpDumpMessage(const IcmpHeader *message);
+void icmpDumpEchoMessage(const IcmpEchoMessage *message);
+void icmpDumpErrorMessage(const IcmpErrorMessage *message);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv4/igmp.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,633 @@
+/**
+ * @file igmp.c
+ * @brief IGMP (Internet Group Management Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * IGMP is used by IP hosts to report their multicast group memberships
+ * to routers. Refer to the following RFCs for complete details:
+ * - RFC 1112: Host Extensions for IP Multicasting
+ * - RFC 2236: Internet Group Management Protocol, Version 2
+ * - RFC 3376: Internet Group Management Protocol, Version 3
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL IGMP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "core/ip.h"
+#include "ipv4/ipv4.h"
+#include "ipv4/igmp.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV4_SUPPORT == ENABLED && IGMP_SUPPORT == ENABLED)
+
+//Tick counter to handle periodic operations
+systime_t igmpTickCounter;
+
+
+/**
+ * @brief IGMP initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t igmpInit(NetInterface *interface)
+{
+   //The default host compatibility mode is IGMPv2
+   interface->igmpv1RouterPresent = FALSE;
+
+   //Start IGMPv1 router present timer
+   interface->igmpv1RouterPresentTimer =
+      osGetSystemTime() + IGMP_V1_ROUTER_PRESENT_TIMEOUT;
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Join the specified host group
+ * @param[in] interface Underlying network interface
+ * @param[in] entry IPv4 filter entry identifying the host group to join
+ * @return Error code
+ **/
+
+error_t igmpJoinGroup(NetInterface *interface, Ipv4FilterEntry *entry)
+{
+   //The all-systems group (address 224.0.0.1) is handled as a special
+   //case. The host starts in Idle Member state for that group on every
+   //interface and never transitions to another state
+   if(entry->addr == IGMP_ALL_SYSTEMS_ADDR)
+   {
+      //Clear flag
+      entry->flag = FALSE;
+      //Enter the Idle Member state
+      entry->state = IGMP_STATE_IDLE_MEMBER;
+   }
+   else
+   {
+      //Link is up?
+      if(interface->linkState)
+      {
+         //When a host joins a multicast group, it should immediately transmit
+         //an unsolicited Membership Report for that group
+         igmpSendReportMessage(interface, entry->addr);
+
+         //Set flag
+         entry->flag = TRUE;
+         //Start timer
+         entry->timer = osGetSystemTime() + IGMP_UNSOLICITED_REPORT_INTERVAL;
+         //Enter the Delaying Member state
+         entry->state = IGMP_STATE_DELAYING_MEMBER;
+      }
+      //Link is down?
+      else
+      {
+         //Clear flag
+         entry->flag = FALSE;
+         //Enter the Idle Member state
+         entry->state = IGMP_STATE_IDLE_MEMBER;
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Leave the specified host group
+ * @param[in] interface Underlying network interface
+ * @param[in] entry IPv4 filter entry identifying the host group to leave
+ * @return Error code
+ **/
+
+error_t igmpLeaveGroup(NetInterface *interface, Ipv4FilterEntry *entry)
+{
+   //Check link state
+   if(interface->linkState)
+   {
+      //Send a Leave Group message if the flag is set
+      if(entry->flag)
+         igmpSendLeaveGroupMessage(interface, entry->addr);
+   }
+
+   //Switch to the Non-Member state
+   entry->state = IGMP_STATE_NON_MEMBER;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief IGMP timer handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * handle IGMP related timers
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void igmpTick(NetInterface *interface)
+{
+   uint_t i;
+   systime_t time;
+   Ipv4FilterEntry *entry;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check IGMPv1 router present timer
+   if(timeCompare(time, interface->igmpv1RouterPresentTimer) >= 0)
+      interface->igmpv1RouterPresent = FALSE;
+
+   //Go through the multicast filter table
+   for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv4Context.multicastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Delaying Member state?
+         if(entry->state == IGMP_STATE_DELAYING_MEMBER)
+         {
+            //Timer expired?
+            if(timeCompare(time, entry->timer) >= 0)
+            {
+               //Send a Membership Report message for the group on the interface
+               igmpSendReportMessage(interface, entry->addr);
+
+               //Set flag
+               entry->flag = TRUE;
+               //Switch to the Idle Member state
+               entry->state = IGMP_STATE_IDLE_MEMBER;
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Callback function for link change event
+ * @param[in] interface Underlying network interface
+ **/
+
+void igmpLinkChangeEvent(NetInterface *interface)
+{
+   uint_t i;
+   systime_t time;
+   Ipv4FilterEntry *entry;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Link up event?
+   if(interface->linkState)
+   {
+      //The default host compatibility mode is IGMPv2
+      interface->igmpv1RouterPresent = FALSE;
+      //Start IGMPv1 router present timer
+      interface->igmpv1RouterPresentTimer = time + IGMP_V1_ROUTER_PRESENT_TIMEOUT;
+
+      //Go through the multicast filter table
+      for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
+      {
+         //Point to the current entry
+         entry = &interface->ipv4Context.multicastFilter[i];
+
+         //Valid entry?
+         if(entry->refCount > 0)
+         {
+            //The all-systems group (address 224.0.0.1) is handled as a special
+            //case. The host starts in Idle Member state for that group on every
+            //interface and never transitions to another state
+            if(entry->addr != IGMP_ALL_SYSTEMS_ADDR)
+            {
+               //Send an unsolicited Membership Report for that group
+               igmpSendReportMessage(interface, entry->addr);
+
+               //Set flag
+               entry->flag = TRUE;
+               //Start timer
+               entry->timer = time + IGMP_UNSOLICITED_REPORT_INTERVAL;
+               //Enter the Delaying Member state
+               entry->state = IGMP_STATE_DELAYING_MEMBER;
+            }
+         }
+      }
+   }
+   //Link down event?
+   else
+   {
+      //Go through the multicast filter table
+      for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
+      {
+         //Point to the current entry
+         entry = &interface->ipv4Context.multicastFilter[i];
+
+         //Valid entry?
+         if(entry->refCount > 0)
+         {
+            //Clear flag
+            entry->flag = FALSE;
+            //Enter the Idle Member state
+            entry->state = IGMP_STATE_IDLE_MEMBER;
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Process incoming IGMP message
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the incoming IGMP message
+ * @param[in] offset Offset to the first byte of the IGMP message
+ **/
+
+void igmpProcessMessage(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+   IgmpMessage *message;
+
+   //Retrieve the length of the IGMP message
+   length = netBufferGetLength(buffer) - offset;
+
+   //Ensure the message length is correct
+   if(length < sizeof(IgmpMessage))
+   {
+      //Debug message
+      TRACE_WARNING("IGMP message length is invalid!\r\n");
+      //Silently discard incoming message
+      return;
+   }
+
+   //Point to the beginning of the IGMP message
+   message = netBufferAt(buffer, offset);
+   //Sanity check
+   if(message == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("IGMP message received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message contents for debugging purpose
+   igmpDumpMessage(message);
+
+   //Verify checksum value
+   if(ipCalcChecksumEx(buffer, offset, length) != 0x0000)
+   {
+      //Debug message
+      TRACE_WARNING("Wrong IGMP header checksum!\r\n");
+      //Drop incoming message
+      return;
+   }
+
+   //Check the type field
+   switch(message->type)
+   {
+   //Membership Query message?
+   case IGMP_TYPE_MEMBERSHIP_QUERY:
+      //Process Membership Query message
+      igmpProcessQueryMessage(interface, message, length);
+      break;
+   //Membership Report message?
+   case IGMP_TYPE_MEMBERSHIP_REPORT_V1:
+   case IGMP_TYPE_MEMBERSHIP_REPORT_V2:
+      //Process Membership Query message
+      igmpProcessReportMessage(interface, message, length);
+      break;
+   //Unknown type?
+   default:
+      //Debug message
+      TRACE_WARNING("Unknown IGMP message type!\r\n");
+      //Discard incoming IGMP message
+      break;
+   }
+}
+
+
+/**
+ * @brief Process incoming Membership Query message
+ * @param[in] interface Underlying network interface
+ * @param[in] message Incoming Membership Query message
+ * @param[in] length Message length
+ **/
+
+void igmpProcessQueryMessage(NetInterface *interface,
+   const IgmpMessage *message, size_t length)
+{
+   uint_t i;
+   systime_t time;
+   systime_t maxRespTime;
+   Ipv4FilterEntry *entry;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //IGMPv1 Membership Query message?
+   if(message->maxRespTime == 0)
+   {
+      //The host receives a query with the Max Response Time field set to 0
+      interface->igmpv1RouterPresent = TRUE;
+      //Restart IGMPv1 router present timer
+      interface->igmpv1RouterPresentTimer = time + IGMP_V1_ROUTER_PRESENT_TIMEOUT;
+      //The maximum response time is 10 seconds by default
+      maxRespTime = IGMP_V1_MAX_RESPONSE_TIME;
+   }
+   //IGMPv2 Membership Query message?
+   else
+   {
+      //The Max Resp Time field specifies the maximum time allowed
+      //before sending a responding report
+      maxRespTime = message->maxRespTime * 10;
+   }
+
+   //Go through the multicast filter table
+   for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv4Context.multicastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //The all-systems group (224.0.0.1) is handled as a special case. The
+         //host starts in Idle Member state for that group on every interface
+         //and never transitions to another state
+         if(entry->addr != IGMP_ALL_SYSTEMS_ADDR)
+         {
+            //A General Query applies to all memberships on the interface from which
+            //the Query is received. A Group-Specific Query applies to membership
+            //in a single group on the interface from which the Query is received
+            if(message->groupAddr == IPV4_UNSPECIFIED_ADDR ||
+               message->groupAddr == entry->addr)
+            {
+               //Delaying Member state?
+               if(entry->state == IGMP_STATE_DELAYING_MEMBER)
+               {
+                  //The timer has not yet expired?
+                  if(timeCompare(time, entry->timer) < 0)
+                  {
+                     //If a timer for the group is already running, it is reset to
+                     //the random value only if the requested Max Response Time is
+                     //less than the remaining value of the running timer
+                     if(maxRespTime < (entry->timer - time))
+                     {
+                        //Restart delay timer
+                        entry->timer = time + igmpRand(maxRespTime);
+                     }
+                  }
+               }
+               //Idle Member state?
+               else if(entry->state == IGMP_STATE_IDLE_MEMBER)
+               {
+                  //Switch to the Delaying Member state
+                  entry->state = IGMP_STATE_DELAYING_MEMBER;
+                  //Delay the response by a random amount of time
+                  entry->timer = time + igmpRand(maxRespTime);
+               }
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Process incoming Membership Report message
+ * @param[in] interface Underlying network interface
+ * @param[in] message Incoming Membership Report message
+ * @param[in] length Message length
+ **/
+
+void igmpProcessReportMessage(NetInterface *interface,
+   const IgmpMessage *message, size_t length)
+{
+   uint_t i;
+   Ipv4FilterEntry *entry;
+
+   //Go through the multicast filter table
+   for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv4Context.multicastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Report messages are ignored for memberships in
+         //the Non-Member or Idle Member state
+         if(entry->state == IGMP_STATE_DELAYING_MEMBER)
+         {
+            //The Membership Report message matches the current entry?
+            if(message->groupAddr == entry->addr)
+            {
+               //Clear flag
+               entry->flag = FALSE;
+               //Switch to the Idle Member state
+               entry->state = IGMP_STATE_IDLE_MEMBER;
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Send Membership Report message
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr IPv4 address specifying the group address
+ * @return Error code
+ **/
+
+error_t igmpSendReportMessage(NetInterface *interface, Ipv4Addr ipAddr)
+{
+   error_t error;
+   size_t offset;
+   IgmpMessage *message;
+   NetBuffer *buffer;
+   Ipv4PseudoHeader pseudoHeader;
+
+   //Make sure the specified address is a valid multicast address
+   if(!ipv4IsMulticastAddr(ipAddr))
+      return ERROR_INVALID_ADDRESS;
+
+   //The all-systems group (224.0.0.1) is handled as a special case.
+   //The host never sends a report for that group
+   if(ipAddr == IGMP_ALL_SYSTEMS_ADDR)
+      return ERROR_INVALID_ADDRESS;
+
+   //Allocate a memory buffer to hold an IGMP message
+   buffer = ipAllocBuffer(sizeof(IgmpMessage), &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the IGMP message
+   message = netBufferAt(buffer, offset);
+
+   //The type of report is determined by the state of the interface
+   if(interface->igmpv1RouterPresent)
+      message->type = IGMP_TYPE_MEMBERSHIP_REPORT_V1;
+   else
+      message->type = IGMP_TYPE_MEMBERSHIP_REPORT_V2;
+
+   //Format the Membership Report message
+   message->maxRespTime = 0;
+   message->checksum = 0;
+   message->groupAddr = ipAddr;
+
+   //Message checksum calculation
+   message->checksum = ipCalcChecksumEx(buffer, offset, sizeof(IgmpMessage));
+
+   //Format IPv4 pseudo header
+   pseudoHeader.srcAddr = interface->ipv4Context.addr;
+   pseudoHeader.destAddr = ipAddr;
+   pseudoHeader.reserved = 0;
+   pseudoHeader.protocol = IPV4_PROTOCOL_IGMP;
+   pseudoHeader.length = HTONS(sizeof(IgmpMessage));
+
+   //Debug message
+   TRACE_INFO("Sending IGMP message (%" PRIuSIZE " bytes)...\r\n", sizeof(IgmpMessage));
+   //Dump message contents for debugging purpose
+   igmpDumpMessage(message);
+
+   //The Membership Report message is sent to the group being reported
+   error = ipv4SendDatagram(interface, &pseudoHeader, buffer, offset, IGMP_TTL);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send Leave Group message
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr IPv4 address specifying the group address being left
+ * @return Error code
+ **/
+
+error_t igmpSendLeaveGroupMessage(NetInterface *interface, Ipv4Addr ipAddr)
+{
+   error_t error;
+   size_t offset;
+   NetBuffer *buffer;
+   IgmpMessage *message;
+   Ipv4PseudoHeader pseudoHeader;
+
+   //Make sure the specified address is a valid multicast address
+   if(!ipv4IsMulticastAddr(ipAddr))
+      return ERROR_INVALID_ADDRESS;
+
+   //The all-systems group (224.0.0.1) is handled as a special case.
+   //The host never sends a Leave Group message for that group
+   if(ipAddr == IGMP_ALL_SYSTEMS_ADDR)
+      return ERROR_INVALID_ADDRESS;
+
+   //If the interface state says the querier is running
+   //IGMPv1, this action should be skipped
+   if(interface->igmpv1RouterPresent)
+      return NO_ERROR;
+
+   //Allocate a memory buffer to hold an IGMP message
+   buffer = ipAllocBuffer(sizeof(IgmpMessage), &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the IGMP message
+   message = netBufferAt(buffer, offset);
+
+   //Format the Leave Group message
+   message->type = IGMP_TYPE_LEAVE_GROUP;
+   message->maxRespTime = 0;
+   message->checksum = 0;
+   message->groupAddr = ipAddr;
+
+   //Message checksum calculation
+   message->checksum = ipCalcChecksumEx(buffer, offset, sizeof(IgmpMessage));
+
+   //Format IPv4 pseudo header
+   pseudoHeader.srcAddr = interface->ipv4Context.addr;
+   pseudoHeader.destAddr = IGMP_ALL_ROUTERS_ADDR;
+   pseudoHeader.reserved = 0;
+   pseudoHeader.protocol = IPV4_PROTOCOL_IGMP;
+   pseudoHeader.length = HTONS(sizeof(IgmpMessage));
+
+   //Debug message
+   TRACE_INFO("Sending IGMP message (%" PRIuSIZE " bytes)...\r\n", sizeof(IgmpMessage));
+   //Dump message contents for debugging purpose
+   igmpDumpMessage(message);
+
+   //The Leave Group message is sent to the all-routers multicast group
+   error = ipv4SendDatagram(interface, &pseudoHeader, buffer, offset, IGMP_TTL);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Get a random value in the specified range
+ * @param[in] max Upper bound
+ * @return Random value in the specified range
+ **/
+
+uint32_t igmpRand(uint32_t max)
+{
+   //Return a random value in the given range
+   return netGetRand() % (max + 1);
+}
+
+
+/**
+ * @brief Dump IGMP message for debugging purpose
+ * @param[in] message Pointer to the IGMP message
+ **/
+
+void igmpDumpMessage(const IgmpMessage *message)
+{
+   //Dump IGMP message
+   TRACE_DEBUG("  Type = 0x%02" PRIX8 "\r\n", message->type);
+   TRACE_DEBUG("  Max Resp Time = 0x%02" PRIX8 "\r\n", message->maxRespTime);
+   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
+   TRACE_DEBUG("  Group Address = %s\r\n", ipv4AddrToString(message->groupAddr, NULL));
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv4/igmp.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,158 @@
+/**
+ * @file igmp.h
+ * @brief IGMP (Internet Group Management Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _IGMP_H
+#define _IGMP_H
+
+//Dependencies
+#include "core/net.h"
+
+//IGMP support
+#ifndef IGMP_SUPPORT
+   #define IGMP_SUPPORT DISABLED
+#elif (IGMP_SUPPORT != ENABLED && IGMP_SUPPORT != DISABLED)
+   #error IGMP_SUPPORT parameter is not valid
+#endif
+
+//IGMP tick interval
+#ifndef IGMP_TICK_INTERVAL
+   #define IGMP_TICK_INTERVAL 1000
+#elif (IGMP_TICK_INTERVAL < 10)
+   #error IGMP_TICK_INTERVAL parameter is not valid
+#endif
+
+//Unsolicited report interval
+#ifndef IGMP_UNSOLICITED_REPORT_INTERVAL
+   #define IGMP_UNSOLICITED_REPORT_INTERVAL 10000
+#elif (IGMP_UNSOLICITED_REPORT_INTERVAL < 1000)
+   #error IGMP_UNSOLICITED_REPORT_INTERVAL parameter is not valid
+#endif
+
+//Maximum response time for IGMPv1 queries
+#ifndef IGMP_V1_MAX_RESPONSE_TIME
+   #define IGMP_V1_MAX_RESPONSE_TIME 10000
+#elif (IGMP_V1_MAX_RESPONSE_TIME < 1000)
+   #error IGMP_V1_MAX_RESPONSE_TIME parameter is not valid
+#endif
+
+//Older version querier present timeout
+#ifndef IGMP_V1_ROUTER_PRESENT_TIMEOUT
+   #define IGMP_V1_ROUTER_PRESENT_TIMEOUT 400000
+#elif (IGMP_V1_ROUTER_PRESENT_TIMEOUT < 1000)
+   #error IGMP_V1_ROUTER_PRESENT_TIMEOUT parameter is not valid
+#endif
+
+//TTL used by IGMP messages
+#define IGMP_TTL 1
+
+//All-Systems address
+#define IGMP_ALL_SYSTEMS_ADDR IPV4_ADDR(224, 0, 0, 1)
+//All-Routers address
+#define IGMP_ALL_ROUTERS_ADDR IPV4_ADDR(224, 0, 0, 2)
+
+
+/**
+ * @brief IGMP host states
+ **/
+
+typedef enum
+{
+   IGMP_STATE_NON_MEMBER      = 0,
+   IGMP_STATE_DELAYING_MEMBER = 1,
+   IGMP_STATE_IDLE_MEMBER     = 2
+} IgmpState;
+
+
+/**
+ * @brief IGMP message type
+ **/
+
+typedef enum
+{
+   IGMP_TYPE_MEMBERSHIP_QUERY     = 0x11,
+   IGMP_TYPE_MEMBERSHIP_REPORT_V1 = 0x12,
+   IGMP_TYPE_MEMBERSHIP_REPORT_V2 = 0x16,
+   IGMP_TYPE_LEAVE_GROUP          = 0x17,
+   IGMP_TYPE_MEMBERSHIP_REPORT_V3 = 0x22
+} IgmpType;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief General IGMP message format
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;        //0
+   uint8_t maxRespTime; //1
+   uint16_t checksum;   //2-3
+   Ipv4Addr groupAddr;  //4-7
+} __end_packed IgmpMessage;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+//Tick counter to handle periodic operations
+extern systime_t igmpTickCounter;
+
+//IGMP related functions
+error_t igmpInit(NetInterface *interface);
+error_t igmpJoinGroup(NetInterface *interface, Ipv4FilterEntry *entry);
+error_t igmpLeaveGroup(NetInterface *interface, Ipv4FilterEntry *entry);
+
+void igmpTick(NetInterface *interface);
+void igmpLinkChangeEvent(NetInterface *interface);
+
+void igmpProcessMessage(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+void igmpProcessQueryMessage(NetInterface *interface,
+   const IgmpMessage *message, size_t length);
+
+void igmpProcessReportMessage(NetInterface *interface,
+   const IgmpMessage *message, size_t length);
+
+error_t igmpSendReportMessage(NetInterface *interface, Ipv4Addr ipAddr);
+error_t igmpSendLeaveGroupMessage(NetInterface *interface, Ipv4Addr ipAddr);
+
+uint32_t igmpRand(uint32_t max);
+
+void igmpDumpMessage(const IgmpMessage *message);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv4/ipv4.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1576 @@
+/**
+ * @file ipv4.c
+ * @brief IPv4 (Internet Protocol Version 4)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The Internet Protocol (IP) provides the functions necessary to deliver a
+ * datagram from a source to a destination over an interconnected system of
+ * networks. Refer to RFC 791 for complete details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL IPV4_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include <ctype.h>
+#include "core/net.h"
+#include "core/ethernet.h"
+#include "core/ip.h"
+#include "core/udp.h"
+#include "core/tcp_fsm.h"
+#include "core/raw_socket.h"
+#include "ipv4/arp.h"
+#include "ipv4/ipv4.h"
+#include "ipv4/ipv4_routing.h"
+#include "ipv4/icmp.h"
+#include "ipv4/igmp.h"
+#include "ipv4/auto_ip.h"
+#include "dhcp/dhcp_client.h"
+#include "mdns/mdns_responder.h"
+#include "mibs/mib2_module.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV4_SUPPORT == ENABLED)
+
+
+/**
+ * @brief IPv4 related initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ipv4Init(NetInterface *interface)
+{
+   Ipv4Context *context;
+
+   //Point to the IPv4 context
+   context = &interface->ipv4Context;
+
+   //Clear the IPv4 context
+   memset(context, 0, sizeof(Ipv4Context));
+
+   //Initialize interface specific variables
+   context->linkMtu = interface->nicDriver->mtu;
+   context->isRouter = FALSE;
+
+   //Identification field is primarily used to identify
+   //fragments of an original IP datagram
+   context->identification = 0;
+
+   //Initialize the list of DNS servers
+   memset(context->dnsServerList, 0, sizeof(context->dnsServerList));
+   //Initialize the multicast filter table
+   memset(context->multicastFilter, 0, sizeof(context->multicastFilter));
+
+#if (IPV4_FRAG_SUPPORT == ENABLED)
+   //Initialize the reassembly queue
+   memset(context->fragQueue, 0, sizeof(context->fragQueue));
+#endif
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Assign host address
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] addr IPv4 host address
+ * @return Error code
+ **/
+
+error_t ipv4SetHostAddr(NetInterface *interface, Ipv4Addr addr)
+{
+   //Check parameters
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //The IPv4 address must be a valid unicast address
+   if(ipv4IsMulticastAddr(addr))
+      return ERROR_INVALID_ADDRESS;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Set up host address
+   interface->ipv4Context.addr = addr;
+   //Clear conflict flag
+   interface->ipv4Context.addrConflict = FALSE;
+
+   //Check whether the new host address is valid
+   if(addr != IPV4_UNSPECIFIED_ADDR)
+   {
+      //The use of the IPv4 address is now unrestricted
+      interface->ipv4Context.addrState = IPV4_ADDR_STATE_VALID;
+   }
+   else
+   {
+      //The IPv4 address is no longer valid
+      interface->ipv4Context.addrState = IPV4_ADDR_STATE_INVALID;
+   }
+
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+   //Restart mDNS probing process
+   mdnsResponderStartProbing(interface->mdnsResponderContext);
+#endif
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Retrieve host address
+ * @param[in] interface Pointer to the desired network interface
+ * @param[out] addr IPv4 host address
+ * @return Error code
+ **/
+
+error_t ipv4GetHostAddr(NetInterface *interface, Ipv4Addr *addr)
+{
+   //Check parameters
+   if(interface == NULL || addr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Check whether the host address is valid
+   if(interface->ipv4Context.addrState == IPV4_ADDR_STATE_VALID)
+   {
+      //Get IPv4 address
+      *addr = interface->ipv4Context.addr;
+   }
+   else
+   {
+      //Return the unspecified address when no address has been assigned
+      *addr = IPV4_UNSPECIFIED_ADDR;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Configure subnet mask
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] mask Subnet mask
+ * @return Error code
+ **/
+
+error_t ipv4SetSubnetMask(NetInterface *interface, Ipv4Addr mask)
+{
+   //Check parameters
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Set up subnet mask
+   interface->ipv4Context.subnetMask = mask;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Retrieve subnet mask
+ * @param[in] interface Pointer to the desired network interface
+ * @param[out] mask Subnet mask
+ * @return Error code
+ **/
+
+error_t ipv4GetSubnetMask(NetInterface *interface, Ipv4Addr *mask)
+{
+   //Check parameters
+   if(interface == NULL || mask == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Get subnet mask
+   *mask = interface->ipv4Context.subnetMask;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Configure default gateway
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] addr Default gateway address
+ * @return Error code
+ **/
+
+error_t ipv4SetDefaultGateway(NetInterface *interface, Ipv4Addr addr)
+{
+   //Check parameters
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //The IPv4 address must be a valid unicast address
+   if(ipv4IsMulticastAddr(addr))
+      return ERROR_INVALID_ADDRESS;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Set up default gateway address
+   interface->ipv4Context.defaultGateway = addr;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Retrieve default gateway
+ * @param[in] interface Pointer to the desired network interface
+ * @param[out] addr Default gateway address
+ * @return Error code
+ **/
+
+error_t ipv4GetDefaultGateway(NetInterface *interface, Ipv4Addr *addr)
+{
+   //Check parameters
+   if(interface == NULL || addr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Get default gateway address
+   *addr = interface->ipv4Context.defaultGateway;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Configure DNS server
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] index This parameter selects between the primary and secondary DNS server
+ * @param[in] addr DNS server address
+ * @return Error code
+ **/
+
+error_t ipv4SetDnsServer(NetInterface *interface, uint_t index, Ipv4Addr addr)
+{
+   //Check parameters
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure that the index is valid
+   if(index >= IPV4_DNS_SERVER_LIST_SIZE)
+      return ERROR_OUT_OF_RANGE;
+
+   //The IPv4 address must be a valid unicast address
+   if(ipv4IsMulticastAddr(addr))
+      return ERROR_INVALID_ADDRESS;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Set up DNS server address
+   interface->ipv4Context.dnsServerList[index] = addr;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Retrieve DNS server
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] index This parameter selects between the primary and secondary DNS server
+ * @param[out] addr DNS server address
+ * @return Error code
+ **/
+
+error_t ipv4GetDnsServer(NetInterface *interface, uint_t index, Ipv4Addr *addr)
+{
+   //Check parameters
+   if(interface == NULL || addr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure that the index is valid
+   if(index >= IPV4_DNS_SERVER_LIST_SIZE)
+   {
+      //Return the unspecified address when the index is out of range
+      *addr = IPV4_UNSPECIFIED_ADDR;
+      //Report an error
+      return ERROR_OUT_OF_RANGE;
+   }
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Get DNS server address
+   *addr = interface->ipv4Context.dnsServerList[index];
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Get IPv4 broadcast address
+ * @param[in] interface Pointer to the desired network interface
+ * @param[out] addr IPv4 broadcast address
+ **/
+
+error_t ipv4GetBroadcastAddr(NetInterface *interface, Ipv4Addr *addr)
+{
+   //Check parameters
+   if(interface == NULL || addr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //The broadcast address is obtained by performing a bitwise OR operation
+   //between the bit complement of the subnet mask and the host IP address
+   *addr = interface->ipv4Context.addr;
+   *addr |= ~interface->ipv4Context.subnetMask;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Callback function for link change event
+ * @param[in] interface Underlying network interface
+ **/
+
+void ipv4LinkChangeEvent(NetInterface *interface)
+{
+   Ipv4Context *context;
+
+   //Point to the IPv4 context
+   context = &interface->ipv4Context;
+
+   //Restore default MTU
+   context->linkMtu = interface->nicDriver->mtu;
+
+#if (ETH_SUPPORT == ENABLED)
+   //Flush ARP cache contents
+   arpFlushCache(interface);
+#endif
+
+#if (IPV4_FRAG_SUPPORT == ENABLED)
+   //Flush the reassembly queue
+   ipv4FlushFragQueue(interface);
+#endif
+
+#if (IGMP_SUPPORT == ENABLED)
+   //Notify IGMP of link state changes
+   igmpLinkChangeEvent(interface);
+#endif
+
+#if (AUTO_IP_SUPPORT == ENABLED)
+   //Notify Auto-IP of link state changes
+   autoIpLinkChangeEvent(interface->autoIpContext);
+#endif
+
+#if (DHCP_CLIENT_SUPPORT == ENABLED)
+   //Notify the DHCP client of link state changes
+   dhcpClientLinkChangeEvent(interface->dhcpClientContext);
+#endif
+}
+
+
+/**
+ * @brief Incoming IPv4 packet processing
+ * @param[in] interface Underlying network interface
+ * @param[in] packet Incoming IPv4 packet
+ * @param[in] length Packet length including header and payload
+ **/
+
+void ipv4ProcessPacket(NetInterface *interface, Ipv4Header *packet, size_t length)
+{
+   //Total number of input datagrams received from interfaces,
+   //including those received in error
+   MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInReceives, 1);
+
+   //Ensure the packet length is greater than 20 bytes
+   if(length < sizeof(Ipv4Header))
+   {
+      //Number of input datagrams discarded due to errors in their IP headers
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInHdrErrors, 1);
+      //Discard the received packet
+      return;
+   }
+
+   //Debug message
+   TRACE_INFO("IPv4 packet received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump IP header contents for debugging purpose
+   ipv4DumpHeader(packet);
+
+   //A packet whose version number is not 4 must be silently discarded
+   if(packet->version != IPV4_VERSION)
+   {
+      //Number of input datagrams discarded due to errors in their IP headers
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInHdrErrors, 1);
+      //Discard the received packet
+      return;
+   }
+
+   //Valid IPv4 header shall contains more than five 32-bit words
+   if(packet->headerLength < 5)
+   {
+      //Number of input datagrams discarded due to errors in their IP headers
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInHdrErrors, 1);
+      //Discard the received packet
+      return;
+   }
+
+   //Ensure the total length is correct before processing the packet
+   if(ntohs(packet->totalLength) < (packet->headerLength * 4))
+   {
+      //Number of input datagrams discarded due to errors in their IP headers
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInHdrErrors, 1);
+      //Discard the received packet
+      return;
+   }
+   if(ntohs(packet->totalLength) > length)
+   {
+      //Number of input datagrams discarded due to errors in their IP headers
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInHdrErrors, 1);
+      //Discard the received packet
+      return;
+   }
+
+   //Source address filtering
+   if(ipv4CheckSourceAddr(interface, packet->srcAddr))
+   {
+      //Number of input datagrams discarded due to errors in their IP headers
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInHdrErrors, 1);
+      //Discard the received packet
+      return;
+   }
+
+#if defined(IPV4_PACKET_FORWARD_HOOK)
+   IPV4_PACKET_FORWARD_HOOK(interface, packet, length);
+#else
+   //Destination address filtering
+   if(ipv4CheckDestAddr(interface, packet->destAddr))
+   {
+#if(IPV4_ROUTING_SUPPORT == ENABLED)
+      NetBuffer1 buffer;
+
+      //Unfragmented datagrams fit in a single chunk
+      buffer.chunkCount = 1;
+      buffer.maxChunkCount = 1;
+      buffer.chunk[0].address = packet;
+      buffer.chunk[0].length = length;
+
+      //Forward the packet according to the routing table
+      ipv4ForwardPacket(interface, (NetBuffer *) &buffer, 0);
+#else
+      //Number of input datagrams discarded because the destination IP
+      //address was not a valid address
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInAddrErrors, 1);
+#endif
+      //We are done
+      return;
+   }
+#endif
+
+   //Packets addressed to a tentative address should be silently discarded
+   if(ipv4IsTentativeAddr(interface, packet->destAddr))
+   {
+      //Number of input datagrams discarded because the destination IP
+      //address was not a valid address
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInAddrErrors, 1);
+      //Discard the received packet
+      return;
+   }
+
+   //The host must verify the IP header checksum on every received
+   //datagram and silently discard every datagram that has a bad
+   //checksum (see RFC 1122 3.2.1.2)
+   if(ipCalcChecksum(packet, packet->headerLength * 4) != 0x0000)
+   {
+      //Debug message
+      TRACE_WARNING("Wrong IP header checksum!\r\n");
+      //Number of input datagrams discarded due to errors in their IP headers
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInHdrErrors, 1);
+      //Discard incoming packet
+      return;
+   }
+
+   //Convert the total length from network byte order
+   length = ntohs(packet->totalLength);
+
+   //A fragmented packet was received?
+   if(ntohs(packet->fragmentOffset) & (IPV4_FLAG_MF | IPV4_OFFSET_MASK))
+   {
+#if (IPV4_FRAG_SUPPORT == ENABLED)
+      //Reassemble the original datagram
+      ipv4ReassembleDatagram(interface, packet, length);
+#endif
+   }
+   else
+   {
+      NetBuffer1 buffer;
+
+      //Unfragmented datagrams fit in a single chunk
+      buffer.chunkCount = 1;
+      buffer.maxChunkCount = 1;
+      buffer.chunk[0].address = packet;
+      buffer.chunk[0].length = length;
+
+      //Pass the IPv4 datagram to the higher protocol layer
+      ipv4ProcessDatagram(interface, (NetBuffer *) &buffer);
+   }
+}
+
+
+/**
+ * @brief Incoming IPv4 datagram processing
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer that holds the incoming IPv4 datagram
+ **/
+
+void ipv4ProcessDatagram(NetInterface *interface, const NetBuffer *buffer)
+{
+   error_t error;
+   size_t offset;
+   size_t length;
+   Ipv4Header *header;
+   IpPseudoHeader pseudoHeader;
+
+   //Retrieve the length of the IPv4 datagram
+   length = netBufferGetLength(buffer);
+
+   //Point to the IPv4 header
+   header = netBufferAt(buffer, 0);
+   //Sanity check
+   if(header == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("IPv4 datagram received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump IP header contents for debugging purpose
+   ipv4DumpHeader(header);
+
+   //Get the offset to the payload
+   offset = header->headerLength * 4;
+   //Compute the length of the payload
+   length -= header->headerLength * 4;
+
+   //Form the IPv4 pseudo header
+   pseudoHeader.length = sizeof(Ipv4PseudoHeader);
+   pseudoHeader.ipv4Data.srcAddr = header->srcAddr;
+   pseudoHeader.ipv4Data.destAddr = header->destAddr;
+   pseudoHeader.ipv4Data.reserved = 0;
+   pseudoHeader.ipv4Data.protocol = header->protocol;
+   pseudoHeader.ipv4Data.length = htons(length);
+
+#if defined(IPV4_DATAGRAM_FORWARD_HOOK)
+   IPV4_DATAGRAM_FORWARD_HOOK(interface, &pseudoHeader, buffer, offset);
+#endif
+
+   //Check the protocol field
+   switch(header->protocol)
+   {
+   //ICMP protocol?
+   case IPV4_PROTOCOL_ICMP:
+      //Process incoming ICMP message
+      icmpProcessMessage(interface, header->srcAddr, buffer, offset);
+#if (RAW_SOCKET_SUPPORT == ENABLED)
+      //Allow raw sockets to process ICMP messages
+      rawSocketProcessIpPacket(interface, &pseudoHeader, buffer, offset);
+#endif
+      //No error to report
+      error = NO_ERROR;
+      //Continue processing
+      break;
+
+#if (IGMP_SUPPORT == ENABLED)
+   //IGMP protocol?
+   case IPV4_PROTOCOL_IGMP:
+      //Process incoming IGMP message
+      igmpProcessMessage(interface, buffer, offset);
+#if (RAW_SOCKET_SUPPORT == ENABLED)
+      //Allow raw sockets to process IGMP messages
+      rawSocketProcessIpPacket(interface, &pseudoHeader, buffer, offset);
+#endif
+      //No error to report
+      error = NO_ERROR;
+      //Continue processing
+      break;
+#endif
+
+#if (TCP_SUPPORT == ENABLED)
+   //TCP protocol?
+   case IPV4_PROTOCOL_TCP:
+      //Process incoming TCP segment
+      tcpProcessSegment(interface, &pseudoHeader, buffer, offset);
+      //No error to report
+      error = NO_ERROR;
+      //Continue processing
+      break;
+#endif
+
+#if (UDP_SUPPORT == ENABLED)
+   //UDP protocol?
+   case IPV4_PROTOCOL_UDP:
+      //Process incoming UDP datagram
+      error = udpProcessDatagram(interface, &pseudoHeader, buffer, offset);
+      //Continue processing
+      break;
+#endif
+
+   //Unknown protocol?
+   default:
+#if (RAW_SOCKET_SUPPORT == ENABLED)
+      //Allow raw sockets to process IPv4 packets
+      error = rawSocketProcessIpPacket(interface, &pseudoHeader, buffer, offset);
+#else
+      //Report an error
+      error = ERROR_PROTOCOL_UNREACHABLE;
+#endif
+      //Continue processing
+      break;
+   }
+
+   //Unreachable protocol?
+   if(error == ERROR_PROTOCOL_UNREACHABLE)
+   {
+      //Number of locally-addressed datagrams received successfully but
+      //discarded because of an unknown or unsupported protocol
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInUnknownProtos, 1);
+
+      //Send a Destination Unreachable message
+      icmpSendErrorMessage(interface, ICMP_TYPE_DEST_UNREACHABLE,
+         ICMP_CODE_PROTOCOL_UNREACHABLE, 0, buffer, 0);
+   }
+   else
+   {
+      //Total number of input datagrams successfully delivered to IP
+      //user-protocols
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipInDelivers, 1);
+   }
+
+   //Unreachable port?
+   if(error == ERROR_PORT_UNREACHABLE)
+   {
+      //Send a Destination Unreachable message
+      icmpSendErrorMessage(interface, ICMP_TYPE_DEST_UNREACHABLE,
+         ICMP_CODE_PORT_UNREACHABLE, 0, buffer, 0);
+   }
+}
+
+
+/**
+ * @brief Send an IPv4 datagram
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IPv4 pseudo header
+ * @param[in] buffer Multi-part buffer containing the payload
+ * @param[in] offset Offset to the first byte of the payload
+ * @param[in] ttl TTL value. Default Time-To-Live is used when this parameter is zero
+ * @return Error code
+ **/
+
+error_t ipv4SendDatagram(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader,
+   NetBuffer *buffer, size_t offset, uint8_t ttl)
+{
+   error_t error;
+   size_t length;
+   uint16_t id;
+
+   //Total number of IP datagrams which local IP user-protocols supplied
+   //to IP in requests for transmission
+   MIB2_INC_COUNTER32(mib2Base.ipGroup.ipOutRequests, 1);
+
+   //Retrieve the length of payload
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check whether the TTL value is zero
+   if(ttl == 0)
+   {
+      //Use default Time-To-Live value
+      ttl = IPV4_DEFAULT_TTL;
+   }
+
+   //Identification field is primarily used to identify
+   //fragments of an original IP datagram
+   id = interface->ipv4Context.identification++;
+
+   //If the payload length is smaller than the network
+   //interface MTU then no fragmentation is needed
+   if((length + sizeof(Ipv4Header)) <= interface->ipv4Context.linkMtu)
+   {
+      //Send data as is
+      error = ipv4SendPacket(interface,
+         pseudoHeader, id, 0, buffer, offset, ttl);
+   }
+   //If the payload length exceeds the network interface MTU
+   //then the device must fragment the data
+   else
+   {
+#if (IPV4_FRAG_SUPPORT == ENABLED)
+      //Fragment IP datagram into smaller packets
+      error = ipv4FragmentDatagram(interface,
+         pseudoHeader, id, buffer, offset, ttl);
+#else
+      //Fragmentation is not supported
+      error = ERROR_MESSAGE_TOO_LONG;
+#endif
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send an IPv4 packet
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IPv4 pseudo header
+ * @param[in] fragId Fragment identification field
+ * @param[in] fragOffset Fragment offset field
+ * @param[in] buffer Multi-part buffer containing the payload
+ * @param[in] offset Offset to the first byte of the payload
+ * @param[in] ttl Time-To-Live value
+ * @return Error code
+ **/
+
+error_t ipv4SendPacket(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader,
+   uint16_t fragId, size_t fragOffset, NetBuffer *buffer, size_t offset, uint8_t ttl)
+{
+   error_t error;
+   size_t length;
+   Ipv4Header *packet;
+
+   //Is there enough space for the IPv4 header?
+   if(offset < sizeof(Ipv4Header))
+      return ERROR_INVALID_PARAMETER;
+
+   //Make room for the header
+   offset -= sizeof(Ipv4Header);
+   //Calculate the size of the entire packet, including header and data
+   length = netBufferGetLength(buffer) - offset;
+
+   //Point to the IPv4 header
+   packet = netBufferAt(buffer, offset);
+
+   //Format IPv4 header
+   packet->version = IPV4_VERSION;
+   packet->headerLength = 5;
+   packet->typeOfService = 0;
+   packet->totalLength = htons(length);
+   packet->identification = htons(fragId);
+   packet->fragmentOffset = htons(fragOffset);
+   packet->timeToLive = ttl;
+   packet->protocol = pseudoHeader->protocol;
+   packet->headerChecksum = 0;
+   packet->srcAddr = pseudoHeader->srcAddr;
+   packet->destAddr = pseudoHeader->destAddr;
+
+   //Calculate IP header checksum
+   packet->headerChecksum = ipCalcChecksumEx(buffer, offset, packet->headerLength * 4);
+
+   //Ensure the source address is valid
+   error = ipv4CheckSourceAddr(interface, pseudoHeader->srcAddr);
+   //Invalid source address?
+   if(error)
+      return error;
+
+   //Destination address is the unspecified address?
+   if(pseudoHeader->destAddr == IPV4_UNSPECIFIED_ADDR)
+   {
+      //Destination address is not acceptable
+      return ERROR_INVALID_ADDRESS;
+   }
+   //Destination address is the loopback address?
+   else if(pseudoHeader->destAddr == IPV4_LOOPBACK_ADDR)
+   {
+      //Not yet implemented...
+      return ERROR_NOT_IMPLEMENTED;
+   }
+
+#if (ETH_SUPPORT == ENABLED)
+   //Ethernet interface?
+   if(interface->nicDriver->type == NIC_TYPE_ETHERNET)
+   {
+      Ipv4Addr destIpAddr;
+      MacAddr destMacAddr;
+
+      //Get the destination IPv4 address
+      destIpAddr = pseudoHeader->destAddr;
+
+      //Destination address is a broadcast address?
+      if(ipv4IsBroadcastAddr(interface, destIpAddr))
+      {
+         //Use of the broadcast MAC address to send the packet
+         destMacAddr = MAC_BROADCAST_ADDR;
+         //No error to report
+         error = NO_ERROR;
+      }
+      //Destination address is a multicast address?
+      else if(ipv4IsMulticastAddr(destIpAddr))
+      {
+         //Map IPv4 multicast address to MAC-layer multicast address
+         error = ipv4MapMulticastAddrToMac(destIpAddr, &destMacAddr);
+      }
+      //Source or destination address is a link-local address?
+      else if(ipv4IsLinkLocalAddr(pseudoHeader->srcAddr) ||
+         ipv4IsLinkLocalAddr(destIpAddr))
+      {
+         //Packets with a link-local source or destination address are not
+         //routable off the link
+         error = arpResolve(interface, destIpAddr, &destMacAddr);
+      }
+      //Destination host is on the local subnet?
+      else if(ipv4IsOnLocalSubnet(interface, destIpAddr))
+      {
+         //Resolve destination address before sending the packet
+         error = arpResolve(interface, destIpAddr, &destMacAddr);
+      }
+      //Destination host is outside the local subnet?
+      else
+      {
+         //Make sure the default gateway is properly set
+         if(interface->ipv4Context.defaultGateway != IPV4_UNSPECIFIED_ADDR)
+         {
+            //Use the default gateway to forward the packet
+            destIpAddr = interface->ipv4Context.defaultGateway;
+            //Perform address resolution
+            error = arpResolve(interface, destIpAddr, &destMacAddr);
+         }
+         else
+         {
+            //Number of IP datagrams discarded because no route could be found
+            //to transmit them to their destination
+            MIB2_INC_COUNTER32(mib2Base.ipGroup.ipOutNoRoutes, 1);
+
+            //Report an error
+            error = ERROR_NO_ROUTE;
+         }
+      }
+
+      //Successful address resolution?
+      if(!error)
+      {
+         //Debug message
+         TRACE_INFO("Sending IPv4 packet (%" PRIuSIZE " bytes)...\r\n", length);
+         //Dump IP header contents for debugging purpose
+         ipv4DumpHeader(packet);
+
+         //Send Ethernet frame
+         error = ethSendFrame(interface, &destMacAddr, buffer, offset, ETH_TYPE_IPV4);
+      }
+      //Address resolution is in progress?
+      else if(error == ERROR_IN_PROGRESS)
+      {
+         //Debug message
+         TRACE_INFO("Enqueuing IPv4 packet (%" PRIuSIZE " bytes)...\r\n", length);
+         //Dump IP header contents for debugging purpose
+         ipv4DumpHeader(packet);
+
+         //Enqueue packets waiting for address resolution
+         error = arpEnqueuePacket(interface, destIpAddr, buffer, offset);
+      }
+      //Address resolution failed?
+      else
+      {
+         //Debug message
+         TRACE_WARNING("Cannot map IPv4 address to Ethernet address!\r\n");
+      }
+   }
+   else
+#endif
+#if (PPP_SUPPORT == ENABLED)
+   //PPP interface?
+   if(interface->nicDriver->type == NIC_TYPE_PPP)
+   {
+      //Debug message
+      TRACE_INFO("Sending IPv4 packet (%" PRIuSIZE " bytes)...\r\n", length);
+      //Dump IP header contents for debugging purpose
+      ipv4DumpHeader(packet);
+
+      //Send PPP frame
+      error = pppSendFrame(interface, buffer, offset, PPP_PROTOCOL_IP);
+   }
+   else
+#endif
+   //Unknown interface type?
+   {
+      //Report an error
+      error = ERROR_INVALID_INTERFACE;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Source IPv4 address filtering
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr Source IPv4 address to be checked
+ * @return Error code
+ **/
+
+error_t ipv4CheckSourceAddr(NetInterface *interface, Ipv4Addr ipAddr)
+{
+   //Broadcast and multicast addresses must not be used as source
+   //address (see RFC 1122 3.2.1.3)
+   if(ipv4IsBroadcastAddr(interface, ipAddr) || ipv4IsMulticastAddr(ipAddr))
+   {
+      //Debug message
+      TRACE_WARNING("Wrong source IPv4 address!\r\n");
+      //The source address not is acceptable
+      return ERROR_INVALID_ADDRESS;
+   }
+
+   //The source address is acceptable
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Destination IPv4 address filtering
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr Destination IPv4 address to be checked
+ * @return Error code
+ **/
+
+error_t ipv4CheckDestAddr(NetInterface *interface, Ipv4Addr ipAddr)
+{
+   error_t error;
+   uint_t i;
+   Ipv4FilterEntry *entry;
+
+   //Filter out any invalid addresses
+   error = ERROR_INVALID_ADDRESS;
+
+   //Broadcast address?
+   if(ipv4IsBroadcastAddr(interface, ipAddr))
+   {
+      //Always accept broadcast address
+      error = NO_ERROR;
+   }
+   //Multicast address?
+   else if(ipv4IsMulticastAddr(ipAddr))
+   {
+      //Go through the multicast filter table
+      for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
+      {
+         //Point to the current entry
+         entry = &interface->ipv4Context.multicastFilter[i];
+
+         //Valid entry?
+         if(entry->refCount > 0)
+         {
+            //Check whether the destination IPv4 address matches
+            //a relevant multicast address
+            if(entry->addr == ipAddr)
+            {
+               //The multicast address is acceptable
+               error = NO_ERROR;
+               //Stop immediately
+               break;
+            }
+         }
+      }
+   }
+   //Unicast address?
+   else
+   {
+      //Valid host address?
+      if(interface->ipv4Context.addrState != IPV4_ADDR_STATE_INVALID)
+      {
+         //Check whether the destination address matches the host address
+         if(interface->ipv4Context.addr == ipAddr)
+         {
+            //The destination address is acceptable
+            error = NO_ERROR;
+         }
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief IPv4 source address selection
+ *
+ * This function selects the source address and the relevant network interface
+ * to be used in order to join the specified destination address
+ *
+ * @param[in,out] interface A pointer to a valid network interface may be provided as
+ *   a hint. The function returns a pointer identifying the interface to be used
+ * @param[in] destAddr Destination IPv4 address
+ * @param[out] srcAddr Local IPv4 address to be used
+ * @return Error code
+ **/
+
+error_t ipv4SelectSourceAddr(NetInterface **interface,
+   Ipv4Addr destAddr, Ipv4Addr *srcAddr)
+{
+   uint_t i;
+   NetInterface *currentInterface;
+   NetInterface *bestInterface;
+
+   //Initialize variables
+   bestInterface = NULL;
+
+   //Loop through network interfaces
+   for(i = 0; i < NET_INTERFACE_COUNT; i++)
+   {
+      //Point to the current interface
+      currentInterface = &netInterface[i];
+
+      //A network interface may be provided as a hint...
+      if(*interface != currentInterface && *interface != NULL)
+      {
+         //Select the next interface in the list
+         continue;
+      }
+
+      //Check the state of the address
+      if(currentInterface->ipv4Context.addrState != IPV4_ADDR_STATE_VALID)
+      {
+         //Select the next interface in the list
+         continue;
+      }
+
+      //Select the first interface as default
+      if(bestInterface == NULL)
+      {
+         //Give the current interface the higher precedence
+         bestInterface = currentInterface;
+         //Select the next interface in the list
+         continue;
+      }
+
+      //Prefer same address
+      if(bestInterface->ipv4Context.addr == destAddr)
+      {
+         //Select the next interface in the list
+         continue;
+      }
+      else if(currentInterface->ipv4Context.addr == destAddr)
+      {
+         //Give the current interface the higher precedence
+         bestInterface = currentInterface;
+         //Select the next interface in the list
+         continue;
+      }
+
+      //Prefer appropriate scope
+      if(ipv4GetAddrScope(currentInterface->ipv4Context.addr) <
+         ipv4GetAddrScope(bestInterface->ipv4Context.addr))
+      {
+         if(ipv4GetAddrScope(currentInterface->ipv4Context.addr) >=
+            ipv4GetAddrScope(destAddr))
+         {
+            //Give the current interface the higher precedence
+            bestInterface = currentInterface;
+         }
+
+         //Select the next interface in the list
+         continue;
+      }
+      else if(ipv4GetAddrScope(bestInterface->ipv4Context.addr) <
+         ipv4GetAddrScope(currentInterface->ipv4Context.addr))
+      {
+         if(ipv4GetAddrScope(bestInterface->ipv4Context.addr) <
+            ipv4GetAddrScope(destAddr))
+         {
+            //Give the current interface the higher precedence
+            bestInterface = currentInterface;
+         }
+
+         //Select the next interface in the list
+         continue;
+      }
+
+      //Prefer appropriate subnet mask
+      if(ipv4IsOnLocalSubnet(bestInterface, destAddr))
+      {
+         //Select the next interface in the list
+         continue;
+      }
+      else if(ipv4IsOnLocalSubnet(currentInterface, destAddr))
+      {
+         //Give the current interface the higher precedence
+         bestInterface = currentInterface;
+         //Select the next interface in the list
+         continue;
+      }
+   }
+
+   //Source address selection failed?
+   if(bestInterface == NULL)
+   {
+      //Report an error
+      return ERROR_NO_ADDRESS;
+   }
+
+   //Return the out-going interface and the source address to be used
+   *interface = bestInterface;
+   *srcAddr = bestInterface->ipv4Context.addr;
+
+   //Successful source address selection
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Check whether an IPv4 address is a broadcast address
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr IPv4 address to be checked
+ * @return TRUE if the IPv4 address is a broadcast address, else FALSE
+ **/
+
+bool_t ipv4IsBroadcastAddr(NetInterface *interface, Ipv4Addr ipAddr)
+{
+   //Check whether the specified IPv4 address is the broadcast address
+   if(ipAddr == IPV4_BROADCAST_ADDR)
+      return TRUE;
+
+   //Check whether the specified IPv4 address belongs to the local network
+   if(ipv4IsOnLocalSubnet(interface, ipAddr))
+   {
+      //Make sure the subnet mask is not 255.255.255.255
+      if(interface->ipv4Context.subnetMask != IPV4_BROADCAST_ADDR)
+      {
+         //Directed broadcast address?
+         if((ipAddr | interface->ipv4Context.subnetMask) == IPV4_BROADCAST_ADDR)
+            return TRUE;
+      }
+   }
+
+   //The specified IPv4 address is not a broadcast address
+   return FALSE;
+}
+
+
+/**
+ * @brief Retrieve the scope of an IPv4 address
+ * @param[in] ipAddr IPv4 address
+ * @return IPv4 address scope
+ **/
+
+uint_t ipv4GetAddrScope(Ipv4Addr ipAddr)
+{
+   uint_t scope;
+
+   //Broadcast address?
+   if(ipAddr == IPV4_BROADCAST_ADDR)
+   {
+      //The broadcast address is never forwarded by the routers connecting
+      //the local network to other networks
+      scope = IPV4_ADDR_SCOPE_LINK_LOCAL;
+   }
+   //Multicast address?
+   else if(ipv4IsMulticastAddr(ipAddr))
+   {
+      //Local Network Control Block?
+      if((ipAddr & IPV4_MULTICAST_LNCB_MASK) == IPV4_MULTICAST_LNCB_PREFIX)
+      {
+         //Addresses in the Local Network Control Block are used for protocol
+         //control traffic that is not forwarded off link
+         scope = IPV4_ADDR_SCOPE_LINK_LOCAL;
+      }
+      //Any other multicast address?
+      else
+      {
+         //Other addresses are assigned global scope
+         scope = IPV4_ADDR_SCOPE_GLOBAL;
+      }
+   }
+   //Unicast address?
+   else
+   {
+      //Loopback address?
+      if((ipAddr & IPV4_LOOPBACK_ADDR_MASK) == IPV4_LOOPBACK_ADDR_PREFIX)
+      {
+         //IPv4 loopback addresses, which have the prefix 127.0.0.0/8,
+         //are assigned interface-local scope
+         scope = IPV4_ADDR_SCOPE_INTERFACE_LOCAL;
+      }
+      //Link-local address?
+      else if((ipAddr & IPV4_LINK_LOCAL_MASK) == IPV4_LINK_LOCAL_PREFIX)
+      {
+         //IPv4 auto-configuration addresses, which have the prefix
+         //169.254.0.0/16, are assigned link-local scope
+         scope = IPV4_ADDR_SCOPE_LINK_LOCAL;
+      }
+      //Any other unicast address?
+      else
+      {
+         //Other addresses are assigned global scope
+         scope = IPV4_ADDR_SCOPE_GLOBAL;
+      }
+   }
+
+   //Return the scope of the specified IPv4 address
+   return scope;
+}
+
+
+/**
+ * @brief Join the specified host group
+ * @param[in] interface Underlying network interface
+ * @param[in] groupAddr IPv4 address identifying the host group to join
+ * @return Error code
+ **/
+
+error_t ipv4JoinMulticastGroup(NetInterface *interface, Ipv4Addr groupAddr)
+{
+   error_t error;
+   uint_t i;
+   Ipv4FilterEntry *entry;
+   Ipv4FilterEntry *firstFreeEntry;
+#if (ETH_SUPPORT == ENABLED)
+   MacAddr macAddr;
+#endif
+
+   //The IPv4 address must be a valid multicast address
+   if(!ipv4IsMulticastAddr(groupAddr))
+      return ERROR_INVALID_ADDRESS;
+
+   //Initialize error code
+   error = NO_ERROR;
+   //Keep track of the first free entry
+   firstFreeEntry = NULL;
+
+   //Go through the multicast filter table
+   for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv4Context.multicastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Check whether the table already contains the specified IPv4 address
+         if(entry->addr == groupAddr)
+         {
+            //Increment the reference count
+            entry->refCount++;
+            //Successful processing
+            return NO_ERROR;
+         }
+      }
+      else
+      {
+         //Keep track of the first free entry
+         if(firstFreeEntry == NULL)
+            firstFreeEntry = entry;
+      }
+   }
+
+   //Check whether the multicast filter table is full
+   if(firstFreeEntry == NULL)
+   {
+      //A new entry cannot be added
+      return ERROR_FAILURE;
+   }
+
+#if (ETH_SUPPORT == ENABLED)
+   //Map the IPv4 multicast address to a MAC-layer address
+   ipv4MapMulticastAddrToMac(groupAddr, &macAddr);
+   //Add the corresponding address to the MAC filter table
+   error = ethAcceptMulticastAddr(interface, &macAddr);
+#endif
+
+   //MAC filter table successfully updated?
+   if(!error)
+   {
+      //Now we can safely add a new entry to the table
+      firstFreeEntry->addr = groupAddr;
+      //Initialize the reference count
+      firstFreeEntry->refCount = 1;
+
+#if (IGMP_SUPPORT == ENABLED)
+      //Report multicast group membership to the router
+      igmpJoinGroup(interface, firstFreeEntry);
+#endif
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Leave the specified host group
+ * @param[in] interface Underlying network interface
+ * @param[in] groupAddr IPv4 address identifying the host group to leave
+ * @return Error code
+ **/
+
+error_t ipv4LeaveMulticastGroup(NetInterface *interface, Ipv4Addr groupAddr)
+{
+   uint_t i;
+   Ipv4FilterEntry *entry;
+#if (ETH_SUPPORT == ENABLED)
+   MacAddr macAddr;
+#endif
+
+   //The IPv4 address must be a valid multicast address
+   if(!ipv4IsMulticastAddr(groupAddr))
+      return ERROR_INVALID_ADDRESS;
+
+   //Go through the multicast filter table
+   for(i = 0; i < IPV4_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv4Context.multicastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Specified IPv4 address found?
+         if(entry->addr == groupAddr)
+         {
+            //Decrement the reference count
+            entry->refCount--;
+
+            //Remove the entry if the reference count drops to zero
+            if(entry->refCount == 0)
+            {
+#if (IGMP_SUPPORT == ENABLED)
+               //Report group membership termination
+               igmpLeaveGroup(interface, entry);
+#endif
+#if (ETH_SUPPORT == ENABLED)
+               //Map the IPv4 multicast address to a MAC-layer address
+               ipv4MapMulticastAddrToMac(groupAddr, &macAddr);
+               //Drop the corresponding address from the MAC filter table
+               ethDropMulticastAddr(interface, &macAddr);
+#endif
+               //Remove the multicast address from the list
+               entry->addr = IPV4_UNSPECIFIED_ADDR;
+            }
+
+            //Successful processing
+            return NO_ERROR;
+         }
+      }
+   }
+
+   //The specified IPv4 address does not exist
+   return ERROR_ADDRESS_NOT_FOUND;
+}
+
+
+/**
+ * @brief Map an host group address to a MAC-layer multicast address
+ * @param[in] ipAddr IPv4 host group address
+ * @param[out] macAddr Corresponding MAC-layer multicast address
+ * @return Error code
+ **/
+
+error_t ipv4MapMulticastAddrToMac(Ipv4Addr ipAddr, MacAddr *macAddr)
+{
+   uint8_t *p;
+
+   //Ensure the specified IPv4 address is a valid host group address
+   if(!ipv4IsMulticastAddr(ipAddr))
+      return ERROR_INVALID_ADDRESS;
+
+   //Cast the address to byte array
+   p = (uint8_t *) &ipAddr;
+
+   //An IP host group address is mapped to an Ethernet multicast address
+   //by placing the low-order 23-bits of the IP address into the low-order
+   //23 bits of the Ethernet multicast address 01-00-5E-00-00-00
+   macAddr->b[0] = 0x01;
+   macAddr->b[1] = 0x00;
+   macAddr->b[2] = 0x5E;
+   macAddr->b[3] = p[1] & 0x7F;
+   macAddr->b[4] = p[2];
+   macAddr->b[5] = p[3];
+
+   //The specified host group address was successfully
+   //mapped to a MAC-layer address
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Convert a dot-decimal string to a binary IPv4 address
+ * @param[in] str NULL-terminated string representing the IPv4 address
+ * @param[out] ipAddr Binary representation of the IPv4 address
+ * @return Error code
+ **/
+
+error_t ipv4StringToAddr(const char_t *str, Ipv4Addr *ipAddr)
+{
+   error_t error;
+   int_t i = 0;
+   int_t value = -1;
+
+   //Parse input string
+   while(1)
+   {
+      //Decimal digit found?
+      if(isdigit((uint8_t) *str))
+      {
+         //First digit to be decoded?
+         if(value < 0) value = 0;
+         //Update the value of the current byte
+         value = (value * 10) + (*str - '0');
+
+         //The resulting value shall be in range 0 to 255
+         if(value > 255)
+         {
+            //The conversion failed
+            error = ERROR_INVALID_SYNTAX;
+            break;
+         }
+      }
+      //Dot separator found?
+      else if(*str == '.' && i < 4)
+      {
+         //Each dot must be preceded by a valid number
+         if(value < 0)
+         {
+            //The conversion failed
+            error = ERROR_INVALID_SYNTAX;
+            break;
+         }
+
+         //Save the current byte
+         ((uint8_t *) ipAddr)[i++] = value;
+         //Prepare to decode the next byte
+         value = -1;
+      }
+      //End of string detected?
+      else if(*str == '\0' && i == 3)
+      {
+         //The NULL character must be preceded by a valid number
+         if(value < 0)
+         {
+            //The conversion failed
+            error = ERROR_INVALID_SYNTAX;
+         }
+         else
+         {
+            //Save the last byte of the IPv4 address
+            ((uint8_t *) ipAddr)[i] = value;
+            //The conversion succeeded
+            error = NO_ERROR;
+         }
+
+         //We are done
+         break;
+      }
+      //Invalid character...
+      else
+      {
+         //The conversion failed
+         error = ERROR_INVALID_SYNTAX;
+         break;
+      }
+
+      //Point to the next character
+      str++;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Convert a binary IPv4 address to dot-decimal notation
+ * @param[in] ipAddr Binary representation of the IPv4 address
+ * @param[out] str NULL-terminated string representing the IPv4 address
+ * @return Pointer to the formatted string
+ **/
+
+char_t *ipv4AddrToString(Ipv4Addr ipAddr, char_t *str)
+{
+   uint8_t *p;
+   static char_t buffer[16];
+
+   //If the NULL pointer is given as parameter, then the internal buffer is used
+   if(str == NULL)
+      str = buffer;
+
+   //Cast the address to byte array
+   p = (uint8_t *) &ipAddr;
+   //Format IPv4 address
+   sprintf(str, "%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 "", p[0], p[1], p[2], p[3]);
+
+   //Return a pointer to the formatted string
+   return str;
+}
+
+
+/**
+ * @brief Dump IPv4 header for debugging purpose
+ * @param[in] ipHeader Pointer to the IPv4 header
+ **/
+
+void ipv4DumpHeader(const Ipv4Header *ipHeader)
+{
+   //Dump IP header contents
+   TRACE_DEBUG("  Version = %" PRIu8 "\r\n", ipHeader->version);
+   TRACE_DEBUG("  Header Length = %" PRIu8 "\r\n", ipHeader->headerLength);
+   TRACE_DEBUG("  Type Of Service = %" PRIu8 "\r\n", ipHeader->typeOfService);
+   TRACE_DEBUG("  Total Length = %" PRIu16 "\r\n", ntohs(ipHeader->totalLength));
+   TRACE_DEBUG("  Identification = %" PRIu16 "\r\n", ntohs(ipHeader->identification));
+   TRACE_DEBUG("  Flags = 0x%01X\r\n", ntohs(ipHeader->fragmentOffset) >> 13);
+   TRACE_DEBUG("  Fragment Offset = %" PRIu16 "\r\n", ntohs(ipHeader->fragmentOffset) & 0x1FFF);
+   TRACE_DEBUG("  Time To Live = %" PRIu8 "\r\n", ipHeader->timeToLive);
+   TRACE_DEBUG("  Protocol = %" PRIu8 "\r\n", ipHeader->protocol);
+   TRACE_DEBUG("  Header Checksum = 0x%04" PRIX16 "\r\n", ntohs(ipHeader->headerChecksum));
+   TRACE_DEBUG("  Src Addr = %s\r\n", ipv4AddrToString(ipHeader->srcAddr, NULL));
+   TRACE_DEBUG("  Dest Addr = %s\r\n", ipv4AddrToString(ipHeader->destAddr, NULL));
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv4/ipv4.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,377 @@
+/**
+ * @file ipv4.h
+ * @brief IPv4 (Internet Protocol Version 4)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _IPV4_H
+#define _IPV4_H
+
+//Forward declaration of structures
+struct _Ipv4Header;
+#define Ipv4Header struct _Ipv4Header
+
+struct _Ipv4PseudoHeader;
+#define Ipv4PseudoHeader struct _Ipv4PseudoHeader
+
+//Dependencies
+#include <string.h>
+#include "core/net.h"
+#include "core/ethernet.h"
+#include "ipv4/ipv4_frag.h"
+
+//IPv4 support
+#ifndef IPV4_SUPPORT
+   #define IPV4_SUPPORT ENABLED
+#elif (IPV4_SUPPORT != ENABLED && IPV4_SUPPORT != DISABLED)
+   #error IPV4_SUPPORT parameter is not valid
+#endif
+
+//Default IPv4 time-to-live value
+#ifndef IPV4_DEFAULT_TTL
+   #define IPV4_DEFAULT_TTL 64
+#elif (IPV4_DEFAULT_TTL < 1)
+   #error IPV4_DEFAULT_TTL parameter is not valid
+#endif
+
+//Maximum number of DNS servers
+#ifndef IPV4_DNS_SERVER_LIST_SIZE
+   #define IPV4_DNS_SERVER_LIST_SIZE 2
+#elif (IPV4_DNS_SERVER_LIST_SIZE < 1)
+   #error IPV4_DNS_SERVER_LIST_SIZE parameter is not valid
+#endif
+
+//Size of the IPv4 multicast filter
+#ifndef IPV4_MULTICAST_FILTER_SIZE
+   #define IPV4_MULTICAST_FILTER_SIZE 4
+#elif (IPV4_MULTICAST_FILTER_SIZE < 1)
+   #error IPV4_MULTICAST_FILTER_SIZE parameter is not valid
+#endif
+
+//Version number for IPv4
+#define IPV4_VERSION 4
+//Minimum MTU
+#define IPV4_MINIMUM_MTU 68
+//Default MTU
+#define IPV4_DEFAULT_MTU 576
+//Minimum header length
+#define IPV4_MIN_HEADER_LENGTH 20
+//Maximum header length
+#define IPV4_MAX_HEADER_LENGTH 60
+
+//Shortcut to data field
+#define IPV4_DATA(packet) PTR_OFFSET(packet, packet->headerLength * 4)
+
+//Macro used for defining an IPv4 address
+#ifdef _CPU_BIG_ENDIAN
+   #define IPV4_ADDR(a, b, c, d) (((uint32_t) (a) << 24) | ((b) << 16) | ((c) << 8) | (d))
+#else
+   #define IPV4_ADDR(a, b, c, d) ((a) | ((b) << 8) | ((c) << 16) | ((uint32_t) (d) << 24))
+#endif
+
+//Unspecified IPv4 address
+#define IPV4_UNSPECIFIED_ADDR IPV4_ADDR(0, 0, 0, 0)
+//Broadcast IPV4 address
+#define IPV4_BROADCAST_ADDR IPV4_ADDR(255, 255, 255, 255)
+
+//Loopback IPv4 address
+#define IPV4_LOOPBACK_ADDR IPV4_ADDR(127, 0, 0, 1)
+#define IPV4_LOOPBACK_ADDR_PREFIX IPV4_ADDR(127, 0, 0, 0)
+#define IPV4_LOOPBACK_ADDR_MASK IPV4_ADDR(255, 0, 0, 0)
+
+//Link-local addresses
+#define IPV4_LINK_LOCAL_PREFIX IPV4_ADDR(169, 254, 0, 0)
+#define IPV4_LINK_LOCAL_MASK IPV4_ADDR(255, 255, 0, 0)
+
+//Multicast addresses
+#define IPV4_MULTICAST_PREFIX IPV4_ADDR(224, 0, 0, 0)
+#define IPV4_MULTICAST_MASK IPV4_ADDR(240, 0, 0, 0)
+
+//Local Network Control Block (RFC 5771)
+#define IPV4_MULTICAST_LNCB_PREFIX IPV4_ADDR(224, 0, 0, 0)
+#define IPV4_MULTICAST_LNCB_MASK IPV4_ADDR(255, 255, 255, 0)
+
+//Internetwork Control Block (RFC 5771)
+#define IPV4_MULTICAST_INCB_PREFIX IPV4_ADDR(224, 0, 1, 0)
+#define IPV4_MULTICAST_INCB_MASK IPV4_ADDR(255, 255, 255, 0)
+
+//IPv4 address classes
+#define IPV4_CLASS_A_ADDR IPV4_ADDR(0, 0, 0, 0)
+#define IPV4_CLASS_A_MASK IPV4_ADDR(128, 0, 0, 0)
+#define IPV4_CLASS_B_ADDR IPV4_ADDR(128, 0, 0, 0)
+#define IPV4_CLASS_B_MASK IPV4_ADDR(192, 0, 0, 0)
+#define IPV4_CLASS_C_ADDR IPV4_ADDR(192, 0, 0, 0)
+#define IPV4_CLASS_C_MASK IPV4_ADDR(224, 0, 0, 0)
+#define IPV4_CLASS_D_ADDR IPV4_ADDR(224, 0, 0, 0)
+#define IPV4_CLASS_D_MASK IPV4_ADDR(240, 0, 0, 0)
+#define IPV4_CLASS_E_ADDR IPV4_ADDR(240, 0, 0, 0)
+#define IPV4_CLASS_E_MASK IPV4_ADDR(240, 0, 0, 0)
+
+//Copy IPv4 address
+#define ipv4CopyAddr(destIpAddr, srcIpAddr) \
+   memcpy(destIpAddr, srcIpAddr, sizeof(Ipv4Addr))
+
+//Compare IPv4 addresses
+#define ipv4CompAddr(ipAddr1, ipAddr2) \
+   (!memcmp(ipAddr1, ipAddr2, sizeof(Ipv4Addr)))
+
+//Determine whether an IPv4 address belongs to the local network
+#define ipv4IsOnLocalSubnet(interface, ipAddr) \
+   ((ipAddr & interface->ipv4Context.subnetMask) == \
+   (interface->ipv4Context.addr & interface->ipv4Context.subnetMask))
+
+//Determine whether an IPv4 address is a link-local address
+#define ipv4IsLinkLocalAddr(ipAddr) \
+   ((ipAddr & IPV4_LINK_LOCAL_MASK) == IPV4_LINK_LOCAL_PREFIX)
+
+//Determine whether an IPv4 address is a multicast address
+#define ipv4IsMulticastAddr(ipAddr) \
+   ((ipAddr & IPV4_MULTICAST_MASK) == IPV4_MULTICAST_PREFIX)
+
+//Check whether an IPv4 address is a tentative address
+#define ipv4IsTentativeAddr(interface, ipAddr) \
+   (interface->ipv4Context.addrState == IPV4_ADDR_STATE_TENTATIVE && \
+   interface->ipv4Context.addr == ipAddr)
+
+
+/**
+ * @brief IPv4 address scopes
+ **/
+
+typedef enum
+{
+   IPV4_ADDR_SCOPE_INTERFACE_LOCAL = 1,
+   IPV4_ADDR_SCOPE_LINK_LOCAL      = 2,
+   IPV4_ADDR_SCOPE_GLOBAL          = 3
+} Ipv4AddrScope;
+
+
+/**
+ * @brief IPv4 address state
+ **/
+
+typedef enum
+{
+   IPV4_ADDR_STATE_INVALID    = 0, ///<An address that is not assigned to any interface
+   IPV4_ADDR_STATE_TENTATIVE  = 1, ///<An address whose uniqueness on a link is being verified
+   IPV4_ADDR_STATE_VALID      = 2  ///<An address assigned to an interface whose use is unrestricted
+} Ipv4AddrState;
+
+
+/**
+ * @brief IPv4 fragment offset field
+ **/
+
+typedef enum
+{
+   IPV4_FLAG_RES    = 0x8000,
+   IPV4_FLAG_DF     = 0x4000,
+   IPV4_FLAG_MF     = 0x2000,
+   IPV4_OFFSET_MASK = 0x1FFF
+} Ipv4FragmentOffset;
+
+
+/**
+ * @brief IPv4 protocol field
+ **/
+
+typedef enum
+{
+   IPV4_PROTOCOL_ICMP = 1,
+   IPV4_PROTOCOL_IGMP = 2,
+   IPV4_PROTOCOL_TCP  = 6,
+   IPV4_PROTOCOL_UDP  = 17
+} Ipv4Protocol;
+
+
+/**
+ * @brief IPv4 option types
+ **/
+
+typedef enum
+{
+   IPV4_OPTION_EEOL   = 0,
+   IPV4_OPTION_NOP    = 1,
+   IPV4_OPTION_RTRALT = 148
+} Ipv4OptionType;
+
+
+/**
+ * @brief IPv4 network address
+ **/
+
+typedef uint32_t Ipv4Addr;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief IPv4 header
+ **/
+
+__start_packed struct _Ipv4Header
+{
+#ifdef _CPU_BIG_ENDIAN
+   uint8_t version : 4;      //0
+   uint8_t headerLength : 4;
+#else
+   uint8_t headerLength : 4; //0
+   uint8_t version : 4;
+#endif
+   uint8_t typeOfService;    //1
+   uint16_t totalLength;     //2-3
+   uint16_t identification;  //4-5
+   uint16_t fragmentOffset;  //6-7
+   uint8_t timeToLive;       //8
+   uint8_t protocol;         //9
+   uint16_t headerChecksum;  //10-11
+   Ipv4Addr srcAddr;         //12-15
+   Ipv4Addr destAddr;        //16-19
+   uint8_t options[];        //20
+} __end_packed;
+
+
+/**
+ * @brief IPv4 pseudo header
+ **/
+
+__start_packed struct _Ipv4PseudoHeader
+{
+   Ipv4Addr srcAddr;  //0-3
+   Ipv4Addr destAddr; //4-7
+   uint8_t reserved;  //8
+   uint8_t protocol;  //9
+   uint16_t length;   //10-11
+} __end_packed;
+
+
+/**
+ * @brief IPv4 option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;    //0
+   uint8_t length;  //1
+   uint8_t value[]; //2
+} __end_packed Ipv4Option;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+/**
+ * @brief IPv4 multicast filter entry
+ **/
+
+typedef struct
+{
+   Ipv4Addr addr;    ///<Multicast address
+   uint_t refCount;  ///<Reference count for the current entry
+   uint_t state;     ///<IGMP host state
+   bool_t flag;      ///<IGMP flag
+   systime_t timer;  ///<Delay timer
+} Ipv4FilterEntry;
+
+
+/**
+ * @brief IPv4 context
+ **/
+
+typedef struct
+{
+   size_t linkMtu;                                              ///<Maximum transmission unit
+   bool_t isRouter;                                             ///<A flag indicating whether routing is enabled on this interface
+   uint16_t identification;                                     ///<IPv4 fragment identification field
+   Ipv4Addr addr;                                               ///<Host address
+   Ipv4AddrState addrState;                                     ///<State of the host address
+   bool_t addrConflict;                                         ///<Address conflict detected
+   Ipv4Addr subnetMask;                                         ///<Subnet mask
+   Ipv4Addr defaultGateway;                                     ///<Default gateway
+   Ipv4Addr dnsServerList[IPV4_DNS_SERVER_LIST_SIZE];           ///<DNS servers
+   Ipv4FilterEntry multicastFilter[IPV4_MULTICAST_FILTER_SIZE]; ///<Multicast filter table
+#if (IPV4_FRAG_SUPPORT == ENABLED)
+   Ipv4FragDesc fragQueue[IPV4_MAX_FRAG_DATAGRAMS];             ///<IPv4 fragment reassembly queue
+#endif
+} Ipv4Context;
+
+
+//IPv4 related functions
+error_t ipv4Init(NetInterface *interface);
+
+error_t ipv4SetHostAddr(NetInterface *interface, Ipv4Addr addr);
+error_t ipv4GetHostAddr(NetInterface *interface, Ipv4Addr *addr);
+
+error_t ipv4SetSubnetMask(NetInterface *interface, Ipv4Addr mask);
+error_t ipv4GetSubnetMask(NetInterface *interface, Ipv4Addr *mask);
+
+error_t ipv4SetDefaultGateway(NetInterface *interface, Ipv4Addr addr);
+error_t ipv4GetDefaultGateway(NetInterface *interface, Ipv4Addr *addr);
+
+error_t ipv4SetDnsServer(NetInterface *interface, uint_t index, Ipv4Addr addr);
+error_t ipv4GetDnsServer(NetInterface *interface, uint_t index, Ipv4Addr *addr);
+
+error_t ipv4GetBroadcastAddr(NetInterface *interface, Ipv4Addr *addr);
+
+void ipv4LinkChangeEvent(NetInterface *interface);
+
+void ipv4ProcessPacket(NetInterface *interface, Ipv4Header *packet, size_t length);
+void ipv4ProcessDatagram(NetInterface *interface, const NetBuffer *buffer);
+
+error_t ipv4SendDatagram(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader,
+   NetBuffer *buffer, size_t offset, uint8_t ttl);
+
+error_t ipv4SendPacket(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader,
+   uint16_t fragId, size_t fragOffset, NetBuffer *buffer, size_t offset, uint8_t ttl);
+
+error_t ipv4CheckSourceAddr(NetInterface *interface, Ipv4Addr ipAddr);
+error_t ipv4CheckDestAddr(NetInterface *interface, Ipv4Addr ipAddr);
+
+error_t ipv4SelectSourceAddr(NetInterface **interface,
+   Ipv4Addr destAddr, Ipv4Addr *srcAddr);
+
+bool_t ipv4IsBroadcastAddr(NetInterface *interface, Ipv4Addr ipAddr);
+
+uint_t ipv4GetAddrScope(Ipv4Addr ipAddr);
+
+error_t ipv4JoinMulticastGroup(NetInterface *interface, Ipv4Addr groupAddr);
+error_t ipv4LeaveMulticastGroup(NetInterface *interface, Ipv4Addr groupAddr);
+
+error_t ipv4MapMulticastAddrToMac(Ipv4Addr ipAddr, MacAddr *macAddr);
+
+error_t ipv4StringToAddr(const char_t *str, Ipv4Addr *ipAddr);
+char_t *ipv4AddrToString(Ipv4Addr ipAddr, char_t *str);
+
+void ipv4DumpHeader(const Ipv4Header *ipHeader);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv4/ipv4_frag.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,636 @@
+/**
+ * @file ipv4_frag.c
+ * @brief IPv4 fragmentation and reassembly
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The Internet Protocol (IP) implements datagram fragmentation, so that
+ * packets may be formed that can pass through a link with a smaller maximum
+ * transmission unit (MTU) than the original datagram size. Refer to the
+ * following RFCs for complete details:
+ * - RFC 791: Internet Protocol specification
+ * - RFC 815: IP datagram reassembly algorithms
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL IPV4_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "core/ip.h"
+#include "ipv4/ipv4.h"
+#include "ipv4/ipv4_frag.h"
+#include "ipv4/icmp.h"
+#include "mibs/mib2_module.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV4_SUPPORT == ENABLED && IPV4_FRAG_SUPPORT == ENABLED)
+
+//Tick counter to handle periodic operations
+systime_t ipv4FragTickCounter;
+
+
+/**
+ * @brief Fragment an IPv4 datagram into smaller packets
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IPv4 pseudo header
+ * @param[in] id Fragment identification
+ * @param[in] payload Multi-part buffer containing the payload
+ * @param[in] payloadOffset Offset to the first payload byte
+ * @param[in] timeToLive TTL value
+ * @return Error code
+ **/
+
+error_t ipv4FragmentDatagram(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader,
+   uint16_t id, const NetBuffer *payload, size_t payloadOffset, uint8_t timeToLive)
+{
+   error_t error;
+   size_t offset;
+   size_t length;
+   size_t payloadLength;
+   size_t fragmentOffset;
+   size_t maxFragmentSize;
+   NetBuffer *fragment;
+
+   //Retrieve the length of the payload
+   payloadLength = netBufferGetLength(payload) - payloadOffset;
+
+   //Allocate a memory buffer to hold IP fragments
+   fragment = ipAllocBuffer(0, &fragmentOffset);
+   //Failed to allocate memory?
+   if(!fragment)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Determine the maximum payload size for fragmented packets
+   maxFragmentSize = interface->ipv4Context.linkMtu - sizeof(Ipv4Header);
+   //The size shall be a multiple of 8-byte blocks
+   maxFragmentSize -= (maxFragmentSize % 8);
+
+   //Initialize error code
+   error = NO_ERROR;
+
+   //Split the payload into multiple IP fragments
+   for(offset = 0; offset < payloadLength; offset += length)
+   {
+      //Flush the contents of the fragment
+      error = netBufferSetLength(fragment, fragmentOffset);
+      //Sanity check
+      if(error)
+         break;
+
+      //Process the last fragment?
+      if((payloadLength - offset) <= maxFragmentSize)
+      {
+         //Size of the current fragment
+         length = payloadLength - offset;
+         //Copy fragment data
+         netBufferConcat(fragment, payload, payloadOffset + offset, length);
+
+         //Do not set the MF flag for the last fragment
+         error = ipv4SendPacket(interface, pseudoHeader, id,
+            offset / 8, fragment, fragmentOffset, timeToLive);
+      }
+      else
+      {
+         //Size of the current fragment (must be a multiple of 8-byte blocks)
+         length = maxFragmentSize;
+         //Copy fragment data
+         netBufferConcat(fragment, payload, payloadOffset + offset, length);
+
+         //Fragmented packets must have the MF flag set
+         error = ipv4SendPacket(interface, pseudoHeader, id,
+            IPV4_FLAG_MF | (offset / 8), fragment, fragmentOffset, timeToLive);
+      }
+
+      //Failed to send current IP packet?
+      if(error)
+         break;
+
+      //Number of IP datagram fragments that have been generated as a result
+      //of fragmentation at this entity
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipFragCreates, 1);
+   }
+
+   //Check status code
+   if(error)
+   {
+      //Number of IP datagrams that have been discarded because they needed
+      //to be fragmented at this entity but could not be
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipFragFails, 1);
+   }
+   else
+   {
+      //Number of IP datagrams that have been successfully fragmented at
+      //this entity
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipFragOKs, 1);
+   }
+
+   //Free previously allocated memory
+   netBufferFree(fragment);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief IPv4 datagram reassembly algorithm
+ * @param[in] interface Underlying network interface
+ * @param[in] packet Pointer to the IPv4 fragmented packet
+ * @param[in] length Packet length including header and payload
+ **/
+
+void ipv4ReassembleDatagram(NetInterface *interface,
+   const Ipv4Header *packet, size_t length)
+{
+   error_t error;
+   uint16_t offset;
+   uint16_t dataFirst;
+   uint16_t dataLast;
+   Ipv4FragDesc *frag;
+   Ipv4HoleDesc *hole;
+   Ipv4HoleDesc *prevHole;
+
+   //Number of IP fragments received which needed to be reassembled
+   MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmReqds, 1);
+
+   //Get the length of the payload
+   length -= packet->headerLength * 4;
+   //Convert the fragment offset from network byte order
+   offset = ntohs(packet->fragmentOffset);
+
+   //Every fragment except the last must contain a multiple of 8 bytes of data
+   if((offset & IPV4_FLAG_MF) && (length % 8))
+   {
+      //Number of failures detected by the IP reassembly algorithm
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmFails, 1);
+      //Drop the incoming fragment
+      return;
+   }
+
+   //Calculate the index of the first byte
+   dataFirst = (offset & IPV4_OFFSET_MASK) * 8;
+   //Calculate the index immediately following the last byte
+   dataLast = dataFirst + (uint16_t) length;
+
+   //Search for a matching IP datagram being reassembled
+   frag = ipv4SearchFragQueue(interface, packet);
+
+   //No matching entry in the reassembly queue?
+   if(frag == NULL)
+   {
+      //Number of failures detected by the IP reassembly algorithm
+      MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmFails, 1);
+      //Drop the incoming fragment
+      return;
+   }
+
+   //The very first fragment requires special handling
+   if(!(offset & IPV4_OFFSET_MASK))
+   {
+      //Calculate the length of the IP header including options
+      frag->headerLength = packet->headerLength * 4;
+
+      //Enforce the size of the reconstructed datagram
+      if((frag->headerLength + frag->dataLength) > IPV4_MAX_FRAG_DATAGRAM_SIZE)
+      {
+         //Number of failures detected by the IP reassembly algorithm
+         MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmFails, 1);
+         //Drop the reconstructed datagram
+         netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+         //Exit immediately
+         return;
+      }
+
+      //Make sure the IP header entirely fits in the first chunk
+      if(frag->headerLength > frag->buffer.chunk[0].size)
+      {
+         //Number of failures detected by the IP reassembly algorithm
+         MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmFails, 1);
+         //Drop the reconstructed datagram
+         netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+         //Exit immediately
+         return;
+      }
+
+      //Fix the length of the first chunk
+      frag->buffer.chunk[0].length = frag->headerLength;
+      //Always take the IP header from the first fragment
+      netBufferWrite((NetBuffer *) &frag->buffer, 0, packet, frag->headerLength);
+   }
+
+   //It may be necessary to increase the size of the buffer...
+   if(dataLast > frag->dataLength)
+   {
+      //Enforce the size of the reconstructed datagram
+      if((frag->headerLength + dataLast) > IPV4_MAX_FRAG_DATAGRAM_SIZE)
+      {
+         //Number of failures detected by the IP reassembly algorithm
+         MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmFails, 1);
+         //Drop the reconstructed datagram
+         netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+         //Exit immediately
+         return;
+      }
+
+      //Adjust the size of the reconstructed datagram
+      error = netBufferSetLength((NetBuffer *) &frag->buffer,
+         frag->headerLength + dataLast + sizeof(Ipv4HoleDesc));
+
+      //Any error to report?
+      if(error)
+      {
+         //Number of failures detected by the IP reassembly algorithm
+         MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmFails, 1);
+         //Drop the reconstructed datagram
+         netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+         //Exit immediately
+         return;
+      }
+
+      //Actual length of the payload
+      frag->dataLength = dataLast;
+   }
+
+   //Select the first hole descriptor from the list
+   hole = ipv4FindHole(frag, frag->firstHole);
+   //Keep track of the previous hole in the list
+   prevHole = NULL;
+
+   //Iterate through the hole descriptors
+   while(hole != NULL)
+   {
+      //Save lower and upper boundaries for later use
+      uint16_t holeFirst = hole->first;
+      uint16_t holeLast = hole->last;
+
+      //Check whether the newly arrived fragment
+      //interacts with this hole in some way
+      if(dataFirst < holeLast && dataLast > holeFirst)
+      {
+         //The current descriptor is no longer valid. We will destroy
+         //it, and in the next two steps, we will determine whether
+         //or not it is necessary to create any new hole descriptors
+         if(prevHole != NULL)
+            prevHole->next = hole->next;
+         else
+            frag->firstHole = hole->next;
+
+         //Is there still a hole at the beginning of the segment?
+         if(dataFirst > holeFirst)
+         {
+            //Create a new entry that describes this hole
+            hole = ipv4FindHole(frag, holeFirst);
+            hole->first = holeFirst;
+            hole->last = dataFirst;
+
+            //Insert the newly created entry into the hole descriptor list
+            if(prevHole != NULL)
+            {
+               hole->next = prevHole->next;
+               prevHole->next = hole->first;
+            }
+            else
+            {
+               hole->next = frag->firstHole;
+               frag->firstHole = hole->first;
+            }
+
+            //Always keep track of the previous hole
+            prevHole = hole;
+         }
+
+         //Is there still a hole at the end of the segment?
+         if(dataLast < holeLast && (offset & IPV4_FLAG_MF))
+         {
+            //Create a new entry that describes this hole
+            hole = ipv4FindHole(frag, dataLast);
+            hole->first = dataLast;
+            hole->last = holeLast;
+
+            //Insert the newly created entry into the hole descriptor list
+            if(prevHole != NULL)
+            {
+               hole->next = prevHole->next;
+               prevHole->next = hole->first;
+            }
+            else
+            {
+               hole->next = frag->firstHole;
+               frag->firstHole = hole->first;
+            }
+
+            //Always keep track of the previous hole
+            prevHole = hole;
+         }
+      }
+      else
+      {
+         //The newly arrived fragment does not interact with the current hole
+         prevHole = hole;
+      }
+
+      //Select the next hole descriptor from the list
+      hole = ipv4FindHole(frag, prevHole ? prevHole->next : frag->firstHole);
+   }
+
+   //Copy data from the fragment to the reassembly buffer
+   netBufferWrite((NetBuffer *) &frag->buffer,
+      frag->headerLength + dataFirst, IPV4_DATA(packet), length);
+
+   //Dump hole descriptor list
+   ipv4DumpHoleList(frag);
+
+   //If the hole descriptor list is empty, the reassembly process is now complete
+   if(!ipv4FindHole(frag, frag->firstHole))
+   {
+      //Discard the extra hole descriptor that follows the reconstructed datagram
+      error = netBufferSetLength((NetBuffer *) &frag->buffer,
+         frag->headerLength + frag->dataLength);
+
+      //Check status code
+      if(error)
+      {
+         //Number of failures detected by the IP reassembly algorithm
+         MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmFails, 1);
+      }
+      else
+      {
+         //Point to the IP header
+         Ipv4Header *datagram = netBufferAt((NetBuffer *) &frag->buffer, 0);
+
+         //Fix IP header
+         datagram->totalLength = htons(frag->headerLength + frag->dataLength);
+         datagram->fragmentOffset = 0;
+         datagram->headerChecksum = 0;
+
+         //Number of IP datagrams successfully reassembled
+         MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmOKs, 1);
+
+         //Pass the original IPv4 datagram to the higher protocol layer
+         ipv4ProcessDatagram(interface, (NetBuffer *) &frag->buffer);
+      }
+
+      //Release previously allocated memory
+      netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+   }
+}
+
+
+/**
+ * @brief Fragment reassembly timeout handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * handle IPv4 fragment reassembly timeout
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void ipv4FragTick(NetInterface *interface)
+{
+   error_t error;
+   uint_t i;
+   systime_t time;
+   Ipv4HoleDesc *hole;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Loop through the reassembly queue
+   for(i = 0; i < IPV4_MAX_FRAG_DATAGRAMS; i++)
+   {
+      //Point to the current entry in the reassembly queue
+      Ipv4FragDesc *frag = &interface->ipv4Context.fragQueue[i];
+
+      //Make sure the entry is currently in use
+      if(frag->buffer.chunkCount > 0)
+      {
+         //If the timer runs out, the partially-reassembled datagram must be
+         //discarded and ICMP Time Exceeded message sent to the source host
+         if((time - frag->timestamp) >= IPV4_FRAG_TIME_TO_LIVE)
+         {
+            //Debug message
+            TRACE_INFO("IPv4 fragment reassembly timeout...\r\n");
+            //Dump IP header contents for debugging purpose
+            ipv4DumpHeader(frag->buffer.chunk[0].address);
+
+            //Number of failures detected by the IP reassembly algorithm
+            MIB2_INC_COUNTER32(mib2Base.ipGroup.ipReasmFails, 1);
+
+            //Point to the first hole descriptor
+            hole = ipv4FindHole(frag, frag->firstHole);
+
+            //Make sure the fragment zero has been received
+            //before sending an ICMP message
+            if(hole != NULL && hole->first > 0)
+            {
+               //Fix the size of the reconstructed datagram
+               error = netBufferSetLength((NetBuffer *) &frag->buffer,
+                  frag->headerLength + hole->first);
+
+               //Check status code
+               if(!error)
+               {
+                  //Send an ICMP Time Exceeded message
+                  icmpSendErrorMessage(interface, ICMP_TYPE_TIME_EXCEEDED,
+                     ICMP_CODE_REASSEMBLY_TIME_EXCEEDED, 0, (NetBuffer *) &frag->buffer, 0);
+               }
+            }
+
+            //Drop the partially reconstructed datagram
+            netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Search for a matching datagram in the reassembly queue
+ * @param[in] interface Underlying network interface
+ * @param[in] packet Incoming IPv4 packet
+ * @return Matching fragment descriptor
+ **/
+
+Ipv4FragDesc *ipv4SearchFragQueue(NetInterface *interface, const Ipv4Header *packet)
+{
+   error_t error;
+   uint_t i;
+   Ipv4Header *datagram;
+   Ipv4FragDesc *frag;
+   Ipv4HoleDesc *hole;
+
+   //Search for a matching IP datagram being reassembled
+   for(i = 0; i < IPV4_MAX_FRAG_DATAGRAMS; i++)
+   {
+      //Point to the current entry in the reassembly queue
+      frag = &interface->ipv4Context.fragQueue[i];
+
+      //Check whether the current entry is used?
+      if(frag->buffer.chunkCount > 0)
+      {
+         //Point to the corresponding datagram
+         datagram = netBufferAt((NetBuffer *) &frag->buffer, 0);
+
+         //Check source and destination addresses
+         if(datagram->srcAddr != packet->srcAddr)
+            continue;
+         if(datagram->destAddr != packet->destAddr)
+            continue;
+         //Compare identification and protocol fields
+         if(datagram->identification != packet->identification)
+            continue;
+         if(datagram->protocol != packet->protocol)
+            continue;
+
+         //A matching entry has been found in the reassembly queue
+         return frag;
+      }
+   }
+
+   //If the current packet does not match an existing entry
+   //in the reassembly queue, then create a new entry
+   for(i = 0; i < IPV4_MAX_FRAG_DATAGRAMS; i++)
+   {
+      //Point to the current entry in the reassembly queue
+      frag = &interface->ipv4Context.fragQueue[i];
+
+      //The current entry is free?
+      if(!frag->buffer.chunkCount)
+      {
+         //Number of chunks that comprise the reassembly buffer
+         frag->buffer.maxChunkCount = arraysize(frag->buffer.chunk);
+
+         //Allocate sufficient memory to hold the IPv4 header and
+         //the first hole descriptor
+         error = netBufferSetLength((NetBuffer *) &frag->buffer,
+            NET_MEM_POOL_BUFFER_SIZE + sizeof(Ipv4HoleDesc));
+
+         //Failed to allocate memory?
+         if(error)
+         {
+            //Clean up side effects
+            netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+            //Exit immediately
+            return NULL;
+         }
+
+         //Initial length of the reconstructed datagram
+         frag->headerLength = packet->headerLength * 4;
+         frag->dataLength = 0;
+
+         //Fix the length of the first chunk
+         frag->buffer.chunk[0].length = frag->headerLength;
+         //Copy IPv4 header from the incoming fragment
+         netBufferWrite((NetBuffer *) &frag->buffer, 0, packet, frag->headerLength);
+
+         //Save current time
+         frag->timestamp = osGetSystemTime();
+         //Create a new entry in the hole descriptor list
+         frag->firstHole = 0;
+
+         //Point to first hole descriptor
+         hole = ipv4FindHole(frag, frag->firstHole);
+         //The entry describes the datagram as being completely missing
+         hole->first = 0;
+         hole->last = IPV4_INFINITY;
+         hole->next = IPV4_INFINITY;
+
+         //Dump hole descriptor list
+         ipv4DumpHoleList(frag);
+
+         //Return the matching fragment descriptor
+         return frag;
+      }
+   }
+
+   //The reassembly queue is full
+   return NULL;
+}
+
+
+/**
+ * @brief Flush IPv4 reassembly queue
+ * @param[in] interface Underlying network interface
+ **/
+
+void ipv4FlushFragQueue(NetInterface *interface)
+{
+   uint_t i;
+
+   //Loop through the reassembly queue
+   for(i = 0; i < IPV4_MAX_FRAG_DATAGRAMS; i++)
+   {
+      //Drop any partially reconstructed datagram
+      netBufferSetLength((NetBuffer *) &interface->ipv4Context.fragQueue[i].buffer, 0);
+   }
+}
+
+
+/**
+ * @brief Retrieve hole descriptor
+ * @param[in] frag IPv4 fragment descriptor
+ * @param[in] offset Offset of the hole
+ * @return A pointer to the hole descriptor is returned if the
+ *   specified offset is valid. Otherwise NULL is returned
+ **/
+
+Ipv4HoleDesc *ipv4FindHole(Ipv4FragDesc *frag, uint16_t offset)
+{
+   //Return a pointer to the hole descriptor
+   return netBufferAt((NetBuffer *) &frag->buffer, frag->headerLength + offset);
+}
+
+
+/**
+ * @brief Dump hole descriptor list
+ * @param[in] frag IPv4 fragment descriptor
+ **/
+
+void ipv4DumpHoleList(Ipv4FragDesc *frag)
+{
+//Check debugging level
+#if (TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+   Ipv4HoleDesc *hole;
+
+   //Debug message
+   TRACE_DEBUG("Hole descriptor list:\r\n");
+   //Select the first hole descriptor from the list
+   hole = ipv4FindHole(frag, frag->firstHole);
+
+   //Loop through the hole descriptor list
+   while(hole != NULL)
+   {
+      //Display current hole
+      TRACE_DEBUG("  %" PRIu16 " - %" PRIu16 "\r\n", hole->first, hole->last);
+      //Select the next hole descriptor from the list
+      hole = ipv4FindHole(frag, hole->next);
+   }
+#endif
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv4/ipv4_frag.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,145 @@
+/**
+ * @file ipv4_frag.h
+ * @brief IPv4 fragmentation and reassembly
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _IPV4_FRAG_H
+#define _IPV4_FRAG_H
+
+//Dependencies
+#include "core/net.h"
+#include "ipv4/ipv4.h"
+
+//IPv4 fragmentation support
+#ifndef IPV4_FRAG_SUPPORT
+   #define IPV4_FRAG_SUPPORT DISABLED
+#elif (IPV4_FRAG_SUPPORT != ENABLED && IPV4_FRAG_SUPPORT != DISABLED)
+   #error IPV4_FRAG_SUPPORT parameter is not valid
+#endif
+
+//Reassembly algorithm tick interval
+#ifndef IPV4_FRAG_TICK_INTERVAL
+   #define IPV4_FRAG_TICK_INTERVAL 1000
+#elif (IPV4_FRAG_TICK_INTERVAL < 10)
+   #error IPV4_FRAG_TICK_INTERVAL parameter is not valid
+#endif
+
+//Maximum number of fragmented packets the host will accept
+//and hold in the reassembly queue simultaneously
+#ifndef IPV4_MAX_FRAG_DATAGRAMS
+   #define IPV4_MAX_FRAG_DATAGRAMS 4
+#elif (IPV4_MAX_FRAG_DATAGRAMS < 1)
+   #error IPV4_MAX_FRAG_DATAGRAMS parameter is not valid
+#endif
+
+//Maximum datagram size the host will accept when reassembling fragments
+#ifndef IPV4_MAX_FRAG_DATAGRAM_SIZE
+   #define IPV4_MAX_FRAG_DATAGRAM_SIZE 8192
+#elif (IPV4_MAX_FRAG_DATAGRAM_SIZE < 576)
+   #error IPV4_MAX_FRAG_DATAGRAM_SIZE parameter is not valid
+#endif
+
+//Maximum time an IPv4 fragment can spend waiting to be reassembled
+#ifndef IPV4_FRAG_TIME_TO_LIVE
+   #define IPV4_FRAG_TIME_TO_LIVE 15000
+#elif (IPV4_FRAG_TIME_TO_LIVE < 1000)
+   #error IPV4_FRAG_TIME_TO_LIVE parameter is not valid
+#endif
+
+//Infinity is implemented by a very large integer
+#define IPV4_INFINITY 0xFFFF
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief Hole descriptor
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t first;
+   uint16_t last;
+   uint16_t next;
+} __end_packed Ipv4HoleDesc;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+/**
+ * @brief Reassembly buffer
+ **/
+
+typedef struct
+{
+   uint_t chunkCount;
+   uint_t maxChunkCount;
+   ChunkDesc chunk[N(IPV4_MAX_FRAG_DATAGRAM_SIZE) + 1];
+} Ipv4ReassemblyBuffer;
+
+
+/**
+ * @brief Fragmented packet descriptor
+ **/
+
+typedef struct
+{
+   systime_t timestamp;         ///<Time at which the first fragment was received
+   size_t headerLength;         ///<Length of the header
+   size_t dataLength;           ///<Length of the payload
+   uint16_t firstHole;          ///<Index of the first hole
+   Ipv4ReassemblyBuffer buffer; ///<Buffer containing the reassembled datagram
+} Ipv4FragDesc;
+
+
+//Tick counter to handle periodic operations
+extern systime_t ipv4FragTickCounter;
+
+//IPv4 datagram fragmentation and reassembly
+error_t ipv4FragmentDatagram(NetInterface *interface, Ipv4PseudoHeader *pseudoHeader,
+   uint16_t id, const NetBuffer *payload, size_t payloadOffset, uint8_t timeToLive);
+
+void ipv4ReassembleDatagram(NetInterface *interface,
+   const Ipv4Header *packet, size_t length);
+
+void ipv4FragTick(NetInterface *interface);
+
+Ipv4FragDesc *ipv4SearchFragQueue(NetInterface *interface, const Ipv4Header *packet);
+void ipv4FlushFragQueue(NetInterface *interface);
+
+Ipv4HoleDesc *ipv4FindHole(Ipv4FragDesc *frag, uint16_t offset);
+void ipv4DumpHoleList(Ipv4FragDesc *frag);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv4/ipv4_routing.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,80 @@
+/**
+ * @file ipv4_routing.h
+ * @brief IPv4 routing
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _IPV4_ROUTING_H
+#define _IPV4_ROUTING_H
+
+//Dependencies
+#include "core/net.h"
+#include "ipv4/ipv4.h"
+
+//IPv4 routing support
+#ifndef IPV4_ROUTING_SUPPORT
+   #define IPV4_ROUTING_SUPPORT DISABLED
+#elif (IPV4_ROUTING_SUPPORT != ENABLED && IPV4_ROUTING_SUPPORT != DISABLED)
+   #error IPV4_ROUTING_SUPPORT parameter is not valid
+#endif
+
+//Size of the IPv4 routing table
+#ifndef IPV4_ROUTING_TABLE_SIZE
+   #define IPV4_ROUTING_TABLE_SIZE 8
+#elif (IPV4_ROUTING_TABLE_SIZE < 1)
+   #error IPV4_ROUTING_TABLE_SIZE parameter is not valid
+#endif
+
+
+/**
+ * @brief Routing table entry
+ **/
+
+typedef struct
+{
+   bool_t valid;            ///<Valid entry
+   Ipv4Addr networkDest;    ///<Network destination
+   Ipv4Addr networkMask;    ///<Subnet mask for this route
+   NetInterface *interface; ///<Outgoing network interface
+   Ipv4Addr nextHop;        ///<Next hop
+   uint_t metric;           ///<Metric value
+} Ipv4RoutingTableEntry;
+
+
+//IPv4 routing related functions
+error_t ipv4InitRouting(void);
+error_t ipv4EnableRouting(NetInterface *interface, bool_t enable);
+
+error_t ipv4AddRoute(Ipv4Addr networkDest, Ipv4Addr networkMask,
+   NetInterface *interface, Ipv4Addr nextHop, uint_t metric);
+
+error_t ipv4DeleteRoute(Ipv4Addr networkDest, Ipv4Addr networkMask);
+error_t ipv4DeleteAllRoutes(void);
+
+error_t ipv4ForwardPacket(NetInterface *srcInterface,
+   const NetBuffer *ipPacket, size_t ipPacketOffset);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/icmpv6.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,664 @@
+/**
+ * @file icmpv6.c
+ * @brief ICMPv6 (Internet Control Message Protocol Version 6)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * ICMPv6 is used by IPv6 nodes to report errors encountered in
+ * processing packets, and to perform other Internet-layer functions.
+ * ICMPv6 is an integral part of IPv6 and must be fully implemented
+ * by every IPv6 node. Refer to the RFC 2463 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL ICMPV6_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include "core/net.h"
+#include "core/ip.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_misc.h"
+#include "ipv6/ipv6_pmtu.h"
+#include "ipv6/icmpv6.h"
+#include "ipv6/mld.h"
+#include "ipv6/ndp.h"
+#include "ipv6/ndp_router_adv.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV6_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Enable support for multicast Echo Request messages
+ * @param[in] interface Underlying network interface
+ * @param[in] enable When the flag is set to TRUE, the host will respond to
+ *   multicast Echo Requests. When the flag is set to FALSE, incoming Echo
+ *   Request messages destined to a multicast address will be dropped
+ * @return Error code
+ **/
+
+error_t icmpv6EnableMulticastEchoRequest(NetInterface *interface, bool_t enable)
+{
+   //Check parameters
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Enable or disable support for multicast Echo Request messages
+   interface->ipv6Context.enableMulticastEchoReq = enable;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Incoming ICMPv6 message processing
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IPv6 pseudo header
+ * @param[in] buffer Multi-part buffer containing the incoming ICMPv6 message
+ * @param[in] offset Offset to the first byte of the ICMPv6 message
+ * @param[in] hopLimit Hop Limit field from IPv6 header
+ **/
+
+void icmpv6ProcessMessage(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   const NetBuffer *buffer, size_t offset, uint8_t hopLimit)
+{
+   size_t length;
+   Icmpv6Header *header;
+
+   //Retrieve the length of the ICMPv6 message
+   length = netBufferGetLength(buffer) - offset;
+
+   //Ensure the message length is correct
+   if(length < sizeof(Icmpv6Header))
+   {
+      //Debug message
+      TRACE_WARNING("ICMPv6 message length is invalid!\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Point to the ICMPv6 message header
+   header = netBufferAt(buffer, offset);
+
+   //Sanity check
+   if(header == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("ICMPv6 message received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message contents for debugging purpose
+   icmpv6DumpMessage(header);
+
+   //Verify checksum value
+   if(ipCalcUpperLayerChecksumEx(pseudoHeader,
+      sizeof(Ipv6PseudoHeader), buffer, offset, length) != 0x0000)
+   {
+      //Debug message
+      TRACE_WARNING("Wrong ICMPv6 header checksum!\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Check whether the destination address is the tentative address
+   if(ipv6IsTentativeAddr(interface, &pseudoHeader->destAddr))
+   {
+      //The interface must accept Neighbor Solicitation and
+      //Neighbor Advertisement messages
+      if(header->type != ICMPV6_TYPE_NEIGHBOR_SOL &&
+         header->type != ICMPV6_TYPE_NEIGHBOR_ADV)
+      {
+         //Other packets addressed to the tentative address
+         //should be silently discarded
+         return;
+      }
+   }
+
+   //Check the type of message
+   switch(header->type)
+   {
+   //Destination Unreachable message?
+   case ICMPV6_TYPE_DEST_UNREACHABLE:
+      //Process Destination Unreachable message
+      icmpv6ProcessDestUnreachable(interface, pseudoHeader, buffer, offset);
+      break;
+   //Packet Too Big message?
+   case ICMPV6_TYPE_PACKET_TOO_BIG:
+      //Process Packet Too Big message
+      icmpv6ProcessPacketTooBig(interface, pseudoHeader, buffer, offset);
+      break;
+   //Echo Request message?
+   case ICMPV6_TYPE_ECHO_REQUEST:
+      //Process Echo Request message
+      icmpv6ProcessEchoRequest(interface, pseudoHeader, buffer, offset);
+      break;
+#if (MLD_SUPPORT == ENABLED)
+   //Multicast Listener Query message?
+   case ICMPV6_TYPE_MULTICAST_LISTENER_QUERY:
+      //Process Multicast Listener Query message
+      mldProcessListenerQuery(interface, pseudoHeader, buffer, offset, hopLimit);
+      break;
+   //Version 1 Multicast Listener Report message?
+   case ICMPV6_TYPE_MULTICAST_LISTENER_REPORT_V1:
+      //Process Version 1 Multicast Listener Report message
+      mldProcessListenerReport(interface, pseudoHeader, buffer, offset, hopLimit);
+      break;
+#endif
+#if (NDP_ROUTER_ADV_SUPPORT == ENABLED)
+   //Router Solicitation message?
+   case ICMPV6_TYPE_ROUTER_SOL:
+      //Process Router Solicitation message
+      ndpProcessRouterSol(interface, pseudoHeader, buffer, offset, hopLimit);
+      break;
+#endif
+#if (NDP_SUPPORT == ENABLED)
+   //Router Advertisement message?
+   case ICMPV6_TYPE_ROUTER_ADV:
+      //Process Router Advertisement message
+      ndpProcessRouterAdv(interface, pseudoHeader, buffer, offset, hopLimit);
+      break;
+   //Neighbor Solicitation message?
+   case ICMPV6_TYPE_NEIGHBOR_SOL:
+      //Process Neighbor Solicitation message
+      ndpProcessNeighborSol(interface, pseudoHeader, buffer, offset, hopLimit);
+      break;
+   //Neighbor Advertisement message?
+   case ICMPV6_TYPE_NEIGHBOR_ADV:
+      //Process Neighbor Advertisement message
+      ndpProcessNeighborAdv(interface, pseudoHeader, buffer, offset, hopLimit);
+      break;
+   //Redirect message?
+   case ICMPV6_TYPE_REDIRECT:
+      //Process Redirect message
+      ndpProcessRedirect(interface, pseudoHeader, buffer, offset, hopLimit);
+      break;
+#endif
+   //Unknown type?
+   default:
+      //Debug message
+      TRACE_WARNING("Unknown ICMPv6 message type!\r\n");
+      //Discard incoming ICMPv6 message
+      break;
+   }
+}
+
+
+/**
+ * @brief Destination Unreachable message processing
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IPv6 pseudo header
+ * @param[in] buffer Multi-part buffer containing the incoming ICMPv6 message
+ * @param[in] offset Offset to the first byte of the ICMPv6 message
+ **/
+
+void icmpv6ProcessDestUnreachable(NetInterface *interface,
+   Ipv6PseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+   Icmpv6DestUnreachableMessage *icmpHeader;
+
+   //Retrieve the length of the Destination Unreachable message
+   length = netBufferGetLength(buffer) - offset;
+
+   //Ensure the packet length is correct
+   if(length < sizeof(Icmpv6DestUnreachableMessage))
+      return;
+
+   //Point to the ICMPv6 header
+   icmpHeader = netBufferAt(buffer, offset);
+
+   //Sanity check
+   if(icmpHeader == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("ICMPv6 Destination Unreachable message received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message contents for debugging purpose
+   icmpv6DumpDestUnreachableMessage(icmpHeader);
+}
+
+
+/**
+ * @brief Packet Too Big message processing
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IPv6 pseudo header
+ * @param[in] buffer Multi-part buffer containing the incoming ICMPv6 message
+ * @param[in] offset Offset to the first byte of the ICMPv6 message
+ **/
+
+void icmpv6ProcessPacketTooBig(NetInterface *interface,
+   Ipv6PseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset)
+{
+   size_t length;
+   Icmpv6PacketTooBigMessage *icmpHeader;
+
+#if (IPV6_PMTU_SUPPORT == ENABLED)
+   uint32_t tentativePathMtu;
+   Ipv6Header* ipHeader;
+#endif
+
+   //Retrieve the length of the Packet Too Big message
+   length = netBufferGetLength(buffer) - offset;
+
+   //Ensure the packet length is correct
+   if(length < sizeof(Icmpv6PacketTooBigMessage))
+      return;
+
+   //Point to the ICMPv6 header
+   icmpHeader = netBufferAt(buffer, offset);
+
+   //Sanity check
+   if(icmpHeader == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("ICMPv6 Packet Too Big message received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message contents for debugging purpose
+   icmpv6DumpPacketTooBigMessage(icmpHeader);
+
+#if (IPV6_PMTU_SUPPORT == ENABLED)
+   //Move to the beginning of the original IPv6 packet
+   offset += sizeof(Icmpv6PacketTooBigMessage);
+   length -= sizeof(Icmpv6PacketTooBigMessage);
+
+   //Ensure the packet length is correct
+   if(length < sizeof(Ipv6Header))
+      return;
+
+   //Point to the original IPv6 header
+   ipHeader = netBufferAt(buffer, offset);
+
+   //Sanity check
+   if(ipHeader == NULL)
+      return;
+
+   //The node uses the value in the MTU field in the Packet Too Big
+   //message as a tentative PMTU value
+   tentativePathMtu = ntohl(icmpHeader->mtu);
+
+   //Update the PMTU for the specified destination address
+   ipv6UpdatePathMtu(interface, &ipHeader->destAddr, tentativePathMtu);
+#endif
+}
+
+
+/**
+ * @brief Echo Request message processing
+ * @param[in] interface Underlying network interface
+ * @param[in] requestPseudoHeader IPv6 pseudo header
+ * @param[in] request Multi-part buffer containing the incoming ICMPv6 message
+ * @param[in] requestOffset Offset to the first byte of the ICMPv6 message
+ **/
+
+void icmpv6ProcessEchoRequest(NetInterface *interface, Ipv6PseudoHeader *requestPseudoHeader,
+   const NetBuffer *request, size_t requestOffset)
+{
+   error_t error;
+   size_t requestLength;
+   size_t replyOffset;
+   size_t replyLength;
+   NetBuffer *reply;
+   Icmpv6EchoMessage *requestHeader;
+   Icmpv6EchoMessage *replyHeader;
+   Ipv6PseudoHeader replyPseudoHeader;
+
+   //Retrieve the length of the Echo Request message
+   requestLength = netBufferGetLength(request) - requestOffset;
+
+   //Ensure the packet length is correct
+   if(requestLength < sizeof(Icmpv6EchoMessage))
+      return;
+
+   //Point to the Echo Request header
+   requestHeader = netBufferAt(request, requestOffset);
+
+   //Sanity check
+   if(requestHeader == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("ICMPv6 Echo Request message received (%" PRIuSIZE " bytes)...\r\n", requestLength);
+   //Dump message contents for debugging purpose
+   icmpv6DumpEchoMessage(requestHeader);
+
+   //Check whether the destination address of the Echo Request
+   //message is a multicast address
+   if(ipv6IsMulticastAddr(&requestPseudoHeader->destAddr))
+   {
+      //If support for multicast Echo Request messages has been explicitly
+      //disabled, then the host shall not respond to the incoming request
+      if(!interface->ipv6Context.enableMulticastEchoReq)
+         return;
+
+      //The source address of the reply must be a unicast address belonging to
+      //the interface on which the multicast Echo Request message was received
+      error = ipv6SelectSourceAddr(&interface,
+         &requestPseudoHeader->srcAddr, &replyPseudoHeader.srcAddr);
+
+      //Any error to report?
+      if(error)
+         return;
+   }
+   else
+   {
+      //The destination address of the Echo Request message is a unicast address
+      replyPseudoHeader.srcAddr = requestPseudoHeader->destAddr;
+   }
+
+   //Allocate memory to hold the Echo Reply message
+   reply = ipAllocBuffer(sizeof(Icmpv6EchoMessage), &replyOffset);
+
+   //Failed to allocate memory?
+   if(reply == NULL)
+      return;
+
+   //Point to the Echo Reply header
+   replyHeader = netBufferAt(reply, replyOffset);
+
+   //Format Echo Reply header
+   replyHeader->type = ICMPV6_TYPE_ECHO_REPLY;
+   replyHeader->code = 0;
+   replyHeader->checksum = 0;
+   replyHeader->identifier = requestHeader->identifier;
+   replyHeader->sequenceNumber = requestHeader->sequenceNumber;
+
+   //Point to the first data byte
+   requestOffset += sizeof(Icmpv6EchoMessage);
+   requestLength -= sizeof(Icmpv6EchoMessage);
+
+   //The data received in the ICMPv6 Echo Request message must be returned
+   //entirely and unmodified in the ICMPv6 Echo Reply message
+   error = netBufferConcat(reply, request, requestOffset, requestLength);
+
+   //Check status code
+   if(!error)
+   {
+      //Get the length of the resulting message
+      replyLength = netBufferGetLength(reply) - replyOffset;
+
+      //Format IPv6 pseudo header
+      replyPseudoHeader.destAddr = requestPseudoHeader->srcAddr;
+      replyPseudoHeader.length = htonl(replyLength);
+      replyPseudoHeader.reserved = 0;
+      replyPseudoHeader.nextHeader = IPV6_ICMPV6_HEADER;
+
+      //Message checksum calculation
+      replyHeader->checksum = ipCalcUpperLayerChecksumEx(&replyPseudoHeader,
+         sizeof(Ipv6PseudoHeader), reply, replyOffset, replyLength);
+
+      //Debug message
+      TRACE_INFO("Sending ICMPv6 Echo Reply message (%" PRIuSIZE " bytes)...\r\n", replyLength);
+      //Dump message contents for debugging purpose
+      icmpv6DumpEchoMessage(replyHeader);
+
+      //Send Echo Reply message
+      ipv6SendDatagram(interface, &replyPseudoHeader, reply, replyOffset, 0);
+   }
+
+   //Free previously allocated memory block
+   netBufferFree(reply);
+}
+
+
+/**
+ * @brief Send an ICMPv6 Error message
+ * @param[in] interface Underlying network interface
+ * @param[in] type Message type
+ * @param[in] code Specific message code
+ * @param[in] parameter Specific message parameter
+ * @param[in] ipPacket Multi-part buffer that holds the invoking IPv6 packet
+ * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet
+ * @return Error code
+ **/
+
+error_t icmpv6SendErrorMessage(NetInterface *interface, uint8_t type, uint8_t code,
+   uint32_t parameter, const NetBuffer *ipPacket, size_t ipPacketOffset)
+{
+   error_t error;
+   size_t offset;
+   size_t length;
+   NetBuffer *icmpMessage;
+   Icmpv6ErrorMessage *icmpHeader;
+   Ipv6Header *ipHeader;
+   Ipv6PseudoHeader pseudoHeader;
+
+   //Retrieve the length of the invoking IPv6 packet
+   length = netBufferGetLength(ipPacket) - ipPacketOffset;
+
+   //Check the length of the IPv6 packet
+   if(length < sizeof(Ipv6Header))
+      return ERROR_INVALID_LENGTH;
+
+   //Point to the header of the invoking packet
+   ipHeader = netBufferAt(ipPacket, ipPacketOffset);
+
+   //Sanity check
+   if(ipHeader == NULL)
+      return ERROR_FAILURE;
+
+   //Check the type of the invoking packet
+   if(ipHeader->nextHeader == IPV6_ICMPV6_HEADER)
+   {
+      //Make sure the ICMPv6 message is valid
+      if(length >= (sizeof(Ipv6Header) + sizeof(Icmpv6Header)))
+      {
+         //Point to the ICMPv6 header
+         icmpHeader = netBufferAt(ipPacket, ipPacketOffset + sizeof(Ipv6Header));
+
+         //Sanity check
+         if(icmpHeader != NULL)
+         {
+            //An ICMPv6 error message must not be originated as a result
+            //of receiving an ICMPv6 error or redirect message
+            if(icmpHeader->type == ICMPV6_TYPE_DEST_UNREACHABLE ||
+               icmpHeader->type == ICMPV6_TYPE_PACKET_TOO_BIG ||
+               icmpHeader->type == ICMPV6_TYPE_TIME_EXCEEDED ||
+               icmpHeader->type == ICMPV6_TYPE_PARAM_PROBLEM ||
+               icmpHeader->type == ICMPV6_TYPE_REDIRECT)
+            {
+               //Do not send the ICMPv6 error message...
+               return ERROR_INVALID_TYPE;
+            }
+         }
+      }
+   }
+
+   //An ICMPv6 error message must not be originated as a result of
+   //receiving a packet destined to an IPv6 multicast address
+   if(ipv6IsMulticastAddr(&ipHeader->destAddr))
+   {
+      //There are two exceptions to this rule
+      if(type == ICMPV6_TYPE_PACKET_TOO_BIG)
+      {
+         //The Packet Too Big Message to allow Path MTU discovery to
+         //work for IPv6 multicast
+      }
+      else if(type == ICMPV6_TYPE_PARAM_PROBLEM &&
+         code == ICMPV6_CODE_UNKNOWN_IPV6_OPTION)
+      {
+         //The Parameter Problem Message, reporting an unrecognized IPv6
+         //option that has the Option Type highest-order two bits set to 10
+      }
+      else
+      {
+         //Do not send the ICMPv6 error message...
+         return ERROR_INVALID_ADDRESS;
+      }
+   }
+
+   //An ICMPv6 error message must not be originated as a result of receiving a
+   //packet whose source address does not uniquely identify a single node (e.g.
+   //the IPv6 unspecified address, an IPv6 multicast address, or an address
+   //known by the ICMPv6 message originator to be an IPv6 anycast address)
+   if(ipv6IsAnycastAddr(interface, &ipHeader->srcAddr))
+      return ERROR_INVALID_ADDRESS;
+
+   //Return as much of invoking IPv6 packet as possible without
+   //the ICMPv6 packet exceeding the minimum IPv6 MTU
+   length = MIN(length, IPV6_DEFAULT_MTU -
+      sizeof(Ipv6Header) - sizeof(Icmpv6ErrorMessage));
+
+   //Allocate a memory buffer to hold the ICMPv6 message
+   icmpMessage = ipAllocBuffer(sizeof(Icmpv6ErrorMessage), &offset);
+
+   //Failed to allocate memory?
+   if(icmpMessage == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the ICMPv6 header
+   icmpHeader = netBufferAt(icmpMessage, offset);
+
+   //Format ICMPv6 Error message
+   icmpHeader->type = type;
+   icmpHeader->code = code;
+   icmpHeader->checksum = 0;
+   icmpHeader->parameter = htonl(parameter);
+
+   //Copy incoming IPv6 packet contents
+   error = netBufferConcat(icmpMessage, ipPacket, ipPacketOffset, length);
+
+   //Check status code
+   if(!error)
+   {
+      //Get the length of the resulting message
+      length = netBufferGetLength(icmpMessage) - offset;
+
+      //Format IPv6 pseudo header
+      pseudoHeader.destAddr = ipHeader->srcAddr;
+      pseudoHeader.length = htonl(length);
+      pseudoHeader.reserved = 0;
+      pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER;
+
+      //Select the relevant source address
+      error = ipv6SelectSourceAddr(&interface,
+         &pseudoHeader.destAddr, &pseudoHeader.srcAddr);
+
+      //Check status code
+      if(!error)
+      {
+         //Message checksum calculation
+         icmpHeader->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader,
+            sizeof(Ipv6PseudoHeader), icmpMessage, offset, length);
+
+         //Debug message
+         TRACE_INFO("Sending ICMPv6 Error message (%" PRIuSIZE " bytes)...\r\n", length);
+         //Dump message contents for debugging purpose
+         icmpv6DumpErrorMessage(icmpHeader);
+
+         //Send ICMPv6 Error message
+         error = ipv6SendDatagram(interface, &pseudoHeader, icmpMessage, offset, 0);
+      }
+   }
+
+   //Free previously allocated memory
+   netBufferFree(icmpMessage);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Dump ICMPv6 message for debugging purpose
+ * @param[in] message Pointer to the ICMP message
+ **/
+
+void icmpv6DumpMessage(const Icmpv6Header *message)
+{
+   //Dump ICMPv6 message
+   TRACE_DEBUG("  Type = %" PRIu8 "\r\n", message->type);
+   TRACE_DEBUG("  Code = %" PRIu8 "\r\n", message->code);
+   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
+}
+
+
+/**
+ * @brief Dump ICMPv6 Destination Unreachable message
+ * @param[in] message Pointer to the ICMPv6 message
+ **/
+
+void icmpv6DumpDestUnreachableMessage(const Icmpv6DestUnreachableMessage *message)
+{
+   //Dump ICMPv6 message
+   TRACE_DEBUG("  Type = %" PRIu8 "\r\n", message->type);
+   TRACE_DEBUG("  Code = %" PRIu8 "\r\n", message->code);
+   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
+}
+
+
+/**
+ * @brief Dump ICMPv6 Packet Too Big message
+ * @param[in] message Pointer to the ICMPv6 message
+ **/
+
+void icmpv6DumpPacketTooBigMessage(const Icmpv6PacketTooBigMessage *message)
+{
+   //Dump ICMPv6 message
+   TRACE_DEBUG("  Type = %" PRIu8 "\r\n", message->type);
+   TRACE_DEBUG("  Code = %" PRIu8 "\r\n", message->code);
+   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
+   TRACE_DEBUG("  MTU = %" PRIu32 "\r\n", ntohl(message->mtu));
+}
+
+
+/**
+ * @brief Dump ICMPv6 Echo Request or Echo Reply message
+ * @param[in] message Pointer to the ICMPv6 message
+ **/
+
+void icmpv6DumpEchoMessage(const Icmpv6EchoMessage *message)
+{
+   //Dump ICMPv6 message
+   TRACE_DEBUG("  Type = %" PRIu8 "\r\n", message->type);
+   TRACE_DEBUG("  Code = %" PRIu8 "\r\n", message->code);
+   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
+   TRACE_DEBUG("  Identifier = 0x%04" PRIX16 "\r\n", ntohs(message->identifier));
+   TRACE_DEBUG("  Sequence Number = 0x%04" PRIX16 "\r\n", ntohs(message->sequenceNumber));
+}
+
+
+/**
+ * @brief Dump generic ICMPv6 Error message
+ * @param[in] message Pointer to the ICMPv6 message
+ **/
+
+void icmpv6DumpErrorMessage(const Icmpv6ErrorMessage *message)
+{
+   //Dump ICMP message
+   TRACE_DEBUG("  Type = %" PRIu8 "\r\n", message->type);
+   TRACE_DEBUG("  Code = %" PRIu8 "\r\n", message->code);
+   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
+   TRACE_DEBUG("  Parameter = %" PRIu32 "\r\n", ntohl(message->parameter));
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/icmpv6.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,258 @@
+/**
+ * @file icmpv6.h
+ * @brief ICMPv6 (Internet Control Message Protocol Version 6)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _ICMPV6_H
+#define _ICMPV6_H
+
+//Dependencies
+#include "core/net.h"
+
+
+/**
+ * @brief ICMPv6 message type
+ *
+ * The type field indicates the type of the message. Its
+ * value determines the format of the remaining data
+ *
+ **/
+
+typedef enum
+{
+   ICMPV6_TYPE_DEST_UNREACHABLE             = 1,
+   ICMPV6_TYPE_PACKET_TOO_BIG               = 2,
+   ICMPV6_TYPE_TIME_EXCEEDED                = 3,
+   ICMPV6_TYPE_PARAM_PROBLEM                = 4,
+   ICMPV6_TYPE_ECHO_REQUEST                 = 128,
+   ICMPV6_TYPE_ECHO_REPLY                   = 129,
+   ICMPV6_TYPE_MULTICAST_LISTENER_QUERY     = 130,
+   ICMPV6_TYPE_MULTICAST_LISTENER_REPORT_V1 = 131,
+   ICMPV6_TYPE_MULTICAST_LISTENER_DONE_V1   = 132,
+   ICMPV6_TYPE_ROUTER_SOL                   = 133,
+   ICMPV6_TYPE_ROUTER_ADV                   = 134,
+   ICMPV6_TYPE_NEIGHBOR_SOL                 = 135,
+   ICMPV6_TYPE_NEIGHBOR_ADV                 = 136,
+   ICMPV6_TYPE_REDIRECT                     = 137,
+   ICMPV6_TYPE_MULTICAST_LISTENER_REPORT_V2 = 143
+} Icmpv6Type;
+
+
+/**
+ * @brief Destination Unreachable message codes
+ **/
+
+typedef enum
+{
+   ICMPV6_CODE_NO_ROUTE_TO_DEST         = 0,
+   ICMPV6_CODE_ADMIN_PROHIBITED         = 1,
+   ICMPV6_CODE_BEYOND_SCOPE_OF_SRC_ADDR = 2,
+   ICMPV6_CODE_ADDR_UNREACHABLE         = 3,
+   ICMPV6_CODE_PORT_UNREACHABLE         = 4
+} Icmpv6DestUnreachableCode;
+
+
+/**
+ * @brief Time Exceeded message codes
+ **/
+
+typedef enum
+{
+   ICMPV6_CODE_HOP_LIMIT_EXCEEDED       = 0,
+   ICMPV6_CODE_REASSEMBLY_TIME_EXCEEDED = 1
+} Icmpv6TimeExceededCode;
+
+
+/**
+ * @brief Parameter Problem message codes
+ **/
+typedef enum
+{
+   ICMPV6_CODE_INVALID_HEADER_FIELD = 0,
+   ICMPV6_CODE_UNKNOWN_NEXT_HEADER  = 1,
+   ICMPV6_CODE_UNKNOWN_IPV6_OPTION  = 2
+} Icmpv6ParamProblemCode;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief ICMPv6 header
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;      //0
+   uint8_t code;      //1
+   uint16_t checksum; //2-3
+   uint8_t data[];    //4
+} __end_packed Icmpv6Header;
+
+
+/**
+ * @brief ICMPv6 Error message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;       //0
+   uint8_t code;       //1
+   uint16_t checksum;  //2-3
+   uint32_t parameter; //4-7
+   uint8_t data[];     //8
+} __end_packed Icmpv6ErrorMessage;
+
+
+/**
+ * @brief ICMPv6 Destination Unreachable message
+ *
+ * A Destination Unreachable message is generated in response to a
+ * packet that cannot be delivered to its destination address for
+ * reasons other than congestion
+ *
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;      //0
+   uint8_t code;      //1
+   uint16_t checksum; //2-3
+   uint32_t unused;   //4-7
+   uint8_t data[];    //8
+} __end_packed Icmpv6DestUnreachableMessage;
+
+
+/**
+ * @brief ICMPv6 Packet Too Big message
+ *
+ * A Packet Too Big message is sent by a router in response
+ * to a packet that it cannot forward because the packet is
+ * larger than the MTU of the outgoing link
+ *
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;      //0
+   uint8_t code;      //1
+   uint16_t checksum; //2-3
+   uint32_t mtu;      //4-7
+   uint8_t data[];    //8
+} __end_packed Icmpv6PacketTooBigMessage;
+
+
+/**
+ * @brief ICMPv6 Time Exceeded message
+ *
+ * A Time Exceeded message is sent by a router when it receives
+ * a packet with a Hop Limit of zero
+ *
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;      //0
+   uint8_t code;      //1
+   uint16_t checksum; //2-3
+   uint32_t unused;   //4-7
+   uint8_t data[];    //8
+} __end_packed Icmpv6TimeExceededMessage;
+
+
+/**
+ * @brief ICMPv6 Parameter Problem message
+ *
+ * A Parameter Problem message is sent by an IPv6 node when it finds a
+ * problem with a field in the IPv6 header or extension headers such
+ * that it cannot complete processing the packet
+ *
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;      //0
+   uint8_t code;      //1
+   uint16_t checksum; //2-3
+   uint32_t pointer;  //4-7
+   uint8_t data[];    //8
+} __end_packed Icmpv6ParamProblemMessage;
+
+
+/**
+ * @brief ICMPv6 Echo Request and Echo Reply messages
+ *
+ * Every node must implement an ICMPv6 Echo responder function that
+ * receives Echo Requests and sends corresponding Echo Replies
+ *
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;            //0
+   uint8_t code;            //1
+   uint16_t checksum;       //2-3
+   uint16_t identifier;     //4-6
+   uint16_t sequenceNumber; //7-8
+   uint8_t data[];          //8
+} __end_packed Icmpv6EchoMessage;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+//ICMPv6 related functions
+error_t icmpv6EnableMulticastEchoRequest(NetInterface *interface, bool_t enable);
+
+void icmpv6ProcessMessage(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   const NetBuffer *buffer, size_t offset, uint8_t hopLimit);
+
+void icmpv6ProcessDestUnreachable(NetInterface *interface,
+   Ipv6PseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset);
+
+void icmpv6ProcessPacketTooBig(NetInterface *interface,
+   Ipv6PseudoHeader *pseudoHeader, const NetBuffer *buffer, size_t offset);
+
+void icmpv6ProcessEchoRequest(NetInterface *interface, Ipv6PseudoHeader *requestPseudoHeader,
+   const NetBuffer *request, size_t requestOffset);
+
+error_t icmpv6SendErrorMessage(NetInterface *interface, uint8_t type, uint8_t code,
+   uint32_t parameter, const NetBuffer *ipPacket, size_t ipPacketOffset);
+
+void icmpv6DumpMessage(const Icmpv6Header *message);
+void icmpv6DumpDestUnreachableMessage(const Icmpv6DestUnreachableMessage *message);
+void icmpv6DumpPacketTooBigMessage(const Icmpv6PacketTooBigMessage *message);
+void icmpv6DumpEchoMessage(const Icmpv6EchoMessage *message);
+void icmpv6DumpErrorMessage(const Icmpv6ErrorMessage *message);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/ipv6.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,2138 @@
+/**
+ * @file ipv6.c
+ * @brief IPv6 (Internet Protocol Version 6)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * IP version 6 (IPv6) is a new version of the Internet Protocol, designed
+ * as the successor to IP version 4 (IPv4). Refer to RFC 2460
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL IPV6_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include <ctype.h>
+#include "core/net.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_frag.h"
+#include "ipv6/ipv6_misc.h"
+#include "ipv6/ipv6_pmtu.h"
+#include "ipv6/ipv6_routing.h"
+#include "ipv6/icmpv6.h"
+#include "ipv6/mld.h"
+#include "ipv6/ndp.h"
+#include "ipv6/ndp_cache.h"
+#include "ipv6/ndp_misc.h"
+#include "ipv6/ndp_router_adv.h"
+#include "ipv6/slaac.h"
+#include "dhcpv6/dhcpv6_client.h"
+#include "core/udp.h"
+#include "core/tcp_fsm.h"
+#include "core/raw_socket.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV6_SUPPORT == ENABLED)
+
+//Unspecified IPv6 address
+const Ipv6Addr IPV6_UNSPECIFIED_ADDR =
+   IPV6_ADDR(0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000);
+
+//Loopback IPv6 address
+const Ipv6Addr IPV6_LOOPBACK_ADDR =
+   IPV6_ADDR(0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001);
+
+//Link-local All-Nodes IPv6 address
+const Ipv6Addr IPV6_LINK_LOCAL_ALL_NODES_ADDR =
+   IPV6_ADDR(0xFF02, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001);
+
+//Link-local All-Routers IPv6 address
+const Ipv6Addr IPV6_LINK_LOCAL_ALL_ROUTERS_ADDR =
+   IPV6_ADDR(0xFF02, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002);
+
+//Link-local IPv6 address prefix
+const Ipv6Addr IPV6_LINK_LOCAL_ADDR_PREFIX =
+   IPV6_ADDR(0xFE80, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000);
+
+//Solicited-node IPv6 address prefix
+const Ipv6Addr IPV6_SOLICITED_NODE_ADDR_PREFIX =
+   IPV6_ADDR(0xFF02, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0xFF00, 0x0000);
+
+
+/**
+ * @brief IPv6 related initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ipv6Init(NetInterface *interface)
+{
+   Ipv6Context *context;
+
+   //Point to the IPv6 context
+   context = &interface->ipv6Context;
+
+   //Clear the IPv6 context
+   memset(context, 0, sizeof(Ipv6Context));
+
+   //Initialize interface specific variables
+   context->linkMtu = interface->nicDriver->mtu;
+   context->isRouter = FALSE;
+   context->curHopLimit = IPV6_DEFAULT_HOP_LIMIT;
+
+   //Multicast ICMPv6 Echo Request messages are allowed by default
+   context->enableMulticastEchoReq = TRUE;
+
+   //Initialize the list of IPv6 addresses assigned to the interface
+   memset(context->addrList, 0, sizeof(context->addrList));
+   //Initialize the Prefix List
+   memset(context->prefixList, 0, sizeof(context->prefixList));
+   //Initialize the Default Router List
+   memset(context->routerList, 0, sizeof(context->routerList));
+   //Initialize the list of DNS servers
+   memset(context->dnsServerList, 0, sizeof(context->dnsServerList));
+   //Initialize the multicast filter table
+   memset(context->multicastFilter, 0, sizeof(context->multicastFilter));
+
+#if (IPV6_FRAG_SUPPORT == ENABLED)
+   //Identification field is used to identify fragments of an original IP datagram
+   context->identification = 0;
+   //Initialize the reassembly queue
+   memset(context->fragQueue, 0, sizeof(context->fragQueue));
+#endif
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Change the MTU of a network interface
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] mtu Maximum transmit unit
+ * @return Error code
+ **/
+
+error_t ipv6SetMtu(NetInterface *interface, size_t mtu)
+{
+   error_t error;
+
+   //Check parameters
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Make sure the specified MTU is greater than or equal to the minimum
+   //IPv6 MTU and does not exceed the maximum MTU of the interface
+   if(mtu >= IPV6_DEFAULT_MTU && mtu <= interface->nicDriver->mtu)
+   {
+      //Set the MTU to be used
+      interface->ipv6Context.linkMtu = mtu;
+      //Successful processing
+      error = NO_ERROR;
+   }
+   else
+   {
+      //The specified MTU is not valid
+      error = ERROR_OUT_OF_RANGE;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Retrieve the MTU for the specified interface
+ * @param[in] interface Pointer to the desired network interface
+ * @param[out] mtu Maximum transmit unit
+ * @return Error code
+ **/
+
+error_t ipv6GetMtu(NetInterface *interface, size_t *mtu)
+{
+   //Check parameters
+   if(interface == NULL || mtu == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Return the current MTU value
+   *mtu = interface->ipv6Context.linkMtu;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Assign link-local address
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] addr Link-local address
+ * @return Error code
+ **/
+
+error_t ipv6SetLinkLocalAddr(NetInterface *interface, const Ipv6Addr *addr)
+{
+   error_t error;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+#if (NDP_SUPPORT == ENABLED)
+   //Check whether Duplicate Address Detection should be performed
+   if(interface->ndpContext.dupAddrDetectTransmits > 0)
+   {
+      //Use the link-local address as a tentative address
+      error = ipv6SetAddr(interface, 0, addr, IPV6_ADDR_STATE_TENTATIVE,
+         NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, TRUE);
+   }
+   else
+#endif
+   {
+      //The use of the link-local address is now unrestricted
+      error = ipv6SetAddr(interface, 0, addr, IPV6_ADDR_STATE_PREFERRED,
+         NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, TRUE);
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Retrieve link-local address
+ * @param[in] interface Pointer to the desired network interface
+ * @param[out] addr link-local address
+ * @return Error code
+ **/
+
+error_t ipv6GetLinkLocalAddr(NetInterface *interface, Ipv6Addr *addr)
+{
+   Ipv6AddrEntry *entry;
+
+   //Check parameters
+   if(interface == NULL || addr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Point to the corresponding address entry
+   entry = &interface->ipv6Context.addrList[0];
+
+   //Check whether the IPv6 address is valid
+   if(entry->state == IPV6_ADDR_STATE_PREFERRED ||
+      entry->state == IPV6_ADDR_STATE_DEPRECATED)
+   {
+      //Get IPv6 address
+      *addr = entry->addr;
+   }
+   else
+   {
+      //Return the unspecified address when no address has been assigned
+      *addr = IPV6_UNSPECIFIED_ADDR;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Assign global address
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] index Zero-based index
+ * @param[in] addr Global address
+ * @return Error code
+ **/
+
+error_t ipv6SetGlobalAddr(NetInterface *interface, uint_t index, const Ipv6Addr *addr)
+{
+   error_t error;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+#if (NDP_SUPPORT == ENABLED)
+   //Check whether Duplicate Address Detection should be performed
+   if(interface->ndpContext.dupAddrDetectTransmits > 0)
+   {
+      //Use the global address as a tentative address
+      error = ipv6SetAddr(interface, index + 1, addr, IPV6_ADDR_STATE_TENTATIVE,
+         NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, TRUE);
+   }
+   else
+#endif
+   {
+      //The use of the global address is now unrestricted
+      error = ipv6SetAddr(interface, index + 1, addr, IPV6_ADDR_STATE_PREFERRED,
+         NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, TRUE);
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Retrieve global address
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] index Zero-based index
+ * @param[out] addr Global address
+ * @return Error code
+ **/
+
+error_t ipv6GetGlobalAddr(NetInterface *interface, uint_t index, Ipv6Addr *addr)
+{
+   Ipv6AddrEntry *entry;
+
+   //Check parameters
+   if(interface == NULL || addr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure that the index is valid
+   if((index + 1) >= IPV6_ADDR_LIST_SIZE)
+   {
+      //Return the unspecified address when the index is out of range
+      *addr = IPV6_UNSPECIFIED_ADDR;
+      //Report an error
+      return ERROR_OUT_OF_RANGE;
+   }
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Point to the corresponding address entry
+   entry = &interface->ipv6Context.addrList[index + 1];
+
+   //Check whether the IPv6 address is valid
+   if(entry->state == IPV6_ADDR_STATE_PREFERRED ||
+      entry->state == IPV6_ADDR_STATE_DEPRECATED)
+   {
+      //Get IPv6 address
+      *addr = entry->addr;
+   }
+   else
+   {
+      //Return the unspecified address when no address has been assigned
+      *addr = IPV6_UNSPECIFIED_ADDR;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Assign anycast address
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] index Zero-based index
+ * @param[in] addr Anycast address
+ * @return Error code
+ **/
+
+error_t ipv6SetAnycastAddr(NetInterface *interface, uint_t index, const Ipv6Addr *addr)
+{
+   error_t error;
+   Ipv6Addr *anycastAddrList;
+   Ipv6Addr solicitedNodeAddr;
+
+   //Check parameters
+   if(interface == NULL || addr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure that the index is valid
+   if(index >= IPV6_ANYCAST_ADDR_LIST_SIZE)
+      return ERROR_OUT_OF_RANGE;
+
+   //The IPv6 address must be a valid unicast address
+   if(ipv6IsMulticastAddr(addr))
+      return ERROR_INVALID_ADDRESS;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Point to the list of anycast addresses assigned to the interface
+   anycastAddrList = interface->ipv6Context.anycastAddrList;
+
+   //Check whether an anycast address is already assigned
+   if(!ipv6CompAddr(&anycastAddrList[index], &IPV6_UNSPECIFIED_ADDR))
+   {
+      //Ethernet interface?
+      if(interface->nicDriver->type == NIC_TYPE_ETHERNET)
+      {
+         //Form the Solicited-Node address
+         ipv6ComputeSolicitedNodeAddr(&anycastAddrList[index], &solicitedNodeAddr);
+         //Leave the Solicited-Node multicast group
+         ipv6LeaveMulticastGroup(interface, &solicitedNodeAddr);
+      }
+   }
+
+   //Assign the specified anycast address to the interface
+   anycastAddrList[index] = *addr;
+
+   //Check whether the anycast address is valid
+   if(!ipv6CompAddr(addr, &IPV6_UNSPECIFIED_ADDR))
+   {
+      //Ethernet interface?
+      if(interface->nicDriver->type == NIC_TYPE_ETHERNET)
+      {
+         //Form the Solicited-Node address for the link-local address
+         ipv6ComputeSolicitedNodeAddr(addr, &solicitedNodeAddr);
+         //Join the Solicited-Node multicast group for each assigned address
+         error = ipv6JoinMulticastGroup(interface, &solicitedNodeAddr);
+      }
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Retrieve anycast address
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] index Zero-based index
+ * @param[out] addr Anycast address
+ * @return Error code
+ **/
+
+error_t ipv6GetAnycastAddr(NetInterface *interface, uint_t index, Ipv6Addr *addr)
+{
+   //Check parameters
+   if(interface == NULL || addr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure that the index is valid
+   if(index >= IPV6_ANYCAST_ADDR_LIST_SIZE)
+   {
+      //Return the unspecified address when the index is out of range
+      *addr = IPV6_UNSPECIFIED_ADDR;
+      //Report an error
+      return ERROR_OUT_OF_RANGE;
+   }
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Return the corresponding address entry
+   *addr = interface->ipv6Context.anycastAddrList[index];
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Configure IPv6 prefix
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] index Zero-based index
+ * @param[in] prefix IPv6 prefix
+ * @param[in] length The number of leading bits in the prefix that are valid
+ **/
+
+error_t ipv6SetPrefix(NetInterface *interface,
+   uint_t index, const Ipv6Addr *prefix, uint_t length)
+{
+   Ipv6PrefixEntry *entry;
+
+   //Check parameters
+   if(interface == NULL || prefix == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure that the index is valid
+   if(index >= IPV6_PREFIX_LIST_SIZE)
+      return ERROR_OUT_OF_RANGE;
+
+   //Make sure the prefix length is valid
+   if(length >= 128)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Point to the corresponding entry
+   entry = &interface->ipv6Context.prefixList[index];
+
+   //Set up IPv6 prefix
+   entry->prefix = *prefix;
+   entry->prefixLength = length;
+
+   //Check prefix length
+   if(length > 0)
+   {
+      //Manually assigned prefixes have infinite lifetime
+      entry->validLifetime = INFINITE_DELAY;
+      entry->preferredLifetime = INFINITE_DELAY;
+      entry->permanent = TRUE;
+   }
+   else
+   {
+      //Immediately time-out the entry
+      entry->validLifetime = 0;
+      entry->preferredLifetime = 0;
+      entry->permanent = FALSE;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Retrieve IPv6 prefix
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] index Zero-based index
+ * @param[out] prefix IPv6 prefix
+ * @param[out] length The number of leading bits in the prefix that are valid
+ * @return Error code
+ **/
+
+error_t ipv6GetPrefix(NetInterface *interface,
+   uint_t index, Ipv6Addr *prefix, uint_t *length)
+{
+   Ipv6PrefixEntry *entry;
+
+   //Check parameters
+   if(interface == NULL || prefix == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure that the index is valid
+   if(index >= IPV6_PREFIX_LIST_SIZE)
+   {
+      //Return the ::/0 prefix when the index is out of range
+      *prefix = IPV6_UNSPECIFIED_ADDR;
+      *length = 0;
+      //Report an error
+      return ERROR_OUT_OF_RANGE;
+   }
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Point to the corresponding entry
+   entry = &interface->ipv6Context.prefixList[index];
+
+   //Check whether the prefix is valid
+   if(entry->validLifetime > 0)
+   {
+      //Get IPv6 prefix
+      *prefix = entry->prefix;
+      *length = entry->prefixLength;
+   }
+   else
+   {
+      //Return the ::/0 prefix when the valid lifetime has expired
+      *prefix = IPV6_UNSPECIFIED_ADDR;
+      *length = 0;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Configure default router
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] index Zero-based index
+ * @param[in] addr Default router address
+ * @return Error code
+ **/
+
+error_t ipv6SetDefaultRouter(NetInterface *interface, uint_t index, const Ipv6Addr *addr)
+{
+   Ipv6RouterEntry *entry;
+
+   //Check parameters
+   if(interface == NULL || addr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure that the index is valid
+   if(index >= IPV6_ROUTER_LIST_SIZE)
+      return ERROR_OUT_OF_RANGE;
+
+   //The IPv6 address must be a valid unicast address
+   if(ipv6IsMulticastAddr(addr))
+      return ERROR_INVALID_ADDRESS;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Point to the corresponding entry
+   entry = &interface->ipv6Context.routerList[index];
+
+   //Set up router address
+   entry->addr = *addr;
+
+   //Valid IPv6 address?
+   if(!ipv6CompAddr(addr, &IPV6_UNSPECIFIED_ADDR))
+   {
+      //Manually assigned routers have infinite lifetime
+      entry->lifetime = INFINITE_DELAY;
+      entry->permanent = TRUE;
+   }
+   else
+   {
+      //Immediately time-out the entry
+      entry->lifetime = 0;
+      entry->permanent = FALSE;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Retrieve default router
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] index Zero-based index
+ * @param[out] addr Default router address
+ * @return Error code
+ **/
+
+error_t ipv6GetDefaultRouter(NetInterface *interface, uint_t index, Ipv6Addr *addr)
+{
+   Ipv6RouterEntry *entry;
+
+   //Check parameters
+   if(interface == NULL || addr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure that the index is valid
+   if(index >= IPV6_ROUTER_LIST_SIZE)
+   {
+      //Return the unspecified address when the index is out of range
+      *addr = IPV6_UNSPECIFIED_ADDR;
+      //Report an error
+      return ERROR_OUT_OF_RANGE;
+   }
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Point to the corresponding entry
+   entry = &interface->ipv6Context.routerList[index];
+
+   //Check the lifetime of the entry
+   if(entry->lifetime > 0)
+   {
+      //Get router address
+      *addr = entry->addr;
+   }
+   else
+   {
+      //Return the unspecified address when the lifetime has expired
+      *addr = IPV6_UNSPECIFIED_ADDR;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Configure DNS server
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] index This parameter selects between the primary and secondary DNS server
+ * @param[in] addr DNS server address
+ * @return Error code
+ **/
+
+error_t ipv6SetDnsServer(NetInterface *interface, uint_t index, const Ipv6Addr *addr)
+{
+   //Check parameters
+   if(interface == NULL || addr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure that the index is valid
+   if(index >= IPV6_DNS_SERVER_LIST_SIZE)
+      return ERROR_OUT_OF_RANGE;
+
+   //The IPv6 address must be a valid unicast address
+   if(ipv6IsMulticastAddr(addr))
+      return ERROR_INVALID_ADDRESS;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Set up DNS server address
+   interface->ipv6Context.dnsServerList[index] = *addr;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Retrieve DNS server
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] index This parameter selects between the primary and secondary DNS server
+ * @param[out] addr DNS server address
+ * @return Error code
+ **/
+
+error_t ipv6GetDnsServer(NetInterface *interface, uint_t index, Ipv6Addr *addr)
+{
+   //Check parameters
+   if(interface == NULL || addr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure that the index is valid
+   if(index >= IPV6_DNS_SERVER_LIST_SIZE)
+   {
+      //Return the unspecified address when the index is out of range
+      *addr = IPV6_UNSPECIFIED_ADDR;
+      //Report an error
+      return ERROR_OUT_OF_RANGE;
+   }
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Get DNS server address
+   *addr = interface->ipv6Context.dnsServerList[index];
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Callback function for link change event
+ * @param[in] interface Underlying network interface
+ **/
+
+void ipv6LinkChangeEvent(NetInterface *interface)
+{
+   uint_t i;
+   Ipv6Context *context;
+   Ipv6AddrEntry *entry;
+
+   //Point to the IPv6 context
+   context = &interface->ipv6Context;
+
+   //Restore default parameters
+   context->linkMtu = interface->nicDriver->mtu;
+   context->curHopLimit = IPV6_DEFAULT_HOP_LIMIT;
+
+   //Clear the list of IPv6 addresses
+   ipv6FlushAddrList(interface);
+   //Clear the Prefix List
+   ipv6FlushPrefixList(interface);
+   //Clear the Default Router List
+   ipv6FlushDefaultRouterList(interface);
+
+#if (IPV6_FRAG_SUPPORT == ENABLED)
+   //Flush the reassembly queue
+   ipv6FlushFragQueue(interface);
+#endif
+
+#if (MLD_SUPPORT == ENABLED)
+   //Notify MLD of link state changes
+   mldLinkChangeEvent(interface);
+#endif
+
+#if (NDP_SUPPORT == ENABLED)
+   //Notify NDP of link state changes
+   ndpLinkChangeEvent(interface);
+#endif
+
+#if (NDP_ROUTER_ADV_SUPPORT == ENABLED)
+   //Notify the RA service of link state changes
+   ndpRouterAdvLinkChangeEvent(interface->ndpRouterAdvContext);
+#endif
+
+#if (SLAAC_SUPPORT == ENABLED)
+   //Notify the SLAAC service of link state changes
+   slaacLinkChangeEvent(interface->slaacContext);
+#endif
+
+#if (DHCPV6_CLIENT_SUPPORT == ENABLED)
+   //Notify the DHCPv6 client of link state changes
+   dhcpv6ClientLinkChangeEvent(interface->dhcpv6ClientContext);
+#endif
+
+   //Go through the list of IPv6 addresses
+   for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &context->addrList[i];
+
+      //Check whether the IPv6 address has been manually assigned
+      if(entry->permanent)
+      {
+#if (NDP_SUPPORT == ENABLED)
+         //Check whether Duplicate Address Detection should be performed
+         if(interface->ndpContext.dupAddrDetectTransmits > 0)
+         {
+            //Use the IPv6 address as a tentative address
+            ipv6SetAddr(interface, i, &entry->addr, IPV6_ADDR_STATE_TENTATIVE,
+               NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, TRUE);
+         }
+         else
+#endif
+         {
+            //The use of the IPv6 address is now unrestricted
+            ipv6SetAddr(interface, i, &entry->addr, IPV6_ADDR_STATE_PREFERRED,
+               NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, TRUE);
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Incoming IPv6 packet processing
+ * @param[in] interface Underlying network interface
+ * @param[in] ipPacket Multi-part buffer that holds the incoming IPv6 packet
+ * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet
+ **/
+
+void ipv6ProcessPacket(NetInterface *interface,
+   NetBuffer *ipPacket, size_t ipPacketOffset)
+{
+   error_t error;
+   size_t i;
+   size_t length;
+   size_t nextHeaderOffset;
+   uint8_t *type;
+   Ipv6Header *ipHeader;
+   IpPseudoHeader pseudoHeader;
+
+   //Retrieve the length of the IPv6 packet
+   length = netBufferGetLength(ipPacket);
+
+   //Ensure the packet length is greater than 40 bytes
+   if(length < sizeof(Ipv6Header))
+      return;
+
+   //Point to the IPv6 header
+   ipHeader = netBufferAt(ipPacket, ipPacketOffset);
+   //Sanity check
+   if(ipHeader == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("IPv6 packet received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump IPv6 header contents for debugging purpose
+   ipv6DumpHeader(ipHeader);
+
+   //Check IP version number
+   if(ipHeader->version != IPV6_VERSION)
+      return;
+   //Ensure the payload length is correct before processing the packet
+   if(ntohs(ipHeader->payloadLength) > (length - sizeof(Ipv6Header)))
+      return;
+   //Source address filtering
+   if(ipv6CheckSourceAddr(interface, &ipHeader->srcAddr))
+      return;
+
+#if defined(IPV6_PACKET_FORWARD_HOOK)
+   IPV6_PACKET_FORWARD_HOOK(interface, ipPacket, ipPacketOffset);
+#else
+   //Destination address filtering
+   if(ipv6CheckDestAddr(interface, &ipHeader->destAddr))
+   {
+#if(IPV6_ROUTING_SUPPORT == ENABLED)
+      //Forward the packet according to the routing table
+      ipv6ForwardPacket(interface, ipPacket, ipPacketOffset);
+#endif
+      //We are done
+      return;
+   }
+#endif
+
+   //Calculate the effective length of the multi-part buffer
+   length = ipPacketOffset + sizeof(Ipv6Header) +
+      ntohs(ipHeader->payloadLength);
+
+   //Adjust the length of the multi-part buffer if necessary
+   netBufferSetLength(ipPacket, length);
+
+   //Form the IPv6 pseudo header
+   pseudoHeader.length = sizeof(Ipv6PseudoHeader);
+   pseudoHeader.ipv6Data.srcAddr = ipHeader->srcAddr;
+   pseudoHeader.ipv6Data.destAddr = ipHeader->destAddr;
+   pseudoHeader.ipv6Data.reserved = 0;
+
+   //Keep track of Next Header field
+   nextHeaderOffset = ipPacketOffset + &ipHeader->nextHeader -
+      (uint8_t *) ipHeader;
+
+   //Point to the first extension header
+   i = ipPacketOffset + sizeof(Ipv6Header);
+
+   //Parse extension headers
+   while(i < length)
+   {
+      //Retrieve the Next Header field of preceding header
+      type = netBufferAt(ipPacket, nextHeaderOffset);
+      //Sanity check
+      if(type == NULL)
+         return;
+
+      //Update IPv6 pseudo header
+      pseudoHeader.ipv6Data.length = htonl(length - i);
+      pseudoHeader.ipv6Data.nextHeader = *type;
+
+      //Each extension header is identified by the
+      //Next Header field of the preceding header
+      switch(*type)
+      {
+      //Hop-by-Hop Options header?
+      case IPV6_HOP_BY_HOP_OPT_HEADER:
+         //Parse current extension header
+         error = ipv6ParseHopByHopOptHeader(interface,
+            ipPacket, ipPacketOffset, &i, &nextHeaderOffset);
+         //Continue processing
+         break;
+
+      //Destination Options header?
+      case IPV6_DEST_OPT_HEADER:
+         //Parse current extension header
+         error = ipv6ParseDestOptHeader(interface,
+            ipPacket, ipPacketOffset, &i, &nextHeaderOffset);
+         //Continue processing
+         break;
+
+      //Routing header?
+      case IPV6_ROUTING_HEADER:
+         //Parse current extension header
+         error = ipv6ParseRoutingHeader(interface,
+            ipPacket, ipPacketOffset, &i, &nextHeaderOffset);
+         //Continue processing
+         break;
+
+      //Fragment header?
+      case IPV6_FRAGMENT_HEADER:
+#if (IPV6_FRAG_SUPPORT == ENABLED)
+         //Parse current extension header
+         ipv6ParseFragmentHeader(interface,
+            ipPacket, ipPacketOffset, i, nextHeaderOffset);
+#endif
+         //Exit immediately
+         return;
+
+      //Authentication header?
+      case IPV6_AUTH_HEADER:
+         //Parse current extension header
+         error = ipv6ParseAuthHeader(interface,
+            ipPacket, ipPacketOffset, &i, &nextHeaderOffset);
+         //Continue processing
+         break;
+
+      //Encapsulating Security Payload header?
+      case IPV6_ESP_HEADER:
+         //Parse current extension header
+         error = ipv6ParseEspHeader(interface,
+            ipPacket, ipPacketOffset, &i, &nextHeaderOffset);
+         //Continue processing
+         break;
+
+      //ICMPv6 header?
+      case IPV6_ICMPV6_HEADER:
+         //Process incoming ICMPv6 message
+         icmpv6ProcessMessage(interface, &pseudoHeader.ipv6Data,
+            ipPacket, i, ipHeader->hopLimit);
+
+#if (RAW_SOCKET_SUPPORT == ENABLED)
+         //Packets addressed to the tentative address should be silently discarded
+         if(!ipv6IsTentativeAddr(interface, &ipHeader->destAddr))
+         {
+            //Allow raw sockets to process ICMPv6 messages
+            rawSocketProcessIpPacket(interface, &pseudoHeader, ipPacket, i);
+         }
+#endif
+         //Exit immediately
+         return;
+
+#if (TCP_SUPPORT == ENABLED)
+      //TCP header?
+      case IPV6_TCP_HEADER:
+         //Packets addressed to the tentative address should be silently discarded
+         if(!ipv6IsTentativeAddr(interface, &ipHeader->destAddr))
+         {
+            //Process incoming TCP segment
+            tcpProcessSegment(interface, &pseudoHeader, ipPacket, i);
+         }
+         //Exit immediately
+         return;
+#endif
+
+#if (UDP_SUPPORT == ENABLED)
+      //UDP header?
+      case IPV6_UDP_HEADER:
+         //Packets addressed to the tentative address should be silently discarded
+         if(!ipv6IsTentativeAddr(interface, &ipHeader->destAddr))
+         {
+            //Process incoming UDP datagram
+            error = udpProcessDatagram(interface, &pseudoHeader, ipPacket, i);
+
+            //Unreachable port?
+            if(error == ERROR_PORT_UNREACHABLE)
+            {
+               //A destination node should originate a Destination Unreachable
+               //message with Code 4 in response to a packet for which the
+               //transport protocol has no listener
+               icmpv6SendErrorMessage(interface, ICMPV6_TYPE_DEST_UNREACHABLE,
+                  ICMPV6_CODE_PORT_UNREACHABLE, 0, ipPacket, ipPacketOffset);
+            }
+         }
+         //Exit immediately
+         return;
+#endif
+
+      //No next header?
+      case IPV6_NO_NEXT_HEADER:
+         //If the payload length field of the IPv6 header indicates the presence of
+         //octets past the end of the previous header, these octets must be ignored
+         return;
+
+      //Unrecognized header type?
+      default:
+         //Debug message
+         TRACE_WARNING("Unrecognized Next Header type\r\n");
+
+         //Packets addressed to the tentative address should be silently discarded
+         if(!ipv6IsTentativeAddr(interface, &ipHeader->destAddr))
+         {
+            //Compute the offset of the unrecognized Next Header field within the packet
+            size_t n = nextHeaderOffset - ipPacketOffset;
+
+            //Send an ICMP Parameter Problem message
+            icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM,
+               ICMPV6_CODE_UNKNOWN_NEXT_HEADER, n, ipPacket, ipPacketOffset);
+         }
+
+         //Discard incoming packet
+         return;
+      }
+
+      //Any error while processing the current extension header?
+      if(error)
+         return;
+   }
+}
+
+
+/**
+ * @brief Parse Hop-by-Hop Options header
+ * @param[in] interface Underlying network interface
+ * @param[in] ipPacket Multi-part buffer containing the IPv6 packet
+ * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet
+ * @param[in,out] headerOffset Offset to the Hop-by-Hop Options header
+ * @param[in,out] nextHeaderOffset Offset to the Next Header field
+ * @brief Error code
+ **/
+
+error_t ipv6ParseHopByHopOptHeader(NetInterface *interface, const NetBuffer *ipPacket,
+   size_t ipPacketOffset, size_t *headerOffset, size_t *nextHeaderOffset)
+{
+   error_t error;
+   size_t n;
+   size_t length;
+   size_t headerLength;
+   Ipv6HopByHopOptHeader *header;
+
+   //Remaining bytes to process in the IPv6 packet
+   length = netBufferGetLength(ipPacket) - *headerOffset;
+
+   //Make sure the extension header is valid
+   if(length < sizeof(Ipv6HopByHopOptHeader))
+      return ERROR_INVALID_HEADER;
+
+   //Point to the Hop-by-Hop Options header
+   header = netBufferAt(ipPacket, *headerOffset);
+   //Sanity check
+   if(header == NULL)
+      return ERROR_FAILURE;
+
+   //Calculate the length of the entire header
+   headerLength = (header->hdrExtLen * 8) + 8;
+
+   //Check header length
+   if(headerLength > length)
+      return ERROR_INVALID_HEADER;
+
+   //Debug message
+   TRACE_DEBUG("  Hop-by-Hop Options header\r\n");
+
+   //The Hop-by-Hop Options header, when present, must immediately follow
+   //the IPv6 header
+   if(*headerOffset != (ipPacketOffset + sizeof(Ipv6Header)))
+   {
+      //Compute the offset of the unrecognized Next Header field within the packet
+      n = *nextHeaderOffset - ipPacketOffset;
+
+      //Send an ICMP Parameter Problem message to the source of the packet
+      icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM,
+         ICMPV6_CODE_UNKNOWN_NEXT_HEADER, n, ipPacket, ipPacketOffset);
+
+      //Discard incoming packet
+      return ERROR_INVALID_HEADER;
+   }
+
+   //Compute the length of the Options field
+   n = headerLength - sizeof(Ipv6HopByHopOptHeader);
+
+   //Parse options
+   error = ipv6ParseOptions(interface, ipPacket, ipPacketOffset,
+      *headerOffset + sizeof(Ipv6HopByHopOptHeader), n);
+
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Keep track of Next Header field
+   *nextHeaderOffset = *headerOffset + &header->nextHeader - (uint8_t *) header;
+   //Point to the next extension header
+   *headerOffset += headerLength;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse Destination Options header
+ * @param[in] interface Underlying network interface
+ * @param[in] ipPacket Multi-part buffer containing the IPv6 packet
+ * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet
+ * @param[in,out] headerOffset Offset to the Destination Options header
+ * @param[in,out] nextHeaderOffset Offset to the Next Header field
+ * @brief Error code
+ **/
+
+error_t ipv6ParseDestOptHeader(NetInterface *interface, const NetBuffer *ipPacket,
+   size_t ipPacketOffset, size_t *headerOffset, size_t *nextHeaderOffset)
+{
+   error_t error;
+   size_t n;
+   size_t length;
+   size_t headerLength;
+   Ipv6DestOptHeader *header;
+
+   //Remaining bytes to process in the IPv6 packet
+   length = netBufferGetLength(ipPacket) - *headerOffset;
+
+   //Make sure the extension header is valid
+   if(length < sizeof(Ipv6DestOptHeader))
+      return ERROR_INVALID_HEADER;
+
+   //Point to the Destination Options header
+   header = netBufferAt(ipPacket, *headerOffset);
+   //Sanity check
+   if(header == NULL)
+      return ERROR_FAILURE;
+
+   //Calculate the length of the entire header
+   headerLength = (header->hdrExtLen * 8) + 8;
+
+   //Check header length
+   if(headerLength > length)
+      return ERROR_INVALID_HEADER;
+
+   //Debug message
+   TRACE_DEBUG("  Destination Options header\r\n");
+
+   //Compute the length of the Options field
+   n = headerLength - sizeof(Ipv6DestOptHeader);
+
+   //Parse options
+   error = ipv6ParseOptions(interface, ipPacket, ipPacketOffset,
+      *headerOffset + sizeof(Ipv6DestOptHeader), n);
+
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Keep track of Next Header field
+   *nextHeaderOffset = *headerOffset + &header->nextHeader - (uint8_t *) header;
+   //Point to the next extension header
+   *headerOffset += headerLength;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse Routing header
+ * @param[in] interface Underlying network interface
+ * @param[in] ipPacket Multi-part buffer containing the IPv6 packet
+ * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet
+ * @param[in,out] headerOffset Offset to the Routing header
+ * @param[in,out] nextHeaderOffset Offset to the Next Header field
+ * @brief Error code
+ **/
+
+error_t ipv6ParseRoutingHeader(NetInterface *interface, const NetBuffer *ipPacket,
+   size_t ipPacketOffset, size_t *headerOffset, size_t *nextHeaderOffset)
+{
+   size_t n;
+   size_t length;
+   size_t headerLength;
+   Ipv6RoutingHeader *header;
+
+   //Remaining bytes to process in the IPv6 packet
+   length = netBufferGetLength(ipPacket) - *headerOffset;
+
+   //Make sure the extension header is valid
+   if(length < sizeof(Ipv6RoutingHeader))
+      return ERROR_INVALID_HEADER;
+
+   //Point to the Routing header
+   header = netBufferAt(ipPacket, *headerOffset);
+   //Sanity check
+   if(header == NULL)
+      return ERROR_FAILURE;
+
+   //Calculate the length of the entire header
+   headerLength = (header->hdrExtLen * 8) + 8;
+
+   //Check header length
+   if(headerLength > length)
+      return ERROR_INVALID_HEADER;
+
+   //Debug message
+   TRACE_DEBUG("  Routing header\r\n");
+
+   //If, while processing a received packet, a node encounters a Routing
+   //header with an unrecognized Routing Type value, the required behavior
+   //of the node depends on the value of the Segments Left field
+   if(header->segmentsLeft != 0)
+   {
+      //Retrieve the offset of the Routing header within the packet
+      n = *headerOffset - ipPacketOffset;
+      //Compute the exact offset of the Routing Type field
+      n += (uint8_t *) &header->routingType - (uint8_t *) header;
+
+      //If Segments Left is non-zero, send an ICMP Parameter Problem,
+      //Code 0, message to the packet's Source Address, pointing to
+      //the unrecognized Routing Type
+      icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM,
+         ICMPV6_CODE_INVALID_HEADER_FIELD, n, ipPacket, ipPacketOffset);
+
+      //The node must discard the packet
+      return ERROR_INVALID_TYPE;
+   }
+
+   //Keep track of Next Header field
+   *nextHeaderOffset = *headerOffset + &header->nextHeader - (uint8_t *) header;
+   //Point to the next extension header
+   *headerOffset += headerLength;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse Authentication header
+ * @param[in] interface Underlying network interface
+ * @param[in] ipPacket Multi-part buffer containing the IPv6 packet
+ * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet
+ * @param[in,out] headerOffset Offset to the Authentication header
+ * @param[in,out] nextHeaderOffset Offset to the Next Header field
+ * @brief Error code
+ **/
+
+error_t ipv6ParseAuthHeader(NetInterface *interface, const NetBuffer *ipPacket,
+   size_t ipPacketOffset, size_t *headerOffset, size_t *nextHeaderOffset)
+{
+   //Debug message
+   TRACE_DEBUG("  Authentication header\r\n");
+   //Authentication not supported
+   return ERROR_FAILURE;
+}
+
+
+/**
+ * @brief Parse Encapsulating Security Payload header
+ * @param[in] interface Underlying network interface
+ * @param[in] ipPacket Multi-part buffer containing the IPv6 packet
+ * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet
+ * @param[in,out] headerOffset Offset to the Encapsulating Security Payload header
+ * @param[in,out] nextHeaderOffset Offset to the Next Header field
+ * @brief Error code
+ **/
+
+error_t ipv6ParseEspHeader(NetInterface *interface, const NetBuffer *ipPacket,
+   size_t ipPacketOffset, size_t *headerOffset, size_t *nextHeaderOffset)
+{
+   //Debug message
+   TRACE_DEBUG("  Encapsulating Security Payload header\r\n");
+   //Authentication not supported
+   return ERROR_FAILURE;
+}
+
+
+/**
+ * @brief Parse IPv6 options
+ * @param[in] interface Underlying network interface
+ * @param[in] ipPacket Multi-part buffer containing the IPv6 packet
+ * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet
+ * @param[in] optOffset Offset to the first byte of the Options field
+ * @param[in] optLength Length of the Options field
+ * @brief Error code
+ **/
+
+error_t ipv6ParseOptions(NetInterface *interface, const NetBuffer *ipPacket,
+   size_t ipPacketOffset, size_t optOffset, size_t optLength)
+{
+   size_t i;
+   size_t n;
+   uint8_t type;
+   uint8_t action;
+   uint8_t *options;
+   Ipv6Option *option;
+   Ipv6Header *ipHeader;
+
+   //Point to the first byte of the Options field
+   options = netBufferAt(ipPacket, optOffset);
+
+   //Sanity check
+   if(options == NULL)
+      return ERROR_FAILURE;
+
+   //Parse options
+   for(i = 0; i < optLength; )
+   {
+      //Point to the current option
+      option = (Ipv6Option *) (options + i);
+      //Get option type
+      type = option->type & IPV6_OPTION_TYPE_MASK;
+
+      //Pad1 option?
+      if(type == IPV6_OPTION_TYPE_PAD1)
+      {
+         //Advance data pointer
+         i++;
+      }
+      //PadN option?
+      else if(type == IPV6_OPTION_TYPE_PADN)
+      {
+         //Malformed IPv6 packet?
+         if((i + sizeof(Ipv6Option)) > optLength)
+            return ERROR_INVALID_LENGTH;
+
+         //Advance data pointer
+         i += sizeof(Ipv6Option) + option->length;
+      }
+      //Unrecognized option?
+      else
+      {
+         //Point to the IPv6 header
+         ipHeader = netBufferAt(ipPacket, ipPacketOffset);
+
+         //Sanity check
+         if(ipHeader == NULL)
+            return ERROR_FAILURE;
+
+         //Get the value of the highest-order two bits
+         action = option->type & IPV6_ACTION_MASK;
+
+         //The highest-order two bits specify the action that must be taken
+         //if the processing IPv6 node does not recognize the option type
+         if(action == IPV6_ACTION_SKIP_OPTION)
+         {
+            //Skip over this option and continue processing the header
+         }
+         else if(action == IPV6_ACTION_DISCARD_PACKET)
+         {
+            //Discard the packet
+            return ERROR_INVALID_OPTION;
+         }
+         else if(action == IPV6_ACTION_SEND_ICMP_ERROR_ALL)
+         {
+            //Calculate the octet offset within the invoking packet
+            //where the error was detected
+            n = optOffset + i - ipPacketOffset;
+
+            //Send an ICMP Parameter Problem message to the source of the
+            //packet, regardless of whether or not the destination address
+            //was a multicast address
+            icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM,
+               ICMPV6_CODE_UNKNOWN_IPV6_OPTION, n, ipPacket, ipPacketOffset);
+
+            //Discard the packet
+            return ERROR_INVALID_OPTION;
+         }
+         else if(action == IPV6_ACTION_SEND_ICMP_ERROR_UNI)
+         {
+            //Send an ICMP Parameter Problem message to the source of the
+            //packet, only if the destination address was not a multicast
+            //address
+            if(!ipv6IsMulticastAddr(&ipHeader->destAddr))
+            {
+               //Calculate the octet offset within the invoking packet
+               //where the error was detected
+               n = optOffset + i - ipPacketOffset;
+
+               //Send the ICMP Parameter Problem message
+               icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM,
+                  ICMPV6_CODE_UNKNOWN_IPV6_OPTION, n, ipPacket, ipPacketOffset);
+            }
+
+            //Discard the packet
+            return ERROR_INVALID_OPTION;
+         }
+
+         //Malformed IPv6 packet?
+         if((i + sizeof(Ipv6Option)) > optLength)
+            return ERROR_INVALID_LENGTH;
+
+         //Advance data pointer
+         i += sizeof(Ipv6Option) + option->length;
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Send an IPv6 datagram
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IPv6 pseudo header
+ * @param[in] buffer Multi-part buffer containing the payload
+ * @param[in] offset Offset to the first byte of the payload
+ * @param[in] hopLimit Hop Limit value. Default value is used when this parameter is zero
+ * @return Error code
+ **/
+
+error_t ipv6SendDatagram(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   NetBuffer *buffer, size_t offset, uint8_t hopLimit)
+{
+   error_t error;
+   size_t length;
+   size_t pathMtu;
+
+   //Retrieve the length of payload
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check whether the Hop Limit value is zero
+   if(hopLimit == 0)
+   {
+      //Use default Hop Limit value
+      hopLimit = interface->ipv6Context.curHopLimit;
+   }
+
+#if (IPV6_PMTU_SUPPORT == ENABLED)
+   //Retrieve the PMTU for the specified destination address
+   pathMtu = ipv6GetPathMtu(interface, &pseudoHeader->destAddr);
+
+   //The PMTU should not exceed the MTU of the first-hop link
+   if(pathMtu > interface->ipv6Context.linkMtu)
+      pathMtu = interface->ipv6Context.linkMtu;
+#else
+   //The PMTU value for the path is assumed to be the MTU of the first-hop link
+   pathMtu = interface->ipv6Context.linkMtu;
+#endif
+
+   //If the payload is smaller than the PMTU then no fragmentation is needed
+   if((length + sizeof(Ipv6Header)) <= pathMtu)
+   {
+      //Send data as is
+      error = ipv6SendPacket(interface, pseudoHeader,
+         0, 0, buffer, offset, hopLimit);
+   }
+   //If the payload length exceeds the PMTU then the device must fragment the data
+   else
+   {
+#if (IPV6_FRAG_SUPPORT == ENABLED)
+      //Fragment IP datagram into smaller packets
+      error = ipv6FragmentDatagram(interface, pseudoHeader,
+         buffer, offset, pathMtu, hopLimit);
+#else
+      //Fragmentation is not supported
+      error = ERROR_MESSAGE_TOO_LONG;
+#endif
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send an IPv6 packet
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IPv6 pseudo header
+ * @param[in] fragId Fragment identification field
+ * @param[in] fragOffset Fragment offset field
+ * @param[in] buffer Multi-part buffer containing the payload
+ * @param[in] offset Offset to the first byte of the payload
+ * @param[in] hopLimit Hop Limit value
+ * @return Error code
+ **/
+
+error_t ipv6SendPacket(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   uint32_t fragId, size_t fragOffset, NetBuffer *buffer, size_t offset, uint8_t hopLimit)
+{
+   error_t error;
+   size_t length;
+   Ipv6Header *packet;
+
+   //Calculate the length of the payload
+   length = netBufferGetLength(buffer) - offset;
+
+   //Add Fragment header?
+   if(fragOffset != 0)
+   {
+      Ipv6FragmentHeader *header;
+
+      //Is there enough space for the IPv6 header and the Fragment header?
+      if(offset < (sizeof(Ipv6Header) + sizeof(Ipv6FragmentHeader)))
+         return ERROR_INVALID_PARAMETER;
+
+      //Make room for the Fragment header
+      offset -= sizeof(Ipv6FragmentHeader);
+      length += sizeof(Ipv6FragmentHeader);
+
+      //Point to the Fragment header
+      header = netBufferAt(buffer, offset);
+      //Format the Fragment header
+      header->nextHeader = pseudoHeader->nextHeader;
+      header->reserved = 0;
+      header->fragmentOffset = htons(fragOffset);
+      header->identification = htonl(fragId);
+
+      //Make room for the IPv6 header
+      offset -= sizeof(Ipv6Header);
+      length += sizeof(Ipv6Header);
+
+      //Point to the IPv6 header
+      packet = netBufferAt(buffer, offset);
+      //Properly set the Next Header field
+      packet->nextHeader = IPV6_FRAGMENT_HEADER;
+   }
+   else
+   {
+      //Is there enough space for the IPv6 header?
+      if(offset < sizeof(Ipv6Header))
+         return ERROR_INVALID_PARAMETER;
+
+      //Make room for the IPv6 header
+      offset -= sizeof(Ipv6Header);
+      length += sizeof(Ipv6Header);
+
+      //Point to the IPv6 header
+      packet = netBufferAt(buffer, offset);
+      //Properly set the Next Header field
+      packet->nextHeader = pseudoHeader->nextHeader;
+   }
+
+   //Format IPv6 header
+   packet->version = IPV6_VERSION;
+   packet->trafficClassH = 0;
+   packet->trafficClassL = 0;
+   packet->flowLabelH = 0;
+   packet->flowLabelL = 0;
+   packet->payloadLength = htons(length - sizeof(Ipv6Header));
+   packet->hopLimit = hopLimit;
+   packet->srcAddr = pseudoHeader->srcAddr;
+   packet->destAddr = pseudoHeader->destAddr;
+
+   //Check whether the source address is acceptable
+   error = ipv6CheckSourceAddr(interface, &pseudoHeader->srcAddr);
+   //Invalid source address?
+   if(error)
+      return error;
+
+   //Destination IPv6 address is the unspecified address?
+   if(ipv6CompAddr(&pseudoHeader->destAddr, &IPV6_UNSPECIFIED_ADDR))
+   {
+      //Destination address is not acceptable
+      return ERROR_INVALID_ADDRESS;
+   }
+   //Destination address is the loopback address?
+   else if(ipv6CompAddr(&pseudoHeader->destAddr, &IPV6_LOOPBACK_ADDR))
+   {
+      //Not yet implemented...
+      return ERROR_NOT_IMPLEMENTED;
+   }
+
+#if (ETH_SUPPORT == ENABLED)
+   //Ethernet interface?
+   if(interface->nicDriver->type == NIC_TYPE_ETHERNET)
+   {
+      Ipv6Addr destIpAddr;
+      MacAddr destMacAddr;
+      NdpDestCacheEntry *entry;
+
+      //When the sending node has a packet to send, it first examines
+      //the Destination Cache
+      entry = ndpFindDestCacheEntry(interface, &pseudoHeader->destAddr);
+
+      //Check whether a matching entry exists
+      if(entry != NULL)
+      {
+         //Retrieve the address of the next-hop
+         destIpAddr = entry->nextHop;
+         //Update timestamp
+         entry->timestamp = osGetSystemTime();
+         //No error to report
+         error = NO_ERROR;
+      }
+      else
+      {
+         //Perform next-hop determination
+         error = ndpSelectNextHop(interface,
+            &pseudoHeader->destAddr, NULL, &destIpAddr);
+
+         //Check status code
+         if(error == NO_ERROR)
+         {
+            //Create a new Destination Cache entry
+            entry = ndpCreateDestCacheEntry(interface);
+
+            //Destination cache entry successfully created?
+            if(entry != NULL)
+            {
+               //Destination address
+               entry->destAddr = pseudoHeader->destAddr;
+               //Address of the next hop
+               entry->nextHop = destIpAddr;
+
+               //Initially, the PMTU value for a path is assumed to be
+               //the MTU of the first-hop link
+               entry->pathMtu = interface->ipv6Context.linkMtu;
+
+               //Set timestamp
+               entry->timestamp = osGetSystemTime();
+            }
+         }
+      }
+
+      //Successful next-hop determination?
+      if(error == NO_ERROR)
+      {
+         //Destination IPv6 address is a multicast address?
+         if(ipv6IsMulticastAddr(&destIpAddr))
+         {
+            //Map IPv6 multicast address to MAC-layer multicast address
+            error = ipv6MapMulticastAddrToMac(&destIpAddr, &destMacAddr);
+         }
+         else
+         {
+            //Resolve host address using Neighbor Discovery protocol
+            error = ndpResolve(interface, &destIpAddr, &destMacAddr);
+         }
+
+         //Successful address resolution?
+         if(error == NO_ERROR)
+         {
+            //Debug message
+            TRACE_INFO("Sending IPv6 packet (%" PRIuSIZE " bytes)...\r\n", length);
+            //Dump IP header contents for debugging purpose
+            ipv6DumpHeader(packet);
+
+            //Send Ethernet frame
+            error = ethSendFrame(interface, &destMacAddr, buffer, offset, ETH_TYPE_IPV6);
+         }
+         //Address resolution is in progress?
+         else if(error == ERROR_IN_PROGRESS)
+         {
+            //Debug message
+            TRACE_INFO("Enqueuing IPv6 packet (%" PRIuSIZE " bytes)...\r\n", length);
+            //Dump IP header contents for debugging purpose
+            ipv6DumpHeader(packet);
+
+            //Enqueue packets waiting for address resolution
+            error = ndpEnqueuePacket(NULL, interface, &destIpAddr, buffer, offset);
+         }
+         //Address resolution failed?
+         else
+         {
+            //Debug message
+            TRACE_WARNING("Cannot map IPv6 address to Ethernet address!\r\n");
+         }
+      }
+   }
+   else
+#endif
+#if (PPP_SUPPORT == ENABLED)
+   //PPP interface?
+   if(interface->nicDriver->type == NIC_TYPE_PPP)
+   {
+      //Debug message
+      TRACE_INFO("Sending IPv6 packet (%" PRIuSIZE " bytes)...\r\n", length);
+      //Dump IP header contents for debugging purpose
+      ipv6DumpHeader(packet);
+
+      //Send PPP frame
+      error = pppSendFrame(interface, buffer, offset, PPP_PROTOCOL_IPV6);
+   }
+   else
+#endif
+   //6LoWPAN interface?
+   if(interface->nicDriver->type == NIC_TYPE_6LOWPAN)
+   {
+      //Debug message
+      TRACE_INFO("Sending IPv6 packet (%" PRIuSIZE " bytes)...\r\n", length);
+      //Dump IP header contents for debugging purpose
+      ipv6DumpHeader(packet);
+
+      //Send the packet over the specified link
+      error = nicSendPacket(interface, buffer, offset);
+   }
+   else
+   //Unknown interface type?
+   {
+      //Report an error
+      error = ERROR_INVALID_INTERFACE;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Join an IPv6 multicast group
+ * @param[in] interface Underlying network interface
+ * @param[in] groupAddr IPv6 Multicast address to join
+ * @return Error code
+ **/
+
+error_t ipv6JoinMulticastGroup(NetInterface *interface, const Ipv6Addr *groupAddr)
+{
+   error_t error;
+   uint_t i;
+   Ipv6FilterEntry *entry;
+   Ipv6FilterEntry *firstFreeEntry;
+#if (ETH_SUPPORT == ENABLED)
+   MacAddr macAddr;
+#endif
+
+   //The IPv6 address must be a valid multicast address
+   if(!ipv6IsMulticastAddr(groupAddr))
+      return ERROR_INVALID_ADDRESS;
+
+   //Initialize error code
+   error = NO_ERROR;
+   //Keep track of the first free entry
+   firstFreeEntry = NULL;
+
+   //Go through the multicast filter table
+   for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.multicastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Check whether the table already contains the specified IPv6 address
+         if(ipv6CompAddr(&entry->addr, groupAddr))
+         {
+            //Increment the reference count
+            entry->refCount++;
+            //Successful processing
+            return NO_ERROR;
+         }
+      }
+      else
+      {
+         //Keep track of the first free entry
+         if(firstFreeEntry == NULL)
+            firstFreeEntry = entry;
+      }
+   }
+
+   //Check whether the multicast filter table is full
+   if(firstFreeEntry == NULL)
+   {
+      //A new entry cannot be added
+      return ERROR_FAILURE;
+   }
+
+#if (ETH_SUPPORT == ENABLED)
+   //Map the IPv6 multicast address to a MAC-layer address
+   ipv6MapMulticastAddrToMac(groupAddr, &macAddr);
+   //Add the corresponding address to the MAC filter table
+   error = ethAcceptMulticastAddr(interface, &macAddr);
+#endif
+
+   //MAC filter table successfully updated?
+   if(!error)
+   {
+      //Now we can safely add a new entry to the table
+      firstFreeEntry->addr = *groupAddr;
+      //Initialize the reference count
+      firstFreeEntry->refCount = 1;
+
+#if (MLD_SUPPORT == ENABLED)
+      //Start listening to the multicast address
+      mldStartListening(interface, firstFreeEntry);
+#endif
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Leave an IPv6 multicast group
+ * @param[in] interface Underlying network interface
+ * @param[in] groupAddr IPv6 multicast address to drop
+ * @return Error code
+ **/
+
+error_t ipv6LeaveMulticastGroup(NetInterface *interface, const Ipv6Addr *groupAddr)
+{
+   uint_t i;
+   Ipv6FilterEntry *entry;
+#if (ETH_SUPPORT == ENABLED)
+   MacAddr macAddr;
+#endif
+
+   //The IPv6 address must be a valid multicast address
+   if(!ipv6IsMulticastAddr(groupAddr))
+      return ERROR_INVALID_ADDRESS;
+
+   //Go through the multicast filter table
+   for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.multicastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Specified IPv6 address found?
+         if(ipv6CompAddr(&entry->addr, groupAddr))
+         {
+            //Decrement the reference count
+            entry->refCount--;
+
+            //Remove the entry if the reference count drops to zero
+            if(entry->refCount == 0)
+            {
+#if (MLD_SUPPORT == ENABLED)
+               //Stop listening to the multicast address
+               mldStopListening(interface, entry);
+#endif
+#if (ETH_SUPPORT == ENABLED)
+               //Map the IPv6 multicast address to a MAC-layer address
+               ipv6MapMulticastAddrToMac(groupAddr, &macAddr);
+               //Drop the corresponding address from the MAC filter table
+               ethDropMulticastAddr(interface, &macAddr);
+#endif
+               //Remove the multicast address from the list
+               entry->addr = IPV6_UNSPECIFIED_ADDR;
+            }
+
+            //Successful processing
+            return NO_ERROR;
+         }
+      }
+   }
+
+   //The specified IPv6 address does not exist
+   return ERROR_ADDRESS_NOT_FOUND;
+}
+
+
+/**
+ * @brief Convert a string representation of an IPv6 address to a binary IPv6 address
+ * @param[in] str NULL-terminated string representing the IPv6 address
+ * @param[out] ipAddr Binary representation of the IPv6 address
+ * @return Error code
+ **/
+
+error_t ipv6StringToAddr(const char_t *str, Ipv6Addr *ipAddr)
+{
+   error_t error;
+   int_t i = 0;
+   int_t j = -1;
+   int_t k = 0;
+   int32_t value = -1;
+
+   //Parse input string
+   while(1)
+   {
+      //Hexadecimal digit found?
+      if(isxdigit((uint8_t) *str))
+      {
+         //First digit to be decoded?
+         if(value < 0)
+            value = 0;
+
+         //Update the value of the current 16-bit word
+         if(isdigit((uint8_t) *str))
+            value = (value * 16) + (*str - '0');
+         else if(isupper((uint8_t) *str))
+            value = (value * 16) + (*str - 'A' + 10);
+         else
+            value = (value * 16) + (*str - 'a' + 10);
+
+         //Check resulting value
+         if(value > 0xFFFF)
+         {
+            //The conversion failed
+            error = ERROR_INVALID_SYNTAX;
+            break;
+         }
+      }
+      //"::" symbol found?
+      else if(!strncmp(str, "::", 2))
+      {
+         //The "::" can only appear once in an IPv6 address
+         if(j >= 0)
+         {
+            //The conversion failed
+            error = ERROR_INVALID_SYNTAX;
+            break;
+         }
+
+         //The "::" symbol is preceded by a number?
+         if(value >= 0)
+         {
+            //Save the current 16-bit word
+            ipAddr->w[i++] = htons(value);
+            //Prepare to decode the next 16-bit word
+            value = -1;
+         }
+
+         //Save the position of the "::" symbol
+         j = i;
+         //Point to the next character
+         str++;
+      }
+      //":" symbol found?
+      else if(*str == ':' && i < 8)
+      {
+         //Each ":" must be preceded by a valid number
+         if(value < 0)
+         {
+            //The conversion failed
+            error = ERROR_INVALID_SYNTAX;
+            break;
+         }
+
+         //Save the current 16-bit word
+         ipAddr->w[i++] = htons(value);
+         //Prepare to decode the next 16-bit word
+         value = -1;
+      }
+      //End of string detected?
+      else if(*str == '\0' && i == 7 && j < 0)
+      {
+         //The NULL character must be preceded by a valid number
+         if(value < 0)
+         {
+            //The conversion failed
+            error = ERROR_INVALID_SYNTAX;
+         }
+         else
+         {
+            //Save the last 16-bit word of the IPv6 address
+            ipAddr->w[i] = htons(value);
+            //The conversion succeeded
+            error = NO_ERROR;
+         }
+
+         //We are done
+         break;
+      }
+      else if(*str == '\0' && i < 7 && j >= 0)
+      {
+         //Save the last 16-bit word of the IPv6 address
+         if(value >= 0)
+            ipAddr->w[i++] = htons(value);
+
+         //Move the part of the address that follows the "::" symbol
+         for(k = 0; k < (i - j); k++)
+            ipAddr->w[7 - k] = ipAddr->w[i - 1 - k];
+         //A sequence of zeroes can now be written in place of "::"
+         for(k = 0; k < (8 - i); k++)
+            ipAddr->w[j + k] = 0;
+
+         //The conversion succeeded
+         error = NO_ERROR;
+         break;
+      }
+      //Invalid character...
+      else
+      {
+         //The conversion failed
+         error = ERROR_INVALID_SYNTAX;
+         break;
+      }
+
+      //Point to the next character
+      str++;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Convert a binary IPv6 address to a string representation
+ *
+ * Call ipv6AddrToString() to convert an IPv6 address to a text representation. The
+ * implementation of ipv6AddrToString() function follows RFC 5952 recommendations
+ *
+ * @param[in] ipAddr Binary representation of the IPv6 address
+ * @param[out] str NULL-terminated string representing the IPv6 address
+ * @return Pointer to the formatted string
+ **/
+
+char_t *ipv6AddrToString(const Ipv6Addr *ipAddr, char_t *str)
+{
+   static char_t buffer[40];
+   uint_t i;
+   uint_t j;
+   char_t *p;
+
+   //Best run of zeroes
+   uint_t zeroRunStart = 0;
+   uint_t zeroRunEnd = 0;
+
+   //If the NULL pointer is given as parameter, then the internal buffer is used
+   if(str == NULL)
+      str = buffer;
+
+   //Find the longest run of zeros for "::" short-handing
+   for(i = 0; i < 8; i++)
+   {
+      //Compute the length of the current sequence of zeroes
+      for(j = i; j < 8 && !ipAddr->w[j]; j++);
+
+      //Keep track of the longest one
+      if((j - i) > 1 && (j - i) > (zeroRunEnd - zeroRunStart))
+      {
+         //The symbol "::" should not be used to shorten just one zero field
+         zeroRunStart = i;
+         zeroRunEnd = j;
+      }
+   }
+
+   //Format IPv6 address
+   for(p = str, i = 0; i < 8; i++)
+   {
+      //Are we inside the best run of zeroes?
+      if(i >= zeroRunStart && i < zeroRunEnd)
+      {
+         //Append a separator
+         *(p++) = ':';
+         //Skip the sequence of zeroes
+         i = zeroRunEnd - 1;
+      }
+      else
+      {
+         //Add a separator between each 16-bit word
+         if(i > 0)
+            *(p++) = ':';
+
+         //Convert the current 16-bit word to string
+         p += sprintf(p, "%" PRIx16, ntohs(ipAddr->w[i]));
+      }
+   }
+
+   //A trailing run of zeroes has been found?
+   if(zeroRunEnd == 8)
+      *(p++) = ':';
+
+   //Properly terminate the string
+   *p = '\0';
+
+   //Return a pointer to the formatted string
+   return str;
+}
+
+
+/**
+ * @brief Dump IPv6 header for debugging purpose
+ * @param[in] ipHeader IPv6 header
+ **/
+
+void ipv6DumpHeader(const Ipv6Header *ipHeader)
+{
+   //Dump IPv6 header contents
+   TRACE_DEBUG("  Version = %" PRIu8 "\r\n", ipHeader->version);
+   TRACE_DEBUG("  Traffic Class = %u\r\n", (ipHeader->trafficClassH << 4) | ipHeader->trafficClassL);
+   TRACE_DEBUG("  Flow Label = 0x%05X\r\n", (ipHeader->flowLabelH << 16) | ntohs(ipHeader->flowLabelL));
+   TRACE_DEBUG("  Payload Length = %" PRIu16 "\r\n", ntohs(ipHeader->payloadLength));
+   TRACE_DEBUG("  Next Header = %" PRIu8 "\r\n", ipHeader->nextHeader);
+   TRACE_DEBUG("  Hop Limit = %" PRIu8 "\r\n", ipHeader->hopLimit);
+   TRACE_DEBUG("  Src Addr = %s\r\n", ipv6AddrToString(&ipHeader->srcAddr, NULL));
+   TRACE_DEBUG("  Dest Addr = %s\r\n", ipv6AddrToString(&ipHeader->destAddr, NULL));
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/ipv6.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,544 @@
+/**
+ * @file ipv6.h
+ * @brief IPv6 (Internet Protocol Version 6)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _IPV6_H
+#define _IPV6_H
+
+//Forward declaration of structures
+struct _Ipv6Header;
+#define Ipv6Header struct _Ipv6Header
+
+struct _Ipv6FragmentHeader;
+#define Ipv6FragmentHeader struct _Ipv6FragmentHeader
+
+struct _Ipv6PseudoHeader;
+#define Ipv6PseudoHeader struct _Ipv6PseudoHeader
+
+//Dependencies
+#include <string.h>
+#include "core/net.h"
+#include "core/ethernet.h"
+#include "ipv6/ipv6_frag.h"
+
+//IPv6 support
+#ifndef IPV6_SUPPORT
+   #define IPV6_SUPPORT DISABLED
+#elif (IPV6_SUPPORT != ENABLED && IPV6_SUPPORT != DISABLED)
+   #error IPV6_SUPPORT parameter is not valid
+#endif
+
+//Default IPv6 Hop Limit field
+#ifndef IPV6_DEFAULT_HOP_LIMIT
+   #define IPV6_DEFAULT_HOP_LIMIT 64
+#elif (IPV6_DEFAULT_HOP_LIMIT < 1)
+   #error IPV6_DEFAULT_HOP_LIMIT parameter is not valid
+#endif
+
+//Maximum number of IPv6 unicast addresses
+#ifndef IPV6_ADDR_LIST_SIZE
+   #define IPV6_ADDR_LIST_SIZE 3
+#elif (IPV6_ADDR_LIST_SIZE < 2)
+   #error IPV6_ADDR_LIST_SIZE parameter is not valid
+#endif
+
+//Maximum number of IPv6 anycast addresses
+#ifndef IPV6_ANYCAST_ADDR_LIST_SIZE
+   #define IPV6_ANYCAST_ADDR_LIST_SIZE 1
+#elif (IPV6_ANYCAST_ADDR_LIST_SIZE < 1)
+   #error IPV6_ANYCAST_ADDR_LIST_SIZE parameter is not valid
+#endif
+
+//Size of the prefix list
+#ifndef IPV6_PREFIX_LIST_SIZE
+   #define IPV6_PREFIX_LIST_SIZE 2
+#elif (IPV6_PREFIX_LIST_SIZE < 1)
+   #error IPV6_PREFIX_LIST_SIZE parameter is not valid
+#endif
+
+//Maximum number number of default routers
+#ifndef IPV6_ROUTER_LIST_SIZE
+   #define IPV6_ROUTER_LIST_SIZE 2
+#elif (IPV6_ROUTER_LIST_SIZE < 1)
+   #error IPV6_ROUTER_LIST_SIZE parameter is not valid
+#endif
+
+//Maximum number of DNS servers
+#ifndef IPV6_DNS_SERVER_LIST_SIZE
+   #define IPV6_DNS_SERVER_LIST_SIZE 2
+#elif (IPV6_DNS_SERVER_LIST_SIZE < 1)
+   #error IPV6_DNS_SERVER_LIST_SIZE parameter is not valid
+#endif
+
+//Size of the IPv6 multicast filter
+#ifndef IPV6_MULTICAST_FILTER_SIZE
+   #define IPV6_MULTICAST_FILTER_SIZE 8
+#elif (IPV6_MULTICAST_FILTER_SIZE < 1)
+   #error IPV6_MULTICAST_FILTER_SIZE parameter is not valid
+#endif
+
+//Version number for IPv6
+#define IPV6_VERSION 6
+//Minimum MTU that routers and physical links are required to handle
+#define IPV6_DEFAULT_MTU 1280
+
+//Macro used for defining an IPv6 address
+#define IPV6_ADDR(a, b, c, d, e, f, g, h) {{{ \
+   MSB(a), LSB(a), MSB(b), LSB(b), MSB(c), LSB(c), MSB(d), LSB(d), \
+   MSB(e), LSB(e), MSB(f), LSB(f), MSB(g), LSB(g), MSB(h), LSB(h)}}}
+
+//Copy IPv6 address
+#define ipv6CopyAddr(destIpAddr, srcIpAddr) \
+   memcpy(destIpAddr, srcIpAddr, sizeof(Ipv6Addr))
+
+//Compare IPv6 addresses
+#define ipv6CompAddr(ipAddr1, ipAddr2) \
+   (!memcmp(ipAddr1, ipAddr2, sizeof(Ipv6Addr)))
+
+//Determine whether an IPv6 address is a link-local unicast address
+#define ipv6IsLinkLocalUnicastAddr(ipAddr) \
+   ((ipAddr)->b[0] == 0xFE && ((ipAddr)->b[1] & 0xC0) == 0x80)
+
+//Determine whether an IPv6 address is a site-local unicast address
+#define ipv6IsSiteLocalUnicastAddr(ipAddr) \
+   ((ipAddr)->b[0] == 0xFE && ((ipAddr)->b[1] & 0xC0) == 0xC0)
+
+//Determine whether an IPv6 address is a multicast address
+#define ipv6IsMulticastAddr(ipAddr) \
+   ((ipAddr)->b[0] == 0xFF)
+
+//Determine whether an IPv6 address is a solicited-node address
+#define ipv6IsSolicitedNodeAddr(ipAddr) \
+   ipv6CompPrefix(ipAddr, &IPV6_SOLICITED_NODE_ADDR_PREFIX, 104)
+
+//Get the state of the link-local address
+#define ipv6GetLinkLocalAddrState(interface) \
+   (interface->ipv6Context.addrList[0].state)
+
+
+/**
+ * @brief IPv6 address scopes
+ **/
+
+typedef enum
+{
+   IPV6_ADDR_SCOPE_INTERFACE_LOCAL    = 1,
+   IPV6_ADDR_SCOPE_LINK_LOCAL         = 2,
+   IPV6_ADDR_SCOPE_ADMIN_LOCAL        = 4,
+   IPV6_ADDR_SCOPE_SITE_LOCAL         = 5,
+   IPV6_ADDR_SCOPE_ORGANIZATION_LOCAL = 8,
+   IPV6_ADDR_SCOPE_GLOBAL             = 14
+} Ipv6AddrScope;
+
+
+/**
+ * @brief IPv6 address state
+ **/
+
+typedef enum
+{
+   IPV6_ADDR_STATE_INVALID    = 0, ///<An address that is not assigned to any interface
+   IPV6_ADDR_STATE_TENTATIVE  = 1, ///<An address whose uniqueness on a link is being verified
+   IPV6_ADDR_STATE_PREFERRED  = 2, ///<An address assigned to an interface whose use is unrestricted
+   IPV6_ADDR_STATE_DEPRECATED = 3  ///<An address assigned to an interface whose use is discouraged
+} Ipv6AddrState;
+
+
+/**
+ * @brief IPv6 Next Header types
+ **/
+
+typedef enum
+{
+   IPV6_HOP_BY_HOP_OPT_HEADER = 0,
+   IPV6_TCP_HEADER            = 6,
+   IPV6_UDP_HEADER            = 17,
+   IPV6_ROUTING_HEADER        = 43,
+   IPV6_FRAGMENT_HEADER       = 44,
+   IPV6_ESP_HEADER            = 50,
+   IPV6_AUTH_HEADER           = 51,
+   IPV6_ICMPV6_HEADER         = 58,
+   IPV6_NO_NEXT_HEADER        = 59,
+   IPV6_DEST_OPT_HEADER       = 60
+} Ipv6NextHeaderType;
+
+
+/**
+ * @brief IPv6 fragment offset field
+ **/
+
+typedef enum
+{
+   IPV6_OFFSET_MASK = 0xFFF8,
+   IPV6_FLAG_RES1   = 0x0004,
+   IPV6_FLAG_RES2   = 0x0002,
+   IPV6_FLAG_M      = 0x0001
+} Ipv6FragmentOffset;
+
+
+/**
+ * @brief IPv6 option types
+ **/
+
+typedef enum
+{
+   IPV6_OPTION_TYPE_MASK = 0x1F,
+   IPV6_OPTION_TYPE_PAD1 = 0x00,
+   IPV6_OPTION_TYPE_PADN = 0x01
+} Ipv6OptionType;
+
+
+/**
+ * @brief Actions to be taken for unrecognized options
+ **/
+
+typedef enum
+{
+   IPV6_ACTION_MASK                = 0xC0,
+   IPV6_ACTION_SKIP_OPTION         = 0x00,
+   IPV6_ACTION_DISCARD_PACKET      = 0x40,
+   IPV6_ACTION_SEND_ICMP_ERROR_ALL = 0x80,
+   IPV6_ACTION_SEND_ICMP_ERROR_UNI = 0xC0
+} Ipv6Actions;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief IPv6 network address
+ **/
+
+typedef __start_packed struct
+{
+   __start_packed union
+   {
+      uint8_t b[16];
+      uint16_t w[8];
+      uint32_t dw[4];
+   };
+} __end_packed Ipv6Addr;
+
+
+/**
+ * @brief IPv6 header
+ **/
+
+__start_packed struct _Ipv6Header
+{
+#ifdef _CPU_BIG_ENDIAN
+   uint8_t version : 4;       //0
+   uint8_t trafficClassH : 4;
+   uint8_t trafficClassL : 4; //1
+   uint8_t flowLabelH : 4;
+#else
+   uint8_t trafficClassH : 4; //0
+   uint8_t version : 4;
+   uint8_t flowLabelH : 4;    //1
+   uint8_t trafficClassL : 4;
+#endif
+   uint16_t flowLabelL;       //2-3
+   uint16_t payloadLength;    //4-5
+   uint8_t nextHeader;        //6
+   uint8_t hopLimit;          //7
+   Ipv6Addr srcAddr;          //8-23
+   Ipv6Addr destAddr;         //24-39
+   uint8_t payload[];         //40
+} __end_packed;
+
+
+/**
+ * @brief IPv6 Hop-by-Hop Options header
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t nextHeader; //0
+   uint8_t hdrExtLen;  //1
+   uint8_t options[];  //2
+} __end_packed Ipv6HopByHopOptHeader;
+
+
+/**
+ * @brief IPv6 Destination Options header
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t nextHeader; //0
+   uint8_t hdrExtLen;  //1
+   uint8_t options[];  //2
+} __end_packed Ipv6DestOptHeader;
+
+
+/**
+ * @brief IPv6 Type 0 Routing header
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t nextHeader;   //0
+   uint8_t hdrExtLen;    //1
+   uint8_t routingType;  //2
+   uint8_t segmentsLeft; //3
+   uint32_t reserved;    //4-7
+   Ipv6Addr address[];   //8
+} __end_packed Ipv6RoutingHeader;
+
+
+/**
+ * @brief IPv6 Fragment header
+ **/
+
+__start_packed struct _Ipv6FragmentHeader
+{
+   uint8_t nextHeader;      //0
+   uint8_t reserved;        //1
+   uint16_t fragmentOffset; //2-3
+   uint32_t identification; //4-7
+} __end_packed;
+
+
+/**
+ * @brief IPv6 Authentication header
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t nextHeader;          //0
+   uint8_t payloadLength;       //1
+   uint16_t reserved;           //2-3
+   uint32_t securityParamIndex; //4-7
+   uint32_t sequenceNumber;     //8-11
+   uint8_t authData[];          //12
+} __end_packed Ipv6AuthHeader;
+
+
+/**
+ * @brief IPv6 Encapsulating Security Payload header
+ **/
+
+typedef __start_packed struct
+{
+   uint32_t securityParamIndex; //0-3
+   uint32_t sequenceNumber;     //4-7
+   uint8_t payloadData[];       //8
+} __end_packed Ipv6EspHeader;
+
+
+/**
+ * @brief IPv6 option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;   //0
+   uint8_t length; //1
+   uint8_t data[]; //2
+} __end_packed Ipv6Option;
+
+
+/**
+ * @brief IPv6 pseudo header
+ **/
+
+__start_packed struct _Ipv6PseudoHeader
+{
+   Ipv6Addr srcAddr;        //0-15
+   Ipv6Addr destAddr;       //16-31
+   uint32_t length;         //32-35
+   uint32_t reserved : 24;  //36-38
+   uint32_t nextHeader : 8; //39
+} __end_packed;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+/**
+ * @brief IPv6 address entry
+ **/
+
+typedef struct
+{
+   Ipv6Addr addr;               ///<IPv6 address
+   Ipv6AddrState state;         ///<IPv6 address state
+   bool_t duplicate;            ///<The address is a duplicate
+   systime_t validLifetime;     ///<Valid lifetime
+   systime_t preferredLifetime; ///<Preferred lifetime
+   bool_t permanent;            ///<Permanently assigned address
+   systime_t timestamp;         ///<Timestamp to manage entry lifetime
+   systime_t dadTimeout;        ///<Timeout value for Duplicate Address Detection
+   uint_t dadRetransmitCount;   ///<Retransmission counter for Duplicate Address Detection
+} Ipv6AddrEntry;
+
+
+/**
+ * @brief Prefix list entry
+ **/
+
+typedef struct
+{
+   Ipv6Addr prefix;             ///<IPv6 prefix information
+   uint8_t prefixLength;        ///<IPv6 prefix length
+   systime_t validLifetime;     ///<Valid lifetime
+   systime_t preferredLifetime; ///<Preferred lifetime
+   bool_t permanent;            ///<Permanently assigned prefix
+   systime_t timestamp;         ///<Timestamp to manage entry lifetime
+} Ipv6PrefixEntry;
+
+
+/**
+ * @brief Default router list entry
+ **/
+
+typedef struct
+{
+   Ipv6Addr addr;       ///<Router address
+   systime_t lifetime;  ///<Router lifetime
+   bool_t permanent;    ///<Permanently assigned router
+   systime_t timestamp; ///<Timestamp to manage entry lifetime
+} Ipv6RouterEntry;
+
+
+/**
+ * @brief IPv6 multicast filter entry
+ **/
+
+typedef struct
+{
+   Ipv6Addr addr;   ///<Multicast address
+   uint_t refCount; ///<Reference count for the current entry
+   uint_t state;    ///<MLD node state
+   bool_t flag;     ///<MLD flag
+   systime_t timer; ///<Delay timer
+} Ipv6FilterEntry;
+
+
+/**
+ * @brief IPv6 context
+ **/
+
+typedef struct
+{
+   size_t linkMtu;                                              ///<Maximum transmission unit
+   bool_t isRouter;                                             ///<A flag indicating whether routing is enabled on this interface
+   uint8_t curHopLimit;                                         ///<Default value for the Hop Limit field
+   bool_t enableMulticastEchoReq;                               ///<Support for multicast ICMPv6 Echo Request messages
+   Ipv6AddrEntry addrList[IPV6_ADDR_LIST_SIZE];                 ///<IPv6 unicast address list
+   Ipv6Addr anycastAddrList[IPV6_ANYCAST_ADDR_LIST_SIZE];       ///<IPv6 anycast address list
+   Ipv6PrefixEntry prefixList[IPV6_PREFIX_LIST_SIZE];           ///<Prefix list
+   Ipv6RouterEntry routerList[IPV6_ROUTER_LIST_SIZE];           ///<Default router list
+   Ipv6Addr dnsServerList[IPV6_DNS_SERVER_LIST_SIZE];           ///<DNS servers
+   Ipv6FilterEntry multicastFilter[IPV6_MULTICAST_FILTER_SIZE]; ///<Multicast filter table
+#if (IPV6_FRAG_SUPPORT == ENABLED)
+   uint32_t identification;                                     ///<IPv6 fragment identification field
+   Ipv6FragDesc fragQueue[IPV6_MAX_FRAG_DATAGRAMS];             ///<IPv6 fragment reassembly queue
+#endif
+} Ipv6Context;
+
+
+//IPv6 related constants
+extern const Ipv6Addr IPV6_UNSPECIFIED_ADDR;
+extern const Ipv6Addr IPV6_LOOPBACK_ADDR;
+extern const Ipv6Addr IPV6_LINK_LOCAL_ALL_NODES_ADDR;
+extern const Ipv6Addr IPV6_LINK_LOCAL_ALL_ROUTERS_ADDR;
+extern const Ipv6Addr IPV6_LINK_LOCAL_ADDR_PREFIX;
+extern const Ipv6Addr IPV6_SOLICITED_NODE_ADDR_PREFIX;
+
+//IPv6 related functions
+error_t ipv6Init(NetInterface *interface);
+
+error_t ipv6SetMtu(NetInterface *interface, size_t mtu);
+error_t ipv6GetMtu(NetInterface *interface, size_t *mtu);
+
+error_t ipv6SetLinkLocalAddr(NetInterface *interface, const Ipv6Addr *addr);
+error_t ipv6GetLinkLocalAddr(NetInterface *interface, Ipv6Addr *addr);
+
+error_t ipv6SetGlobalAddr(NetInterface *interface, uint_t index, const Ipv6Addr *addr);
+error_t ipv6GetGlobalAddr(NetInterface *interface, uint_t index, Ipv6Addr *addr);
+
+error_t ipv6SetAnycastAddr(NetInterface *interface, uint_t index, const Ipv6Addr *addr);
+error_t ipv6GetAnycastAddr(NetInterface *interface, uint_t index, Ipv6Addr *addr);
+
+error_t ipv6SetPrefix(NetInterface *interface,
+   uint_t index, const Ipv6Addr *prefix, uint_t length);
+
+error_t ipv6GetPrefix(NetInterface *interface,
+   uint_t index, Ipv6Addr *prefix, uint_t *length);
+
+error_t ipv6SetDefaultRouter(NetInterface *interface, uint_t index, const Ipv6Addr *addr);
+error_t ipv6GetDefaultRouter(NetInterface *interface, uint_t index, Ipv6Addr *addr);
+
+error_t ipv6SetDnsServer(NetInterface *interface, uint_t index, const Ipv6Addr *addr);
+error_t ipv6GetDnsServer(NetInterface *interface, uint_t index, Ipv6Addr *addr);
+
+void ipv6LinkChangeEvent(NetInterface *interface);
+
+void ipv6ProcessPacket(NetInterface *interface,
+   NetBuffer *ipPacket, size_t ipPacketOffset);
+
+error_t ipv6ParseHopByHopOptHeader(NetInterface *interface, const NetBuffer *ipPacket,
+   size_t ipPacketOffset, size_t *headerOffset, size_t *nextHeaderOffset);
+
+error_t ipv6ParseDestOptHeader(NetInterface *interface, const NetBuffer *ipPacket,
+   size_t ipPacketOffset, size_t *headerOffset, size_t *nextHeaderOffset);
+
+error_t ipv6ParseRoutingHeader(NetInterface *interface, const NetBuffer *ipPacket,
+   size_t ipPacketOffset, size_t *headerOffset, size_t *nextHeaderOffset);
+
+error_t ipv6ParseAuthHeader(NetInterface *interface, const NetBuffer *ipPacket,
+   size_t ipPacketOffset, size_t *headerOffset, size_t *nextHeaderOffset);
+
+error_t ipv6ParseEspHeader(NetInterface *interface, const NetBuffer *ipPacket,
+   size_t ipPacketOffset, size_t *headerOffset, size_t *nextHeaderOffset);
+
+error_t ipv6ParseOptions(NetInterface *interface, const NetBuffer *ipPacket,
+   size_t ipPacketOffset, size_t optOffset, size_t optLength);
+
+error_t ipv6SendDatagram(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   NetBuffer *buffer, size_t offset, uint8_t hopLimit);
+
+error_t ipv6SendPacket(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   uint32_t fragId, size_t fragOffset, NetBuffer *buffer, size_t offset, uint8_t hopLimit);
+
+error_t ipv6JoinMulticastGroup(NetInterface *interface, const Ipv6Addr *groupAddr);
+error_t ipv6LeaveMulticastGroup(NetInterface *interface, const Ipv6Addr *groupAddr);
+
+error_t ipv6StringToAddr(const char_t *str, Ipv6Addr *ipAddr);
+char_t *ipv6AddrToString(const Ipv6Addr *ipAddr, char_t *str);
+
+void ipv6DumpHeader(const Ipv6Header *ipHeader);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/ipv6_frag.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,657 @@
+/**
+ * @file ipv6_frag.c
+ * @brief IPv6 fragmentation and reassembly
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL IPV6_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "core/ip.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_frag.h"
+#include "ipv6/icmpv6.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV6_SUPPORT == ENABLED && IPV6_FRAG_SUPPORT == ENABLED)
+
+//Tick counter to handle periodic operations
+systime_t ipv6FragTickCounter;
+
+
+/**
+ * @brief Fragment IPv6 datagram into smaller packets
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IPv6 pseudo header
+ * @param[in] payload Multi-part buffer containing the payload
+ * @param[in] payloadOffset Offset to the first payload byte
+ * @param[in] pathMtu PMTU value
+ * @param[in] hopLimit Hop Limit value
+ * @return Error code
+ **/
+
+error_t ipv6FragmentDatagram(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   const NetBuffer *payload, size_t payloadOffset, size_t pathMtu, uint8_t hopLimit)
+{
+   error_t error;
+   uint32_t id;
+   size_t offset;
+   size_t length;
+   size_t payloadLength;
+   size_t fragmentOffset;
+   size_t maxFragmentSize;
+   NetBuffer *fragment;
+
+   //Identification field is used to identify fragments of an original IP datagram
+   id = interface->ipv6Context.identification++;
+
+   //Retrieve the length of the payload
+   payloadLength = netBufferGetLength(payload) - payloadOffset;
+
+   //Allocate a memory buffer to hold IP fragments
+   fragment = ipAllocBuffer(0, &fragmentOffset);
+   //Failed to allocate memory?
+   if(!fragment)
+      return ERROR_OUT_OF_MEMORY;
+
+   //The node should never set its PMTU estimate below the IPv6 minimum link MTU
+   pathMtu = MAX(pathMtu, IPV6_DEFAULT_MTU);
+
+   //Determine the maximum payload size for fragmented packets
+   maxFragmentSize = pathMtu - sizeof(Ipv6Header) - sizeof(Ipv6FragmentHeader);
+   //The size shall be a multiple of 8-byte blocks
+   maxFragmentSize -= (maxFragmentSize % 8);
+
+   //Initialize error code
+   error = NO_ERROR;
+
+   //Split the payload into multiple IP fragments
+   for(offset = 0; offset < payloadLength; offset += length)
+   {
+      //Flush the contents of the fragment
+      error = netBufferSetLength(fragment, fragmentOffset);
+      //Sanity check
+      if(error)
+         break;
+
+      //Process the last fragment?
+      if((payloadLength - offset) <= maxFragmentSize)
+      {
+         //Size of the current fragment
+         length = payloadLength - offset;
+         //Copy fragment data
+         netBufferConcat(fragment, payload, payloadOffset + offset, length);
+
+         //Do not set the MF flag for the last fragment
+         error = ipv6SendPacket(interface, pseudoHeader, id,
+            offset, fragment, fragmentOffset, hopLimit);
+      }
+      else
+      {
+         //Size of the current fragment (must be a multiple of 8-byte blocks)
+         length = maxFragmentSize;
+         //Copy fragment data
+         netBufferConcat(fragment, payload, payloadOffset + offset, length);
+
+         //Fragmented packets must have the M flag set
+         error = ipv6SendPacket(interface, pseudoHeader, id,
+            offset | IPV6_FLAG_M, fragment, fragmentOffset, hopLimit);
+      }
+
+      //Failed to send current IP fragment?
+      if(error)
+         break;
+   }
+
+   //Free previously allocated memory
+   netBufferFree(fragment);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse Fragment header and reassemble original datagram
+ * @param[in] interface Underlying network interface
+ * @param[in] ipPacket Multi-part buffer containing the incoming IPv6 packet
+ * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet
+ * @param[in] fragHeaderOffset Offset to the Fragment header
+ * @param[in] nextHeaderOffset Offset to the Next Header field of the previous header
+ **/
+
+void ipv6ParseFragmentHeader(NetInterface *interface, const NetBuffer *ipPacket,
+   size_t ipPacketOffset, size_t fragHeaderOffset, size_t nextHeaderOffset)
+{
+   error_t error;
+   size_t n;
+   size_t length;
+   uint16_t offset;
+   uint16_t dataFirst;
+   uint16_t dataLast;
+   Ipv6FragDesc *frag;
+   Ipv6HoleDesc *hole;
+   Ipv6HoleDesc *prevHole;
+   Ipv6Header *ipHeader;
+   Ipv6FragmentHeader *fragHeader;
+
+   //Remaining bytes to process in the payload
+   length = netBufferGetLength(ipPacket) - fragHeaderOffset;
+
+   //Ensure the fragment header is valid
+   if(length < sizeof(Ipv6FragmentHeader))
+      return;
+
+   //Point to the IPv6 header
+   ipHeader = netBufferAt(ipPacket, ipPacketOffset);
+   //Sanity check
+   if(ipHeader == NULL)
+      return;
+
+   //Point to the Fragment header
+   fragHeader = netBufferAt(ipPacket, fragHeaderOffset);
+   //Sanity check
+   if(fragHeader == NULL)
+      return;
+
+   //Calculate the length of the fragment
+   length -= sizeof(Ipv6FragmentHeader);
+   //Convert the fragment offset from network byte order
+   offset = ntohs(fragHeader->fragmentOffset);
+
+   //Every fragment except the last must contain a multiple of 8 bytes of data
+   if((offset & IPV6_FLAG_M) && (length % 8))
+   {
+      //Compute the offset of the Payload Length field within the packet
+      n = (uint8_t *) &ipHeader->payloadLength - (uint8_t *) ipHeader;
+
+      //The fragment must be discarded and an ICMP Parameter Problem
+      //message should be sent to the source of the fragment, pointing
+      //to the Payload Length field of the fragment packet
+      icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM,
+         ICMPV6_CODE_INVALID_HEADER_FIELD, n, ipPacket, ipPacketOffset);
+
+      //Exit immediately
+      return;
+   }
+
+   //Calculate the index of the first byte
+   dataFirst = offset & IPV6_OFFSET_MASK;
+   //Calculate the index immediately following the last byte
+   dataLast = dataFirst + (uint16_t) length;
+
+   //Search for a matching IP datagram being reassembled
+   frag = ipv6SearchFragQueue(interface, ipHeader, fragHeader);
+   //No matching entry in the reassembly queue?
+   if(frag == NULL)
+      return;
+
+   //The very first fragment requires special handling
+   if(!(offset & IPV6_OFFSET_MASK))
+   {
+      uint8_t *p;
+
+      //Calculate the length of the unfragmentable part
+      frag->unfragPartLength = fragHeaderOffset - ipPacketOffset;
+
+      //The size of the reconstructed datagram exceeds the maximum value?
+      if((frag->unfragPartLength + frag->fragPartLength) > IPV6_MAX_FRAG_DATAGRAM_SIZE)
+      {
+         //Retrieve the offset of the Fragment header within the packet
+         n = fragHeaderOffset - ipPacketOffset;
+         //Compute the exact offset of the Fragment Offset field
+         n += (uint8_t *) &fragHeader->fragmentOffset - (uint8_t *) fragHeader;
+
+         //The fragment must be discarded and an ICMP Parameter Problem
+         //message should be sent to the source of the fragment, pointing
+         //to the Fragment Offset field of the fragment packet
+         icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM,
+            ICMPV6_CODE_INVALID_HEADER_FIELD, n, ipPacket, ipPacketOffset);
+
+         //Drop the reconstructed datagram
+         netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+         //Exit immediately
+         return;
+      }
+
+      //Make sure the unfragmentable part entirely fits in the first chunk
+      if(frag->unfragPartLength > frag->buffer.chunk[0].size)
+      {
+         //Drop the reconstructed datagram
+         netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+         //Exit immediately
+         return;
+      }
+
+      //Fix the length of the first chunk
+      frag->buffer.chunk[0].length = frag->unfragPartLength;
+
+      //The unfragmentable part of the reassembled packet consists
+      //of all headers up to, but not including, the Fragment header
+      //of the first fragment packet
+      netBufferCopy((NetBuffer *) &frag->buffer, 0, ipPacket,
+         ipPacketOffset, frag->unfragPartLength);
+
+      //Point to the Next Header field of the last header
+      p = netBufferAt((NetBuffer *) &frag->buffer,
+         nextHeaderOffset - ipPacketOffset);
+
+      //The Next Header field of the last header of the unfragmentable
+      //part is obtained from the Next Header field of the first
+      //fragment's Fragment header
+      *p = fragHeader->nextHeader;
+   }
+
+   //It may be necessary to increase the size of the buffer...
+   if(dataLast > frag->fragPartLength)
+   {
+      //The size of the reconstructed datagram exceeds the maximum value?
+      if((frag->unfragPartLength + dataLast) > IPV6_MAX_FRAG_DATAGRAM_SIZE)
+      {
+         //Retrieve the offset of the Fragment header within the packet
+         n = fragHeaderOffset - ipPacketOffset;
+         //Compute the exact offset of the Fragment Offset field
+         n += (uint8_t *) &fragHeader->fragmentOffset - (uint8_t *) fragHeader;
+
+         //The fragment must be discarded and an ICMP Parameter Problem
+         //message should be sent to the source of the fragment, pointing
+         //to the Fragment Offset field of the fragment packet
+         icmpv6SendErrorMessage(interface, ICMPV6_TYPE_PARAM_PROBLEM,
+            ICMPV6_CODE_INVALID_HEADER_FIELD, n, ipPacket, ipPacketOffset);
+
+         //Drop the reconstructed datagram
+         netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+         //Exit immediately
+         return;
+      }
+
+      //Adjust the size of the reconstructed datagram
+      error = netBufferSetLength((NetBuffer *) &frag->buffer,
+         frag->unfragPartLength + dataLast + sizeof(Ipv6HoleDesc));
+
+      //Any error to report?
+      if(error)
+      {
+         //Drop the reconstructed datagram
+         netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+         //Exit immediately
+         return;
+      }
+
+      //Actual length of the fragmentable part
+      frag->fragPartLength = dataLast;
+   }
+
+   //Select the first hole descriptor from the list
+   hole = ipv6FindHole(frag, frag->firstHole);
+   //Keep track of the previous hole in the list
+   prevHole = NULL;
+
+   //Iterate through the hole descriptors
+   while(hole != NULL)
+   {
+      //Save lower and upper boundaries for later use
+      uint16_t holeFirst = hole->first;
+      uint16_t holeLast = hole->last;
+
+      //Check whether the newly arrived fragment
+      //interacts with this hole in some way
+      if(dataFirst < holeLast && dataLast > holeFirst)
+      {
+         //The current descriptor is no longer valid. We will destroy
+         //it, and in the next two steps, we will determine whether
+         //or not it is necessary to create any new hole descriptors
+         if(prevHole != NULL)
+            prevHole->next = hole->next;
+         else
+            frag->firstHole = hole->next;
+
+         //Is there still a hole at the beginning of the segment?
+         if(dataFirst > holeFirst)
+         {
+            //Create a new entry that describes this hole
+            hole = ipv6FindHole(frag, holeFirst);
+            hole->first = holeFirst;
+            hole->last = dataFirst;
+
+            //Insert the newly created entry into the hole descriptor list
+            if(prevHole != NULL)
+            {
+               hole->next = prevHole->next;
+               prevHole->next = hole->first;
+            }
+            else
+            {
+               hole->next = frag->firstHole;
+               frag->firstHole = hole->first;
+            }
+
+            //Always keep track of the previous hole
+            prevHole = hole;
+         }
+
+         //Is there still a hole at the end of the segment?
+         if(dataLast < holeLast && (offset & IPV6_FLAG_M))
+         {
+            //Create a new entry that describes this hole
+            hole = ipv6FindHole(frag, dataLast);
+            hole->first = dataLast;
+            hole->last = holeLast;
+
+            //Insert the newly created entry into the hole descriptor list
+            if(prevHole != NULL)
+            {
+               hole->next = prevHole->next;
+               prevHole->next = hole->first;
+            }
+            else
+            {
+               hole->next = frag->firstHole;
+               frag->firstHole = hole->first;
+            }
+
+            //Always keep track of the previous hole
+            prevHole = hole;
+         }
+      }
+      else
+      {
+         //The newly arrived fragment does not interact with the current hole
+         prevHole = hole;
+      }
+
+      //Select the next hole descriptor from the list
+      hole = ipv6FindHole(frag, prevHole ? prevHole->next : frag->firstHole);
+   }
+
+   //Copy data from the fragment to the reassembly buffer
+   netBufferCopy((NetBuffer *) &frag->buffer, frag->unfragPartLength + dataFirst,
+      ipPacket, fragHeaderOffset + sizeof(Ipv6FragmentHeader), length);
+
+   //Dump hole descriptor list
+   ipv6DumpHoleList(frag);
+
+   //If the hole descriptor list is empty, the reassembly process is now complete
+   if(!ipv6FindHole(frag, frag->firstHole))
+   {
+      //Discard the extra hole descriptor that follows the reconstructed datagram
+      error = netBufferSetLength((NetBuffer *) &frag->buffer,
+         frag->unfragPartLength + frag->fragPartLength);
+
+      //Check status code
+      if(!error)
+      {
+         //Point to the IPv6 header
+         Ipv6Header *datagram = netBufferAt((NetBuffer *) &frag->buffer, 0);
+
+         //Fix the Payload Length field
+         datagram->payloadLength = htons(frag->unfragPartLength +
+            frag->fragPartLength - sizeof(Ipv6Header));
+
+         //Pass the original IPv6 datagram to the higher protocol layer
+         ipv6ProcessPacket(interface, (NetBuffer *) &frag->buffer, 0);
+      }
+
+      //Release previously allocated memory
+      netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+   }
+}
+
+
+/**
+ * @brief Fragment reassembly timeout handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * handle IPv6 fragment reassembly timeout
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void ipv6FragTick(NetInterface *interface)
+{
+   error_t error;
+   uint_t i;
+   systime_t time;
+   Ipv6HoleDesc *hole;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Loop through the reassembly queue
+   for(i = 0; i < IPV6_MAX_FRAG_DATAGRAMS; i++)
+   {
+      //Point to the current entry in the reassembly queue
+      Ipv6FragDesc *frag = &interface->ipv6Context.fragQueue[i];
+
+      //Make sure the entry is currently in use
+      if(frag->buffer.chunkCount > 0)
+      {
+         //If the timer runs out, the partially-reassembled datagram must be
+         //discarded and ICMPv6 Time Exceeded message sent to the source host
+         if((time - frag->timestamp) >= IPV6_FRAG_TIME_TO_LIVE)
+         {
+            //Debug message
+            TRACE_INFO("IPv6 fragment reassembly timeout...\r\n");
+            //Dump IP header contents for debugging purpose
+            ipv6DumpHeader(frag->buffer.chunk[0].address);
+
+            //Point to the first hole descriptor
+            hole = ipv6FindHole(frag, frag->firstHole);
+
+            //Make sure the fragment zero has been received
+            //before sending an ICMPv6 message
+            if(hole != NULL && hole->first > 0)
+            {
+               //Fix the size of the reconstructed datagram
+               error = netBufferSetLength((NetBuffer *) &frag->buffer,
+                  frag->unfragPartLength + hole->first);
+
+               //Check status code
+               if(!error)
+               {
+                  //Send an ICMPv6 Time Exceeded message
+                  icmpv6SendErrorMessage(interface, ICMPV6_TYPE_TIME_EXCEEDED,
+                     ICMPV6_CODE_REASSEMBLY_TIME_EXCEEDED, 0, (NetBuffer *) &frag->buffer, 0);
+               }
+            }
+
+            //Drop the partially reconstructed datagram
+            netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Search for a matching datagram in the reassembly queue
+ * @param[in] interface Underlying network interface
+ * @param[in] packet Incoming IPv6 packet
+ * @param[in] header Pointer to the Fragment header
+ * @return Matching fragment descriptor
+ **/
+
+Ipv6FragDesc *ipv6SearchFragQueue(NetInterface *interface,
+   Ipv6Header *packet, Ipv6FragmentHeader *header)
+{
+   error_t error;
+   uint_t i;
+   Ipv6Header *datagram;
+   Ipv6FragDesc *frag;
+   Ipv6HoleDesc *hole;
+
+   //Search for a matching IP datagram being reassembled
+   for(i = 0; i < IPV6_MAX_FRAG_DATAGRAMS; i++)
+   {
+      //Point to the current entry in the reassembly queue
+      frag = &interface->ipv6Context.fragQueue[i];
+
+      //Check whether the current entry is used?
+      if(frag->buffer.chunkCount > 0)
+      {
+         //Point to the corresponding datagram
+         datagram = netBufferAt((NetBuffer *) &frag->buffer, 0);
+
+         //Check source and destination addresses
+         if(!ipv6CompAddr(&datagram->srcAddr, &packet->srcAddr))
+            continue;
+         if(!ipv6CompAddr(&datagram->destAddr, &packet->destAddr))
+            continue;
+         //Compare fragment identification fields
+         if(frag->identification != header->identification)
+            continue;
+
+         //A matching entry has been found in the reassembly queue
+         return frag;
+      }
+   }
+
+   //If the current packet does not match an existing entry
+   //in the reassembly queue, then create a new entry
+   for(i = 0; i < IPV6_MAX_FRAG_DATAGRAMS; i++)
+   {
+      //Point to the current entry in the reassembly queue
+      frag = &interface->ipv6Context.fragQueue[i];
+
+      //The current entry is free?
+      if(!frag->buffer.chunkCount)
+      {
+         //Number of chunks that comprise the reassembly buffer
+         frag->buffer.maxChunkCount = arraysize(frag->buffer.chunk);
+
+         //Allocate sufficient memory to hold the IPv6 header and
+         //the first hole descriptor
+         error = netBufferSetLength((NetBuffer *) &frag->buffer,
+            NET_MEM_POOL_BUFFER_SIZE + sizeof(Ipv6HoleDesc));
+
+         //Failed to allocate memory?
+         if(error)
+         {
+            //Clean up side effects
+            netBufferSetLength((NetBuffer *) &frag->buffer, 0);
+            //Exit immediately
+            return NULL;
+         }
+
+         //Initial length of the reconstructed datagram
+         frag->unfragPartLength = sizeof(Ipv6Header);
+         frag->fragPartLength = 0;
+
+         //Fix the length of the first chunk
+         frag->buffer.chunk[0].length = frag->unfragPartLength;
+         //Copy IPv6 header from the incoming fragment
+         netBufferWrite((NetBuffer *) &frag->buffer, 0, packet, frag->unfragPartLength);
+
+         //Save current time
+         frag->timestamp = osGetSystemTime();
+         //Record fragment identification field
+         frag->identification = header->identification;
+         //Create a new entry in the hole descriptor list
+         frag->firstHole = 0;
+
+         //Point to first hole descriptor
+         hole = ipv6FindHole(frag, frag->firstHole);
+         //The entry describes the datagram as being completely missing
+         hole->first = 0;
+         hole->last = IPV6_INFINITY;
+         hole->next = IPV6_INFINITY;
+
+         //Dump hole descriptor list
+         ipv6DumpHoleList(frag);
+
+         //Return the matching fragment descriptor
+         return frag;
+      }
+   }
+
+   //The reassembly queue is full
+   return NULL;
+}
+
+
+/**
+ * @brief Flush IPv6 reassembly queue
+ * @param[in] interface Underlying network interface
+ **/
+
+void ipv6FlushFragQueue(NetInterface *interface)
+{
+   uint_t i;
+
+   //Loop through the reassembly queue
+   for(i = 0; i < IPV6_MAX_FRAG_DATAGRAMS; i++)
+   {
+      //Drop any partially reconstructed datagram
+      netBufferSetLength((NetBuffer *) &interface->ipv6Context.fragQueue[i].buffer, 0);
+   }
+}
+
+
+/**
+ * @brief Retrieve hole descriptor
+ * @param[in] frag IPv6 fragment descriptor
+ * @param[in] offset Offset of the hole
+ * @return A pointer to the hole descriptor is returned if the
+ *   specified offset is valid. Otherwise NULL is returned
+ **/
+
+Ipv6HoleDesc *ipv6FindHole(Ipv6FragDesc *frag, uint16_t offset)
+{
+   //Return a pointer to the hole descriptor
+   return netBufferAt((NetBuffer *) &frag->buffer, frag->unfragPartLength + offset);
+}
+
+
+/**
+ * @brief Dump hole descriptor list
+ * @param[in] frag IPv6 fragment descriptor
+ **/
+
+void ipv6DumpHoleList(Ipv6FragDesc *frag)
+{
+//Check debugging level
+#if (TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+   Ipv6HoleDesc *hole;
+
+   //Debug message
+   TRACE_DEBUG("Hole descriptor list:\r\n");
+   //Select the first hole descriptor from the list
+   hole = ipv6FindHole(frag, frag->firstHole);
+
+   //Loop through the hole descriptor list
+   while(hole != NULL)
+   {
+      //Display current hole
+      TRACE_DEBUG("  %" PRIu16 " - %" PRIu16 "\r\n", hole->first, hole->last);
+      //Select the next hole descriptor from the list
+      hole = ipv6FindHole(frag, hole->next);
+   }
+#endif
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/ipv6_frag.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,148 @@
+/**
+ * @file ipv6_frag.h
+ * @brief IPv6 fragmentation and reassembly
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _IPV6_FRAG_H
+#define _IPV6_FRAG_H
+
+//Dependencies
+#include "core/net.h"
+#include "ipv6/ipv6.h"
+
+//IPv6 fragmentation support
+#ifndef IPV6_FRAG_SUPPORT
+   #define IPV6_FRAG_SUPPORT DISABLED
+#elif (IPV6_FRAG_SUPPORT != ENABLED && IPV6_FRAG_SUPPORT != DISABLED)
+   #error IPV6_FRAG_SUPPORT parameter is not valid
+#endif
+
+//Reassembly algorithm tick interval
+#ifndef IPV6_FRAG_TICK_INTERVAL
+   #define IPV6_FRAG_TICK_INTERVAL 1000
+#elif (IPV6_FRAG_TICK_INTERVAL < 10)
+   #error IPV6_FRAG_TICK_INTERVAL parameter is not valid
+#endif
+
+//Maximum number of fragmented packets the host will accept
+//and hold in the reassembly queue simultaneously
+#ifndef IPV6_MAX_FRAG_DATAGRAMS
+   #define IPV6_MAX_FRAG_DATAGRAMS 4
+#elif (IPV6_MAX_FRAG_DATAGRAMS < 1)
+   #error IPV6_MAX_FRAG_DATAGRAMS parameter is not valid
+#endif
+
+//Maximum datagram size the host will accept when reassembling fragments
+#ifndef IPV6_MAX_FRAG_DATAGRAM_SIZE
+   #define IPV6_MAX_FRAG_DATAGRAM_SIZE 8192
+#elif (IPV6_MAX_FRAG_DATAGRAM_SIZE < 1280)
+   #error IPV6_MAX_FRAG_DATAGRAM_SIZE parameter is not valid
+#endif
+
+//Maximum time an IPv6 fragment can spend waiting to be reassembled
+#ifndef IPV6_FRAG_TIME_TO_LIVE
+   #define IPV6_FRAG_TIME_TO_LIVE 15000
+#elif (IPV6_FRAG_TIME_TO_LIVE < 1000)
+   #error IPV6_FRAG_TIME_TO_LIVE parameter is not valid
+#endif
+
+//Infinity is implemented by a very large integer
+#define IPV6_INFINITY 0xFFFF
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief Hole descriptor
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t first;
+   uint16_t last;
+   uint16_t next;
+} __end_packed Ipv6HoleDesc;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+/**
+ * @brief Reassembly buffer
+ **/
+
+typedef struct
+{
+   uint_t chunkCount;
+   uint_t maxChunkCount;
+   ChunkDesc chunk[N(IPV6_MAX_FRAG_DATAGRAM_SIZE) + 1];
+} Ipv6ReassemblyBuffer;
+
+
+/**
+ * @brief Fragmented packet descriptor
+ **/
+
+typedef struct
+{
+   systime_t timestamp;         ///<Time at which the first fragment was received
+   uint32_t identification;     ///<Fragment identification field
+   size_t unfragPartLength;     ///<Length of the unfragmentable part
+   size_t fragPartLength;       ///<Length of the fragmentable part
+   uint16_t firstHole;          ///<Index of the first hole
+   Ipv6ReassemblyBuffer buffer; ///<Buffer containing the reassembled datagram
+} Ipv6FragDesc;
+
+
+//Tick counter to handle periodic operations
+extern systime_t ipv6FragTickCounter;
+
+//IPv6 datagram fragmentation and reassembly
+error_t ipv6FragmentDatagram(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   const NetBuffer *payload, size_t payloadOffset, size_t pathMtu, uint8_t hopLimit);
+
+void ipv6ParseFragmentHeader(NetInterface *interface, const NetBuffer *ipPacket,
+   size_t ipPacketOffset, size_t fragHeaderOffset, size_t nextHeaderOffset);
+
+void ipv6FragTick(NetInterface *interface);
+
+Ipv6FragDesc *ipv6SearchFragQueue(NetInterface *interface,
+   Ipv6Header *packet, Ipv6FragmentHeader *header);
+
+void ipv6FlushFragQueue(NetInterface *interface);
+
+Ipv6HoleDesc *ipv6FindHole(Ipv6FragDesc *frag, uint16_t offset);
+void ipv6DumpHoleList(Ipv6FragDesc *frag);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/ipv6_misc.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1346 @@
+/**
+ * @file ipv6_misc.c
+ * @brief Helper functions for IPv6
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL IPV6_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_misc.h"
+#include "ipv6/ndp.h"
+#include "ipv6/ndp_cache.h"
+#include "mdns/mdns_responder.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV6_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Get the state of the specified IPv6 address
+ * @param[in] interface Underlying network interface
+ * @param[in] addr IPv6 address
+ * @return Address state
+ **/
+
+Ipv6AddrState ipv6GetAddrState(NetInterface *interface, const Ipv6Addr *addr)
+{
+   uint_t i;
+   Ipv6AddrEntry *entry;
+
+   //Loop through the list of IPv6 addresses assigned to the interface
+   for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.addrList[i];
+
+      //Valid IPv6 address
+      if(entry->state != IPV6_ADDR_STATE_INVALID)
+      {
+         //Check whether the current entry matches the specified address
+         if(ipv6CompAddr(&entry->addr, addr))
+         {
+            //Return the state of the IPv6 address
+            return entry->state;
+         }
+      }
+   }
+
+   //The specified IPv6 address is not valid
+   return IPV6_ADDR_STATE_INVALID;
+}
+
+
+/**
+ * @brief Set IPv6 address and address state
+ * @param[in] interface Pointer to the desired network interface
+ * @param[in] index Zero-based index
+ * @param[in] addr IPv6 address
+ * @param[in] state State of the IPv6 address
+ * @param[in] validLifetime Valid lifetime
+ * @param[in] preferredLifetime Preferred lifetime
+ * @param[in] permanent Permanently assigned address
+ * @return Error code
+ **/
+
+error_t ipv6SetAddr(NetInterface *interface, uint_t index, const Ipv6Addr *addr,
+   Ipv6AddrState state, systime_t validLifetime, systime_t preferredLifetime, bool_t permanent)
+{
+   error_t error;
+   Ipv6AddrEntry *entry;
+   Ipv6Addr solicitedNodeAddr;
+
+   //Check parameters
+   if(interface == NULL || addr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure that the index is valid
+   if(index >= IPV6_ADDR_LIST_SIZE)
+      return ERROR_OUT_OF_RANGE;
+
+   //The IPv6 address must be a valid unicast address
+   if(ipv6IsMulticastAddr(addr))
+      return ERROR_INVALID_ADDRESS;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Point to the corresponding entry
+   entry = &interface->ipv6Context.addrList[index];
+
+   //Check whether an IPv6 address is already assigned
+   if(!ipv6CompAddr(&entry->addr, &IPV6_UNSPECIFIED_ADDR))
+   {
+      //Check the state of the IPv6 address
+      if(entry->state != IPV6_ADDR_STATE_INVALID)
+      {
+         //Ethernet interface?
+         if(interface->nicDriver->type == NIC_TYPE_ETHERNET)
+         {
+            //Form the Solicited-Node address
+            ipv6ComputeSolicitedNodeAddr(&entry->addr, &solicitedNodeAddr);
+            //Leave the Solicited-Node multicast group
+            ipv6LeaveMulticastGroup(interface, &solicitedNodeAddr);
+         }
+      }
+   }
+
+   //The current IPv6 address is no more valid
+   entry->state = IPV6_ADDR_STATE_INVALID;
+   entry->validLifetime = 0;
+   entry->preferredLifetime = 0;
+   entry->permanent = FALSE;
+
+   //Assign the new IPv6 address
+   entry->addr = *addr;
+
+   //Check whether the new IPv6 address is valid
+   if(!ipv6CompAddr(addr, &IPV6_UNSPECIFIED_ADDR))
+   {
+      //Check the state of the IPv6 address
+      if(state != IPV6_ADDR_STATE_INVALID)
+      {
+         //Ethernet interface?
+         if(interface->nicDriver->type == NIC_TYPE_ETHERNET)
+         {
+            //Form the Solicited-Node address for the link-local address
+            ipv6ComputeSolicitedNodeAddr(addr, &solicitedNodeAddr);
+            //Join the Solicited-Node multicast group for each assigned address
+            error = ipv6JoinMulticastGroup(interface, &solicitedNodeAddr);
+         }
+         //6LoWPAN interface?
+         else if(interface->nicDriver->type == NIC_TYPE_6LOWPAN)
+         {
+            //There is no need to join the solicited-node multicast address,
+            //since nobody multicasts Neighbor Solicitations in this type of
+            //network (refer to RFC 6775 section 5.2)
+         }
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Set the state of the IPv6 address
+         entry->state = state;
+
+         //Clear duplicate flag
+         entry->duplicate = FALSE;
+
+         //Save preferred and valid lifetimes
+         entry->preferredLifetime = preferredLifetime;
+         entry->validLifetime = validLifetime;
+
+         //Set time stamp
+         entry->timestamp = osGetSystemTime();
+
+         //Initialize DAD related variables
+         entry->dadTimeout = 0;
+         entry->dadRetransmitCount = 0;
+      }
+
+      //This flag tells whether the IPv6 address should be permanently assigned
+      entry->permanent = permanent;
+   }
+
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+   //Link-local address?
+   if(index == 0)
+   {
+      //Restart mDNS probing process
+      mdnsResponderStartProbing(interface->mdnsResponderContext);
+   }
+#endif
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Add a new entry to the list of IPv6 addresses
+ * @param[in] interface Underlying network interface
+ * @param[in] addr IPv6 address
+ * @param[in] validLifetime Valid lifetime, in seconds
+ * @param[in] preferredLifetime Preferred lifetime, in seconds
+ **/
+
+void ipv6AddAddr(NetInterface *interface, const Ipv6Addr *addr,
+   uint32_t validLifetime, uint32_t preferredLifetime)
+{
+   uint_t i;
+   Ipv6AddrEntry *entry;
+
+   //Check the valid lifetime
+   if(validLifetime != NDP_INFINITE_LIFETIME)
+   {
+      //The length of time in seconds that the address is valid
+      if(validLifetime < (MAX_DELAY / 1000))
+         validLifetime *= 1000;
+      else
+         validLifetime = MAX_DELAY;
+   }
+   else
+   {
+      //A value of all one bits (0xffffffff) represents infinity
+      validLifetime = INFINITE_DELAY;
+   }
+
+   //Check the preferred lifetime
+   if(preferredLifetime != NDP_INFINITE_LIFETIME)
+   {
+      //The length of time in seconds that the address remains preferred
+      if(preferredLifetime < (MAX_DELAY / 1000))
+         preferredLifetime *= 1000;
+      else
+         preferredLifetime = MAX_DELAY;
+   }
+   else
+   {
+      //A value of all one bits (0xffffffff) represents infinity
+      preferredLifetime = INFINITE_DELAY;
+   }
+
+   //Loop through the list of IPv6 addresses assigned to the interface
+   for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.addrList[i];
+
+      //Check the state of the IPv6 address
+      if(entry->state == IPV6_ADDR_STATE_PREFERRED ||
+         entry->state == IPV6_ADDR_STATE_DEPRECATED)
+      {
+         //Check whether the current entry matches the specified address
+         if(ipv6CompAddr(&entry->addr, addr))
+         {
+            //The IPv6 address should be preserved if it has been manually assigned
+            if(!entry->permanent)
+            {
+               //Update the lifetimes of the entry
+               entry->validLifetime = validLifetime;
+               entry->preferredLifetime = preferredLifetime;
+
+               //Save current time
+               entry->timestamp = osGetSystemTime();
+               //Update the state of the IPv6 address
+               entry->state = IPV6_ADDR_STATE_PREFERRED;
+            }
+
+            //Exit immediately
+            return;
+         }
+      }
+   }
+
+   //If no matching entry was found, then try to create a new entry
+   for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.addrList[i];
+
+      //Check the state of the IPv6 address
+      if(entry->state == IPV6_ADDR_STATE_INVALID)
+      {
+         //The IPv6 address should be preserved if it has been manually assigned
+         if(!entry->permanent)
+         {
+#if (NDP_SUPPORT == ENABLED)
+            //Check whether Duplicate Address Detection should be performed
+            if(interface->ndpContext.dupAddrDetectTransmits > 0)
+            {
+               //Use the IPv6 address as a tentative address
+               ipv6SetAddr(interface, i, addr, IPV6_ADDR_STATE_TENTATIVE,
+                  validLifetime, preferredLifetime, FALSE);
+            }
+            else
+#endif
+            {
+               //The use of the IPv6 address is now unrestricted
+               ipv6SetAddr(interface, i, addr, IPV6_ADDR_STATE_PREFERRED,
+                  validLifetime, preferredLifetime, FALSE);
+            }
+
+            //Exit immediately
+            return;
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Remove an entry from the list of IPv6 addresses
+ * @param[in] interface Underlying network interface
+ * @param[in] addr IPv6 address
+ **/
+
+void ipv6RemoveAddr(NetInterface *interface, const Ipv6Addr *addr)
+{
+   uint_t i;
+   Ipv6AddrEntry *entry;
+
+   //Loop through the list of IPv6 addresses assigned to the interface
+   for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.addrList[i];
+
+      //Valid IPv6 address?
+      if(entry->validLifetime)
+      {
+         //Check whether the current entry matches the specified address
+         if(ipv6CompAddr(&entry->addr, addr))
+         {
+            //The IPv6 address should be preserved if it has been manually assigned
+            if(!entry->permanent)
+            {
+               //Remove the IPv6 address from the list
+               ipv6SetAddr(interface, i, &IPV6_UNSPECIFIED_ADDR,
+                  IPV6_ADDR_STATE_INVALID, 0, 0, FALSE);
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Add a new entry to the Prefix List
+ * @param[in] interface Underlying network interface
+ * @param[in] prefix IPv6 prefix
+ * @param[in] length The number of leading bits in the prefix that are valid
+ * @param[in] validLifetime Valid lifetime, in seconds
+ * @param[in] preferredLifetime Preferred lifetime, in seconds
+ **/
+
+void ipv6AddPrefix(NetInterface *interface, const Ipv6Addr *prefix,
+   uint_t length, uint32_t validLifetime, uint32_t preferredLifetime)
+{
+   uint_t i;
+   Ipv6PrefixEntry *entry;
+   Ipv6PrefixEntry *firstFreeEntry;
+
+   //Keep track of the first free entry
+   firstFreeEntry = NULL;
+
+   //Loop through the Prefix List
+   for(i = 0; i < IPV6_PREFIX_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.prefixList[i];
+
+      //Valid prefix?
+      if(entry->validLifetime)
+      {
+         //Compare prefix length against the specified value
+         if(entry->prefixLength == length)
+         {
+            //Check whether the current entry matches the specified prefix
+            if(ipv6CompPrefix(&entry->prefix, prefix, length))
+               break;
+         }
+      }
+      else
+      {
+         //The IPv6 prefix should be preserved if it has been manually assigned
+         if(!entry->permanent)
+         {
+            //Keep track of the first free entry
+            if(firstFreeEntry == NULL)
+               firstFreeEntry = entry;
+         }
+      }
+   }
+
+   //No matching entry found?
+   if(i >= IPV6_PREFIX_LIST_SIZE)
+      entry = firstFreeEntry;
+
+   //Update the entry if necessary
+   if(entry != NULL)
+   {
+      //The IPv6 prefix should be preserved if it has been manually assigned
+      if(!entry->permanent)
+      {
+         //Save the IPv6 prefix
+         entry->prefix = *prefix;
+         entry->prefixLength = length;
+
+         //Check the valid lifetime
+         if(validLifetime != NDP_INFINITE_LIFETIME)
+         {
+            //The length of time in seconds that the prefix is valid
+            //for the purpose of on-link determination
+            if(validLifetime < (MAX_DELAY / 1000))
+               entry->validLifetime = validLifetime * 1000;
+            else
+               entry->validLifetime = MAX_DELAY;
+         }
+         else
+         {
+            //A value of all one bits (0xffffffff) represents infinity
+            entry->validLifetime = INFINITE_DELAY;
+         }
+
+         //Check the preferred lifetime
+         if(preferredLifetime != NDP_INFINITE_LIFETIME)
+         {
+            //The length of time in seconds that addresses generated from the
+            //prefix via stateless address autoconfiguration remain preferred
+            if(preferredLifetime < (MAX_DELAY / 1000))
+               entry->preferredLifetime = preferredLifetime * 1000;
+            else
+               entry->preferredLifetime = MAX_DELAY;
+         }
+         else
+         {
+            //A value of all one bits (0xffffffff) represents infinity
+            entry->preferredLifetime = INFINITE_DELAY;
+         }
+
+         //Save current time
+         entry->timestamp = osGetSystemTime();
+      }
+   }
+}
+
+
+/**
+ * @brief Remove an entry from the Prefix List
+ * @param[in] interface Underlying network interface
+ * @param[in] prefix IPv6 prefix
+ * @param[in] length The number of leading bits in the prefix that are valid
+ **/
+
+void ipv6RemovePrefix(NetInterface *interface, const Ipv6Addr *prefix, uint_t length)
+{
+   uint_t i;
+   Ipv6PrefixEntry *entry;
+
+   //Loop through the Prefix List
+   for(i = 0; i < IPV6_PREFIX_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.prefixList[i];
+
+      //Valid prefix?
+      if(entry->validLifetime)
+      {
+         //Compare prefix length against the specified value
+         if(entry->prefixLength == length)
+         {
+            //Check whether the current entry matches the specified prefix
+            if(ipv6CompPrefix(&entry->prefix, prefix, length))
+            {
+               //The IPv6 prefix should be preserved if it has been manually assigned
+               if(!entry->permanent)
+               {
+                  //When removing an entry from the Prefix List, there is no need
+                  //to purge any entries from the Destination or Neighbor Caches
+                  entry->prefix = IPV6_UNSPECIFIED_ADDR;
+                  entry->prefixLength = 0;
+                  entry->validLifetime = 0;
+               }
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Add a new entry to the Default Router List
+ * @param[in] interface Underlying network interface
+ * @param[in] addr IPv6 address of the router
+ * @param[in] lifetime Router lifetime, in seconds
+ **/
+
+void ipv6AddDefaultRouter(NetInterface *interface,
+   const Ipv6Addr *addr, uint16_t lifetime)
+{
+   uint_t i;
+   Ipv6RouterEntry *entry;
+   Ipv6RouterEntry *firstFreeEntry;
+
+   //Keep track of the first free entry
+   firstFreeEntry = NULL;
+
+   //Loop through the Default Router List
+   for(i = 0; i < IPV6_ROUTER_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.routerList[i];
+
+      //Check the lifetime associated with the default router
+      if(entry->lifetime)
+      {
+         //Check whether the current entry matches the specified router address
+         if(ipv6CompAddr(&entry->addr, addr))
+            break;
+      }
+      else
+      {
+         //The router address should be preserved if it has been manually assigned
+         if(!entry->permanent)
+         {
+            //Keep track of the first free entry
+            if(firstFreeEntry == NULL)
+               firstFreeEntry = entry;
+         }
+      }
+   }
+
+   //No matching entry found?
+   if(i >= IPV6_ROUTER_LIST_SIZE)
+      entry = firstFreeEntry;
+
+   //Update the entry if necessary
+   if(entry != NULL)
+   {
+      //The router address should be preserved if it has been manually assigned
+      if(!entry->permanent)
+      {
+         //Save the IPv6 address of the router
+         entry->addr = *addr;
+         //The lifetime associated with the default router
+         entry->lifetime = lifetime * 1000;
+         //Save current time
+         entry->timestamp = osGetSystemTime();
+      }
+   }
+}
+
+
+/**
+ * @brief Remove an entry from the Default Router List
+ * @param[in] interface Underlying network interface
+ * @param[in] addr IPv6 address of the router to be removed from the list
+ **/
+
+void ipv6RemoveDefaultRouter(NetInterface *interface, const Ipv6Addr *addr)
+{
+   uint_t i;
+   bool_t flag;
+   Ipv6RouterEntry *entry;
+
+   //This flag will be set if any entry has been removed from
+   //the Default Router List
+   flag = FALSE;
+
+   //Loop through the Default Router List
+   for(i = 0; i < IPV6_ROUTER_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.routerList[i];
+
+      //Check the lifetime associated with the default router
+      if(entry->lifetime)
+      {
+         //Check whether the current entry matches the specified router address
+         if(ipv6CompAddr(&entry->addr, addr))
+         {
+            //The router address should be preserved if it has been manually assigned
+            if(!entry->permanent)
+            {
+               //Immediately time-out the entry
+               entry->addr = IPV6_UNSPECIFIED_ADDR;
+               entry->lifetime = 0;
+
+               //Set flag
+               flag = TRUE;
+            }
+         }
+      }
+   }
+
+   //Check whether an entry has been removed from the list
+   if(flag)
+   {
+#if (NDP_SUPPORT == ENABLED)
+      //When removing a router from the Default Router list, the node must
+      //update the Destination Cache in such a way that all entries using
+      //the router perform next-hop determination again
+      ndpFlushDestCache(interface);
+#endif
+   }
+}
+
+
+/**
+ * @brief Flush the list of IPv6 addresses
+ * @param[in] interface Underlying network interface
+ **/
+
+void ipv6FlushAddrList(NetInterface *interface)
+{
+   uint_t i;
+   Ipv6AddrEntry *entry;
+
+   //Go through the list of IPv6 addresses
+   for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.addrList[i];
+
+      //Valid IPv6 address?
+      if(entry->validLifetime > 0)
+      {
+         //The IPv6 address should be preserved if it has been manually assigned
+         if(!entry->permanent)
+         {
+            //The IPv6 address is not longer valid
+            ipv6SetAddr(interface, i, &IPV6_UNSPECIFIED_ADDR,
+               IPV6_ADDR_STATE_INVALID, 0, 0, FALSE);
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Flush the Prefix List
+ * @param[in] interface Underlying network interface
+ **/
+
+void ipv6FlushPrefixList(NetInterface *interface)
+{
+   uint_t i;
+   Ipv6PrefixEntry *entry;
+
+   //Go through the Prefix List
+   for(i = 0; i < IPV6_PREFIX_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.prefixList[i];
+
+      //Valid IPv6 prefix?
+      if(entry->validLifetime > 0)
+      {
+         //The IPv6 prefix should be preserved if it has been manually assigned
+         if(!entry->permanent)
+         {
+            //Clear the current entry
+            entry->prefix = IPV6_UNSPECIFIED_ADDR;
+            entry->prefixLength = 0;
+            //Remove the entry from the Prefix List
+            entry->validLifetime = 0;
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Flush the Default Router List
+ * @param[in] interface Underlying network interface
+ **/
+
+void ipv6FlushDefaultRouterList(NetInterface *interface)
+{
+   uint_t i;
+   Ipv6RouterEntry *entry;
+
+   //Go through the Default Router List
+   for(i = 0; i < IPV6_ROUTER_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.routerList[i];
+
+      //Valid entry?
+      if(entry->lifetime > 0)
+      {
+         //The router address should be preserved if it has been manually assigned
+         if(!entry->permanent)
+         {
+            //Clear the current entry
+            entry->addr = IPV6_UNSPECIFIED_ADDR;
+            //Remove the entry from the Default Router List
+            entry->lifetime = 0;
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Flush the list of DNS servers
+ * @param[in] interface Underlying network interface
+ **/
+
+void ipv6FlushDnsServerList(NetInterface *interface)
+{
+   //Clear the list of DNS servers
+   memset(interface->ipv6Context.dnsServerList, 0,
+      sizeof(interface->ipv6Context.dnsServerList));
+}
+
+
+/**
+ * @brief Source IPv6 address filtering
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr Source IPv6 address to be checked
+ * @return Error code
+ **/
+
+error_t ipv6CheckSourceAddr(NetInterface *interface, const Ipv6Addr *ipAddr)
+{
+   //Multicast addresses cannot be used as source address
+   if(ipv6IsMulticastAddr(ipAddr))
+   {
+      //Debug message
+      TRACE_WARNING("Wrong source IPv6 address!\r\n");
+      //The source address not is acceptable
+      return ERROR_INVALID_ADDRESS;
+   }
+
+   //The source address is acceptable
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Destination IPv6 address filtering
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr Destination IPv6 address to be checked
+ * @return Error code
+ **/
+
+error_t ipv6CheckDestAddr(NetInterface *interface, const Ipv6Addr *ipAddr)
+{
+   error_t error;
+   uint_t i;
+
+   //Filter out any invalid addresses
+   error = ERROR_INVALID_ADDRESS;
+
+   //Multicast address?
+   if(ipv6IsMulticastAddr(ipAddr))
+   {
+      //Go through the multicast filter table
+      for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
+      {
+         Ipv6FilterEntry *entry;
+
+         //Point to the current entry
+         entry = &interface->ipv6Context.multicastFilter[i];
+
+         //Valid entry?
+         if(entry->refCount > 0)
+         {
+            //Check whether the destination IPv6 address matches
+            //a relevant multicast address
+            if(ipv6CompAddr(&entry->addr, ipAddr))
+            {
+               //The multicast address is acceptable
+               error = NO_ERROR;
+               //Stop immediately
+               break;
+            }
+         }
+      }
+   }
+   //Unicast address?
+   else
+   {
+      //Loop through the list of IPv6 unicast addresses
+      for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++)
+      {
+         Ipv6AddrEntry *entry;
+
+         //Point to the current entry
+         entry = &interface->ipv6Context.addrList[i];
+
+         //Valid entry?
+         if(entry->state != IPV6_ADDR_STATE_INVALID)
+         {
+            //Check whether the destination address matches a valid unicast
+            //address assigned to the interface
+            if(ipv6CompAddr(&entry->addr, ipAddr))
+            {
+               //The destination address is acceptable
+               error = NO_ERROR;
+               //We are done
+               break;
+            }
+         }
+      }
+
+      //Check whether the specified is a valid unicast address
+      if(error == ERROR_INVALID_ADDRESS)
+      {
+         Ipv6Addr *anycastAddrList;
+
+         //Point to the list of anycast addresses assigned to the interface
+         anycastAddrList = interface->ipv6Context.anycastAddrList;
+
+         //Loop through the list of IPv6 anycast addresses
+         for(i = 0; i < IPV6_ANYCAST_ADDR_LIST_SIZE; i++)
+         {
+            //Valid entry?
+            if(!ipv6CompAddr(&anycastAddrList[i], &IPV6_UNSPECIFIED_ADDR))
+            {
+               //Check whether the destination address matches a valid anycast
+               //address assigned to the interface
+               if(ipv6CompAddr(&anycastAddrList[i], ipAddr))
+               {
+                  //The destination address is acceptable
+                  error = NO_ERROR;
+                  //We are done
+                  break;
+               }
+            }
+         }
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief IPv6 source address selection
+ *
+ * This function selects the source address and the relevant network interface
+ * to be used in order to join the specified destination address
+ *
+ * @param[in,out] interface A pointer to a valid network interface may be provided as
+ *   a hint. The function returns a pointer identifying the interface to be used
+ * @param[in] destAddr Destination IPv6 address
+ * @param[out] srcAddr Local IPv6 address to be used
+ * @return Error code
+ **/
+
+error_t ipv6SelectSourceAddr(NetInterface **interface,
+   const Ipv6Addr *destAddr, Ipv6Addr *srcAddr)
+{
+   uint_t i;
+   uint_t j;
+   NetInterface *currentInterface;
+   NetInterface *bestInterface;
+   Ipv6AddrEntry *currentAddr;
+   Ipv6AddrEntry *bestAddr;
+
+   //Initialize variables
+   bestInterface = NULL;
+   bestAddr = NULL;
+
+   //Loop through network interfaces
+   for(i = 0; i < NET_INTERFACE_COUNT; i++)
+   {
+      //Point to the current interface
+      currentInterface = &netInterface[i];
+
+      //A network interface may be provided as a hint...
+      if(*interface != currentInterface && *interface != NULL)
+      {
+         //Select the next interface in the list
+         continue;
+      }
+
+      //A sort of the candidate source addresses is being performed, where a
+      //set of rules define the ordering among addresses (refer to RFC 6724)
+      for(j = 0; j < IPV6_ADDR_LIST_SIZE; j++)
+      {
+         //Point to the current entry
+         currentAddr = &currentInterface->ipv6Context.addrList[j];
+
+         //Check the state of the address
+         if(currentAddr->state == IPV6_ADDR_STATE_PREFERRED ||
+            currentAddr->state == IPV6_ADDR_STATE_DEPRECATED)
+         {
+            //Select the first address as default
+            if(bestAddr == NULL)
+            {
+               //Give the current source address the higher precedence
+               bestInterface = currentInterface;
+               bestAddr = currentAddr;
+
+               //Select the next address in the list
+               continue;
+            }
+
+            //Rule 1: Prefer same address
+            if(ipv6CompAddr(&bestAddr->addr, destAddr))
+            {
+               //Select the next address in the list
+               continue;
+            }
+            else if(ipv6CompAddr(&currentAddr->addr, destAddr))
+            {
+               //Give the current source address the higher precedence
+               bestInterface = currentInterface;
+               bestAddr = currentAddr;
+
+               //Select the next address in the list
+               continue;
+            }
+
+            //Rule 2: Prefer appropriate scope
+            if(ipv6GetAddrScope(&currentAddr->addr) < ipv6GetAddrScope(&bestAddr->addr))
+            {
+               if(ipv6GetAddrScope(&currentAddr->addr) >= ipv6GetAddrScope(destAddr))
+               {
+                  //Give the current source address the higher precedence
+                  bestInterface = currentInterface;
+                  bestAddr = currentAddr;
+               }
+
+               //Select the next address in the list
+               continue;
+            }
+            else if(ipv6GetAddrScope(&bestAddr->addr) < ipv6GetAddrScope(&currentAddr->addr))
+            {
+               if(ipv6GetAddrScope(&bestAddr->addr) < ipv6GetAddrScope(destAddr))
+               {
+                  //Give the current source address the higher precedence
+                  bestInterface = currentInterface;
+                  bestAddr = currentAddr;
+               }
+
+               //Select the next address in the list
+               continue;
+            }
+
+            //Rule 3: Avoid deprecated addresses
+            if(bestAddr->state == IPV6_ADDR_STATE_PREFERRED &&
+               currentAddr->state == IPV6_ADDR_STATE_DEPRECATED)
+            {
+               //Select the next address in the list
+               continue;
+            }
+            else if(currentAddr->state == IPV6_ADDR_STATE_PREFERRED &&
+               bestAddr->state == IPV6_ADDR_STATE_DEPRECATED)
+            {
+               //Give the current source address the higher precedence
+               bestInterface = currentInterface;
+               bestAddr = currentAddr;
+
+               //Select the next address in the list
+               continue;
+            }
+
+            //Rule 8: Use longest matching prefix
+            if(ipv6GetCommonPrefixLength(&currentAddr->addr, destAddr) >
+               ipv6GetCommonPrefixLength(&bestAddr->addr, destAddr))
+            {
+               //Give the current source address the higher precedence
+               bestInterface = currentInterface;
+               bestAddr = currentAddr;
+            }
+         }
+      }
+   }
+
+   //Source address selection failed?
+   if(bestAddr == NULL)
+   {
+      //Report an error
+      return ERROR_NO_ADDRESS;
+   }
+
+   //Return the out-going interface and the source address to be used
+   *interface = bestInterface;
+   *srcAddr = bestAddr->addr;
+
+   //Successful source address selection
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Check whether an IPv6 address is on-link
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr IPv6 address to be checked
+ * @return TRUE if the IPv6 address is on-link, else FALSE
+ **/
+
+bool_t ipv6IsOnLink(NetInterface *interface, const Ipv6Addr *ipAddr)
+{
+   uint_t i;
+   Ipv6PrefixEntry *entry;
+
+   //Link-local prefix?
+   if(ipv6IsLinkLocalUnicastAddr(ipAddr))
+   {
+      //The link-local prefix is considered to be on the prefix
+      //list with an infinite invalidation timer regardless of
+      //whether routers are advertising a prefix for it
+      return TRUE;
+   }
+
+   //Loop through the Prefix List
+   for(i = 0; i < IPV6_PREFIX_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.prefixList[i];
+
+      //Valid prefix?
+      if(entry->validLifetime > 0)
+      {
+         //Check the specified address against the prefix
+         if(ipv6CompPrefix(ipAddr, &entry->prefix, entry->prefixLength))
+         {
+            //The specified IPv6 address is on-link
+            return TRUE;
+         }
+      }
+   }
+
+   //The specified IPv6 address is off-link
+   return FALSE;
+}
+
+
+/**
+ * @brief Check whether an IPv6 address is a tentative address
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr IPv6 address to be checked
+ * @return TRUE if the IPv6 address is a tentative address, else FALSE
+ **/
+
+bool_t ipv6IsTentativeAddr(NetInterface *interface, const Ipv6Addr *ipAddr)
+{
+   uint_t i;
+   Ipv6AddrEntry *entry;
+
+   //Loop through the list of IPv6 unicast addresses
+   for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.addrList[i];
+
+      //Tentative address?
+      if(entry->state == IPV6_ADDR_STATE_TENTATIVE)
+      {
+         //Check whether the specified address matches a valid unicast
+         //address assigned to the interface
+         if(ipv6CompAddr(&entry->addr, ipAddr))
+         {
+            //The specified IPv6 address is a tentative address
+            return TRUE;
+         }
+      }
+   }
+
+   //The specified IPv6 address is not a tentative address
+   return FALSE;
+}
+
+
+/**
+ * @brief Check whether an IPv6 address is an anycast address
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr IPv6 address to be checked
+ * @return TRUE if the IPv6 address is an anycast address, else FALSE
+ **/
+
+bool_t ipv6IsAnycastAddr(NetInterface *interface, const Ipv6Addr *ipAddr)
+{
+   uint_t i;
+   Ipv6Addr *anycastAddrList;
+
+   //Point to the list of anycast addresses assigned to the interface
+   anycastAddrList = interface->ipv6Context.anycastAddrList;
+
+   //Loop through the list of IPv6 anycast addresses
+   for(i = 0; i < IPV6_ANYCAST_ADDR_LIST_SIZE; i++)
+   {
+      //Valid entry?
+      if(!ipv6CompAddr(&anycastAddrList[i], &IPV6_UNSPECIFIED_ADDR))
+      {
+         //Check whether the specified address matches a valid anycast
+         //address assigned to the interface
+         if(ipv6CompAddr(&anycastAddrList[i], ipAddr))
+         {
+            //The specified IPv6 address is an anycast address
+            return TRUE;
+         }
+      }
+   }
+
+   //The specified IPv6 address is not an anycast address
+   return FALSE;
+}
+
+
+/**
+ * @brief Compare IPv6 address prefixes
+ * @param[in] ipAddr1 Pointer to the first IPv6 address
+ * @param[in] ipAddr2 Pointer to the second IPv6 address
+ * @param[in] length Prefix length
+ * @return TRUE if the prefixes match each other, else FALSE
+ **/
+
+bool_t ipv6CompPrefix(const Ipv6Addr *ipAddr1, const Ipv6Addr *ipAddr2, size_t length)
+{
+   size_t n = length / 8;
+   size_t m = length % 8;
+
+   //Ensure the prefix length is valid
+   if(length > 128)
+      return FALSE;
+
+   //Compare the first part
+   if(n > 0)
+   {
+      if(memcmp(ipAddr1, ipAddr2, n))
+         return FALSE;
+   }
+
+   //Compare the remaining bits, if any
+   if(m > 0)
+   {
+      //Calculate the mask to be applied
+      uint8_t mask = ((1 << m) - 1) << (8 - m);
+
+      //Check remaining bits
+      if((ipAddr1->b[n] & mask) != (ipAddr2->b[n] & mask))
+         return FALSE;
+   }
+
+   //The prefixes match each other
+   return TRUE;
+}
+
+
+/**
+ * @brief Retrieve the scope of an IPv6 address
+ * @param[in] ipAddr Pointer to an IPv6 address
+ * @return IPv6 address scope
+ **/
+
+uint_t ipv6GetAddrScope(const Ipv6Addr *ipAddr)
+{
+   uint_t scope;
+
+   //Multicast address?
+   if(ipv6IsMulticastAddr(ipAddr))
+   {
+      //Retrieve the scope of the multicast address
+      scope = ipv6GetMulticastAddrScope(ipAddr);
+   }
+   //Loopback address?
+   else if(ipv6CompAddr(ipAddr, &IPV6_LOOPBACK_ADDR))
+   {
+      //The loopback address may be used by a node to send an IPv6 packet to itself
+      scope = IPV6_ADDR_SCOPE_INTERFACE_LOCAL;
+   }
+   //Link-local unicast address?
+   else if(ipv6IsLinkLocalUnicastAddr(ipAddr))
+   {
+      //A link-local address is for use on a single link
+      scope = IPV6_ADDR_SCOPE_LINK_LOCAL;
+   }
+   //Site-local unicast address?
+   else if(ipv6IsSiteLocalUnicastAddr(ipAddr))
+   {
+      //A site-local address is for use in a single site
+      scope = IPV6_ADDR_SCOPE_SITE_LOCAL;
+   }
+   //Global address?
+   else
+   {
+      //Global scope
+      scope = IPV6_ADDR_SCOPE_GLOBAL;
+   }
+
+   //Return the scope of the specified IPv6 address
+   return scope;
+}
+
+
+/**
+ * @brief Retrieve the scope of an IPv6 multicast address
+ * @param[in] ipAddr Pointer to an IPv6 multicast address
+ * @return IPv6 address scope
+ **/
+
+uint_t ipv6GetMulticastAddrScope(const Ipv6Addr *ipAddr)
+{
+   uint_t scope;
+
+   //The scope field is a 4-bit value
+   scope = ipAddr->b[1] & 0x0F;
+
+   //If the scope field contains the reserved value F, an IPv6 packet
+   //must be treated the same as packets destined to a global multicast
+   //address (refer to RFC 3513 section 2.7)
+   if(scope == 0x0F)
+      scope = IPV6_ADDR_SCOPE_GLOBAL;
+
+   //Return the scope of the specified IPv6 multicast address
+   return scope;
+}
+
+
+/**
+ * @brief Compute the length of the longest common prefix
+ * @param[in] ipAddr1 Pointer to the first IPv6 address
+ * @param[in] ipAddr2 Pointer to the second IPv6 address
+ * @return The length of the longest common prefix, in bits
+ **/
+
+uint_t ipv6GetCommonPrefixLength(const Ipv6Addr *ipAddr1, const Ipv6Addr *ipAddr2)
+{
+   uint_t i;
+   uint_t j;
+
+   //Clear bit counter
+   j = 0;
+
+   //Perform a byte-for-byte comparison
+   for(i = 0; i < sizeof(Ipv6Addr); i++)
+   {
+      //Loop as long as prefixes match
+      if(ipAddr1->b[i] != ipAddr2->b[i])
+         break;
+   }
+
+   //Mismatch?
+   if(i < sizeof(Ipv6Addr))
+   {
+      //Perform a bit-for-bit comparison
+      for(j = 0; j < 8; j++)
+      {
+         //Calculate the mask to be applied
+         uint8_t mask = 1 << (7 - j);
+
+         //Loop as long as prefixes match
+         if((ipAddr1->b[i] & mask) != (ipAddr2->b[i] & mask))
+            break;
+      }
+   }
+
+   //Return the length of the longest common prefix, in bits
+   return i * 8 + j;
+}
+
+
+/**
+ * @brief Form a solicited-node address from an IPv6 address
+ * @param[in] ipAddr Unicast or anycast address
+ * @param[out] solicitedNodeAddr Corresponding solicited-node address
+ * @return Error code
+ **/
+
+error_t ipv6ComputeSolicitedNodeAddr(const Ipv6Addr *ipAddr,
+   Ipv6Addr *solicitedNodeAddr)
+{
+   //Ensure the specified address is a valid unicast or anycast address
+   if(ipv6IsMulticastAddr(ipAddr))
+      return ERROR_INVALID_ADDRESS;
+
+   //Copy the 104-bit prefix
+   ipv6CopyAddr(solicitedNodeAddr, &IPV6_SOLICITED_NODE_ADDR_PREFIX);
+
+   //Take the low-order 24 bits of the address (unicast or
+   //anycast) and append those bits to the prefix
+   solicitedNodeAddr->b[13] = ipAddr->b[13];
+   solicitedNodeAddr->b[14] = ipAddr->b[14];
+   solicitedNodeAddr->b[15] = ipAddr->b[15];
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Map an IPv6 multicast address to a MAC-layer multicast address
+ * @param[in] ipAddr IPv6 multicast address
+ * @param[out] macAddr Corresponding MAC-layer multicast address
+ * @return Error code
+ **/
+
+error_t ipv6MapMulticastAddrToMac(const Ipv6Addr *ipAddr, MacAddr *macAddr)
+{
+   //Ensure the specified IPv6 address is a multicast address
+   if(!ipv6IsMulticastAddr(ipAddr))
+      return ERROR_INVALID_ADDRESS;
+
+   //To support IPv6 multicasting, MAC address range of 33-33-00-00-00-00
+   //to 33-33-FF-FF-FF-FF is reserved (refer to RFC 2464)
+   macAddr->b[0] = 0x33;
+   macAddr->b[1] = 0x33;
+
+   //The low-order 32 bits of the IPv6 multicast address are mapped directly
+   //to the low-order 32 bits in the MAC-layer multicast address
+   macAddr->b[2] = ipAddr->b[12];
+   macAddr->b[3] = ipAddr->b[13];
+   macAddr->b[4] = ipAddr->b[14];
+   macAddr->b[5] = ipAddr->b[15];
+
+   //The specified IPv6 multicast address was successfully
+   //mapped to a MAC-layer address
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Generate a IPv6 link-local address from an interface identifier
+ * @param[in] interfaceId Interface identifier
+ * @param[out] ipAddr Corresponding IPv6 link-local address
+ **/
+
+void ipv6GenerateLinkLocalAddr(const Eui64* interfaceId, Ipv6Addr *ipAddr)
+{
+   //A link-local address is formed by combining the well-known
+   //link-local prefix fe80::/10 with the interface identifier
+   ipAddr->w[0] = HTONS(0xFE80);
+   ipAddr->w[1] = HTONS(0x0000);
+   ipAddr->w[2] = HTONS(0x0000);
+   ipAddr->w[3] = HTONS(0x0000);
+   ipAddr->w[4] = interfaceId->w[0];
+   ipAddr->w[5] = interfaceId->w[1];
+   ipAddr->w[6] = interfaceId->w[2];
+   ipAddr->w[7] = interfaceId->w[3];
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/ipv6_misc.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,86 @@
+/**
+ * @file ipv6_misc.h
+ * @brief Helper functions for IPv6
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _IPV6_MISC_H
+#define _IPV6_MISC_H
+
+//Dependencies
+#include "core/net.h"
+#include "ipv6/ipv6.h"
+
+//IPv6 related functions
+Ipv6AddrState ipv6GetAddrState(NetInterface *interface, const Ipv6Addr *addr);
+
+error_t ipv6SetAddr(NetInterface *interface, uint_t index, const Ipv6Addr *addr,
+   Ipv6AddrState state, systime_t validLifetime, systime_t preferredLifetime, bool_t permanent);
+
+void ipv6AddAddr(NetInterface *interface, const Ipv6Addr *addr,
+   uint32_t validLifetime, uint32_t preferredLifetime);
+
+void ipv6RemoveAddr(NetInterface *interface, const Ipv6Addr *addr);
+
+void ipv6AddPrefix(NetInterface *interface, const Ipv6Addr *prefix,
+   uint_t length, uint32_t validLifetime, uint32_t preferredLifetime);
+
+void ipv6RemovePrefix(NetInterface *interface, const Ipv6Addr *prefix, uint_t length);
+
+void ipv6AddDefaultRouter(NetInterface *interface,
+   const Ipv6Addr *addr, uint16_t lifetime);
+
+void ipv6RemoveDefaultRouter(NetInterface *interface, const Ipv6Addr *addr);
+
+void ipv6FlushAddrList(NetInterface *interface);
+void ipv6FlushPrefixList(NetInterface *interface);
+void ipv6FlushDefaultRouterList(NetInterface *interface);
+void ipv6FlushDnsServerList(NetInterface *interface);
+
+error_t ipv6CheckSourceAddr(NetInterface *interface, const Ipv6Addr *ipAddr);
+error_t ipv6CheckDestAddr(NetInterface *interface, const Ipv6Addr *ipAddr);
+
+error_t ipv6SelectSourceAddr(NetInterface **interface,
+   const Ipv6Addr *destAddr, Ipv6Addr *srcAddr);
+
+bool_t ipv6IsOnLink(NetInterface *interface, const Ipv6Addr *ipAddr);
+bool_t ipv6IsTentativeAddr(NetInterface *interface, const Ipv6Addr *ipAddr);
+bool_t ipv6IsAnycastAddr(NetInterface *interface, const Ipv6Addr *ipAddr);
+
+bool_t ipv6CompPrefix(const Ipv6Addr *ipAddr1, const Ipv6Addr *ipAddr2, size_t length);
+
+uint_t ipv6GetAddrScope(const Ipv6Addr *ipAddr);
+uint_t ipv6GetMulticastAddrScope(const Ipv6Addr *ipAddr);
+uint_t ipv6GetCommonPrefixLength(const Ipv6Addr *ipAddr1, const Ipv6Addr *ipAddr2);
+
+error_t ipv6ComputeSolicitedNodeAddr(const Ipv6Addr *ipAddr,
+   Ipv6Addr *solicitedNodeAddr);
+
+error_t ipv6MapMulticastAddrToMac(const Ipv6Addr *ipAddr, MacAddr *macAddr);
+
+void ipv6GenerateLinkLocalAddr(const Eui64* interfaceId, Ipv6Addr *ipAddr);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/ipv6_pmtu.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,116 @@
+/**
+ * @file ipv6_pmtu.c
+ * @brief Path MTU Discovery for IPv6
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL IPV6_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "core/ip.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_pmtu.h"
+#include "ipv6/ndp.h"
+#include "ipv6/ndp_cache.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV6_SUPPORT == ENABLED && IPV6_PMTU_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Retrieve the PMTU for the specified path
+ * @param[in] interface Underlying network interface
+ * @param[in] destAddr Destination IPv6 address
+ * @return PMTU value
+ **/
+
+size_t ipv6GetPathMtu(NetInterface *interface, const Ipv6Addr *destAddr)
+{
+   size_t pathMtu;
+
+#if (NDP_SUPPORT == ENABLED)
+   NdpDestCacheEntry *entry;
+
+   //Search the Destination Cache for the specified IPv6 address
+   entry = ndpFindDestCacheEntry(interface, destAddr);
+
+   //Check whether a matching entry has been found in the Destination Cache
+   if(entry != NULL)
+   {
+      //Use the existing PMTU estimate
+      pathMtu = entry->pathMtu;
+   }
+   else
+   {
+      //If no entry exists in the Destination Cache, the PMTU value for
+      //the path is assumed to be the MTU of the first-hop link
+      pathMtu = interface->ipv6Context.linkMtu;
+   }
+#else
+   //The PMTU value for the path is assumed to be the MTU of the first-hop link
+   pathMtu = interface->ipv6Context.linkMtu;
+#endif
+
+   //Return the PMTU value
+   return pathMtu;
+}
+
+
+/**
+ * @brief Update the PMTU for the specified path
+ * @param[in] interface Underlying network interface
+ * @param[in] destAddr Destination IPv6 address
+ * @param[in] tentativePathMtu Tentative PMTU value
+ **/
+
+void ipv6UpdatePathMtu(NetInterface *interface,
+   const Ipv6Addr *destAddr, size_t tentativePathMtu)
+{
+#if (NDP_SUPPORT == ENABLED)
+   NdpDestCacheEntry *entry;
+
+   //The destination address from the original packet is used to determine
+   //which path the message applies to
+   entry = ndpFindDestCacheEntry(interface, destAddr);
+
+   //Check whether a matching entry has been found in the Destination Cache
+   if(entry != NULL)
+   {
+      //Compare the tentative PMTU to the existing PMTU
+      if(tentativePathMtu < entry->pathMtu)
+      {
+         //If the tentative PMTU is less than the existing PMTU estimate,
+         //the tentative PMTU replaces the existing PMTU
+         entry->pathMtu = tentativePathMtu;
+      }
+   }
+#endif
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/ipv6_pmtu.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,49 @@
+/**
+ * @file ipv6_pmtu.h
+ * @brief Path MTU Discovery for IPv6
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _IPV6_PMTU_H
+#define _IPV6_PMTU_H
+
+//Dependencies
+#include "core/net.h"
+
+//Path MTU discovery support
+#ifndef IPV6_PMTU_SUPPORT
+   #define IPV6_PMTU_SUPPORT ENABLED
+#elif (IPV6_PMTU_SUPPORT != ENABLED && IPV6_PMTU_SUPPORT != DISABLED)
+   #error IPV6_PMTU_SUPPORT parameter is not valid
+#endif
+
+//Path MTU discovery related functions
+size_t ipv6GetPathMtu(NetInterface *interface, const Ipv6Addr *destAddr);
+
+void ipv6UpdatePathMtu(NetInterface *interface,
+   const Ipv6Addr *destAddr, size_t tentativePathMtu);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/ipv6_routing.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,639 @@
+/**
+ * @file ipv6_routing.c
+ * @brief IPv6 routing
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL IPV6_TRACE_LEVEL
+
+//Dependencies
+#include <limits.h>
+#include "core/net.h"
+#include "core/ip.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_misc.h"
+#include "ipv6/ipv6_routing.h"
+#include "ipv6/icmpv6.h"
+#include "ipv6/ndp.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV6_SUPPORT == ENABLED && IPV6_ROUTING_SUPPORT == ENABLED)
+
+//IPv6 routing table
+static Ipv6RoutingTableEntry ipv6RoutingTable[IPV6_ROUTING_TABLE_SIZE];
+
+
+/**
+ * @brief Initialize IPv6 routing table
+ * @return Error code
+ **/
+
+error_t ipv6InitRouting(void)
+{
+   //Clear the routing table
+   memset(ipv6RoutingTable, 0, sizeof(ipv6RoutingTable));
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Enable routing for the specified interface
+ * @param[in] interface Underlying network interface
+ * @param[in] enable When the flag is set to TRUE, routing is enabled on the
+ *   interface and the router can forward packets to or from the interface
+ * @return Error code
+ **/
+
+error_t ipv6EnableRouting(NetInterface *interface, bool_t enable)
+{
+   //Check parameters
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Enable or disable routing
+   interface->ipv6Context.isRouter = enable;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Add a new entry in the IPv6 routing table
+ * @param[in] prefix Network destination
+ * @param[in] prefixLength Length of the prefix, in bits
+ * @param[in] interface Network interface where to forward the packet
+ * @param[in] nextHop IPv6 address of the next hop
+ * @param[in] metric Metric value
+ * @return Error code
+ **/
+
+error_t ipv6AddRoute(const Ipv6Addr *prefix, uint_t prefixLength,
+   NetInterface *interface, const Ipv6Addr *nextHop, uint_t metric)
+{
+   error_t error;
+   uint_t i;
+   Ipv6RoutingTableEntry *entry;
+   Ipv6RoutingTableEntry *firstFreeEntry;
+
+   //Check parameters
+   if(prefix == NULL || interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Keep track of the first free entry
+   firstFreeEntry = NULL;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Loop through routing table entries
+   for(i = 0; i < IPV6_ROUTING_TABLE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &ipv6RoutingTable[i];
+
+      //Valid entry?
+      if(entry->valid)
+      {
+         //Check prefix length
+         if(entry->prefixLength == prefixLength)
+         {
+            //Check whether the current entry matches the specified destination
+            if(ipv6CompPrefix(&entry->prefix, prefix, prefixLength))
+               break;
+         }
+      }
+      else
+      {
+         //Keep track of the first free entry
+         if(firstFreeEntry == NULL)
+            firstFreeEntry = entry;
+      }
+   }
+
+   //If the routing table does not contain the specified destination,
+   //then a new entry should be created
+   if(i >= IPV6_ROUTING_TABLE_SIZE)
+      entry = firstFreeEntry;
+
+   //Check whether the routing table runs out of space
+   if(entry != NULL)
+   {
+      //Network destination
+      entry->prefix = *prefix;
+      entry->prefixLength = prefixLength;
+
+      //Interface where to forward the packet
+      entry->interface = interface;
+
+      //Address of the next hop
+      if(nextHop != NULL)
+         entry->nextHop = *nextHop;
+      else
+         entry->nextHop = IPV6_UNSPECIFIED_ADDR;
+
+      //Metric value
+      entry->metric = metric;
+      //The entry is now valid
+      entry->valid = TRUE;
+
+      //Sucessful processing
+      error = NO_ERROR;
+   }
+   else
+   {
+      //The routing table is full
+      error = ERROR_FAILURE;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Remove an entry from the IPv6 routing table
+ * @param[in] prefix Network destination
+ * @param[in] prefixLength Length of the prefix, in bits
+ * @return Error code
+ **/
+
+error_t ipv6DeleteRoute(const Ipv6Addr *prefix, uint_t prefixLength)
+{
+   error_t error;
+   uint_t i;
+   Ipv6RoutingTableEntry *entry;
+
+   //Initialize status code
+   error = ERROR_NOT_FOUND;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Loop through routing table entries
+   for(i = 0; i < IPV6_ROUTING_TABLE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &ipv6RoutingTable[i];
+
+      //Valid entry?
+      if(entry->valid)
+      {
+         //Check prefix length
+         if(entry->prefixLength == prefixLength)
+         {
+            //Check whether the current entry matches the specified destination
+            if(ipv6CompPrefix(&entry->prefix, prefix, prefixLength))
+            {
+               //Delete current entry
+               entry->valid = FALSE;
+               //The route was successfully deleted from the routing table
+               error = NO_ERROR;
+            }
+         }
+      }
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Delete all routes from the IPv6 routing table
+ * @return Error code
+ **/
+
+error_t ipv6DeleteAllRoutes(void)
+{
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Clear the routing table
+   memset(ipv6RoutingTable, 0, sizeof(ipv6RoutingTable));
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Forward an IPv6 packet
+ * @param[in] srcInterface Network interface on which the packet was received
+ * @param[in] ipPacket Multi-part buffer that holds the IPv6 packet to forward
+ * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet
+ * @return Error code
+ **/
+
+error_t ipv6ForwardPacket(NetInterface *srcInterface,
+   NetBuffer *ipPacket, size_t ipPacketOffset)
+{
+   error_t error;
+   uint_t i;
+   uint_t metric;
+   uint_t prefixLength;
+   bool_t match;
+   size_t length;
+   size_t destOffset;
+   NetInterface *destInterface;
+   NetBuffer *destBuffer;
+   Ipv6Header *ipHeader;
+   Ipv6RoutingTableEntry *entry;
+   Ipv6Addr destIpAddr;
+
+   //Silently drop any IP packets received on an interface that has
+   //not been assigned a valid link-local address
+   if(ipv6GetLinkLocalAddrState(srcInterface) != IPV6_ADDR_STATE_PREFERRED)
+      return ERROR_NOT_CONFIGURED;
+
+   //If routing is not enabled on the interface, then the router cannot
+   //forward packets from the interface
+   if(!srcInterface->ipv6Context.isRouter)
+      return ERROR_FAILURE;
+
+   //Calculate the length of the IPv6 packet
+   length = netBufferGetLength(ipPacket) - ipPacketOffset;
+
+   //Ensure the packet length is greater than 40 bytes
+   if(length < sizeof(Ipv6Header))
+      return ERROR_INVALID_LENGTH;
+
+   //Point to the IPv6 header
+   ipHeader = netBufferAt(ipPacket, ipPacketOffset);
+
+   //Sanity check
+   if(ipHeader == NULL)
+      return ERROR_FAILURE;
+
+   //An IPv6 packet with a source address of unspecified must never be
+   //forwarded by an IPv6 router (refer to RFC section 3513 2.5.2)
+   if(ipv6CompAddr(&ipHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR))
+      return ERROR_INVALID_ADDRESS;
+
+   //The unspecified address must not be used as the destination address
+   //of IPv6 packets (refer to RFC section 3513 2.5.2)
+   if(ipv6CompAddr(&ipHeader->destAddr, &IPV6_UNSPECIFIED_ADDR))
+      return ERROR_INVALID_ADDRESS;
+
+   //An IPv6 packet with a destination address of loopback must never be
+   //forwarded by an IPv6 router (refer to RFC 3513 section 2.5.3)
+   if(ipv6CompAddr(&ipHeader->destAddr, &IPV6_LOOPBACK_ADDR))
+      return ERROR_INVALID_ADDRESS;
+
+   //Check whether the destination address is a link-local address
+   if(ipv6IsLinkLocalUnicastAddr(&ipHeader->destAddr))
+   {
+      //Forward the packet on the same network interface
+      destInterface = srcInterface;
+      //Next hop
+      destIpAddr = ipHeader->destAddr;
+   }
+   else
+   {
+      //Lowest metric value
+      metric = UINT_MAX;
+      //Longest prefix length
+      prefixLength = 0;
+      //Outgoing network interface
+      destInterface = NULL;
+
+      //Route determination process
+      for(i = 0; i < IPV6_ROUTING_TABLE_SIZE; i++)
+      {
+         //Point to the current entry
+         entry = &ipv6RoutingTable[i];
+
+         //Valid entry?
+         if(entry->valid && entry->interface != NULL)
+         {
+            //Clear flag
+            match = FALSE;
+
+            //Do not forward any IP packets to an interface that has not
+            //been assigned a valid link-local address...
+            if(ipv6GetLinkLocalAddrState(entry->interface) == IPV6_ADDR_STATE_PREFERRED)
+            {
+               //If routing is enabled on the interface, then the router
+               //can forward packets to the interface
+               if(entry->interface->ipv6Context.isRouter)
+               {
+                  //Compare the destination address with the current entry for a match
+                  if(ipv6CompPrefix(&ipHeader->destAddr, &entry->prefix, entry->prefixLength))
+                  {
+                     //The longest matching route is the most specific route to the
+                     //destination IPv6 address...
+                     if(entry->prefixLength > prefixLength)
+                     {
+                        //Give the current route the higher precedence
+                        match = TRUE;
+                     }
+                     else if(entry->prefixLength == prefixLength)
+                     {
+                        //If multiple entries with the longest match are found, the
+                        //router uses the lowest metric to select the best route
+                        if(entry->metric < metric)
+                        {
+                           //Give the current route the higher precedence
+                           match = TRUE;
+                        }
+                     }
+                  }
+               }
+            }
+
+            //Matching entry?
+            if(match)
+            {
+               //Select the current route
+               metric = entry->metric;
+               prefixLength = entry->prefixLength;
+
+               //Outgoing interface on which to forward the packet
+               destInterface = entry->interface;
+
+               //Next hop
+               if(!ipv6CompAddr(&entry->nextHop, &IPV6_UNSPECIFIED_ADDR))
+                  destIpAddr = entry->nextHop;
+               else
+                  destIpAddr = ipHeader->destAddr;
+            }
+         }
+      }
+   }
+
+   //No route to the destination?
+   if(destInterface == NULL)
+   {
+      //A Destination Unreachable message should be generated by a router
+      //in response to a packet that cannot be delivered
+      icmpv6SendErrorMessage(srcInterface, ICMPV6_TYPE_DEST_UNREACHABLE,
+         ICMPV6_CODE_NO_ROUTE_TO_DEST, 0, ipPacket, ipPacketOffset);
+
+      //Exit immediately
+      return ERROR_NO_ROUTE;
+   }
+
+   //Check whether the length of the IPv6 packet is larger than the link MTU
+   if(length > destInterface->ipv6Context.linkMtu)
+   {
+      //A Packet Too Big must be sent by a router in response to a packet
+      //that it cannot forward because the packet is larger than the MTU
+      //of the outgoing link
+      icmpv6SendErrorMessage(srcInterface, ICMPV6_TYPE_PACKET_TOO_BIG,
+         0, destInterface->ipv6Context.linkMtu, ipPacket, ipPacketOffset);
+
+      //Exit immediately
+      return ERROR_INVALID_LENGTH;
+   }
+
+   //Check whether the packet is explicitly addressed to the router itself
+   if(!ipv6CheckDestAddr(destInterface, &ipHeader->destAddr))
+   {
+      //Valid unicast address?
+      if(!ipv6IsMulticastAddr(&ipHeader->destAddr))
+      {
+         //Process IPv6 packet
+         //ipv6ProcessPacket(destInterface, ipPacket, ipPacketOffset);
+         //Exit immediately
+         return NO_ERROR;
+      }
+   }
+
+   //Check whether the IPv6 packet is about to be sent out the interface
+   //on which it was received
+   if(destInterface == srcInterface)
+   {
+#if (NDP_SUPPORT == ENABLED)
+      //A router should send a Redirect message whenever it forwards a packet
+      //that is not explicitly addressed to itself in which the source address
+      //identifies a neighbor, and
+      if(ipv6IsOnLink(srcInterface, &ipHeader->srcAddr))
+      {
+         //The router determines that a better first-hop node resides on the
+         //same link as the sending node for the destination address of the
+         //packet being forwarded, and
+         if(ipv6IsOnLink(destInterface, &destIpAddr))
+         {
+            //The destination address of the packet is not a multicast address
+            if(!ipv6IsMulticastAddr(&ipHeader->destAddr))
+            {
+               //Transmit a Redirect message
+               ndpSendRedirect(srcInterface, &destIpAddr, ipPacket, ipPacketOffset);
+            }
+         }
+      }
+#endif
+   }
+   else
+   {
+      //Check whether the scope of the source address is smaller than the
+      //scope of the destination address
+      if(ipv6GetAddrScope(&ipHeader->srcAddr) < ipv6GetAddrScope(&ipHeader->destAddr))
+      {
+         //A Destination Unreachable message should be generated by a router
+         //in response to a packet that cannot be delivered without leaving
+         //the scope of the source address
+         icmpv6SendErrorMessage(srcInterface, ICMPV6_TYPE_DEST_UNREACHABLE,
+            ICMPV6_CODE_BEYOND_SCOPE_OF_SRC_ADDR, 0, ipPacket, ipPacketOffset);
+
+         //Exit immediately
+         return ERROR_INVALID_ADDRESS;
+      }
+   }
+
+   //Hop Limit exceeded in transit?
+   if(ipHeader->hopLimit <= 1)
+   {
+      //If a router receives a packet with a Hop Limit of zero, or if a router
+      //decrements a packet's Hop Limit to zero, it must discard the packet
+      //and originate an ICMPv6 Time Exceeded message
+      icmpv6SendErrorMessage(srcInterface, ICMPV6_TYPE_TIME_EXCEEDED,
+         ICMPV6_CODE_HOP_LIMIT_EXCEEDED, 0, ipPacket, ipPacketOffset);
+
+      //Exit immediately
+      return ERROR_FAILURE;
+   }
+
+   //The Hop-by-Hop Options header, when present, must immediately follow
+   //the IPv6 header. Its presence is indicated by the value zero in the
+   //Next Header field of the IPv6 header
+   if(ipHeader->nextHeader == IPV6_HOP_BY_HOP_OPT_HEADER)
+   {
+      //Point to the extension header
+      size_t headerOffset = ipPacketOffset + sizeof(Ipv6Header);
+
+      //Calculate the offset of the Next Header field
+      size_t nextHeaderOffset = ipPacketOffset +
+         &ipHeader->nextHeader - (uint8_t *) ipHeader;
+
+      //The Hop-by-Hop Options header is used to carry optional information
+      //that must be examined by every node along a packet's delivery path
+      error = ipv6ParseHopByHopOptHeader(srcInterface,
+         ipPacket, ipPacketOffset, &headerOffset, &nextHeaderOffset);
+
+      //Any error while processing the extension header?
+      if(error)
+         return error;
+   }
+
+   //Allocate a buffer to hold the IPv6 packet
+   destBuffer = ethAllocBuffer(length, &destOffset);
+
+   //Successful memory allocation?
+   if(destBuffer != NULL)
+   {
+      //Copy IPv6 header
+      error = netBufferCopy(destBuffer, destOffset,
+         ipPacket, ipPacketOffset, length);
+
+      //Check status code
+      if(!error)
+      {
+         //Point to the IPv6 header
+         ipHeader = netBufferAt(destBuffer, destOffset);
+         //Every time a router forwards a packet, it decrements the Hop Limit field
+         ipHeader->hopLimit--;
+
+#if (ETH_SUPPORT == ENABLED)
+         //Ethernet interface?
+         if(destInterface->nicDriver->type == NIC_TYPE_ETHERNET)
+         {
+            MacAddr destMacAddr;
+
+            //Destination IPv6 address
+            if(ipv6CompAddr(&destIpAddr, &IPV6_UNSPECIFIED_ADDR))
+               destIpAddr = ipHeader->destAddr;
+
+            //Check whether the destination IPv6 address is a multicast address?
+            if(ipv6IsMulticastAddr(&destIpAddr))
+            {
+               //Map IPv6 multicast address to MAC-layer multicast address
+               error = ipv6MapMulticastAddrToMac(&destIpAddr, &destMacAddr);
+            }
+            else
+            {
+               //Resolve host address using Neighbor Discovery protocol
+               error = ndpResolve(destInterface, &destIpAddr, &destMacAddr);
+            }
+
+            //Successful address resolution?
+            if(!error)
+            {
+               //Debug message
+               TRACE_INFO("Forwarding IPv6 packet to %s (%" PRIuSIZE " bytes)...\r\n",
+                  destInterface->name, length);
+               //Dump IP header contents for debugging purpose
+               ipv6DumpHeader(ipHeader);
+
+               //Send Ethernet frame
+               error = ethSendFrame(destInterface, &destMacAddr,
+                  destBuffer, destOffset, ETH_TYPE_IPV6);
+            }
+            //Address resolution is in progress?
+            else if(error == ERROR_IN_PROGRESS)
+            {
+               //Debug message
+               TRACE_INFO("Enqueuing IPv6 packet (%" PRIuSIZE " bytes)...\r\n", length);
+               //Dump IP header contents for debugging purpose
+               ipv6DumpHeader(ipHeader);
+
+               //Enqueue packets waiting for address resolution
+               error = ndpEnqueuePacket(srcInterface, destInterface,
+                  &destIpAddr, destBuffer, destOffset);
+            }
+            //Address resolution failed?
+            else
+            {
+               //Debug message
+               TRACE_WARNING("Cannot map IPv6 address to Ethernet address!\r\n");
+            }
+         }
+         else
+#endif
+#if (PPP_SUPPORT == ENABLED)
+         //PPP interface?
+         if(destInterface->nicDriver->type == NIC_TYPE_PPP)
+         {
+            //Debug message
+            TRACE_INFO("Forwarding IPv6 packet to %s (%" PRIuSIZE " bytes)...\r\n",
+               destInterface->name, length);
+            //Dump IP header contents for debugging purpose
+            ipv6DumpHeader(ipHeader);
+
+            //Send PPP frame
+            error = pppSendFrame(destInterface, destBuffer, destOffset, PPP_PROTOCOL_IPV6);
+         }
+         else
+#endif
+         //6LoWPAN interface?
+         if(destInterface->nicDriver->type == NIC_TYPE_6LOWPAN)
+         {
+            //Debug message
+            TRACE_INFO("Forwarding IPv6 packet to %s (%" PRIuSIZE " bytes)...\r\n",
+               destInterface->name, length);
+            //Dump IP header contents for debugging purpose
+            ipv6DumpHeader(ipHeader);
+
+            //Send the packet over the specified link
+            error = nicSendPacket(destInterface, destBuffer, destOffset);
+         }
+         else
+         //Unknown interface type?
+         {
+            //Report an error
+            error = ERROR_INVALID_INTERFACE;
+         }
+      }
+
+      //Free previously allocated memory
+      netBufferFree(destBuffer);
+   }
+   else
+   {
+      //Failed to allocate memory
+      error = ERROR_OUT_OF_MEMORY;
+   }
+
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/ipv6_routing.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,80 @@
+/**
+ * @file ipv6_routing.h
+ * @brief IPv6 routing
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _IPV6_ROUTING_H
+#define _IPV6_ROUTING_H
+
+//Dependencies
+#include "core/net.h"
+#include "ipv6/ipv6.h"
+
+//IPv6 routing support
+#ifndef IPV6_ROUTING_SUPPORT
+   #define IPV6_ROUTING_SUPPORT DISABLED
+#elif (IPV6_ROUTING_SUPPORT != ENABLED && IPV6_ROUTING_SUPPORT != DISABLED)
+   #error IPV6_ROUTING_SUPPORT parameter is not valid
+#endif
+
+//Size of the IPv6 routing table
+#ifndef IPV6_ROUTING_TABLE_SIZE
+   #define IPV6_ROUTING_TABLE_SIZE 8
+#elif (IPV6_ROUTING_TABLE_SIZE < 1)
+   #error IPV6_ROUTING_TABLE_SIZE parameter is not valid
+#endif
+
+
+/**
+ * @brief Routing table entry
+ **/
+
+typedef struct
+{
+   bool_t valid;            ///<Valid entry
+   Ipv6Addr prefix;         ///<Destination
+   uint_t prefixLength;     ///<IPv6 prefix length
+   NetInterface *interface; ///<Outgoing network interface
+   Ipv6Addr nextHop;        ///<Next hop
+   uint_t metric;           ///<Metric value
+} Ipv6RoutingTableEntry;
+
+
+//IPv6 routing related functions
+error_t ipv6InitRouting(void);
+error_t ipv6EnableRouting(NetInterface *interface, bool_t enable);
+
+error_t ipv6AddRoute(const Ipv6Addr *prefix, uint_t prefixLength,
+   NetInterface *interface, const Ipv6Addr *nextHop, uint_t metric);
+
+error_t ipv6DeleteRoute(const Ipv6Addr *prefix, uint_t prefixLength);
+error_t ipv6DeleteAllRoutes(void);
+
+error_t ipv6ForwardPacket(NetInterface *srcInterface,
+   NetBuffer *ipPacket, size_t ipPacketOffset);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/mld.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,591 @@
+/**
+ * @file mld.c
+ * @brief MLD (Multicast Listener Discovery for IPv6)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * MLD is used by an IPv6 router to discover the presence of multicast
+ * listeners on its directly attached links, and to discover specifically
+ * which multicast addresses are of interest to those neighboring nodes.
+ * Refer to the following RFCs for complete details:
+ * - RFC 2710: Multicast Listener Discovery (MLD) for IPv6
+ * - RFC 3810: Multicast Listener Discovery Version 2 (MLDv2) for IPv6
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL MLD_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "core/ip.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/icmpv6.h"
+#include "ipv6/mld.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV6_SUPPORT == ENABLED && MLD_SUPPORT == ENABLED)
+
+//Tick counter to handle periodic operations
+systime_t mldTickCounter;
+
+
+/**
+ * @brief MLD initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mldInit(NetInterface *interface)
+{
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Start listening to the address on the interface
+ * @param[in] interface Underlying network interface
+ * @param[in] entry IPv6 filter entry identifying the address to listen to
+ * @return Error code
+ **/
+
+error_t mldStartListening(NetInterface *interface, Ipv6FilterEntry *entry)
+{
+   //The link-scope all-nodes address (FF02::1) is handled as a special
+   //case. The host starts in Idle Listener state for that address on
+   //every interface and never transitions to another state
+   if(ipv6CompAddr(&entry->addr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR))
+   {
+      //Clear flag
+      entry->flag = FALSE;
+      //Enter the Idle Listener state
+      entry->state = MLD_STATE_IDLE_LISTENER;
+   }
+   else
+   {
+      //Link is up?
+      if(interface->linkState)
+      {
+         //Send a Multicast Listener Report message for the group on the interface
+         mldSendListenerReport(interface, &entry->addr);
+
+         //Set flag
+         entry->flag = TRUE;
+         //Start timer
+         entry->timer = osGetSystemTime() + MLD_UNSOLICITED_REPORT_INTERVAL;
+         //Enter the Delaying Listener state
+         entry->state = MLD_STATE_DELAYING_LISTENER;
+      }
+      //Link is down?
+      else
+      {
+         //Clear flag
+         entry->flag = FALSE;
+         //Enter the Idle Listener state
+         entry->state = MLD_STATE_IDLE_LISTENER;
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Stop listening to the address on the interface
+ * @param[in] interface Underlying network interface
+ * @param[in] entry IPv6 filter entry identifying the multicast address to leave
+ * @return Error code
+ **/
+
+error_t mldStopListening(NetInterface *interface, Ipv6FilterEntry *entry)
+{
+   //Check link state
+   if(interface->linkState)
+   {
+      //Send a Multicast Listener Done message if the flag is set
+      if(entry->flag)
+         mldSendListenerDone(interface, &entry->addr);
+   }
+
+   //Switch to the Non-Listener state
+   entry->state = MLD_STATE_NON_LISTENER;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief MLD timer handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * handle MLD related timers
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void mldTick(NetInterface *interface)
+{
+   uint_t i;
+   systime_t time;
+   Ipv6FilterEntry *entry;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Go through the multicast filter table
+   for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.multicastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Delaying Listener state?
+         if(entry->state == MLD_STATE_DELAYING_LISTENER)
+         {
+            //Timer expired?
+            if(timeCompare(time, entry->timer) >= 0)
+            {
+               //Send a Multicast Listener Report message
+               mldSendListenerReport(interface, &entry->addr);
+
+               //Set flag
+               entry->flag = TRUE;
+               //Switch to the Idle Listener state
+               entry->state = MLD_STATE_IDLE_LISTENER;
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Callback function for link change event
+ * @param[in] interface Underlying network interface
+ **/
+
+void mldLinkChangeEvent(NetInterface *interface)
+{
+   uint_t i;
+   systime_t time;
+   Ipv6FilterEntry *entry;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Link up event?
+   if(interface->linkState)
+   {
+      //Go through the multicast filter table
+      for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
+      {
+         //Point to the current entry
+         entry = &interface->ipv6Context.multicastFilter[i];
+
+         //Valid entry?
+         if(entry->refCount > 0)
+         {
+            //The link-scope all-nodes address (FF02::1) is handled as a special
+            //case. The host starts in Idle Listener state for that address on
+            //every interface and never transitions to another state
+            if(!ipv6CompAddr(&entry->addr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR))
+            {
+               //Send an unsolicited Multicast Listener Report message for that group
+               mldSendListenerReport(interface, &entry->addr);
+
+               //Set flag
+               entry->flag = TRUE;
+               //Start timer
+               entry->timer = time + MLD_UNSOLICITED_REPORT_INTERVAL;
+               //Enter the Delaying Listener state
+               entry->state = MLD_STATE_DELAYING_LISTENER;
+            }
+         }
+      }
+   }
+   //Link down event?
+   else
+   {
+      //Go through the multicast filter table
+      for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
+      {
+         //Point to the current entry
+         entry = &interface->ipv6Context.multicastFilter[i];
+
+         //Valid entry?
+         if(entry->refCount > 0)
+         {
+            //Clear flag
+            entry->flag = FALSE;
+            //Enter the Idle Listener state
+            entry->state = MLD_STATE_IDLE_LISTENER;
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Process incoming Multicast Listener Query message
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IPv6 pseudo header
+ * @param[in] buffer Multi-part buffer containing the incoming MLD message
+ * @param[in] offset Offset to the first byte of the MLD message
+ * @param[in] hopLimit Hop Limit field from IPv6 header
+ **/
+
+void mldProcessListenerQuery(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   const NetBuffer *buffer, size_t offset, uint8_t hopLimit)
+{
+   uint_t i;
+   size_t length;
+   systime_t time;
+   systime_t maxRespDelay;
+   MldMessage *message;
+   Ipv6FilterEntry *entry;
+
+   //Retrieve the length of the MLD message
+   length = netBufferGetLength(buffer) - offset;
+
+   //The message must be at least 24 octets long
+   if(length < sizeof(MldMessage))
+      return;
+
+   //Point to the beginning of the MLD message
+   message = netBufferAt(buffer, offset);
+   //Sanity check
+   if(message == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("MLD message received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message contents for debugging purpose
+   mldDumpMessage(message);
+
+   //Make sure the source address of the message is a valid link-local address
+   if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr))
+      return;
+
+   //Check the Hop Limit field
+   if(hopLimit != MLD_HOP_LIMIT)
+      return;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //The Max Resp Delay field specifies the maximum time allowed
+   //before sending a responding report
+   maxRespDelay = message->maxRespDelay * 10;
+
+   //Go through the multicast filter table
+   for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.multicastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //The link-scope all-nodes address (FF02::1) is handled as a special
+         //case. The host starts in Idle Listener state for that address on
+         //every interface and never transitions to another state
+         if(!ipv6CompAddr(&entry->addr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR))
+         {
+            //A General Query is used to learn which multicast addresses have listeners
+            //on an attached link. A Multicast-Address-Specific Query is used to learn
+            //if a particular multicast address has any listeners on an attached link
+            if(ipv6CompAddr(&message->multicastAddr, &IPV6_UNSPECIFIED_ADDR) ||
+               ipv6CompAddr(&message->multicastAddr, &entry->addr))
+            {
+               //Delaying Listener state?
+               if(entry->state == MLD_STATE_DELAYING_LISTENER)
+               {
+                  //The timer has not yet expired?
+                  if(timeCompare(time, entry->timer) < 0)
+                  {
+                     //If a timer for the address is already running, it is reset to
+                     //the new random value only if the requested Max Response Delay
+                     //is less than the remaining value of the running timer
+                     if(maxRespDelay < (entry->timer - time))
+                     {
+                        //Restart delay timer
+                        entry->timer = time + mldRand(maxRespDelay);
+                     }
+                  }
+               }
+               //Idle Listener state?
+               else if(entry->state == MLD_STATE_IDLE_LISTENER)
+               {
+                  //Switch to the Delaying Listener state
+                  entry->state = MLD_STATE_DELAYING_LISTENER;
+                  //Delay the response by a random amount of time
+                  entry->timer = time + mldRand(maxRespDelay);
+               }
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Process incoming Multicast Listener Report message
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IPv6 pseudo header
+ * @param[in] buffer Multi-part buffer containing the incoming MLD message
+ * @param[in] offset Offset to the first byte of the MLD message
+ * @param[in] hopLimit Hop Limit field from IPv6 header
+ **/
+
+void mldProcessListenerReport(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   const NetBuffer *buffer, size_t offset, uint8_t hopLimit)
+{
+   uint_t i;
+   size_t length;
+   MldMessage *message;
+   Ipv6FilterEntry *entry;
+
+   //Retrieve the length of the MLD message
+   length = netBufferGetLength(buffer) - offset;
+
+   //The message must be at least 24 octets long
+   if(length < sizeof(MldMessage))
+      return;
+
+   //Point to the beginning of the MLD message
+   message = netBufferAt(buffer, offset);
+   //Sanity check
+   if(message == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("MLD message received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message contents for debugging purpose
+   mldDumpMessage(message);
+
+   //Make sure the source address of the message is a valid link-local address
+   if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr))
+      return;
+   //Check the Hop Limit field
+   if(hopLimit != MLD_HOP_LIMIT)
+      return;
+
+   //Go through the multicast filter table
+   for(i = 0; i < IPV6_MULTICAST_FILTER_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.multicastFilter[i];
+
+      //Valid entry?
+      if(entry->refCount > 0)
+      {
+         //Report messages are ignored for multicast addresses
+         //in the Non-Listener or Idle Listener state
+         if(entry->state == MLD_STATE_DELAYING_LISTENER)
+         {
+            //The Multicast Listener Report message matches the current entry?
+            if(ipv6CompAddr(&message->multicastAddr, &entry->addr))
+            {
+               //Clear flag
+               entry->flag = FALSE;
+               //Switch to the Idle Listener state
+               entry->state = MLD_STATE_IDLE_LISTENER;
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Send Multicast Listener Report message
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr IPv6 address specifying the multicast address
+ * @return Error code
+ **/
+
+error_t mldSendListenerReport(NetInterface *interface, Ipv6Addr *ipAddr)
+{
+   error_t error;
+   size_t offset;
+   MldMessage *message;
+   NetBuffer *buffer;
+   Ipv6PseudoHeader pseudoHeader;
+
+   //Make sure the specified address is a valid multicast address
+   if(!ipv6IsMulticastAddr(ipAddr))
+      return ERROR_INVALID_ADDRESS;
+
+   //The link-scope all-nodes address (FF02::1) is handled as a special
+   //case. The host never sends a report for that address
+   if(ipv6CompAddr(ipAddr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR))
+      return ERROR_INVALID_ADDRESS;
+
+   //Allocate a memory buffer to hold a MLD message
+   buffer = ipAllocBuffer(sizeof(MldMessage), &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the MLD message
+   message = netBufferAt(buffer, offset);
+
+   //Format the Multicast Listener Report message
+   message->type = ICMPV6_TYPE_MULTICAST_LISTENER_REPORT_V1;
+   message->code = 0;
+   message->checksum = 0;
+   message->maxRespDelay = 0;
+   message->reserved = 0;
+   message->multicastAddr = *ipAddr;
+
+   //Format IPv6 pseudo header
+   pseudoHeader.srcAddr = interface->ipv6Context.addrList[0].addr;
+   pseudoHeader.destAddr = *ipAddr;
+   pseudoHeader.length = HTONS(sizeof(MldMessage));
+   pseudoHeader.reserved = 0;
+   pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER;
+
+   //Message checksum calculation
+   message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader,
+      sizeof(Ipv6PseudoHeader), buffer, offset, sizeof(MldMessage));
+
+   //Debug message
+   TRACE_INFO("Sending MLD message (%" PRIuSIZE " bytes)...\r\n", sizeof(MldMessage));
+   //Dump message contents for debugging purpose
+   mldDumpMessage(message);
+
+   //The Multicast Listener Report message is sent to the multicast address being reported
+   error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, MLD_HOP_LIMIT);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send Multicast Listener Done message
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr IPv6 address specifying the multicast address being left
+ * @return Error code
+ **/
+
+error_t mldSendListenerDone(NetInterface *interface, Ipv6Addr *ipAddr)
+{
+   error_t error;
+   size_t offset;
+   MldMessage *message;
+   NetBuffer *buffer;
+   Ipv6PseudoHeader pseudoHeader;
+
+   //Make sure the specified address is a valid multicast address
+   if(!ipv6IsMulticastAddr(ipAddr))
+      return ERROR_INVALID_ADDRESS;
+
+   //The link-scope all-nodes address (FF02::1) is handled as a special
+   //case. The host never sends a report for that address
+   if(ipv6CompAddr(ipAddr, &IPV6_LINK_LOCAL_ALL_NODES_ADDR))
+      return ERROR_INVALID_ADDRESS;
+
+   //Allocate a memory buffer to hold a MLD message
+   buffer = ipAllocBuffer(sizeof(MldMessage), &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the MLD message
+   message = netBufferAt(buffer, offset);
+
+   //Format the Multicast Listener Done message
+   message->type = ICMPV6_TYPE_MULTICAST_LISTENER_DONE_V1;
+   message->code = 0;
+   message->checksum = 0;
+   message->maxRespDelay = 0;
+   message->reserved = 0;
+   message->multicastAddr = *ipAddr;
+
+   //Format IPv6 pseudo header
+   pseudoHeader.srcAddr = interface->ipv6Context.addrList[0].addr;
+   pseudoHeader.destAddr = IPV6_LINK_LOCAL_ALL_ROUTERS_ADDR;
+   pseudoHeader.length = HTONS(sizeof(MldMessage));
+   pseudoHeader.reserved = 0;
+   pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER;
+
+   //Message checksum calculation
+   message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader,
+      sizeof(Ipv6PseudoHeader), buffer, offset, sizeof(MldMessage));
+
+   //Debug message
+   TRACE_INFO("Sending MLD message (%" PRIuSIZE " bytes)...\r\n", sizeof(MldMessage));
+   //Dump message contents for debugging purpose
+   mldDumpMessage(message);
+
+   //The Multicast Listener Done message is sent to the all-routers multicast address
+   error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, MLD_HOP_LIMIT);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Get a random value in the specified range
+ * @param[in] max Upper bound
+ * @return Random value in the specified range
+ **/
+
+uint32_t mldRand(uint32_t max)
+{
+   //Return a random value in the given range
+   return netGetRand() % (max + 1);
+}
+
+
+/**
+ * @brief Dump MLD message for debugging purpose
+ * @param[in] message Pointer to the MLD message
+ **/
+
+void mldDumpMessage(const MldMessage *message)
+{
+   //Dump MLD message
+   TRACE_DEBUG("  Type = %" PRIu8 "\r\n", message->type);
+   TRACE_DEBUG("  Code = %" PRIu8 "\r\n", message->code);
+   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
+   TRACE_DEBUG("  Max Resp Delay = %" PRIu16 "\r\n", message->maxRespDelay);
+   TRACE_DEBUG("  Multicast Address = %s\r\n", ipv6AddrToString(&message->multicastAddr, NULL));
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/mld.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,124 @@
+/**
+ * @file mld.h
+ * @brief MLD (Multicast Listener Discovery for IPv6)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MLD_H
+#define _MLD_H
+
+//Dependencies
+#include "core/net.h"
+
+//MLD support
+#ifndef MLD_SUPPORT
+   #define MLD_SUPPORT DISABLED
+#elif (MLD_SUPPORT != ENABLED && MLD_SUPPORT != DISABLED)
+   #error MLD_SUPPORT parameter is not valid
+#endif
+
+//MLD tick interval
+#ifndef MLD_TICK_INTERVAL
+   #define MLD_TICK_INTERVAL 1000
+#elif (MLD_TICK_INTERVAL < 10)
+   #error MLD_TICK_INTERVAL parameter is not valid
+#endif
+
+//Unsolicited report interval
+#ifndef MLD_UNSOLICITED_REPORT_INTERVAL
+   #define MLD_UNSOLICITED_REPORT_INTERVAL 10000
+#elif (MLD_UNSOLICITED_REPORT_INTERVAL < 1000)
+   #error MLD_UNSOLICITED_REPORT_INTERVAL parameter is not valid
+#endif
+
+//Hop Limit used by MLD messages
+#define MLD_HOP_LIMIT 1
+
+
+/**
+ * @brief MLD node states
+ **/
+
+typedef enum
+{
+   MLD_STATE_NON_LISTENER      = 0,
+   MLD_STATE_DELAYING_LISTENER = 1,
+   MLD_STATE_IDLE_LISTENER     = 2
+} MldState;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief MLD message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;           //0
+   uint8_t code;           //1
+   uint16_t checksum;      //2-3
+   uint16_t maxRespDelay;  //4-5
+   uint16_t reserved;      //6-7
+   Ipv6Addr multicastAddr; //8-23
+} __end_packed MldMessage;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+//Tick counter to handle periodic operations
+extern systime_t mldTickCounter;
+
+//MLD related functions
+error_t mldInit(NetInterface *interface);
+error_t mldStartListening(NetInterface *interface, Ipv6FilterEntry *entry);
+error_t mldStopListening(NetInterface *interface, Ipv6FilterEntry *entry);
+
+void mldTick(NetInterface *interface);
+void mldLinkChangeEvent(NetInterface *interface);
+
+void mldProcessListenerQuery(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   const NetBuffer *buffer, size_t offset, uint8_t hopLimit);
+
+void mldProcessListenerReport(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   const NetBuffer *buffer, size_t offset, uint8_t hopLimit);
+
+error_t mldSendListenerReport(NetInterface *interface, Ipv6Addr *ipAddr);
+error_t mldSendListenerDone(NetInterface *interface, Ipv6Addr *ipAddr);
+
+uint32_t mldRand(uint32_t max);
+
+void mldDumpMessage(const MldMessage *message);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/ndp.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1927 @@
+/**
+ * @file ndp.c
+ * @brief NDP (Neighbor Discovery Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The Neighbor Discovery Protocol is responsible for address autoconfiguration
+ * of nodes, discovery of the link-layer addresses of other nodes, duplicate
+ * address detection, finding available routers and address prefix discovery.
+ * Refer to RFC 4861 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NDP_TRACE_LEVEL
+
+//Dependencies
+#include <limits.h>
+#include <string.h>
+#include "core/net.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_misc.h"
+#include "ipv6/icmpv6.h"
+#include "ipv6/ndp.h"
+#include "ipv6/ndp_cache.h"
+#include "ipv6/ndp_misc.h"
+#include "ipv6/slaac.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV6_SUPPORT == ENABLED && NDP_SUPPORT == ENABLED)
+
+//Tick counter to handle periodic operations
+systime_t ndpTickCounter;
+
+
+/**
+ * @brief Neighbor cache initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ndpInit(NetInterface *interface)
+{
+   NdpContext *context;
+
+   //Point to the NDP context
+   context = &interface->ndpContext;
+
+   //Clear the NDP context
+   memset(context, 0, sizeof(NdpContext));
+
+   //Initialize interface specific variables
+   context->reachableTime = NDP_REACHABLE_TIME;
+   context->retransTimer = NDP_RETRANS_TIMER;
+   context->dupAddrDetectTransmits = NDP_DUP_ADDR_DETECT_TRANSMITS;
+   context->minRtrSolicitationDelay = NDP_MIN_RTR_SOLICITATION_DELAY;
+   context->maxRtrSolicitationDelay = NDP_MAX_RTR_SOLICITATION_DELAY;
+   context->rtrSolicitationInterval = NDP_RTR_SOLICITATION_INTERVAL;
+   context->maxRtrSolicitations = NDP_MAX_RTR_SOLICITATIONS;
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Address resolution using Neighbor Discovery protocol
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr IPv6 address
+ * @param[in] macAddr Physical address matching the specified IPv6 address
+ * @return Error code
+ **/
+
+error_t ndpResolve(NetInterface *interface, const Ipv6Addr *ipAddr, MacAddr *macAddr)
+{
+   error_t error;
+   NdpNeighborCacheEntry *entry;
+
+   //Search the ndpCacheMutex cache for the specified IPv6 address
+   entry = ndpFindNeighborCacheEntry(interface, ipAddr);
+
+   //Check whether a matching entry has been found
+   if(entry != NULL)
+   {
+      //Check the state of the Neighbor cache entry
+      if(entry->state == NDP_STATE_INCOMPLETE)
+      {
+         //The address resolution is already in progress
+         error = ERROR_IN_PROGRESS;
+      }
+      else if(entry->state == NDP_STATE_STALE)
+      {
+         //Copy the MAC address associated with the specified IPv6 address
+         *macAddr = entry->macAddr;
+
+         //Start delay timer
+         entry->timestamp = osGetSystemTime();
+         //Delay before sending the first probe
+         entry->timeout = NDP_DELAY_FIRST_PROBE_TIME;
+         //Switch to the DELAY state
+         entry->state = NDP_STATE_DELAY;
+
+         //Successful address resolution
+         error = NO_ERROR;
+      }
+      else
+      {
+         //Copy the MAC address associated with the specified IPv6 address
+         *macAddr = entry->macAddr;
+
+         //Successful address resolution
+         error = NO_ERROR;
+      }
+   }
+   else
+   {
+      //If no entry exists, then create a new one
+      entry = ndpCreateNeighborCacheEntry(interface);
+
+      //Neighbor Cache entry successfully created?
+      if(entry != NULL)
+      {
+         //Record the IPv6 address whose MAC address is unknown
+         entry->ipAddr = *ipAddr;
+
+         //Reset retransmission counter
+         entry->retransmitCount = 0;
+         //No packet are pending in the transmit queue
+         entry->queueSize = 0;
+
+         //Send a multicast Neighbor Solicitation message
+         ndpSendNeighborSol(interface, ipAddr, TRUE);
+
+         //Save the time at which the message was sent
+         entry->timestamp = osGetSystemTime();
+         //Set timeout value
+         entry->timeout = interface->ndpContext.retransTimer;
+         //Enter INCOMPLETE state
+         entry->state = NDP_STATE_INCOMPLETE;
+
+         //The address resolution is in progress
+         error = ERROR_IN_PROGRESS;
+      }
+      else
+      {
+         //Failed to create Neighbor Cache entry...
+         error = ERROR_OUT_OF_RESOURCES;
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Enqueue an IPv6 packet waiting for address resolution
+ * @param[in] srcInterface Interface from which the packet has been received
+ * @param[in] destInterface Interface on which the packet should be sent
+ * @param[in] ipAddr IPv6 address of the destination host
+ * @param[in] buffer Multi-part buffer containing the packet to be enqueued
+ * @param[in] offset Offset to the first byte of the packet
+ * @return Error code
+ **/
+
+error_t ndpEnqueuePacket(NetInterface *srcInterface, NetInterface *destInterface,
+   const Ipv6Addr *ipAddr, NetBuffer *buffer, size_t offset)
+{
+   error_t error;
+   uint_t i;
+   size_t length;
+   NdpNeighborCacheEntry *entry;
+
+   //Retrieve the length of the multi-part buffer
+   length = netBufferGetLength(buffer);
+
+   //Search the Neighbor cache for the specified IPv6 address
+   entry = ndpFindNeighborCacheEntry(destInterface, ipAddr);
+
+   //Check whether a matching entry exists
+   if(entry != NULL)
+   {
+      //Check current state
+      if(entry->state == NDP_STATE_INCOMPLETE)
+      {
+         //Check whether the packet queue is full
+         if(entry->queueSize >= NDP_MAX_PENDING_PACKETS)
+         {
+            //When the queue overflows, the new arrival should replace the oldest entry
+            netBufferFree(entry->queue[0].buffer);
+
+            //Make room for the new packet
+            for(i = 1; i < NDP_MAX_PENDING_PACKETS; i++)
+               entry->queue[i - 1] = entry->queue[i];
+
+            //Adjust the number of pending packets
+            entry->queueSize--;
+         }
+
+         //Index of the entry to be filled in
+         i = entry->queueSize;
+         //Allocate a memory buffer to store the packet
+         entry->queue[i].buffer = netBufferAlloc(length);
+
+         //Successful memory allocation?
+         if(entry->queue[i].buffer != NULL)
+         {
+            //If the IPv6 packet has been forwarded, record the network
+            //interface from which the packet has been received
+            entry->queue[i].srcInterface = srcInterface;
+
+            //Copy the contents of the IPv6 packet
+            netBufferCopy(entry->queue[i].buffer, 0, buffer, 0, length);
+            //Offset to the first byte of the IPv6 header
+            entry->queue[i].offset = offset;
+
+            //Increment the number of queued packets
+            entry->queueSize++;
+            //The packet was successfully enqueued
+            error = NO_ERROR;
+         }
+         else
+         {
+            //Failed to allocate memory
+            error = ERROR_OUT_OF_MEMORY;
+         }
+      }
+      else
+      {
+         //The address is already resolved
+         error = ERROR_UNEXPECTED_STATE;
+      }
+   }
+   else
+   {
+      //No matching entry in Neighbor Cache
+      error = ERROR_NOT_FOUND;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief NDP timer handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void ndpTick(NetInterface *interface)
+{
+   systime_t time;
+   NdpContext *context;
+
+   //Point to the NDP context
+   context = &interface->ndpContext;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //When an interface becomes enabled, a host may send some Router
+   //Solicitation messages to obtain Router Advertisements quickly
+   if(interface->linkState && !interface->ipv6Context.isRouter)
+   {
+      //Make sure that a valid link-local address has been assigned to the interface
+      if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED)
+      {
+         //The host should transmit up to MAX_RTR_SOLICITATIONS Router
+         //Solicitation messages
+         if(context->rtrSolicitationCount == 0)
+         {
+            //Set time stamp
+            context->timestamp = time;
+
+            //Check whether the host has already performed Duplicate Address
+            //Detection for the link-local address
+            if(context->dupAddrDetectTransmits > 0)
+            {
+               //If a host has already performed a random delay since the interface
+               //became enabled, there is no need to delay again before sending the
+               //first Router Solicitation message
+               context->timeout = 0;
+            }
+            else
+            {
+               //Before a host sends an initial solicitation, it should delay the
+               //transmission for a random amount of time in order to alleviate
+               //congestion when many hosts start up on a link at the same time
+               context->timeout = netGetRandRange(context->minRtrSolicitationDelay,
+                  context->maxRtrSolicitationDelay);
+            }
+
+            //Prepare to send the first Router Solicitation message
+            context->rtrSolicitationCount = 1;
+         }
+         else if(context->rtrSolicitationCount <= context->maxRtrSolicitations)
+         {
+            //Once the host sends a Router Solicitation, and receives a valid
+            //Router Advertisement with a non-zero Router Lifetime, the host must
+            //desist from sending additional solicitations on that interface
+            if(!context->rtrAdvReceived)
+            {
+               //Check current time
+               if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+               {
+                  //Send Router Solicitation message
+                  ndpSendRouterSol(interface);
+
+                  //Save the time at which the message was sent
+                  context->timestamp = time;
+                  //Set timeout value
+                  context->timeout = context->rtrSolicitationInterval;
+                  //Increment retransmission counter
+                  context->rtrSolicitationCount++;
+               }
+            }
+         }
+      }
+   }
+
+   //Periodically update the Neighbor Cache
+   ndpUpdateNeighborCache(interface);
+
+   //Manage the lifetime of IPv6 addresses
+   ndpUpdateAddrList(interface);
+
+   //Periodically update the Prefix List
+   ndpUpdatePrefixList(interface);
+
+   //Periodically update the Default Router List
+   ndpUpdateDefaultRouterList(interface);
+}
+
+
+/**
+ * @brief Callback function for link change event
+ * @param[in] interface Underlying network interface
+ **/
+
+void ndpLinkChangeEvent(NetInterface *interface)
+{
+   NdpContext *context;
+
+   //Point to the NDP context
+   context = &interface->ndpContext;
+
+   //Restore default parameters
+   context->reachableTime = NDP_REACHABLE_TIME;
+   context->retransTimer = NDP_RETRANS_TIMER;
+   context->dupAddrDetectTransmits = NDP_DUP_ADDR_DETECT_TRANSMITS;
+   context->minRtrSolicitationDelay = NDP_MIN_RTR_SOLICITATION_DELAY;
+   context->maxRtrSolicitationDelay = NDP_MAX_RTR_SOLICITATION_DELAY;
+   context->rtrSolicitationInterval = NDP_RTR_SOLICITATION_INTERVAL;
+   context->maxRtrSolicitations = NDP_MAX_RTR_SOLICITATIONS;
+
+   //Reset retransmission counter for RS messages
+   context->rtrSolicitationCount = 0;
+   //Valid RA message not yet received
+   context->rtrAdvReceived = FALSE;
+
+   //Flush the Neighbor Cache
+   ndpFlushNeighborCache(interface);
+   //Flush the Destination Cache
+   ndpFlushDestCache(interface);
+}
+
+
+/**
+ * @brief Router Advertisement message processing
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IPv6 pseudo header
+ * @param[in] buffer Multi-part buffer containing the Router Advertisement message
+ * @param[in] offset Offset to the first byte of the message
+ * @param[in] hopLimit Hop Limit field from IPv6 header
+ **/
+
+void ndpProcessRouterAdv(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   const NetBuffer *buffer, size_t offset, uint8_t hopLimit)
+{
+   error_t error;
+   uint32_t n;
+   size_t length;
+   NdpRouterAdvMessage *message;
+   NdpMtuOption *mtuOption;
+   NdpPrefixInfoOption *prefixInfoOption;
+#if (ETH_SUPPORT == ENABLED)
+   NdpLinkLayerAddrOption *linkLayerAddrOption;
+   NdpNeighborCacheEntry *entry;
+#endif
+
+   //Retrieve the length of the message
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the length of the Router Advertisement message
+   if(length < sizeof(NdpRouterAdvMessage))
+      return;
+
+   //Point to the beginning of the message
+   message = netBufferAt(buffer, offset);
+   //Sanity check
+   if(message == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("Router Advertisement message received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message contents for debugging purpose
+   ndpDumpRouterAdvMessage(message);
+
+   //Routers must use their link-local address as the source for the
+   //Router Advertisement so that hosts can uniquely identify routers
+   if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr))
+      return;
+
+   //The IPv6 Hop Limit field must have a value of 255 to ensure
+   //that the packet has not been forwarded by a router
+   if(hopLimit != NDP_HOP_LIMIT)
+      return;
+
+   //ICMPv6 Code must be 0. An advertisement that passes the validity
+   //checks is called a valid advertisement
+   if(message->code)
+      return;
+
+   //Calculate the length of the Options field
+   length -= sizeof(NdpRouterAdvMessage);
+
+   //Parse Options field
+   error = ndpCheckOptions(message->options, length);
+   //All included options must have a length that is greater than zero
+   if(error)
+      return;
+
+   //Check the Router Lifetime value
+   if(ntohs(message->routerLifetime) != 0)
+   {
+      //Add a new entry in the Default Router List
+      ipv6AddDefaultRouter(interface, &pseudoHeader->srcAddr,
+         ntohs(message->routerLifetime));
+
+      //The host should send at least one solicitation in the case where
+      //an advertisement is received prior to having sent a solicitation
+      if(interface->ndpContext.rtrSolicitationCount > 1)
+      {
+         //Once the host sends a Router Solicitation, and receives a valid
+         //Router Advertisement with a non-zero Router Lifetime, the host must
+         //desist from sending additional solicitations on that interface
+         interface->ndpContext.rtrAdvReceived = TRUE;
+      }
+   }
+   else
+   {
+      //Immediately time-out the entry
+      ipv6RemoveDefaultRouter(interface, &pseudoHeader->srcAddr);
+   }
+
+   //6LoWPAN interface?
+   if(interface->nicDriver->type == NIC_TYPE_6LOWPAN)
+   {
+      //In all cases, the Router Solicitation retransmissions are terminated
+      //when a Router Advertisement is received (refer to RFC 6675 5.3)
+      interface->ndpContext.rtrAdvReceived = TRUE;
+   }
+
+   //A Router Advertisement field (Cur Hop Limit, Reachable Time, and
+   //Retrans Timer) may contain a value denoting that it is unspecified.
+   //In such cases, the parameter should be ignored and the host should
+   //continue using whatever value it is already using
+   if(message->curHopLimit != 0)
+   {
+      //Get the default value that should be placed in the Hop Count
+      //field of the IP header for outgoing IP packets
+      interface->ipv6Context.curHopLimit = message->curHopLimit;
+   }
+
+   //A value of zero means unspecified...
+   if(message->reachableTime != 0)
+   {
+      //The Reachable Time field holds the time, in milliseconds, that
+      //a node assumes a neighbor is reachable after having received a
+      //reachability confirmation
+      interface->ndpContext.reachableTime = ntohl(message->reachableTime);
+   }
+
+   //A value of zero means unspecified...
+   if(message->retransTimer != 0)
+   {
+      //The Retrans Timer field holds the time, in milliseconds,
+      //between retransmitted Neighbor Solicitation messages
+      interface->ndpContext.retransTimer = ntohl(message->retransTimer);
+   }
+
+#if (ETH_SUPPORT == ENABLED)
+   //Search for the Source Link-Layer Address option
+   linkLayerAddrOption = ndpGetOption(message->options,
+      length, NDP_OPT_SOURCE_LINK_LAYER_ADDR);
+
+   //Source Link-Layer Address option found?
+   if(linkLayerAddrOption != NULL && linkLayerAddrOption->length == 1)
+   {
+      //Debug message
+      TRACE_DEBUG("  Source Link-Layer Address = %s\r\n",
+         macAddrToString(&linkLayerAddrOption->linkLayerAddr, NULL));
+   }
+   else
+   {
+      //No valid Source Link-Layer Address option...
+      linkLayerAddrOption = NULL;
+   }
+
+   //Search the Neighbor cache for the router
+   entry = ndpFindNeighborCacheEntry(interface, &pseudoHeader->srcAddr);
+
+   //No matching entry has been found?
+   if(!entry)
+   {
+      //If the advertisement contains a Source Link-Layer Address option,
+      //the link-layer address should be recorded in the Neighbor cache
+      if(linkLayerAddrOption)
+      {
+         //Create an entry for the router
+         entry = ndpCreateNeighborCacheEntry(interface);
+
+         //Neighbor cache entry successfully created?
+         if(entry)
+         {
+            //Record the IPv6 address and the corresponding MAC address
+            entry->ipAddr = pseudoHeader->srcAddr;
+            entry->macAddr = linkLayerAddrOption->linkLayerAddr;
+            //The IsRouter flag must be set to TRUE
+            entry->isRouter = TRUE;
+            //Save current time
+            entry->timestamp = osGetSystemTime();
+            //The reachability state must be set to STALE
+            entry->state = NDP_STATE_STALE;
+         }
+      }
+   }
+   else
+   {
+      //The sender of a Router Advertisement is implicitly assumed to be a router
+      entry->isRouter = TRUE;
+
+      //Check if the advertisement contains a Source Link-Layer Address option
+      if(linkLayerAddrOption)
+      {
+         //INCOMPLETE state?
+         if(entry->state == NDP_STATE_INCOMPLETE)
+         {
+            //Record link-layer address
+            entry->macAddr = linkLayerAddrOption->linkLayerAddr;
+            //Send all the packets that are pending for transmission
+            n = ndpSendQueuedPackets(interface, entry);
+            //Save current time
+            entry->timestamp = osGetSystemTime();
+
+            //Check whether any packets have been sent
+            if(n > 0)
+            {
+               //Start delay timer
+               entry->timeout = NDP_DELAY_FIRST_PROBE_TIME;
+               //Switch to the DELAY state
+               entry->state = NDP_STATE_DELAY;
+            }
+            else
+            {
+               //Enter the STALE state
+               entry->state = NDP_STATE_STALE;
+            }
+         }
+         //REACHABLE, STALE, DELAY or PROBE state?
+         else
+         {
+            //Different link-layer address than cached?
+            if(!macCompAddr(&entry->macAddr, &linkLayerAddrOption->linkLayerAddr))
+            {
+               //Update link-layer address
+               entry->macAddr = linkLayerAddrOption->linkLayerAddr;
+               //Save current time
+               entry->timestamp = osGetSystemTime();
+               //The reachability state must be set to STALE
+               entry->state = NDP_STATE_STALE;
+            }
+         }
+      }
+   }
+#endif
+
+   //Search for the MTU option
+   mtuOption = ndpGetOption(message->options, length, NDP_OPT_MTU);
+
+   //MTU option found?
+   if(mtuOption != NULL && mtuOption->length == 1)
+   {
+      //This option specifies the recommended MTU for the link
+      n = ntohl(mtuOption->mtu);
+
+      //The host should copy the option's value so long as the value is greater
+      //than or equal to the minimum IPv6 MTU and does not exceed the maximum
+      //MTU of the interface
+      if(n >= IPV6_DEFAULT_MTU && n <= interface->nicDriver->mtu)
+      {
+         //Save the MTU value
+         interface->ipv6Context.linkMtu = n;
+      }
+   }
+
+   //Point to the beginning of the Options field
+   n = 0;
+
+   //Parse Options field
+   while(1)
+   {
+      //Search the Options field for any Prefix Information options
+      prefixInfoOption = ndpGetOption(message->options + n,
+         length - n, NDP_OPT_PREFIX_INFORMATION);
+
+      //No more option of the specified type?
+      if(prefixInfoOption == NULL)
+         break;
+
+      //Hosts use the advertised on-link prefixes to build and maintain
+      //a list that is used in deciding when a packet's destination is
+      //on-link or beyond a router
+      ndpParsePrefixInfoOption(interface, prefixInfoOption);
+
+      //Retrieve the offset to the current position
+      n = (uint8_t *) prefixInfoOption - message->options;
+      //Jump to the next option
+      n += prefixInfoOption->length * 8;
+   }
+
+#if (SLAAC_SUPPORT == ENABLED)
+   //Stateless Address Autoconfiguration is currently used?
+   if(interface->slaacContext != NULL)
+   {
+      //Process the valid advertisement
+      slaacParseRouterAdv(interface->slaacContext, message,
+         length + sizeof(NdpRouterAdvMessage));
+   }
+#endif
+}
+
+
+/**
+ * @brief Neighbor Solicitation message processing
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IPv6 pseudo header
+ * @param[in] buffer Multi-part buffer containing the Neighbor Solicitation message
+ * @param[in] offset Offset to the first byte of the message
+ * @param[in] hopLimit Hop Limit field from IPv6 header
+ **/
+
+void ndpProcessNeighborSol(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   const NetBuffer *buffer, size_t offset, uint8_t hopLimit)
+{
+#if (ETH_SUPPORT == ENABLED)
+   error_t error;
+   uint_t i;
+   uint_t n;
+   size_t length;
+   bool_t validTarget;
+   NdpNeighborSolMessage *message;
+   NdpLinkLayerAddrOption *option;
+   NdpNeighborCacheEntry *neighborCacheEntry;
+   Ipv6AddrEntry *addrEntry;
+
+   //Retrieve the length of the message
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the length of the Neighbor Solicitation message
+   if(length < sizeof(NdpNeighborSolMessage))
+      return;
+
+   //Point to the beginning of the message
+   message = netBufferAt(buffer, offset);
+   //Sanity check
+   if(message == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("Neighbor Solicitation message received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message contents for debugging purpose
+   ndpDumpNeighborSolMessage(message);
+
+   //The IPv6 Hop Limit field must have a value of 255 to ensure
+   //that the packet has not been forwarded by a router
+   if(hopLimit != NDP_HOP_LIMIT)
+      return;
+
+   //ICMPv6 Code must be 0
+   if(message->code)
+      return;
+
+   //If the IP source address is the unspecified address, the IP
+   //destination address must be a solicited-node multicast address
+   if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR) &&
+      !ipv6IsSolicitedNodeAddr(&pseudoHeader->destAddr))
+   {
+      //Debug message
+      TRACE_WARNING("Destination address must be a solicited-node address!\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Calculate the length of the Options field
+   length -= sizeof(NdpNeighborSolMessage);
+
+   //Parse Options field
+   error = ndpCheckOptions(message->options, length);
+   //All included options must have a length that is greater than zero
+   if(error)
+      return;
+
+   //Search for the Source Link-Layer Address option
+   option = ndpGetOption(message->options,
+      length, NDP_OPT_SOURCE_LINK_LAYER_ADDR);
+
+   //The target address must a valid unicast or anycast address assigned to
+   //the interface or a tentative address on which DAD is being performed
+   validTarget = FALSE;
+
+   //Loop through the IPv6 addresses assigned to the interface
+   for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      addrEntry = &interface->ipv6Context.addrList[i];
+
+      //Compare target address
+      if(ipv6CompAddr(&addrEntry->addr, &message->targetAddr))
+      {
+         //Check address state
+         if(addrEntry->state == IPV6_ADDR_STATE_TENTATIVE)
+         {
+            //If the source address of the Neighbor Solicitation is the
+            //unspecified address, the solicitation is from a node
+            //performing Duplicate Address Detection
+            if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR))
+            {
+               //The source link-layer address must not be included when the
+               //source IP address is the unspecified address...
+               if(option == NULL)
+               {
+                  //Debug message
+                  TRACE_WARNING("The tentative address %s is a duplicate!\r\n",
+                     ipv6AddrToString(&addrEntry->addr, NULL));
+
+                  //The tentative address is a duplicate and should not be used
+                  addrEntry->duplicate = TRUE;
+               }
+            }
+
+            //In all cases, a node must not respond to a Neighbor Solicitation
+            //for a tentative address
+            return;
+         }
+         else if(addrEntry->state != IPV6_ADDR_STATE_INVALID)
+         {
+            //The target address is a valid address assigned to the interface
+            validTarget = TRUE;
+            //We are done
+            break;
+         }
+      }
+   }
+
+   //Invalid target address?
+   if(!validTarget)
+   {
+      //The Neighbor Solicitation must be discarded if the target address
+      //is not a valid anycast address assigned to the interface
+      if(!ipv6IsAnycastAddr(interface, &message->targetAddr))
+      {
+         //Debug message
+         TRACE_WARNING("Wrong target address!\r\n");
+         //Exit immediately
+         return;
+      }
+   }
+
+   //Source Link-Layer Address option found?
+   if(option != NULL && option->length == 1)
+   {
+      //Debug message
+      TRACE_DEBUG("  Source Link-Layer Address = %s\r\n",
+         macAddrToString(&option->linkLayerAddr, NULL));
+
+      //The Source Link-Layer Address option must not be included when the
+      //source IP address is the unspecified address
+      if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR))
+         return;
+
+      //Search the Neighbor Cache for the source address of the solicitation
+      neighborCacheEntry = ndpFindNeighborCacheEntry(interface, &pseudoHeader->srcAddr);
+
+      //No matching entry has been found?
+      if(!neighborCacheEntry)
+      {
+         //Create an entry
+         neighborCacheEntry = ndpCreateNeighborCacheEntry(interface);
+
+         //Neighbor Cache entry successfully created?
+         if(neighborCacheEntry)
+         {
+            //Record the IPv6 and the corresponding MAC address
+            neighborCacheEntry->ipAddr = pseudoHeader->srcAddr;
+            neighborCacheEntry->macAddr = option->linkLayerAddr;
+            //Save current time
+            neighborCacheEntry->timestamp = osGetSystemTime();
+            //Enter the STALE state
+            neighborCacheEntry->state = NDP_STATE_STALE;
+         }
+      }
+      else
+      {
+         //INCOMPLETE state?
+         if(neighborCacheEntry->state == NDP_STATE_INCOMPLETE)
+         {
+            //Record link-layer address
+            neighborCacheEntry->macAddr = option->linkLayerAddr;
+            //Send all the packets that are pending for transmission
+            n = ndpSendQueuedPackets(interface, neighborCacheEntry);
+            //Save current time
+            neighborCacheEntry->timestamp = osGetSystemTime();
+
+            //Check whether any packets have been sent
+            if(n > 0)
+            {
+               //Start delay timer
+               neighborCacheEntry->timeout = NDP_DELAY_FIRST_PROBE_TIME;
+               //Switch to the DELAY state
+               neighborCacheEntry->state = NDP_STATE_DELAY;
+            }
+            else
+            {
+               //Enter the STALE state
+               neighborCacheEntry->state = NDP_STATE_STALE;
+            }
+         }
+         //REACHABLE, STALE, DELAY or PROBE state?
+         else
+         {
+            //Different link-layer address than cached?
+            if(!macCompAddr(&neighborCacheEntry->macAddr, &option->linkLayerAddr))
+            {
+               //Update link-layer address
+               neighborCacheEntry->macAddr = option->linkLayerAddr;
+               //Save current time
+               neighborCacheEntry->timestamp = osGetSystemTime();
+               //Enter the STALE state
+               neighborCacheEntry->state = NDP_STATE_STALE;
+            }
+         }
+      }
+   }
+   //Source Link-Layer Address option not found?
+   else
+   {
+      //The Source Link-Layer Address option must not be included when the
+      //source IP address is the unspecified address. Otherwise, this option
+      //must be included in multicast solicitations
+      if(!ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR) &&
+         ipv6IsMulticastAddr(&pseudoHeader->destAddr))
+      {
+         //Debug message
+         TRACE_WARNING("The Source Link-Layer Address must be included!\r\n");
+         //Exit immediately
+         return;
+      }
+   }
+
+   //After any updates to the Neighbor cache, the node sends a Neighbor
+   //Advertisement response as described in RFC 4861 7.2.4
+   ndpSendNeighborAdv(interface, &message->targetAddr, &pseudoHeader->srcAddr);
+#endif
+}
+
+
+/**
+ * @brief Neighbor Advertisement message processing
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IPv6 pseudo header
+ * @param[in] buffer Multi-part buffer containing the Neighbor Advertisement message
+ * @param[in] offset Offset to the first byte of the message
+ * @param[in] hopLimit Hop Limit field from IPv6 header
+ **/
+
+void ndpProcessNeighborAdv(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   const NetBuffer *buffer, size_t offset, uint8_t hopLimit)
+{
+#if (ETH_SUPPORT == ENABLED)
+   error_t error;
+   uint_t i;
+   uint_t n;
+   size_t length;
+   bool_t differentLinkLayerAddr;
+   NdpNeighborAdvMessage *message;
+   NdpLinkLayerAddrOption *option;
+   NdpNeighborCacheEntry *neighborCacheEntry;
+   Ipv6AddrEntry *addrEntry;
+
+   //Retrieve the length of the message
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the length of the Neighbor Advertisement message
+   if(length < sizeof(NdpNeighborAdvMessage))
+      return;
+
+   //Point to the beginning of the message
+   message = netBufferAt(buffer, offset);
+   //Sanity check
+   if(message == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("Neighbor Advertisement message received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message contents for debugging purpose
+   ndpDumpNeighborAdvMessage(message);
+
+   //The IPv6 Hop Limit field must have a value of 255 to ensure
+   //that the packet has not been forwarded by a router
+   if(hopLimit != NDP_HOP_LIMIT)
+      return;
+
+   //ICMPv6 Code must be 0
+   if(message->code)
+      return;
+
+   //The target address must not be a multicast address
+   if(ipv6IsMulticastAddr(&message->targetAddr))
+   {
+      //Debug message
+      TRACE_WARNING("Target address must not be a multicast address!\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //If the destination address is a multicast address
+   //then the Solicited flag must be zero
+   if(ipv6IsMulticastAddr(&pseudoHeader->destAddr) && message->s)
+   {
+      //Debug message
+      TRACE_WARNING("Solicited flag must be zero!\r\n");
+      //Exit immediately
+      return;
+   }
+
+   //Calculate the length of the Options field
+   length -= sizeof(NdpNeighborAdvMessage);
+
+   //Parse Options field
+   error = ndpCheckOptions(message->options, length);
+   //All included options must have a length that is greater than zero
+   if(error)
+      return;
+
+   //Duplicate address detection
+   for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      addrEntry = &interface->ipv6Context.addrList[i];
+
+      //Valid entry?
+      if(addrEntry->state != IPV6_ADDR_STATE_INVALID)
+      {
+         //Check whether the target address is tentative or matches
+         //a unicast address assigned to the interface
+         if(ipv6CompAddr(&addrEntry->addr, &message->targetAddr))
+         {
+            //Debug message
+            TRACE_WARNING("The address %s is a duplicate!\r\n",
+               ipv6AddrToString(&addrEntry->addr, NULL));
+
+            //The address is a duplicate and should not be used
+            addrEntry->duplicate = TRUE;
+            //Exit immediately
+            return;
+         }
+      }
+   }
+
+   //Search the Neighbor cache for the specified target address
+   neighborCacheEntry = ndpFindNeighborCacheEntry(interface, &message->targetAddr);
+
+   //If no entry exists, the advertisement should be silently discarded
+   if(neighborCacheEntry)
+   {
+      //This flag tells whether the supplied link-layer
+      //address differs from that in the cache
+      differentLinkLayerAddr = FALSE;
+
+      //Search for the Target Link-Layer Address option
+      option = ndpGetOption(message->options,
+         length, NDP_OPT_TARGET_LINK_LAYER_ADDR);
+
+      //Target Link-Layer Address option found?
+      if(option != NULL && option->length == 1)
+      {
+         //Debug message
+         TRACE_DEBUG("  Target Link-Layer Address = %s\r\n",
+            macAddrToString(&option->linkLayerAddr, NULL));
+
+         //Different link-layer address than cached?
+         if(!macCompAddr(&neighborCacheEntry->macAddr, &option->linkLayerAddr))
+            differentLinkLayerAddr = TRUE;
+      }
+
+      //INCOMPLETE state?
+      if(neighborCacheEntry->state == NDP_STATE_INCOMPLETE)
+      {
+         //If no Target Link-Layer Address option is included, the receiving
+         //node should silently discard the received advertisement
+         if(option != NULL && option->length == 1)
+         {
+            //Record the link-layer address
+            neighborCacheEntry->macAddr = option->linkLayerAddr;
+            //Send all the packets that are pending for transmission
+            n = ndpSendQueuedPackets(interface, neighborCacheEntry);
+            //Save current time
+            neighborCacheEntry->timestamp = osGetSystemTime();
+
+            //Solicited flag is set?
+            if(message->s)
+            {
+               //Computing the random ReachableTime value
+               neighborCacheEntry->timeout = interface->ndpContext.reachableTime;
+               //Switch to the REACHABLE state
+               neighborCacheEntry->state = NDP_STATE_REACHABLE;
+            }
+            else
+            {
+               //Check whether any packets have been sent
+               if(n > 0)
+               {
+                  //Start delay timer
+                  neighborCacheEntry->timeout = NDP_DELAY_FIRST_PROBE_TIME;
+                  //Switch to the DELAY state
+                  neighborCacheEntry->state = NDP_STATE_DELAY;
+               }
+               else
+               {
+                  //Enter the STALE state
+                  neighborCacheEntry->state = NDP_STATE_STALE;
+               }
+            }
+         }
+      }
+      //REACHABLE, STALE, DELAY or PROBE state?
+      else
+      {
+         //Check whether the Override flag is clear and the supplied
+         //link-layer address differs from that in the cache
+         if(!message->o && differentLinkLayerAddr)
+         {
+            //REACHABLE state?
+            if(neighborCacheEntry->state == NDP_STATE_REACHABLE)
+            {
+               //Save current time
+               neighborCacheEntry->timestamp = osGetSystemTime();
+               //Enter the STALE state
+               neighborCacheEntry->state = NDP_STATE_STALE;
+            }
+         }
+         else
+         {
+            //Solicited flag is set?
+            if(message->s)
+            {
+               //Different link-layer address than cached?
+               if(differentLinkLayerAddr)
+               {
+                  //The link-layer address must be inserted in the cache
+                  neighborCacheEntry->macAddr = option->linkLayerAddr;
+               }
+
+               //Save current time
+               neighborCacheEntry->timestamp = osGetSystemTime();
+               //Computing the random ReachableTime value
+               neighborCacheEntry->timeout = interface->ndpContext.reachableTime;
+               //Switch to the REACHABLE state
+               neighborCacheEntry->state = NDP_STATE_REACHABLE;
+            }
+            else
+            {
+               //Different link-layer address than cached?
+               if(differentLinkLayerAddr)
+               {
+                  //The link-layer address must be inserted in the cache
+                  neighborCacheEntry->macAddr = option->linkLayerAddr;
+                  //Save current time
+                  neighborCacheEntry->timestamp = osGetSystemTime();
+                  //The state must be set to STALE
+                  neighborCacheEntry->state = NDP_STATE_STALE;
+               }
+            }
+         }
+      }
+
+      //The IsRouter flag in the cache entry must be set based
+      //on the Router flag in the received advertisement
+      if(message->r)
+      {
+         //The neighbor is a router
+         neighborCacheEntry->isRouter = TRUE;
+      }
+      else
+      {
+         //Check whether the IsRouter flag changes from TRUE to FALSE
+         //as a result of this update
+         if(neighborCacheEntry->isRouter)
+         {
+            //The node must remove that router from the Default Router list
+            //and update the Destination cache entries for all destinations
+            //using that neighbor as a router
+            ipv6RemoveDefaultRouter(interface, &neighborCacheEntry->ipAddr);
+         }
+
+         //The neighbor is a host
+         neighborCacheEntry->isRouter = FALSE;
+      }
+   }
+#endif
+}
+
+
+/**
+ * @brief Redirect message processing
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IPv6 pseudo header
+ * @param[in] buffer Multi-part buffer containing the Redirect message
+ * @param[in] offset Offset to the first byte of the message
+ * @param[in] hopLimit Hop Limit field from IPv6 header
+ **/
+
+void ndpProcessRedirect(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   const NetBuffer *buffer, size_t offset, uint8_t hopLimit)
+{
+#if (ETH_SUPPORT == ENABLED)
+   error_t error;
+   uint_t n;
+   size_t length;
+   NdpRedirectMessage *message;
+   NdpLinkLayerAddrOption *option;
+   NdpNeighborCacheEntry *neighborCacheEntry;
+   NdpDestCacheEntry *destCacheEntry;
+
+   //Retrieve the length of the message
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the length of the Redirect message
+   if(length < sizeof(NdpRedirectMessage))
+      return;
+
+   //Point to the beginning of the message
+   message = netBufferAt(buffer, offset);
+   //Sanity check
+   if(message == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("Redirect message received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message contents for debugging purpose
+   ndpDumpRedirectMessage(message);
+
+   //The IPv6 Hop Limit field must have a value of 255 to ensure
+   //that the packet has not been forwarded by a router
+   if(hopLimit != NDP_HOP_LIMIT)
+      return;
+
+   //ICMPv6 Code must be 0
+   if(message->code)
+      return;
+
+   //Routers must use their link-local address as the source for Redirect
+   //messages so that hosts can uniquely identify routers
+   if(!ipv6IsLinkLocalUnicastAddr(&pseudoHeader->srcAddr))
+      return;
+
+   //The IP source address of the Redirect must be the same as the current
+   //first-hop router for the specified Destination address
+   if(!ndpIsFirstHopRouter(interface, &message->destAddr, &pseudoHeader->srcAddr))
+      return;
+
+   //The Destination Address field in the Redirect message must not
+   //contain a multicast address
+   if(ipv6IsMulticastAddr(&message->destAddr))
+      return;
+
+   //The Target Address must be either a link-local address (when redirected
+   //to a router) or the same as the Destination Address (when redirected to
+   //the on-link destination)
+   if(!ipv6IsLinkLocalUnicastAddr(&message->targetAddr) &&
+      !ipv6CompAddr(&message->targetAddr, &message->destAddr))
+   {
+      //Silently discard the received Redirect message
+      return;
+   }
+
+   //Calculate the length of the Options field
+   length -= sizeof(NdpNeighborAdvMessage);
+
+   //Parse Options field
+   error = ndpCheckOptions(message->options, length);
+   //All included options must have a length that is greater than zero
+   if(error)
+      return;
+
+   //Search the Destination cache for the specified address
+   destCacheEntry = ndpFindDestCacheEntry(interface, &message->destAddr);
+
+   //Check whether a corresponding Destination cache entry exists
+   if(destCacheEntry)
+   {
+      //The entry is updated with information learned from Redirect messages
+      destCacheEntry->nextHop = message->targetAddr;
+      //Save current time
+      destCacheEntry->timestamp = osGetSystemTime();
+   }
+   else
+   {
+      //If no Destination Cache entry exists for the destination, an
+      //implementation should create such an entry
+      destCacheEntry = ndpCreateDestCacheEntry(interface);
+
+      //Destination cache entry successfully created?
+      if(destCacheEntry)
+      {
+         //Destination address
+         destCacheEntry->destAddr = message->destAddr;
+         //Address of the next hop
+         destCacheEntry->nextHop = message->targetAddr;
+
+         //Initially, the PMTU value for a path is assumed to be
+         //the MTU of the first-hop link
+         destCacheEntry->pathMtu = interface->ipv6Context.linkMtu;
+
+         //Save current time
+         destCacheEntry->timestamp = osGetSystemTime();
+      }
+   }
+
+   //Search for the Target Link-Layer Address option
+   option = ndpGetOption(message->options,
+      length, NDP_OPT_TARGET_LINK_LAYER_ADDR);
+
+   //If the Redirect contains a Target Link-Layer Address option, the host
+   //either creates or updates the Neighbor Cache entry for the target
+   if(option != NULL && option->length == 1)
+   {
+      //Debug message
+      TRACE_DEBUG("  Target Link-Layer Address = %s\r\n",
+         macAddrToString(&option->linkLayerAddr, NULL));
+
+      //Search the Neighbor cache for the specified target address
+      neighborCacheEntry = ndpFindNeighborCacheEntry(interface, &message->targetAddr);
+
+      //No matching entry has been found?
+      if(!neighborCacheEntry)
+      {
+         //Create an entry for the target
+         neighborCacheEntry = ndpCreateNeighborCacheEntry(interface);
+
+         //Neighbor cache entry successfully created?
+         if(neighborCacheEntry)
+         {
+            //Record the Target address
+            neighborCacheEntry->ipAddr = message->targetAddr;
+            //The cached link-layer address is copied from the option
+            neighborCacheEntry->macAddr = option->linkLayerAddr;
+            //Newly created Neighbor Cache entries should set the IsRouter flag to FALSE
+            neighborCacheEntry->isRouter = FALSE;
+            //Save current time
+            neighborCacheEntry->timestamp = osGetSystemTime();
+            //The reachability state must be set to STALE
+            neighborCacheEntry->state = NDP_STATE_STALE;
+         }
+      }
+      else
+      {
+         //If the Target Address is not the same as the Destination Address,
+         //the host must set IsRouter to TRUE for the target
+         if(!ipv6CompAddr(&message->targetAddr, &message->destAddr))
+            neighborCacheEntry->isRouter = TRUE;
+
+         //INCOMPLETE state?
+         if(neighborCacheEntry->state == NDP_STATE_INCOMPLETE)
+         {
+            //Record link-layer address
+            neighborCacheEntry->macAddr = option->linkLayerAddr;
+            //Send all the packets that are pending for transmission
+            n = ndpSendQueuedPackets(interface, neighborCacheEntry);
+            //Save current time
+            neighborCacheEntry->timestamp = osGetSystemTime();
+
+            //Check whether any packets have been sent
+            if(n > 0)
+            {
+               //Start delay timer
+               neighborCacheEntry->timeout = NDP_DELAY_FIRST_PROBE_TIME;
+               //Switch to the DELAY state
+               neighborCacheEntry->state = NDP_STATE_DELAY;
+            }
+            else
+            {
+               //Enter the STALE state
+               neighborCacheEntry->state = NDP_STATE_STALE;
+            }
+         }
+         //REACHABLE, STALE, DELAY or PROBE state?
+         else
+         {
+            //Different link-layer address than cached?
+            if(!macCompAddr(&neighborCacheEntry->macAddr, &option->linkLayerAddr))
+            {
+               //Update link-layer address
+               neighborCacheEntry->macAddr = option->linkLayerAddr;
+               //Save current time
+               neighborCacheEntry->timestamp = osGetSystemTime();
+               //The reachability state must be set to STALE
+               neighborCacheEntry->state = NDP_STATE_STALE;
+            }
+         }
+      }
+   }
+#endif
+}
+
+
+/**
+ * @brief Send a Router Solicitation message
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t ndpSendRouterSol(NetInterface *interface)
+{
+   error_t error;
+   size_t offset;
+   size_t length;
+   NetBuffer *buffer;
+   NdpRouterSolMessage *message;
+   Ipv6PseudoHeader pseudoHeader;
+
+   //The destination address is typically the all-routers multicast address
+   pseudoHeader.destAddr = IPV6_LINK_LOCAL_ALL_ROUTERS_ADDR;
+
+   //Select the most appropriate source address to be used when
+   //sending the Router Solicitation message
+   error = ipv6SelectSourceAddr(&interface,
+      &pseudoHeader.destAddr, &pseudoHeader.srcAddr);
+
+   //No address assigned to the interface?
+   if(error)
+   {
+      //Use the unspecified address if no address is assigned
+      //to the sending interface
+      pseudoHeader.srcAddr = IPV6_UNSPECIFIED_ADDR;
+   }
+
+   //The only defined option that may appear in a Router Solicitation
+   //message is the Source Link-Layer Address option
+   length = sizeof(NdpRouterSolMessage) + sizeof(NdpLinkLayerAddrOption);
+
+   //Allocate a memory buffer to hold the Router Solicitation message
+   buffer = ipAllocBuffer(length, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the message
+   message = netBufferAt(buffer, offset);
+
+   //Format Router Solicitation message
+   message->type = ICMPV6_TYPE_ROUTER_SOL;
+   message->code = 0;
+   message->checksum = 0;
+   message->reserved = 0;
+
+   //Length of the message, excluding any option
+   length = sizeof(NdpRouterSolMessage);
+
+   //The Source Link-Layer Address option must not be included
+   //when the source IPv6 address is the unspecified address
+   if(!ipv6CompAddr(&pseudoHeader.srcAddr, &IPV6_UNSPECIFIED_ADDR))
+   {
+#if (ETH_SUPPORT == ENABLED)
+      //Check whether a MAC address has been assigned to the interface
+      if(!macCompAddr(&interface->macAddr, &MAC_UNSPECIFIED_ADDR))
+      {
+         //Add Source Link-Layer Address option
+         ndpAddOption(message, &length, NDP_OPT_SOURCE_LINK_LAYER_ADDR,
+            &interface->macAddr, sizeof(MacAddr));
+      }
+#endif
+   }
+
+   //Adjust the length of the multi-part buffer
+   netBufferSetLength(buffer, offset + length);
+
+   //Format IPv6 pseudo header
+   pseudoHeader.length = htonl(length);
+   pseudoHeader.reserved = 0;
+   pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER;
+
+   //Calculate ICMPv6 header checksum
+   message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader,
+      sizeof(Ipv6PseudoHeader), buffer, offset, length);
+
+   //Debug message
+   TRACE_INFO("Sending Router Solicitation message (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message contents for debugging purpose
+   ndpDumpRouterSolMessage(message);
+
+   //Send Router Solicitation message
+   error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, NDP_HOP_LIMIT);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send a Neighbor Solicitation message
+ * @param[in] interface Underlying network interface
+ * @param[in] targetIpAddr Target IPv6 address
+ * @param[in] multicast Unicast or unicast Neighbor Solicitation message
+ * @return Error code
+ **/
+
+error_t ndpSendNeighborSol(NetInterface *interface,
+   const Ipv6Addr *targetIpAddr, bool_t multicast)
+{
+   error_t error;
+   size_t offset;
+   size_t length;
+   NetBuffer *buffer;
+   NdpNeighborSolMessage *message;
+   Ipv6PseudoHeader pseudoHeader;
+
+   //Multicast Neighbor Solicitation message?
+   if(multicast)
+   {
+      //Compute the solicited-node multicast address that
+      //corresponds to the target IPv6 address
+      ipv6ComputeSolicitedNodeAddr(targetIpAddr, &pseudoHeader.destAddr);
+   }
+   else
+   {
+      //Unicast Neighbor Solicitation message
+      pseudoHeader.destAddr = *targetIpAddr;
+   }
+
+   //Check whether the target address is a tentative address
+   if(ipv6IsTentativeAddr(interface, targetIpAddr))
+   {
+      //The IPv6 source is set to the unspecified address
+      pseudoHeader.srcAddr = IPV6_UNSPECIFIED_ADDR;
+   }
+   else
+   {
+      //Select the most appropriate source address to be used
+      //when sending the Neighbor Solicitation message
+      error = ipv6SelectSourceAddr(&interface,
+         targetIpAddr, &pseudoHeader.srcAddr);
+
+      //No address assigned to the interface?
+      if(error)
+         return error;
+   }
+
+   //The only defined option that may appear in a Neighbor Solicitation
+   //message is the Source Link-Layer Address option
+   length = sizeof(NdpNeighborSolMessage) + sizeof(NdpLinkLayerAddrOption);
+
+   //Allocate a memory buffer to hold the Neighbor Solicitation message
+   buffer = ipAllocBuffer(length, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the message
+   message = netBufferAt(buffer, offset);
+
+   //Format Neighbor Solicitation message
+   message->type = ICMPV6_TYPE_NEIGHBOR_SOL;
+   message->code = 0;
+   message->checksum = 0;
+   message->reserved = 0;
+   message->targetAddr = *targetIpAddr;
+
+   //Length of the message, excluding any option
+   length = sizeof(NdpNeighborSolMessage);
+
+   //The Source Link-Layer Address option must not be included
+   //when the source IPv6 address is the unspecified address
+   if(!ipv6CompAddr(&pseudoHeader.srcAddr, &IPV6_UNSPECIFIED_ADDR))
+   {
+#if (ETH_SUPPORT == ENABLED)
+      //Add Source Link-Layer Address option
+      ndpAddOption(message, &length, NDP_OPT_SOURCE_LINK_LAYER_ADDR,
+         &interface->macAddr, sizeof(MacAddr));
+#endif
+   }
+
+   //Adjust the length of the multi-part buffer
+   netBufferSetLength(buffer, offset + length);
+
+   //Format IPv6 pseudo header
+   pseudoHeader.length = htonl(length);
+   pseudoHeader.reserved = 0;
+   pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER;
+
+   //Calculate ICMPv6 header checksum
+   message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader,
+      sizeof(Ipv6PseudoHeader), buffer, offset, length);
+
+   //Debug message
+   TRACE_INFO("Sending Neighbor Solicitation message (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message contents for debugging purpose
+   ndpDumpNeighborSolMessage(message);
+
+   //Send Neighbor Solicitation message
+   error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, NDP_HOP_LIMIT);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send a Neighbor Advertisement message
+ * @param[in] interface Underlying network interface
+ * @param[in] targetIpAddr Target IPv6 address
+ * @param[in] destIpAddr Destination IPv6 address
+ * @return Error code
+ **/
+
+error_t ndpSendNeighborAdv(NetInterface *interface,
+   const Ipv6Addr *targetIpAddr, const Ipv6Addr *destIpAddr)
+{
+   error_t error;
+   size_t offset;
+   size_t length;
+   NetBuffer *buffer;
+   NdpNeighborAdvMessage *message;
+   Ipv6PseudoHeader pseudoHeader;
+
+   //Destination IP address is the unspecified address?
+   if(ipv6CompAddr(destIpAddr, &IPV6_UNSPECIFIED_ADDR))
+   {
+      //If the destination is the unspecified address, the node must
+      //multicast the advertisement to the all-nodes address
+      pseudoHeader.destAddr = IPV6_LINK_LOCAL_ALL_NODES_ADDR;
+   }
+   else
+   {
+      //Otherwise, the node must unicast the advertisement to
+      //the destination IP address
+      pseudoHeader.destAddr = *destIpAddr;
+   }
+
+   //Check whether the target address is a valid anycast address
+   //assigned to the interface
+   if(ipv6IsAnycastAddr(interface, targetIpAddr))
+   {
+      //Select the most appropriate source address to be used
+      //when sending the Neighbor Advertisement message
+      error = ipv6SelectSourceAddr(&interface,
+         targetIpAddr, &pseudoHeader.srcAddr);
+
+      //No address assigned to the interface?
+      if(error)
+         return error;
+   }
+   else
+   {
+      //Set the source IP address
+      pseudoHeader.srcAddr = *targetIpAddr;
+   }
+
+   //The only defined option that may appear in a Neighbor Advertisement
+   //message is the Target Link-Layer Address option
+   length = sizeof(NdpNeighborAdvMessage) + sizeof(NdpLinkLayerAddrOption);
+
+   //Allocate a memory buffer to hold the Neighbor Advertisement message
+   buffer = ipAllocBuffer(length, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the message
+   message = netBufferAt(buffer, offset);
+
+   //Format Neighbor Advertisement message
+   message->type = ICMPV6_TYPE_NEIGHBOR_ADV;
+   message->code = 0;
+   message->checksum = 0;
+   message->reserved1 = 0;
+   message->reserved2 = 0;
+   message->targetAddr = *targetIpAddr;
+
+   //The Router flag indicates that the sender is a router
+   if(interface->ipv6Context.isRouter)
+      message->r = TRUE;
+   else
+      message->r = FALSE;
+
+   //If the destination is the unspecified address, the node must set
+   //the Solicited flag to zero
+   if(ipv6CompAddr(destIpAddr, &IPV6_UNSPECIFIED_ADDR))
+      message->s = FALSE;
+   else
+      message->s = TRUE;
+
+   //The Override flag should not be set in solicited advertisements
+   //for anycast addresses
+   if(ipv6IsAnycastAddr(interface, targetIpAddr))
+      message->o = FALSE;
+   else
+      message->o = TRUE;
+
+   //Length of the message, excluding any option
+   length = sizeof(NdpNeighborAdvMessage);
+
+#if (ETH_SUPPORT == ENABLED)
+   //Add Target Link-Layer Address option
+   ndpAddOption(message, &length, NDP_OPT_TARGET_LINK_LAYER_ADDR,
+      &interface->macAddr, sizeof(MacAddr));
+#endif
+
+   //Adjust the length of the multi-part buffer
+   netBufferSetLength(buffer, offset + length);
+
+   //Format IPv6 pseudo header
+   pseudoHeader.length = htonl(length);
+   pseudoHeader.reserved = 0;
+   pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER;
+
+   //Calculate ICMPv6 header checksum
+   message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader,
+      sizeof(Ipv6PseudoHeader), buffer, offset, length);
+
+   //Debug message
+   TRACE_INFO("Sending Neighbor Advertisement message (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message contents for debugging purpose
+   ndpDumpNeighborAdvMessage(message);
+
+   //Send Neighbor Advertisement message
+   error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, NDP_HOP_LIMIT);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send a Redirect message
+ * @param[in] interface Underlying network interface
+ * @param[in] targetAddr IPv6 address that is a better first hop to use
+ *   for the destination address
+ * @param[in] ipPacket Multi-part buffer that holds the IPv6 packet that
+ *   triggered the sending of the Redirect
+ * @param[in] ipPacketOffset Offset to the first byte of the IPv6 packet
+ * @return Error code
+ **/
+
+error_t ndpSendRedirect(NetInterface *interface, const Ipv6Addr *targetAddr,
+   const NetBuffer *ipPacket, size_t ipPacketOffset)
+{
+   error_t error;
+   size_t offset;
+   size_t length;
+   size_t ipPacketLength;
+   size_t optionLength;
+   size_t paddingLength;
+   NetBuffer *buffer;
+   NdpRedirectMessage *message;
+   NdpRedirectedHeaderOption *option;
+   NdpNeighborCacheEntry *entry;
+   Ipv6Header *ipHeader;
+   Ipv6PseudoHeader pseudoHeader;
+   uint8_t padding[8];
+
+   //Retrieve the length of the forwarded IPv6 packet
+   ipPacketLength = netBufferGetLength(ipPacket) - ipPacketOffset;
+
+   //Check the length of the IPv6 packet
+   if(ipPacketLength < sizeof(Ipv6Header))
+      return ERROR_INVALID_LENGTH;
+
+   //Point to the header of the invoking packet
+   ipHeader = netBufferAt(ipPacket, ipPacketOffset);
+   //Sanity check
+   if(ipHeader == NULL)
+      return ERROR_FAILURE;
+
+   //The only defined options that may appear in a Redirect message are the
+   //Target Link-Layer Address option and the Redirected Header option
+   length = sizeof(NdpRedirectMessage) + sizeof(NdpLinkLayerAddrOption) +
+      sizeof(NdpRedirectedHeaderOption);
+
+   //Allocate a memory buffer to hold the Redirect message
+   buffer = ipAllocBuffer(length, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the message
+   message = netBufferAt(buffer, offset);
+
+   //Format Redirect message
+   message->type = ICMPV6_TYPE_REDIRECT;
+   message->code = 0;
+   message->checksum = 0;
+   message->reserved = 0;
+   message->targetAddr = *targetAddr;
+   message->destAddr = ipHeader->destAddr;
+
+   //Length of the message, excluding any option
+   length = sizeof(NdpRedirectMessage);
+
+   //Search the Neighbor cache for the specified target address
+   entry = ndpFindNeighborCacheEntry(interface, targetAddr);
+
+   //Include the link-layer address of the target, if known
+   if(entry != NULL)
+   {
+      //Add Target Link-Layer Address option
+      ndpAddOption(message, &length, NDP_OPT_TARGET_LINK_LAYER_ADDR,
+         &entry->macAddr, sizeof(MacAddr));
+   }
+
+   //Retrieve the length of the IPv6 packet that triggered the sending
+   //of the Redirect
+   ipPacketLength = netBufferGetLength(ipPacket) - ipPacketOffset;
+
+   //Return as much of the forwarded IPv6 packet as can fit without
+   //the redirect packet exceeding the minimum IPv6 MTU
+   ipPacketLength = MIN(ipPacketLength, IPV6_DEFAULT_MTU -
+      sizeof(NdpRedirectedHeaderOption) - length);
+
+   //Length of the Redirected Header option in units of 8 bytes including
+   //the type and length fields
+   optionLength = (ipPacketLength + sizeof(NdpOption) + 7) / 8;
+
+   //Add Redirected Header option
+   option = (NdpRedirectedHeaderOption *) ((uint8_t *) message + length);
+
+   //Format Redirected Header option
+   option->type = NDP_OPT_REDIRECTED_HEADER;
+   option->length = (uint8_t) optionLength;
+   option->reserved1 = 0;
+   option->reserved2 = 0;
+
+   //Update the length of Redirect message
+   length += sizeof(NdpRedirectedHeaderOption);
+
+   //Adjust the length of the multi-part buffer
+   netBufferSetLength(buffer, offset + length);
+
+   //Copy the contents of the forwarded IPv6 packet
+   error = netBufferConcat(buffer, ipPacket, ipPacketOffset, ipPacketLength);
+
+   //Check status code
+   if(!error)
+   {
+      //Options should be padded when necessary to ensure that they end on
+      //their natural 64-bit boundaries
+      if((ipPacketLength + sizeof(NdpRedirectedHeaderOption)) < (optionLength * 8))
+      {
+         //Determine the amount of padding data to append
+         paddingLength = (optionLength * 8) - ipPacketLength -
+            sizeof(NdpRedirectedHeaderOption);
+
+         //Prepare padding data
+         memset(padding, 0, paddingLength);
+         //Append padding bytes
+         error = netBufferAppend(buffer, padding, paddingLength);
+      }
+   }
+
+   //Check status code
+   if(!error)
+   {
+      //Get the length of the resulting message
+      length = netBufferGetLength(buffer) - offset;
+
+      //Format IPv6 pseudo header
+      pseudoHeader.srcAddr = interface->ipv6Context.addrList[0].addr;
+      pseudoHeader.destAddr = ipHeader->srcAddr;
+      pseudoHeader.length = htonl(length);
+      pseudoHeader.reserved = 0;
+      pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER;
+
+      //Message checksum calculation
+      message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader,
+         sizeof(Ipv6PseudoHeader), buffer, offset, length);
+
+      //Debug message
+      TRACE_INFO("Sending Redirect message (%" PRIuSIZE " bytes)...\r\n", length);
+      //Dump message contents for debugging purpose
+      ndpDumpRedirectMessage(message);
+
+      //Send Redirect message
+      error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, NDP_HOP_LIMIT);
+   }
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Dump Router Solicitation message for debugging purpose
+ * @param[in] message Router Solicitation message
+ **/
+
+void ndpDumpRouterSolMessage(const NdpRouterSolMessage *message)
+{
+   //Dump Router Solicitation message
+   TRACE_DEBUG("  Type = %" PRIu8 "\r\n", message->type);
+   TRACE_DEBUG("  Code = %" PRIu8 "\r\n", message->code);
+   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
+}
+
+
+/**
+ * @brief Dump Router Advertisement message for debugging purpose
+ * @param[in] message Router Advertisement message
+ **/
+
+void ndpDumpRouterAdvMessage(const NdpRouterAdvMessage *message)
+{
+   //Dump Router Advertisement message
+   TRACE_DEBUG("  Type = %" PRIu8 "\r\n", message->type);
+   TRACE_DEBUG("  Code = %" PRIu8 "\r\n", message->code);
+   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
+   TRACE_DEBUG("  Cur Hop Limit = %" PRIu8 "\r\n", message->curHopLimit);
+   TRACE_DEBUG("  M = %" PRIu8 "\r\n", message->m);
+   TRACE_DEBUG("  O = %" PRIu8 "\r\n", message->o);
+   TRACE_DEBUG("  Router Lifetime = %" PRIu16 "\r\n", ntohs(message->routerLifetime));
+   TRACE_DEBUG("  Reachable Time = %" PRIu32 "\r\n", ntohl(message->reachableTime));
+   TRACE_DEBUG("  Retrans Timer = %" PRIu32 "\r\n", ntohl(message->retransTimer));
+}
+
+
+/**
+ * @brief Dump Neighbor Solicitation message for debugging purpose
+ * @param[in] message Neighbor Solicitation message
+ **/
+
+void ndpDumpNeighborSolMessage(const NdpNeighborSolMessage *message)
+{
+   //Dump Neighbor Solicitation message
+   TRACE_DEBUG("  Type = %" PRIu8 "\r\n", message->type);
+   TRACE_DEBUG("  Code = %" PRIu8 "\r\n", message->code);
+   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
+   TRACE_DEBUG("  Target Address = %s\r\n", ipv6AddrToString(&message->targetAddr, NULL));
+}
+
+
+/**
+ * @brief Dump Neighbor Advertisement message for debugging purpose
+ * @param[in] message Neighbor Advertisement message
+ **/
+
+void ndpDumpNeighborAdvMessage(const NdpNeighborAdvMessage *message)
+{
+   //Dump Neighbor Advertisement message
+   TRACE_DEBUG("  Type = %" PRIu8 "\r\n", message->type);
+   TRACE_DEBUG("  Code = %" PRIu8 "\r\n", message->code);
+   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
+   TRACE_DEBUG("  R = %" PRIu8 "\r\n", message->r);
+   TRACE_DEBUG("  S = %" PRIu8 "\r\n", message->s);
+   TRACE_DEBUG("  O = %" PRIu8 "\r\n", message->o);
+   TRACE_DEBUG("  Target Address = %s\r\n", ipv6AddrToString(&message->targetAddr, NULL));
+}
+
+
+/**
+ * @brief Dump Redirect message for debugging purpose
+ * @param[in] message Redirect message
+ **/
+
+void ndpDumpRedirectMessage(const NdpRedirectMessage *message)
+{
+   //Dump Neighbor Advertisement message
+   TRACE_DEBUG("  Type = %" PRIu8 "\r\n", message->type);
+   TRACE_DEBUG("  Code = %" PRIu8 "\r\n", message->code);
+   TRACE_DEBUG("  Checksum = 0x%04" PRIX16 "\r\n", ntohs(message->checksum));
+   TRACE_DEBUG("  Target Address = %s\r\n", ipv6AddrToString(&message->targetAddr, NULL));
+   TRACE_DEBUG("  Destination Address = %s\r\n", ipv6AddrToString(&message->destAddr, NULL));
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/ndp.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,627 @@
+/**
+ * @file ndp.h
+ * @brief NDP (Neighbor Discovery Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _NDP_H
+#define _NDP_H
+
+//Dependencies
+#include "core/net.h"
+
+//NDP support
+#ifndef NDP_SUPPORT
+   #define NDP_SUPPORT ENABLED
+#elif (NDP_SUPPORT != ENABLED && NDP_SUPPORT != DISABLED)
+   #error NDP_SUPPORT parameter is not valid
+#endif
+
+//NDP tick interval
+#ifndef NDP_TICK_INTERVAL
+   #define NDP_TICK_INTERVAL 200
+#elif (NDP_TICK_INTERVAL < 10)
+   #error NDP_TICK_INTERVAL parameter is not valid
+#endif
+
+//Neighbor cache size
+#ifndef NDP_NEIGHBOR_CACHE_SIZE
+   #define NDP_NEIGHBOR_CACHE_SIZE 8
+#elif (NDP_NEIGHBOR_CACHE_SIZE < 1)
+   #error NDP_NEIGHBOR_CACHE_SIZE parameter is not valid
+#endif
+
+//Destination cache size
+#ifndef NDP_DEST_CACHE_SIZE
+   #define NDP_DEST_CACHE_SIZE 8
+#elif (NDP_DEST_CACHE_SIZE < 1)
+   #error NDP_DEST_CACHE_SIZE parameter is not valid
+#endif
+
+//Maximum number of packets waiting for address resolution to complete
+#ifndef NDP_MAX_PENDING_PACKETS
+   #define NDP_MAX_PENDING_PACKETS 2
+#elif (NDP_MAX_PENDING_PACKETS < 1)
+   #error NDP_MAX_PENDING_PACKETS parameter is not valid
+#endif
+
+//Maximum time interval between Router Advertisements
+#ifndef NDP_MAX_RTR_ADVERT_INTERVAL
+   #define NDP_MAX_RTR_ADVERT_INTERVAL 600000
+#elif (NDP_MAX_RTR_ADVERT_INTERVAL < 1000)
+   #error NDP_MAX_RTR_ADVERT_INTERVAL parameter is not valid
+#endif
+
+//Maximum time interval between initial Router Advertisements
+#ifndef NDP_MAX_INITIAL_RTR_ADVERT_INTERVAL
+   #define NDP_MAX_INITIAL_RTR_ADVERT_INTERVAL 16000
+#elif (NDP_MAX_INITIAL_RTR_ADVERT_INTERVAL < 1000)
+   #error NDP_MAX_INITIAL_RTR_ADVERT_INTERVAL parameter is not valid
+#endif
+
+//Maximum number of initial Router Advertisements
+#ifndef NDP_MAX_INITIAL_RTR_ADVERTISEMENTS
+   #define NDP_MAX_INITIAL_RTR_ADVERTISEMENTS 3
+#elif (NDP_MAX_INITIAL_RTR_ADVERTISEMENTS < 1)
+   #error NDP_MAX_INITIAL_RTR_ADVERTISEMENTS parameter is not valid
+#endif
+
+//Maximum number of final Router Advertisements
+#ifndef NDP_MAX_FINAL_RTR_ADVERTISEMENTS
+   #define NDP_MAX_FINAL_RTR_ADVERTISEMENTS 3
+#elif (NDP_MAX_FINAL_RTR_ADVERTISEMENTS < 1)
+   #error NDP_MAX_FINAL_RTR_ADVERTISEMENTS parameter is not valid
+#endif
+
+//Minimum delay between Router Advertisements
+#ifndef NDP_MIN_DELAY_BETWEEN_RAS
+   #define NDP_MIN_DELAY_BETWEEN_RAS 3000
+#elif (NDP_MIN_DELAY_BETWEEN_RAS < 1000)
+   #error NDP_MIN_DELAY_BETWEEN_RAS parameter is not valid
+#endif
+
+//Maximum delay for Router Advertisements sent in response to a Router Solicitation
+#ifndef NDP_MAX_RA_DELAY_TIME
+   #define NDP_MAX_RA_DELAY_TIME 500
+#elif (NDP_MAX_RA_DELAY_TIME < 100)
+   #error NDP_MAX_RA_DELAY_TIME parameter is not valid
+#endif
+
+//Minimum delay before transmitting the first Router Solicitation message
+#ifndef NDP_MIN_RTR_SOLICITATION_DELAY
+   #define NDP_MIN_RTR_SOLICITATION_DELAY 0
+#elif (NDP_MIN_RTR_SOLICITATION_DELAY < 0)
+   #error NDP_MIN_RTR_SOLICITATION_DELAY parameter is not valid
+#endif
+
+//Maximum delay before transmitting the first Router Solicitation message
+#ifndef NDP_MAX_RTR_SOLICITATION_DELAY
+   #define NDP_MAX_RTR_SOLICITATION_DELAY 1000
+#elif (NDP_MAX_RTR_SOLICITATION_DELAY < 0)
+   #error NDP_MAX_RTR_SOLICITATION_DELAY parameter is not valid
+#endif
+
+//The time between retransmissions of Router Solicitation messages
+#ifndef NDP_RTR_SOLICITATION_INTERVAL
+   #define NDP_RTR_SOLICITATION_INTERVAL 4000
+#elif (NDP_RTR_SOLICITATION_INTERVAL < 1000)
+   #error NDP_RTR_SOLICITATION_INTERVAL parameter is not valid
+#endif
+
+//Number of retransmissions for Router Solicitation messages
+#ifndef NDP_MAX_RTR_SOLICITATIONS
+   #define NDP_MAX_RTR_SOLICITATIONS 3
+#elif (NDP_MAX_RTR_SOLICITATIONS < 1)
+   #error NDP_MAX_RTR_SOLICITATIONS parameter is not valid
+#endif
+
+//Number of retransmissions for multicast Neighbor Solicitation messages
+#ifndef NDP_MAX_MULTICAST_SOLICIT
+   #define NDP_MAX_MULTICAST_SOLICIT 3
+#elif (NDP_MAX_MULTICAST_SOLICIT < 1)
+   #error NDP_MAX_MULTICAST_SOLICIT parameter is not valid
+#endif
+
+//Number of retransmissions for unicast Neighbor Solicitation messages
+#ifndef NDP_MAX_UNICAST_SOLICIT
+   #define NDP_MAX_UNICAST_SOLICIT 3
+#elif (NDP_MAX_UNICAST_SOLICIT < 1)
+   #error NDP_MAX_UNICAST_SOLICIT parameter is not valid
+#endif
+
+//Maximum number of Neighbor Solicitation messages sent while performing DAD
+#ifndef NDP_DUP_ADDR_DETECT_TRANSMITS
+   #define NDP_DUP_ADDR_DETECT_TRANSMITS 1
+#elif (NDP_DUP_ADDR_DETECT_TRANSMITS < 0)
+   #error NDP_DUP_ADDR_DETECT_TRANSMITS parameter is not valid
+#endif
+
+//Delay before sending Neighbor Advertisements if the target address is an anycast address
+#ifndef NDP_MAX_ANYCAST_DELAY_TIME
+   #define NDP_MAX_ANYCAST_DELAY_TIME 1000
+#elif (NDP_MAX_ANYCAST_DELAY_TIME < 100)
+   #error NDP_MAX_ANYCAST_DELAY_TIME parameter is not valid
+#endif
+
+//Maximum number of unsolicited Neighbor Advertisements
+#ifndef NDP_MAX_NEIGHBOR_ADVERTISEMENT
+   #define NDP_MAX_NEIGHBOR_ADVERTISEMENT 3
+#elif (NDP_MAX_NEIGHBOR_ADVERTISEMENT < 0)
+   #error NDP_MAX_NEIGHBOR_ADVERTISEMENT parameter is not valid
+#endif
+
+//The time a neighbor is considered reachable after receiving a reachability confirmation
+#ifndef NDP_REACHABLE_TIME
+   #define NDP_REACHABLE_TIME 30000
+#elif (NDP_REACHABLE_TIME < 1000)
+   #error NDP_REACHABLE_TIME parameter is not valid
+#endif
+
+//The time between retransmissions of Neighbor Solicitation messages
+#ifndef NDP_RETRANS_TIMER
+   #define NDP_RETRANS_TIMER 1000
+#elif (NDP_RETRANS_TIMER < 100)
+   #error NDP_RETRANS_TIMER parameter is not valid
+#endif
+
+//Delay before sending the first probe
+#ifndef NDP_DELAY_FIRST_PROBE_TIME
+   #define NDP_DELAY_FIRST_PROBE_TIME 5000
+#elif (NDP_DELAY_FIRST_PROBE_TIME < 1000)
+   #error NDP_DELAY_FIRST_PROBE_TIME parameter is not valid
+#endif
+
+//Hop Limit used by NDP messages
+#define NDP_HOP_LIMIT 255
+
+//Infinite lifetime
+#define NDP_INFINITE_LIFETIME 0xFFFFFFFF
+
+
+/**
+ * @brief Neighbor Discovery options
+ **/
+
+typedef enum
+{
+   NDP_OPT_SOURCE_LINK_LAYER_ADDR = 1,
+   NDP_OPT_TARGET_LINK_LAYER_ADDR = 2,
+   NDP_OPT_PREFIX_INFORMATION     = 3,
+   NDP_OPT_REDIRECTED_HEADER      = 4,
+   NDP_OPT_MTU                    = 5,
+   NDP_OPT_ROUTE_INFORMATION      = 24,
+   NDP_OPT_RECURSIVE_DNS_SERVER   = 25,
+   NDP_OPT_DNS_SEARCH_LIST        = 31,
+   NDP_OPT_6LOWPAN_CONTEXT        = 34,
+   NDP_OPT_ANY                    = 255
+} NdpOptionType;
+
+
+/**
+ * @brief Router selection preferences
+ **/
+
+typedef enum
+{
+   NDP_ROUTER_SEL_PREFERENCE_MEDIUM   = 0,
+   NDP_ROUTER_SEL_PREFERENCE_HIGH     = 1,
+   NDP_ROUTER_SEL_PREFERENCE_RESERVED = 2,
+   NDP_ROUTER_SEL_PREFERENCE_LOW      = 3
+} NdpRouterSelPreference;
+
+
+/**
+ * @brief Neighbor cache entry states
+ **/
+
+typedef enum
+{
+   NDP_STATE_NONE       = 0,
+   NDP_STATE_INCOMPLETE = 1,
+   NDP_STATE_REACHABLE  = 2,
+   NDP_STATE_STALE      = 3,
+   NDP_STATE_DELAY      = 4,
+   NDP_STATE_PROBE      = 5,
+   NDP_STATE_PERMANENT  = 6
+} NdpState;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief Router Solicitation message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;      //0
+   uint8_t code;      //1
+   uint16_t checksum; //2-3
+   uint32_t reserved; //4-7
+   uint8_t options[]; //8
+} __end_packed NdpRouterSolMessage;
+
+
+/**
+ * @brief Router Advertisement message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;                  //0
+   uint8_t code;                  //1
+   uint16_t checksum;             //2-3
+   uint8_t curHopLimit;           //4
+#ifdef _CPU_BIG_ENDIAN
+   uint8_t m : 1;                 //5
+   uint8_t o : 1;
+   uint8_t h : 1;
+   uint8_t prf : 2;
+   uint8_t p : 1;
+   uint8_t reserved : 2;
+#else
+   uint8_t reserved : 2;          //5
+   uint8_t p : 1;
+   uint8_t prf : 2;
+   uint8_t h : 1;
+   uint8_t o : 1;
+   uint8_t m : 1;
+#endif
+   uint16_t routerLifetime;       //6-7
+   uint32_t reachableTime;        //8-11
+   uint32_t retransTimer;         //12-15
+   uint8_t options[];             //16
+} __end_packed NdpRouterAdvMessage;
+
+
+/**
+ * @brief Neighbor Solicitation message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;        //0
+   uint8_t code;        //1
+   uint16_t checksum;   //2-3
+   uint32_t reserved;   //4-7
+   Ipv6Addr targetAddr; //8-23
+   uint8_t options[];   //24
+} __end_packed NdpNeighborSolMessage;
+
+
+/**
+ * @brief Neighbor Advertisement message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;            //0
+   uint8_t code;            //1
+   uint16_t checksum;       //2-3
+#ifdef _CPU_BIG_ENDIAN
+   uint32_t r : 1;          //4
+   uint32_t s : 1;
+   uint32_t o : 1;
+   uint32_t reserved1 : 5;
+   uint32_t reserved2 : 24; //5-7
+#else
+   uint32_t reserved1 : 5;  //4
+   uint32_t o : 1;
+   uint32_t s : 1;
+   uint32_t r : 1;
+   uint32_t reserved2 : 24; //5-7
+#endif
+   Ipv6Addr targetAddr;     //8-23
+   uint8_t options[];       //24
+} __end_packed NdpNeighborAdvMessage;
+
+
+/**
+ * @brief Redirect message
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;        //0
+   uint8_t code;        //1
+   uint16_t checksum;   //2-3
+   uint32_t reserved;   //4-7
+   Ipv6Addr targetAddr; //8-23
+   Ipv6Addr destAddr;   //24-39
+   uint8_t options[];   //40
+} __end_packed NdpRedirectMessage;
+
+
+/**
+ * @brief Neighbor Discovery option general format
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;    //0
+   uint8_t length;  //1
+   uint8_t value[]; //2
+} __end_packed NdpOption;
+
+
+/**
+ * @brief Source/Target Link-Layer Address option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;          //0
+   uint8_t length;        //1
+   MacAddr linkLayerAddr; //2-7
+} __end_packed NdpLinkLayerAddrOption;
+
+
+/**
+ * @brief Prefix Information option (PIO)
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;               //0
+   uint8_t length;             //1
+   uint8_t prefixLength;       //2
+#ifdef _CPU_BIG_ENDIAN
+   uint8_t l : 1;              //3
+   uint8_t a : 1;
+   uint8_t reserved1 : 6;
+#else
+   uint8_t reserved1 : 6;      //3
+   uint8_t a : 1;
+   uint8_t l : 1;
+#endif
+   uint32_t validLifetime;     //4-7
+   uint32_t preferredLifetime; //8-11
+   uint32_t reserved2;         //12-15
+   Ipv6Addr prefix;            //16-31
+} __end_packed NdpPrefixInfoOption;
+
+
+/**
+ * @brief Redirected Header option (RHO)
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;       //0
+   uint8_t length;     //1
+   uint16_t reserved1; //2-3
+   uint32_t reserved2; //4-7
+   uint8_t ipPacket[]; //8
+} __end_packed NdpRedirectedHeaderOption;
+
+
+/**
+ * @brief MTU option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;      //0
+   uint8_t length;    //1
+   uint16_t reserved; //2-3
+   uint32_t mtu;      //4-7
+} __end_packed NdpMtuOption;
+
+
+/**
+ * @brief Route Information option (RIO)
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;           //0
+   uint8_t length;         //1
+   uint8_t prefixLength;   //2
+#ifdef _CPU_BIG_ENDIAN
+   uint8_t reserved1 : 3;  //3
+   uint8_t prf : 2;
+   uint8_t reserved2 : 3;
+#else
+   uint8_t reserved2 : 3;  //3
+   uint8_t prf : 2;
+   uint8_t reserved1 : 3;
+#endif
+   uint32_t routeLifetime; //4-7
+   Ipv6Addr prefix;        //8
+} __end_packed NdpRouteInfoOption;
+
+
+/**
+ * @brief Recursive DNS Server option (RDNSS)
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;       //0
+   uint8_t length;     //1
+   uint16_t reserved;  //2-3
+   uint32_t lifetime;  //4-7
+   Ipv6Addr address[]; //8
+} __end_packed NdpRdnssOption;
+
+
+/**
+ * @brief DNS Search List option (DNSSL)
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;          //0
+   uint8_t length;        //1
+   uint16_t reserved;     //2-3
+   uint32_t lifetime;     //4-7
+   uint8_t domainNames[]; //8
+} __end_packed NdpDnsslOption;
+
+
+/**
+ * @brief 6LoWPAN Context option (6CO)
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;           //0
+   uint8_t length;         //1
+   uint8_t contextLength;  //2
+#ifdef _CPU_BIG_ENDIAN
+   uint8_t reserved1 : 3;  //3
+   uint8_t c : 1;
+   uint8_t cid : 4;
+#else
+   uint8_t cid : 4;        //3
+   uint8_t c : 1;
+   uint8_t reserved1 : 3;
+#endif
+   uint16_t reserved2;     //4-5
+   uint16_t validLifetime; //6-7
+   Ipv6Addr contextPrefix; //8
+} __end_packed NdpContextOption;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+/**
+ * @brief NDP queue item
+ **/
+
+typedef struct
+{
+   NetInterface *srcInterface; //<Interface from which the packet has been received
+   NetBuffer *buffer;          ///<Packet waiting for address resolution
+   size_t offset;              ///<Offset to the first byte of the packet
+} NdpQueueItem;
+
+
+/**
+ * @brief Neighbor cache entry
+ **/
+
+typedef struct
+{
+   NdpState state;                              ///<Reachability state
+   Ipv6Addr ipAddr;                             ///<Unicast IPv6 address
+   MacAddr macAddr;                             ///<Link layer address associated with the IPv6 address
+   bool_t isRouter;                             ///<A flag indicating whether the neighbor is a router or a host
+   systime_t timestamp;                         ///<Timestamp to manage entry lifetime
+   systime_t timeout;                           ///<Timeout value
+   uint_t retransmitCount;                      ///<Retransmission counter
+   NdpQueueItem queue[NDP_MAX_PENDING_PACKETS]; ///<Packets waiting for address resolution to complete
+   uint_t queueSize;                            ///<Number of queued packets
+} NdpNeighborCacheEntry;
+
+
+/**
+ * @brief Destination cache entry
+ **/
+
+typedef struct
+{
+   Ipv6Addr destAddr;   ///<Destination IPv6 address
+   Ipv6Addr nextHop;    ///<IPv6 address of the next-hop neighbor
+   size_t pathMtu;      ///<Path MTU
+   systime_t timestamp; ///<Timestamp to manage entry lifetime
+} NdpDestCacheEntry;
+
+
+/**
+ * @brief NDP context
+ **/
+
+typedef struct
+{
+   uint32_t reachableTime;                                       ///<The time a node assumes a neighbor is reachable
+   uint32_t retransTimer;                                        ///<The time between retransmissions of NS messages
+   uint_t dupAddrDetectTransmits;                                ///<Maximum number of NS messages sent while performing DAD
+   systime_t minRtrSolicitationDelay;                            ///<Minimum delay before transmitting the first RS message
+   systime_t maxRtrSolicitationDelay;                            ///<Maximum delay before transmitting the first RS message
+   systime_t rtrSolicitationInterval;                            ///<Time interval between retransmissions of RS messages
+   uint_t maxRtrSolicitations;                                   ///<Number of retransmissions for RS messages
+   uint_t rtrSolicitationCount;                                  ///<Retransmission counter for RS messages
+   bool_t rtrAdvReceived;                                        ///<Valid RA message received
+   systime_t timestamp;                                          ///<Timestamp to manage retransmissions
+   systime_t timeout;                                            ///<Timeout value
+   NdpNeighborCacheEntry neighborCache[NDP_NEIGHBOR_CACHE_SIZE]; ///<Neighbor cache
+   NdpDestCacheEntry destCache[NDP_DEST_CACHE_SIZE];             ///<Destination cache
+} NdpContext;
+
+
+//Tick counter to handle periodic operations
+extern systime_t ndpTickCounter;
+
+//NDP related functions
+error_t ndpInit(NetInterface *interface);
+
+error_t ndpResolve(NetInterface *interface, const Ipv6Addr *ipAddr, MacAddr *macAddr);
+
+error_t ndpEnqueuePacket(NetInterface *srcInterface, NetInterface *destInterface,
+   const Ipv6Addr *ipAddr, NetBuffer *buffer, size_t offset);
+
+void ndpTick(NetInterface *interface);
+void ndpLinkChangeEvent(NetInterface *interface);
+
+void ndpProcessRouterAdv(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   const NetBuffer *buffer, size_t offset, uint8_t hopLimit);
+
+void ndpProcessNeighborSol(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   const NetBuffer *buffer, size_t offset, uint8_t hopLimit);
+
+void ndpProcessNeighborAdv(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   const NetBuffer *buffer, size_t offset, uint8_t hopLimit);
+
+void ndpProcessRedirect(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   const NetBuffer *buffer, size_t offset, uint8_t hopLimit);
+
+error_t ndpSendRouterSol(NetInterface *interface);
+
+error_t ndpSendNeighborSol(NetInterface *interface,
+   const Ipv6Addr *targetIpAddr, bool_t multicast);
+
+error_t ndpSendNeighborAdv(NetInterface *interface,
+   const Ipv6Addr *targetIpAddr, const Ipv6Addr *destIpAddr);
+
+error_t ndpSendRedirect(NetInterface *interface, const Ipv6Addr *targetAddr,
+   const NetBuffer *ipPacket, size_t ipPacketOffset);
+
+void ndpDumpRouterSolMessage(const NdpRouterSolMessage *message);
+void ndpDumpRouterAdvMessage(const NdpRouterAdvMessage *message);
+void ndpDumpNeighborSolMessage(const NdpNeighborSolMessage *message);
+void ndpDumpNeighborAdvMessage(const NdpNeighborAdvMessage *message);
+void ndpDumpRedirectMessage(const NdpRedirectMessage *message);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/ndp_cache.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,451 @@
+/**
+ * @file ndp_cache.c
+ * @brief Neighbor and destination cache management
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NDP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "ipv6/icmpv6.h"
+#include "ipv6/ndp.h"
+#include "ipv6/ndp_cache.h"
+#include "ipv6/ndp_misc.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV6_SUPPORT == ENABLED && NDP_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Create a new entry in the Neighbor cache
+ * @param[in] interface Underlying network interface
+ * @return Pointer to the newly created entry
+ **/
+
+NdpNeighborCacheEntry *ndpCreateNeighborCacheEntry(NetInterface *interface)
+{
+   uint_t i;
+   NdpNeighborCacheEntry *entry;
+   NdpNeighborCacheEntry *oldestEntry;
+
+   //Keep track of the oldest entry
+   oldestEntry = &interface->ndpContext.neighborCache[0];
+
+   //Loop through Neighbor cache entries
+   for(i = 0; i < NDP_NEIGHBOR_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ndpContext.neighborCache[i];
+
+      //Check whether the entry is currently in used or not
+      if(entry->state == NDP_STATE_NONE)
+      {
+         //Erase contents
+         memset(entry, 0, sizeof(NdpNeighborCacheEntry));
+         //Return a pointer to the Neighbor cache entry
+         return entry;
+      }
+
+      //Keep track of the oldest entry in the table
+      if(timeCompare(entry->timestamp, oldestEntry->timestamp) < 0)
+         oldestEntry = entry;
+   }
+
+   //Drop any pending packets
+   ndpFlushQueuedPackets(interface, oldestEntry);
+   //The oldest entry is removed whenever the table runs out of space
+   memset(oldestEntry, 0, sizeof(NdpNeighborCacheEntry));
+
+   //Return a pointer to the Neighbor cache entry
+   return oldestEntry;
+}
+
+
+/**
+ * @brief Search the Neighbor cache for a given IPv6 address
+ * @param[in] interface Underlying network interface
+ * @param[in] ipAddr IPv6 address
+ * @return A pointer to the matching entry is returned. NULL is returned if
+ *   the specified IPv6 address could not be found in the Neighbor cache
+ **/
+
+NdpNeighborCacheEntry *ndpFindNeighborCacheEntry(NetInterface *interface, const Ipv6Addr *ipAddr)
+{
+   uint_t i;
+   NdpNeighborCacheEntry *entry;
+
+   //Loop through Neighbor cache entries
+   for(i = 0; i < NDP_NEIGHBOR_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ndpContext.neighborCache[i];
+
+      //Check whether the entry is currently in used
+      if(entry->state != NDP_STATE_NONE)
+      {
+         //Current entry matches the specified address?
+         if(ipv6CompAddr(&entry->ipAddr, ipAddr))
+            return entry;
+      }
+   }
+
+   //No matching entry in Neighbor cache...
+   return NULL;
+}
+
+
+/**
+ * @brief Periodically update Neighbor cache
+ * @param[in] interface Underlying network interface
+ **/
+
+void ndpUpdateNeighborCache(NetInterface *interface)
+{
+   uint_t i;
+   systime_t time;
+   NdpNeighborCacheEntry *entry;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Go through Neighbor cache
+   for(i = 0; i < NDP_NEIGHBOR_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ndpContext.neighborCache[i];
+
+      //INCOMPLETE state?
+      if(entry->state == NDP_STATE_INCOMPLETE)
+      {
+         //The Neighbor Solicitation timed out?
+         if(timeCompare(time, entry->timestamp + entry->timeout) >= 0)
+         {
+            //Increment retransmission counter
+            entry->retransmitCount++;
+
+            //Check whether the maximum number of retransmissions has been exceeded
+            if(entry->retransmitCount < NDP_MAX_MULTICAST_SOLICIT)
+            {
+               //Retransmit the multicast Neighbor Solicitation message
+               ndpSendNeighborSol(interface, &entry->ipAddr, TRUE);
+
+               //Save the time at which the message was sent
+               entry->timestamp = time;
+               //Set timeout value
+               entry->timeout = interface->ndpContext.retransTimer;
+            }
+            else
+            {
+               //Drop packets that are waiting for address resolution
+               ndpFlushQueuedPackets(interface, entry);
+               //The entry should be deleted since address resolution has failed
+               entry->state = NDP_STATE_NONE;
+            }
+         }
+      }
+      //REACHABLE state?
+      else if(entry->state == NDP_STATE_REACHABLE)
+      {
+         //Periodically time out Neighbor cache entries
+         if(timeCompare(time, entry->timestamp + entry->timeout) >= 0)
+         {
+            //Save current time
+            entry->timestamp = osGetSystemTime();
+            //Enter STALE state
+            entry->state = NDP_STATE_STALE;
+         }
+      }
+      //STALE state?
+      else if(entry->state == NDP_STATE_STALE)
+      {
+         //The neighbor is no longer known to be reachable but until traffic
+         //is sent to the neighbor, no attempt should be made to verify its
+         //reachability
+      }
+      //DELAY state?
+      else if(entry->state == NDP_STATE_DELAY)
+      {
+         //Wait for the specified delay before sending the first probe
+         if(timeCompare(time, entry->timestamp + entry->timeout) >= 0)
+         {
+            Ipv6Addr ipAddr;
+
+            //Save the time at which the message was sent
+            entry->timestamp = time;
+            //Set timeout value
+            entry->timeout = interface->ndpContext.retransTimer;
+            //Switch to the PROBE state
+            entry->state = NDP_STATE_PROBE;
+
+            //Target address
+            ipAddr = entry->ipAddr;
+
+            //Send a unicast Neighbor Solicitation message
+            ndpSendNeighborSol(interface, &ipAddr, FALSE);
+         }
+      }
+      //PROBE state?
+      else if(entry->state == NDP_STATE_PROBE)
+      {
+         //The request timed out?
+         if(timeCompare(time, entry->timestamp + entry->timeout) >= 0)
+         {
+            //Increment retransmission counter
+            entry->retransmitCount++;
+
+            //Check whether the maximum number of retransmissions has been exceeded
+            if(entry->retransmitCount < NDP_MAX_UNICAST_SOLICIT)
+            {
+               Ipv6Addr ipAddr;
+
+               //Save the time at which the packet was sent
+               entry->timestamp = time;
+               //Set timeout value
+               entry->timeout = interface->ndpContext.retransTimer;
+
+               //Target address
+               ipAddr = entry->ipAddr;
+
+               //Send a unicast Neighbor Solicitation message
+               ndpSendNeighborSol(interface, &ipAddr, FALSE);
+            }
+            else
+            {
+               //The entry should be deleted since the host is not reachable anymore
+               entry->state = NDP_STATE_NONE;
+
+               //If at some point communication ceases to proceed, as determined
+               //by the Neighbor Unreachability Detection algorithm, next-hop
+               //determination may need to be performed again...
+               ndpUpdateNextHop(interface, &entry->ipAddr);
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Flush Neighbor cache
+ * @param[in] interface Underlying network interface
+ **/
+
+void ndpFlushNeighborCache(NetInterface *interface)
+{
+   uint_t i;
+   NdpNeighborCacheEntry *entry;
+
+   //Loop through Neighbor cache entries
+   for(i = 0; i < NDP_NEIGHBOR_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ndpContext.neighborCache[i];
+
+      //Drop packets that are waiting for address resolution
+      ndpFlushQueuedPackets(interface, entry);
+      //Release Neighbor cache entry
+      entry->state = NDP_STATE_NONE;
+   }
+}
+
+
+/**
+ * @brief Send packets that are waiting for address resolution
+ * @param[in] interface Underlying network interface
+ * @param[in] entry Pointer to a Neighbor cache entry
+ * @return The number of packets that have been sent
+ **/
+
+uint_t ndpSendQueuedPackets(NetInterface *interface, NdpNeighborCacheEntry *entry)
+{
+   uint_t i;
+   NdpQueueItem *item;
+
+   //Reset packet counter
+   i = 0;
+
+   //Check current state
+   if(entry->state == NDP_STATE_INCOMPLETE)
+   {
+      //Loop through the queued packets
+      for(i = 0; i < entry->queueSize; i++)
+      {
+         //Point to the current queue item
+         item = &entry->queue[i];
+
+#if (ETH_SUPPORT == ENABLED)
+         //Ethernet interface?
+         if(interface->nicDriver->type == NIC_TYPE_ETHERNET)
+         {
+            //Send the IPv6 packet
+            ethSendFrame(interface, &entry->macAddr,
+               item->buffer, item->offset, ETH_TYPE_IPV6);
+         }
+#endif
+         //Release memory buffer
+         netBufferFree(item->buffer);
+      }
+   }
+
+   //The queue is now empty
+   entry->queueSize = 0;
+
+   //Return the number of packets that have been sent
+   return i;
+}
+
+
+/**
+ * @brief Flush packet queue
+ * @param[in] interface Underlying network interface
+ * @param[in] entry Pointer to a Neighbor cache entry
+ **/
+
+void ndpFlushQueuedPackets(NetInterface *interface, NdpNeighborCacheEntry *entry)
+{
+   uint_t i;
+   NdpQueueItem *item;
+
+   //Check current state
+   if(entry->state == NDP_STATE_INCOMPLETE)
+   {
+      //Loop through the queued packets
+      for(i = 0; i < entry->queueSize; i++)
+      {
+         //Point to the current queue item
+         item = &entry->queue[i];
+
+         //Check whether the address resolution has failed
+         if(entry->retransmitCount >= NDP_MAX_MULTICAST_SOLICIT)
+         {
+            //Check whether the packet has been forwarded
+            if(item->srcInterface != NULL)
+            {
+               //A Destination Unreachable message should be generated by a
+               //router in response to a packet that cannot be delivered
+               icmpv6SendErrorMessage(item->srcInterface, ICMPV6_TYPE_DEST_UNREACHABLE,
+                  ICMPV6_CODE_ADDR_UNREACHABLE, 0, item->buffer, item->offset);
+            }
+         }
+
+         //Release memory buffer
+         netBufferFree(item->buffer);
+      }
+   }
+
+   //The queue is now empty
+   entry->queueSize = 0;
+}
+
+
+/**
+ * @brief Create a new entry in the Destination Cache
+ * @param[in] interface Underlying network interface
+ * @return Pointer to the newly created entry
+ **/
+
+NdpDestCacheEntry *ndpCreateDestCacheEntry(NetInterface *interface)
+{
+   uint_t i;
+   NdpDestCacheEntry *entry;
+   NdpDestCacheEntry *oldestEntry;
+
+   //Keep track of the oldest entry
+   oldestEntry = &interface->ndpContext.destCache[0];
+
+   //Loop through Destination cache entries
+   for(i = 0; i < NDP_DEST_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ndpContext.destCache[i];
+
+      //Check whether the entry is currently in used or not
+      if(ipv6CompAddr(&entry->destAddr, &IPV6_UNSPECIFIED_ADDR))
+      {
+         //Erase contents
+         memset(entry, 0, sizeof(NdpDestCacheEntry));
+         //Return a pointer to the Destination cache entry
+         return entry;
+      }
+
+      //Keep track of the oldest entry in the table
+      if(timeCompare(entry->timestamp, oldestEntry->timestamp) < 0)
+         oldestEntry = entry;
+   }
+
+   //The oldest entry is removed whenever the table runs out of space
+   memset(oldestEntry, 0, sizeof(NdpDestCacheEntry));
+
+   //Return a pointer to the Destination cache entry
+   return oldestEntry;
+}
+
+
+/**
+ * @brief Search the Destination Cache for a given destination address
+ * @param[in] interface Underlying network interface
+ * @param[in] destAddr Destination IPv6 address
+ * @return A pointer to the matching entry is returned. NULL is returned if
+ *   the specified address could not be found in the Destination cache
+ **/
+
+NdpDestCacheEntry *ndpFindDestCacheEntry(NetInterface *interface, const Ipv6Addr *destAddr)
+{
+   uint_t i;
+   NdpDestCacheEntry *entry;
+
+   //Loop through Destination Cache entries
+   for(i = 0; i < NDP_DEST_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ndpContext.destCache[i];
+
+      //Current entry matches the specified destination address?
+      if(ipv6CompAddr(&entry->destAddr, destAddr))
+         return entry;
+   }
+
+   //No matching entry in Destination Cache...
+   return NULL;
+}
+
+
+/**
+ * @brief Flush Destination Cache
+ * @param[in] interface Underlying network interface
+ **/
+
+void ndpFlushDestCache(NetInterface *interface)
+{
+   //Clear the Destination Cache
+   memset(interface->ndpContext.destCache, 0,
+      sizeof(interface->ndpContext.destCache));
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/ndp_cache.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,51 @@
+/**
+ * @file ndp_cache.h
+ * @brief Neighbor and destination cache management
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _NDP_CACHE_H
+#define _NDP_CACHE_H
+
+//Dependencies
+#include "core/net.h"
+#include "ipv6/ndp.h"
+
+//NDP related functions
+NdpNeighborCacheEntry *ndpCreateNeighborCacheEntry(NetInterface *interface);
+NdpNeighborCacheEntry *ndpFindNeighborCacheEntry(NetInterface *interface, const Ipv6Addr *ipAddr);
+
+void ndpUpdateNeighborCache(NetInterface *interface);
+void ndpFlushNeighborCache(NetInterface *interface);
+
+uint_t ndpSendQueuedPackets(NetInterface *interface, NdpNeighborCacheEntry *entry);
+void ndpFlushQueuedPackets(NetInterface *interface, NdpNeighborCacheEntry *entry);
+
+NdpDestCacheEntry *ndpCreateDestCacheEntry(NetInterface *interface);
+NdpDestCacheEntry *ndpFindDestCacheEntry(NetInterface *interface, const Ipv6Addr *destAddr);
+void ndpFlushDestCache(NetInterface *interface);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/ndp_misc.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,706 @@
+/**
+ * @file ndp_misc.c
+ * @brief Helper functions for NDP (Neighbor Discovery Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NDP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_misc.h"
+#include "ipv6/ndp.h"
+#include "ipv6/ndp_cache.h"
+#include "ipv6/ndp_misc.h"
+#include "mdns/mdns_responder.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV6_SUPPORT == ENABLED && NDP_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Parse Prefix Information Option
+ * @param[in] interface Underlying network interface
+ * @param[in] option Pointer to the Prefix Information option
+ **/
+
+void ndpParsePrefixInfoOption(NetInterface *interface, NdpPrefixInfoOption *option)
+{
+   //Make sure the Prefix Information option is valid
+   if(option == NULL || option->length != 4)
+      return;
+
+   //A prefix Information option that have the on-link flag set indicates a
+   //prefix identifying a range of addresses that should be considered on-link
+   if(!option->l)
+      return;
+
+   //If the prefix is the link-local prefix, silently ignore the
+   //Prefix Information option
+   if(ipv6CompPrefix(&option->prefix, &IPV6_LINK_LOCAL_ADDR_PREFIX, 10))
+      return;
+
+   //If the preferred lifetime is greater than the valid lifetime,
+   //silently ignore the Prefix Information option
+   if(ntohl(option->preferredLifetime) > ntohl(option->validLifetime))
+      return;
+
+   //Check whether the Valid Lifetime field is non-zero
+   if(ntohl(option->validLifetime) != 0)
+   {
+      //If the prefix is not already present in the Prefix List, create a new
+      //entry for the prefix. If the prefix is already present in the list,
+      //reset its invalidation timer
+      ipv6AddPrefix(interface, &option->prefix, option->prefixLength,
+         ntohl(option->validLifetime), ntohl(option->preferredLifetime));
+   }
+   else
+   {
+      //If the new Lifetime value is zero, time-out the prefix immediately
+      ipv6RemovePrefix(interface, &option->prefix, option->prefixLength);
+   }
+}
+
+
+/**
+ * @brief Manage the lifetime of IPv6 addresses
+ * @param[in] interface Underlying network interface
+ **/
+
+void ndpUpdateAddrList(NetInterface *interface)
+{
+   uint_t i;
+   systime_t time;
+   Ipv6AddrEntry *entry;
+   NdpContext *context;
+
+   //Point to the NDP context
+   context = &interface->ndpContext;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Go through the list of IPv6 addresses
+   for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.addrList[i];
+
+      //Tentative address?
+      if(entry->state == IPV6_ADDR_STATE_TENTATIVE)
+      {
+         //Check whether the link is up
+         if(interface->linkState)
+         {
+            //To check an address, a node should send Neighbor Solicitation messages
+            if(entry->dadRetransmitCount == 0)
+            {
+               //Set time stamp
+               entry->timestamp = time;
+
+               //Check whether Duplicate Address Detection should be performed
+               if(context->dupAddrDetectTransmits > 0)
+               {
+                  //Link-local address?
+                  if(i == 0)
+                  {
+                     //Delay before transmitting the first solicitation
+                     entry->dadTimeout = netGetRandRange(0, NDP_MAX_RTR_SOLICITATION_DELAY);
+                     //Prepare to send the first Neighbor Solicitation message
+                     entry->dadRetransmitCount = 1;
+                  }
+                  else
+                  {
+                     //Valid link-local address?
+                     if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED)
+                     {
+                        //Prepare to send the first Neighbor Solicitation message
+                        entry->dadTimeout = 0;
+                        entry->dadRetransmitCount = 1;
+                     }
+                  }
+               }
+               else
+               {
+                  //Do not perform Duplicate Address Detection
+                  entry->state = IPV6_ADDR_STATE_PREFERRED;
+               }
+            }
+            else
+            {
+               //Check current time
+               if(timeCompare(time, entry->timestamp + entry->dadTimeout) >= 0)
+               {
+                  //Duplicate Address Detection failed?
+                  if(entry->duplicate)
+                  {
+                     //A tentative address that is determined to be a duplicate
+                     //must not be assigned to an interface
+                     if(entry->permanent)
+                     {
+                        //The IPv6 address should be preserved if it has been
+                        //manually assigned
+                        ipv6SetAddr(interface, i, &entry->addr,
+                           IPV6_ADDR_STATE_INVALID, 0, 0, TRUE);
+                     }
+                     else
+                     {
+                        //The IPv6 address is no more valid and should be
+                        //removed from the list
+                        ipv6SetAddr(interface, i, &IPV6_UNSPECIFIED_ADDR,
+                           IPV6_ADDR_STATE_INVALID, 0, 0, FALSE);
+                     }
+                  }
+                  //Duplicate Address Detection is on-going?
+                  else if(entry->dadRetransmitCount <= context->dupAddrDetectTransmits)
+                  {
+                     //Send a multicast Neighbor Solicitation message
+                     ndpSendNeighborSol(interface, &entry->addr, TRUE);
+
+                     //Set timeout value
+                     entry->dadTimeout += context->retransTimer;
+                     //Increment retransmission counter
+                     entry->dadRetransmitCount++;
+                  }
+                  //Duplicate Address Detection is complete?
+                  else
+                  {
+                     //The use of the IPv6 address is now unrestricted
+                     entry->state = IPV6_ADDR_STATE_PREFERRED;
+
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+                     //Link-local address?
+                     if(i == 0)
+                     {
+                        //Restart mDNS probing process
+                        mdnsResponderStartProbing(interface->mdnsResponderContext);
+                     }
+#endif
+                  }
+               }
+            }
+         }
+      }
+      //Preferred address?
+      else if(entry->state == IPV6_ADDR_STATE_PREFERRED)
+      {
+         //An IPv6 address with an infinite preferred lifetime is never timed out
+         if(entry->preferredLifetime != NDP_INFINITE_LIFETIME)
+         {
+            //When the preferred lifetime expires, the address becomes deprecated
+            if(timeCompare(time, entry->timestamp + entry->preferredLifetime) >= 0)
+            {
+               //A deprecated address should continue to be used as a source
+               //address in existing communications, but should not be used
+               //to initiate new communications
+               entry->state = IPV6_ADDR_STATE_DEPRECATED;
+            }
+         }
+      }
+      //Deprecated address?
+      else if(entry->state == IPV6_ADDR_STATE_DEPRECATED)
+      {
+         //An IPv6 address with an infinite valid lifetime is never timed out
+         if(entry->validLifetime != NDP_INFINITE_LIFETIME)
+         {
+            //When the valid lifetime expires, the address becomes invalid
+            if(timeCompare(time, entry->timestamp + entry->validLifetime) >= 0)
+            {
+               //The IPv6 address is no more valid and should be removed from the list
+               ipv6SetAddr(interface, i, &IPV6_UNSPECIFIED_ADDR,
+                  IPV6_ADDR_STATE_INVALID, 0, 0, FALSE);
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Periodically update Prefix List
+ * @param[in] interface Underlying network interface
+ **/
+
+void ndpUpdatePrefixList(NetInterface *interface)
+{
+   uint_t i;
+   systime_t time;
+   Ipv6PrefixEntry *entry;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Go through the Prefix List
+   for(i = 0; i < IPV6_PREFIX_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.prefixList[i];
+
+      //Check the lifetime value
+      if(entry->validLifetime > 0 && entry->validLifetime < INFINITE_DELAY)
+      {
+         //A node should retain entries in the Prefix List until their
+         //lifetimes expire
+         if(timeCompare(time, entry->timestamp + entry->validLifetime) >= 0)
+         {
+            //When removing an entry from the Prefix List, there is no need
+            //to purge any entries from the Destination or Neighbor Caches
+            ipv6RemovePrefix(interface, &entry->prefix, entry->prefixLength);
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Periodically update Default Router List
+ * @param[in] interface Underlying network interface
+ **/
+
+void ndpUpdateDefaultRouterList(NetInterface *interface)
+{
+   uint_t i;
+   bool_t flag;
+   systime_t time;
+   Ipv6RouterEntry *entry;
+
+   //This flag will be set if any entry has been removed from
+   //the Default Router List
+   flag = FALSE;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Go through the Default Router List
+   for(i = 0; i < IPV6_ROUTER_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.routerList[i];
+
+      //Check the lifetime value
+      if(entry->lifetime > 0 && entry->lifetime < INFINITE_DELAY)
+      {
+         //A node should retain entries in the Default Router List until
+         //their lifetimes expire
+         if(timeCompare(time, entry->timestamp + entry->lifetime) >= 0)
+         {
+            //Immediately time-out the entry
+            entry->addr = IPV6_UNSPECIFIED_ADDR;
+            entry->lifetime = 0;
+
+            //Set flag
+            flag = TRUE;
+         }
+      }
+   }
+
+   //Check whether an entry has been removed from the list
+   if(flag)
+   {
+      //When removing an entry from the Default Router List, any entries
+      //in the Destination Cache that go through that router must perform
+      //next-hop determination again to select a new default router
+      ndpFlushDestCache(interface);
+   }
+}
+
+
+/**
+ * @brief Default Router Selection
+ * @param[in] interface Underlying network interface
+ * @param[in] unreachableAddr IPv6 address of the unreachable router (optional parameter)
+ * @param[out] addr IPv6 address of the default router to be used
+ * @return Error code
+ **/
+
+error_t ndpSelectDefaultRouter(NetInterface *interface,
+   const Ipv6Addr *unreachableAddr, Ipv6Addr *addr)
+{
+   uint_t i;
+   uint_t j;
+   uint_t k;
+   Ipv6RouterEntry *routerEntry;
+   NdpNeighborCacheEntry *neighborCacheEntry;
+
+   //Initialize index
+   i = 0;
+
+   //This parameter is optional...
+   if(unreachableAddr != NULL)
+   {
+      //Search the Default Router List for the router whose reachability is suspect
+      for(j = 0; j < IPV6_ROUTER_LIST_SIZE; j++)
+      {
+         //Point to the current entry
+         routerEntry = &interface->ipv6Context.routerList[j];
+
+         //Check the lifetime associated with the default router
+         if(routerEntry->lifetime)
+         {
+            //Check the router address against the address whose reachability is suspect
+            if(ipv6CompAddr(&routerEntry->addr, unreachableAddr))
+            {
+               //Routers should be selected in a round-robin fashion
+               i = j + 1;
+               //We are done
+               break;
+            }
+         }
+      }
+   }
+
+   //Routers that are reachable or probably reachable should be preferred
+   //over routers whose reachability is unknown or suspect
+   for(j = 0; j < IPV6_ROUTER_LIST_SIZE; j++)
+   {
+      //Get current index
+      k = (i + j) % IPV6_ROUTER_LIST_SIZE;
+
+      //Point to the corresponding entry
+      routerEntry = &interface->ipv6Context.routerList[k];
+
+      //Check the lifetime associated with the default router
+      if(routerEntry->lifetime)
+      {
+         //Search the Neighbor Cache for the router address
+         neighborCacheEntry = ndpFindNeighborCacheEntry(interface, &routerEntry->addr);
+
+         //Check whether the router is reachable or probably reachable
+         if(neighborCacheEntry != NULL)
+         {
+            //Any state other than INCOMPLETE?
+            if(neighborCacheEntry->state != NDP_STATE_INCOMPLETE)
+            {
+               //Return the IPv6 address of the default router
+               *addr = routerEntry->addr;
+               //Successful default router selection
+               return NO_ERROR;
+            }
+         }
+      }
+   }
+
+   //When no routers on the list are known to be reachable or probably
+   //reachable, routers should be selected in a round-robin fashion, so
+   //that subsequent requests for a default router do not return the
+   //same router until all other routers have been selected
+   for(j = 0; j < IPV6_ROUTER_LIST_SIZE; j++)
+   {
+      //Get current index
+      k = (i + j) % IPV6_ROUTER_LIST_SIZE;
+
+      //Point to the corresponding entry
+      routerEntry = &interface->ipv6Context.routerList[k];
+
+      //Check the lifetime associated with the default router
+      if(routerEntry->lifetime)
+      {
+         //Return the IPv6 address of the default router
+         *addr = routerEntry->addr;
+         //Successful default router selection
+         return NO_ERROR;
+      }
+   }
+
+   //No default router found...
+   return ERROR_NO_ROUTE;
+}
+
+
+/**
+ * @brief Check whether an address is the first-hop router for the specified destination
+ * @param[in] interface Underlying network interface
+ * @param[in] destAddr Destination address
+ * @param[in] nextHop First-hop address to be checked
+ * @return TRUE if the address is the first-hop router, else FALSE
+ **/
+
+bool_t ndpIsFirstHopRouter(NetInterface *interface,
+   const Ipv6Addr *destAddr, const Ipv6Addr *nextHop)
+{
+   uint_t i;
+   bool_t isFirstHopRouter;
+   Ipv6RouterEntry *routerEntry;
+   NdpDestCacheEntry *destCacheEntry;
+
+   //Clear flag
+   isFirstHopRouter = FALSE;
+
+   //Search the cache for the specified destination address
+   destCacheEntry = ndpFindDestCacheEntry(interface, destAddr);
+
+   //Any matching entry?
+   if(destCacheEntry != NULL)
+   {
+      //Check if the address is the same as the current first-hop
+      //router for the specified destination
+      if(ipv6CompAddr(&destCacheEntry->nextHop, nextHop))
+         isFirstHopRouter = TRUE;
+   }
+   else
+   {
+      //Loop through the Default Router List
+      for(i = 0; i < IPV6_ROUTER_LIST_SIZE; i++)
+      {
+         //Point to the current entry
+         routerEntry = &interface->ipv6Context.routerList[i];
+
+         //Check the lifetime associated with the default router
+         if(routerEntry->lifetime)
+         {
+            //Check whether the current entry matches the specified address
+            if(ipv6CompAddr(&routerEntry->addr, nextHop))
+            {
+               //The specified address is a valid first-hop router
+               isFirstHopRouter = TRUE;
+               //We are done
+               break;
+            }
+         }
+      }
+   }
+
+   //Return TRUE if the address is the same as the current first-hop
+   //router for the specified destination
+   return isFirstHopRouter;
+}
+
+
+/**
+ * @brief Next-hop determination
+ * @param[in] interface Underlying network interface
+ * @param[in] destAddr Destination address
+ * @param[in] unreachableNextHop Address of the unreachable next-hop (optional parameter)
+ * @param[out] nextHop Next-hop address to be used
+ * @return Error code
+ **/
+
+error_t ndpSelectNextHop(NetInterface *interface, const Ipv6Addr *destAddr,
+   const Ipv6Addr *unreachableNextHop, Ipv6Addr *nextHop)
+{
+   error_t error;
+
+   //Destination IPv6 address is a multicast address?
+   if(ipv6IsMulticastAddr(destAddr))
+   {
+      //For multicast packets, the next-hop is always the (multicast)
+      //destination address and is considered to be on-link
+      *nextHop = *destAddr;
+      //Successful next-hop determination
+      error = NO_ERROR;
+   }
+   else
+   {
+      //The sender performs a longest prefix match against the Prefix
+      //List to determine whether the packet's destination is on-link
+      //or off-link
+      if(ipv6IsOnLink(interface, destAddr))
+      {
+         //If the destination is on-link, the next-hop address is the
+         //same as the packet's destination address
+         *nextHop = *destAddr;
+         //Successful next-hop determination
+         error = NO_ERROR;
+      }
+      else
+      {
+         //If the destination is off-link, the sender selects a router
+         //from the Default Router List
+         error = ndpSelectDefaultRouter(interface, unreachableNextHop, nextHop);
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Update next-hop field of Destination Cache entries
+ * @param[in] interface Underlying network interface
+ * @param[in] unreachableNextHop Address of the unreachable next-hop
+ **/
+
+void ndpUpdateNextHop(NetInterface *interface, const Ipv6Addr *unreachableNextHop)
+{
+   error_t error;
+   uint_t i;
+   NdpDestCacheEntry *entry;
+
+   //Go through Destination Cache
+   for(i = 0; i < NDP_DEST_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ndpContext.destCache[i];
+
+      //Check whether the unreachable IPv6 address is used a first-hop router
+      if(ipv6CompAddr(&entry->nextHop, unreachableNextHop))
+      {
+         //Perform next-hop determination
+         error = ndpSelectNextHop(interface, &entry->destAddr,
+            &entry->nextHop, &entry->nextHop);
+
+         //Next-hop determination failed?
+         if(error)
+         {
+            //Remove the current entry from the Destination Cache
+            entry->destAddr = IPV6_UNSPECIFIED_ADDR;
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Append an option to a NDP message
+ * @param[in] message Pointer to the NDP message
+ * @param[in,out] messageLength Length of the entire message
+ * @param[in] type Option type
+ * @param[in] value Option value
+ * @param[in] length Length of the option value
+ **/
+
+void ndpAddOption(void *message, size_t *messageLength,
+   uint8_t type, const void *value, size_t length)
+{
+   size_t optionLength;
+   size_t paddingLength;
+   NdpOption *option;
+
+   //Length of the option in units of 8 bytes including the type and length fields
+   optionLength = (length + sizeof(NdpOption) + 7) / 8;
+
+   //Sanity check
+   if(optionLength <= UINT8_MAX)
+   {
+      //Point to the buffer where the option is to be written
+      option = (NdpOption *) ((uint8_t *) message + *messageLength);
+
+      //Option type
+      option->type = type;
+      //Option length
+      option->length = (uint8_t) optionLength;
+      //Option value
+      memcpy(option->value, value, length);
+
+      //Options should be padded when necessary to ensure that they end on
+      //their natural 64-bit boundaries
+      if((length + sizeof(NdpOption)) < (optionLength * 8))
+      {
+         //Determine the amount of padding data to append
+         paddingLength = (optionLength * 8) - length - sizeof(NdpOption);
+         //Write padding data
+         memset(option->value + length, 0, paddingLength);
+      }
+
+      //Adjust the length of the NDP message
+      *messageLength += optionLength * 8;
+   }
+}
+
+
+/**
+ * @brief Find a specified option in a NDP message
+ * @param[in] options Pointer to the Options field
+ * @param[in] length Length of the Options field
+ * @param[in] type Type of the option to find
+ * @return If the specified option is found, a pointer to the corresponding
+ *   option is returned. Otherwise NULL pointer is returned
+ **/
+
+void *ndpGetOption(uint8_t *options, size_t length, uint8_t type)
+{
+   size_t i;
+   NdpOption *option;
+
+   //Point to the very first option of the NDP message
+   i = 0;
+
+   //Parse options
+   while((i + sizeof(NdpOption)) <= length)
+   {
+      //Point to the current option
+      option = (NdpOption *) (options + i);
+
+      //Nodes must silently discard an NDP message that contains
+      //an option with length zero
+      if(option->length == 0)
+         break;
+      //Check option length
+      if((i + option->length * 8) > length)
+         break;
+
+      //Current option type matches the specified one?
+      if(option->type == type || type == NDP_OPT_ANY)
+         return option;
+
+      //Jump to next the next option
+      i += option->length * 8;
+   }
+
+   //Specified option type not found
+   return NULL;
+}
+
+
+/**
+ * @brief Check NDP message options
+ * @param[in] options Pointer to the Options field
+ * @param[in] length Length of the Options field
+ * @return Error code
+ **/
+
+error_t ndpCheckOptions(const uint8_t *options, size_t length)
+{
+   size_t i;
+   NdpOption *option;
+
+   //Point to the very first option of the NDP message
+   i = 0;
+
+   //Parse options
+   while((i + sizeof(NdpOption)) <= length)
+   {
+      //Point to the current option
+      option = (NdpOption *) (options + i);
+
+      //Nodes must silently discard an NDP message that contains
+      //an option with length zero
+      if(option->length == 0)
+         return ERROR_INVALID_OPTION;
+
+      //Jump to next the next option
+      i += option->length * 8;
+   }
+
+   //The Options field is valid
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/ndp_misc.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,62 @@
+/**
+ * @file ndp_misc.h
+ * @brief Helper functions for NDP (Neighbor Discovery Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _NDP_MISC_H
+#define _NDP_MISC_H
+
+//Dependencies
+#include "core/net.h"
+#include "ipv6/ndp.h"
+
+//NDP related functions
+void ndpParsePrefixInfoOption(NetInterface *interface, NdpPrefixInfoOption *option);
+
+void ndpUpdateAddrList(NetInterface *interface);
+void ndpUpdatePrefixList(NetInterface *interface);
+void ndpUpdateDefaultRouterList(NetInterface *interface);
+
+error_t ndpSelectDefaultRouter(NetInterface *interface,
+   const Ipv6Addr *unreachableAddr, Ipv6Addr *addr);
+
+bool_t ndpIsFirstHopRouter(NetInterface *interface,
+   const Ipv6Addr *destAddr, const Ipv6Addr *nextHop);
+
+error_t ndpSelectNextHop(NetInterface *interface, const Ipv6Addr *destAddr,
+   const Ipv6Addr *unreachableNextHop, Ipv6Addr *nextHop);
+
+void ndpUpdateNextHop(NetInterface *interface, const Ipv6Addr *nextHop);
+
+void ndpAddOption(void *message, size_t *messageLength,
+   uint8_t type, const void *value, size_t length);
+
+void *ndpGetOption(uint8_t *options, size_t length, uint8_t type);
+
+error_t ndpCheckOptions(const uint8_t *options, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/ndp_router_adv.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,774 @@
+/**
+ * @file ndp_router_adv.c
+ * @brief Router advertisement service
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NDP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "core/ip.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_misc.h"
+#include "ipv6/icmpv6.h"
+#include "ipv6/ndp.h"
+#include "ipv6/ndp_cache.h"
+#include "ipv6/ndp_misc.h"
+#include "ipv6/ndp_router_adv.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV6_SUPPORT == ENABLED && NDP_ROUTER_ADV_SUPPORT == ENABLED)
+
+//Tick counter to handle periodic operations
+systime_t ndpRouterAdvTickCounter;
+
+
+/**
+ * @brief Initialize settings with default values
+ * @param[out] settings Structure that contains the RA service configuration variables
+ **/
+
+void ndpRouterAdvGetDefaultSettings(NdpRouterAdvSettings *settings)
+{
+   //Underlying network interface
+   settings->interface = netGetDefaultInterface();
+
+   //The maximum time allowed between sending unsolicited multicast
+   //Router Advertisements from the interface
+   settings->maxRtrAdvInterval = NDP_MAX_RTR_ADVERT_INTERVAL;
+
+   //The minimum time allowed between sending unsolicited multicast
+   //Router Advertisements from the interface
+   settings->minRtrAdvInterval = NDP_MAX_RTR_ADVERT_INTERVAL / 3;
+
+   //The default value to be placed in the Cur Hop Limit field in the
+   //Router Advertisement messages sent by the router
+   settings->curHopLimit = 0;
+
+   //The value to be placed in the Managed Address Configuration
+   //flag in the Router Advertisement
+   settings->managedFlag = FALSE;
+
+   //The value to be placed in the Other Configuration flag
+   //in the Router Advertisement
+   settings->otherConfigFlag = FALSE;
+
+   //The value to be placed in the Mobile IPv6 Home Agent
+   //flag in the Router Advertisement
+   settings->homeAgentFlag = FALSE;
+
+   //The value to be placed in the Router Selection Preferences
+   //field in the Router Advertisement
+   settings->preference = NDP_ROUTER_SEL_PREFERENCE_MEDIUM;
+
+   //The value to be placed in the Neighbor Discovery Proxy
+   //flag in the Router Advertisement
+   settings->proxyFlag = FALSE;
+
+   //The value to be placed in the Router Lifetime field of
+   //Router Advertisements sent from the interface
+   settings->defaultLifetime = 3 * (NDP_MAX_RTR_ADVERT_INTERVAL / 1000);
+
+   //The value to be placed in the Reachable Time field in the
+   //Router Advertisement messages sent by the router
+   settings->reachableTime = 0;
+
+   //The value to be placed in the Retrans Timer field in the
+   //Router Advertisement messages sent by the router
+   settings->retransTimer = 0;
+
+   //The value to be placed in the MTU option sent by the router
+   settings->linkMtu = 0;
+
+   //A list of prefixes to be placed in Prefix Information options (PIO)
+   //in Router Advertisement messages sent from the interface
+   settings->prefixList = NULL;
+   settings->prefixListLength = 0;
+
+   //A list of routes to be placed in Route Information options (RIO)
+   //in Router Advertisement messages sent from the interface
+   settings->routeList = NULL;
+   settings->routeListLength = 0;
+
+   //A list of header compression contexts to be placed in the 6LoWPAN Context
+   //options (6CO) in Router Advertisement messages sent from the interface
+   settings->contextList = NULL;
+   settings->contextListLength = 0;
+}
+
+
+/**
+ * @brief RA service initialization
+ * @param[in] context Pointer to the RA service context
+ * @param[in] settings RA service configuration variables
+ * @return Error code
+ **/
+
+error_t ndpRouterAdvInit(NdpRouterAdvContext *context, const NdpRouterAdvSettings *settings)
+{
+   NetInterface *interface;
+
+   //Debug message
+   TRACE_INFO("Initializing Router Advertisement service...\r\n");
+
+   //Ensure the parameters are valid
+   if(!context || !settings)
+      return ERROR_INVALID_PARAMETER;
+
+   //Valid network interface?
+   if(!settings->interface)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Point to the underlying network interface
+   interface = settings->interface;
+
+   //Clear the RA service context
+   memset(context, 0, sizeof(NdpRouterAdvContext));
+   //Save user settings
+   context->settings = *settings;
+
+   //The RA service is currently disabled on the interface
+   context->running = FALSE;
+   //Attach the RA service context to the network interface
+   interface->ndpRouterAdvContext = context;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Start RA service
+ * @param[in] context Pointer to the RA service context
+ * @return Error code
+ **/
+
+error_t ndpRouterAdvStart(NdpRouterAdvContext *context)
+{
+   error_t error;
+   NetInterface *interface;
+
+   //Check parameter
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Starting Router Advertisement service...\r\n");
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Check whether the service is running
+   if(!context->running)
+   {
+      //Point to the underlying network interface
+      interface = context->settings.interface;
+
+      //Join the All-Routers multicast address
+      error = ipv6JoinMulticastGroup(interface, &IPV6_LINK_LOCAL_ALL_ROUTERS_ADDR);
+
+      //Successful membership registration?
+      if(!error)
+      {
+         //Reset variables
+         context->timestamp = osGetSystemTime();
+         context->timeout = 0;
+         context->routerAdvCount = 0;
+
+         //Enable the router to forward packets to or from the interface
+         interface->ipv6Context.isRouter = TRUE;
+
+         //Default Hop Limit value
+         if(context->settings.curHopLimit != 0)
+            interface->ipv6Context.curHopLimit = context->settings.curHopLimit;
+
+         //The time a node assumes a neighbor is reachable
+         if(context->settings.reachableTime != 0)
+            interface->ndpContext.reachableTime = context->settings.reachableTime;
+
+         //The time between retransmissions of NS messages
+         if(context->settings.retransTimer != 0)
+            interface->ndpContext.retransTimer = context->settings.retransTimer;
+
+         //Start transmitting Router Advertisements
+         context->running = TRUE;
+      }
+   }
+   else
+   {
+      //The service is already running...
+      error = NO_ERROR;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Stop RA service
+ * @param[in] context Pointer to the RA service context
+ * @return Error code
+ **/
+
+error_t ndpRouterAdvStop(NdpRouterAdvContext *context)
+{
+   error_t error;
+   NetInterface *interface;
+
+   //Check parameter
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Stopping Router Advertisement service...\r\n");
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Check whether the service is running
+   if(context->running)
+   {
+      //Point to the underlying network interface
+      interface = context->settings.interface;
+
+      //The router should transmit one or more final multicast Router
+      //Advertisements with a Router Lifetime field of zero
+      ndpSendRouterAdv(context, 0);
+
+      //Leave the All-Routers multicast address
+      error = ipv6LeaveMulticastGroup(interface, &IPV6_LINK_LOCAL_ALL_ROUTERS_ADDR);
+
+      //Restore default parameters
+      interface->ipv6Context.curHopLimit = IPV6_DEFAULT_HOP_LIMIT;
+      interface->ndpContext.reachableTime = NDP_REACHABLE_TIME;
+      interface->ndpContext.retransTimer = NDP_RETRANS_TIMER;
+
+      //Stop transmitting Router Advertisements
+      context->running = FALSE;
+   }
+   else
+   {
+      //The service is not running...
+      error = NO_ERROR;
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief RA service timer handler
+ * @param[in] context Pointer to the RA service context
+ **/
+
+void ndpRouterAdvTick(NdpRouterAdvContext *context)
+{
+   systime_t time;
+   NetInterface *interface;
+   NdpRouterAdvSettings *settings;
+
+   //Make sure the RA service has been properly instantiated
+   if(context == NULL)
+      return;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+   //Point to the router configuration variables
+   settings = &context->settings;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Make sure that the link is up and the service is running
+   if(interface->linkState && context->running)
+   {
+      //Make sure that a valid link-local address has been assigned to the interface
+      if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED)
+      {
+         //Check current time
+         if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+         {
+            //Send an unsolicited Router Advertisement
+            ndpSendRouterAdv(context, context->settings.defaultLifetime);
+
+            //Save the time at which the message was sent
+            context->timestamp = time;
+
+            //Whenever a multicast advertisement is sent from an interface, the
+            //timer is reset to a uniformly distributed random value between
+            //MinRtrAdvInterval and MaxRtrAdvInterval
+            context->timeout = netGetRandRange(settings->minRtrAdvInterval,
+               settings->maxRtrAdvInterval);
+
+            //First Router Advertisements to be sent from this interface?
+            if(context->routerAdvCount < NDP_MAX_INITIAL_RTR_ADVERTISEMENTS)
+            {
+               //For the first few advertisements sent from an interface when it
+               //becomes an advertising interface, the randomly chosen interval
+               //should not be greater than MAX_INITIAL_RTR_ADVERT_INTERVAL
+               context->timeout = MIN(context->timeout, NDP_MAX_INITIAL_RTR_ADVERT_INTERVAL);
+            }
+
+            //Increment counter
+            context->routerAdvCount++;
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Callback function for link change event
+ * @param[in] context Pointer to the RA service context
+ **/
+
+void ndpRouterAdvLinkChangeEvent(NdpRouterAdvContext *context)
+{
+   NetInterface *interface;
+
+   //Make sure the RA service has been properly instantiated
+   if(context == NULL)
+      return;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Reset variables
+   context->timestamp = osGetSystemTime();
+   context->timeout = 0;
+   context->routerAdvCount = 0;
+
+   //Default Hop Limit value
+   if(context->settings.curHopLimit != 0)
+      interface->ipv6Context.curHopLimit = context->settings.curHopLimit;
+
+   //The time a node assumes a neighbor is reachable
+   if(context->settings.reachableTime != 0)
+      interface->ndpContext.reachableTime = context->settings.reachableTime;
+
+   //The time between retransmissions of NS messages
+   if(context->settings.retransTimer != 0)
+      interface->ndpContext.retransTimer = context->settings.retransTimer;
+}
+
+
+/**
+ * @brief Router Solicitation message processing
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader IPv6 pseudo header
+ * @param[in] buffer Multi-part buffer containing the Router Advertisement message
+ * @param[in] offset Offset to the first byte of the message
+ * @param[in] hopLimit Hop Limit field from IPv6 header
+ **/
+
+void ndpProcessRouterSol(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   const NetBuffer *buffer, size_t offset, uint8_t hopLimit)
+{
+   error_t error;
+   uint_t n;
+   size_t length;
+   systime_t time;
+   systime_t delay;
+   NdpRouterAdvContext *context;
+   NdpRouterSolMessage *message;
+   NdpLinkLayerAddrOption *option;
+   NdpNeighborCacheEntry *entry;
+
+   //Point to the RA service context
+   context = interface->ndpRouterAdvContext;
+
+   //A host must silently discard any received Router Solicitation
+   if(context == NULL)
+      return;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Retrieve the length of the message
+   length = netBufferGetLength(buffer) - offset;
+
+   //Check the length of the Router Solicitation message
+   if(length < sizeof(NdpRouterSolMessage))
+      return;
+
+   //Point to the beginning of the message
+   message = netBufferAt(buffer, offset);
+   //Sanity check
+   if(message == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("Router Solicitation message received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message contents for debugging purpose
+   ndpDumpRouterSolMessage(message);
+
+   //The IPv6 Hop Limit field must have a value of 255 to ensure
+   //that the packet has not been forwarded by a router
+   if(hopLimit != NDP_HOP_LIMIT)
+      return;
+
+   //ICMPv6 Code must be 0
+   if(message->code)
+      return;
+
+   //Calculate the length of the Options field
+   length -= sizeof(NdpRouterSolMessage);
+
+   //Parse Options field
+   error = ndpCheckOptions(message->options, length);
+   //All included options must have a length that is greater than zero
+   if(error)
+      return;
+
+   //Search for the Source Link-Layer Address option
+   option = ndpGetOption(message->options,
+      length, NDP_OPT_SOURCE_LINK_LAYER_ADDR);
+
+   //Source Link-Layer Address option found?
+   if(option != NULL && option->length == 1)
+   {
+      //Debug message
+      TRACE_DEBUG("  Source Link-Layer Address = %s\r\n",
+         macAddrToString(&option->linkLayerAddr, NULL));
+
+      //The Source Link-Layer Address option must not be included when the
+      //source IP address is the unspecified address
+      if(ipv6CompAddr(&pseudoHeader->srcAddr, &IPV6_UNSPECIFIED_ADDR))
+         return;
+
+      //Search the Neighbor Cache for the source address of the solicitation
+      entry = ndpFindNeighborCacheEntry(interface, &pseudoHeader->srcAddr);
+
+      //No matching entry has been found?
+      if(!entry)
+      {
+         //Create an entry
+         entry = ndpCreateNeighborCacheEntry(interface);
+
+         //Neighbor Cache entry successfully created?
+         if(entry)
+         {
+            //Record the IPv6 and the corresponding MAC address
+            entry->ipAddr = pseudoHeader->srcAddr;
+            entry->macAddr = option->linkLayerAddr;
+            //The IsRouter flag must be set to FALSE
+            entry->isRouter = FALSE;
+            //Save current time
+            entry->timestamp = time;
+            //Enter the STALE state
+            entry->state = NDP_STATE_STALE;
+         }
+      }
+      else
+      {
+         //If a Neighbor Cache entry for the solicitation's sender exists
+         //the entry's IsRouter flag must be set to FALSE
+         entry->isRouter = FALSE;
+
+         //INCOMPLETE state?
+         if(entry->state == NDP_STATE_INCOMPLETE)
+         {
+            //Record link-layer address
+            entry->macAddr = option->linkLayerAddr;
+            //Send all the packets that are pending for transmission
+            n = ndpSendQueuedPackets(interface, entry);
+            //Save current time
+            entry->timestamp = time;
+
+            //Check whether any packets have been sent
+            if(n > 0)
+            {
+               //Start delay timer
+               entry->timeout = NDP_DELAY_FIRST_PROBE_TIME;
+               //Switch to the DELAY state
+               entry->state = NDP_STATE_DELAY;
+            }
+            else
+            {
+               //Enter the STALE state
+               entry->state = NDP_STATE_STALE;
+            }
+         }
+         //REACHABLE, STALE, DELAY or PROBE state?
+         else
+         {
+            //Different link-layer address than cached?
+            if(!macCompAddr(&entry->macAddr, &option->linkLayerAddr))
+            {
+               //Update link-layer address
+               entry->macAddr = option->linkLayerAddr;
+               //Save current time
+               entry->timestamp = time;
+               //Enter the STALE state
+               entry->state = NDP_STATE_STALE;
+            }
+         }
+      }
+   }
+
+   //Upon receipt of a Router Solicitation, compute a random delay within the
+   //range 0 through MAX_RA_DELAY_TIME
+   delay = netGetRandRange(0, NDP_MAX_RA_DELAY_TIME);
+
+   //If the computed value corresponds to a time later than the time the next
+   //multicast Router Advertisement is scheduled to be sent, ignore the random
+   //delay and send the advertisement at the already-scheduled time
+   if(timeCompare(time + delay, context->timestamp + context->timeout) > 0)
+      return;
+
+   //Check whether the router sent a multicast Router Advertisement (solicited
+   //or unsolicited) within the last MIN_DELAY_BETWEEN_RAS seconds
+   if(timeCompare(time, context->timestamp + NDP_MIN_DELAY_BETWEEN_RAS) < 0)
+   {
+      //Schedule the advertisement to be sent at a time corresponding to
+      //MIN_DELAY_BETWEEN_RAS plus the random value after the previous
+      //advertisement was sent. This ensures that the multicast Router
+      //Advertisements are rate limited
+      context->timeout = NDP_MIN_DELAY_BETWEEN_RAS + delay;
+   }
+   else
+   {
+      //Schedule the sending of a Router Advertisement at the time given
+      //by the random value
+      context->timeout = time + delay - context->timestamp;
+   }
+}
+
+
+/**
+ * @brief Send a Router Advertisement message
+ * @param[in] context Pointer to the RA service context
+ * @param[in] routerLifetime Router Lifetime field
+ * @return Error code
+ **/
+
+error_t ndpSendRouterAdv(NdpRouterAdvContext *context, uint16_t routerLifetime)
+{
+   error_t error;
+   uint_t i;
+   uint32_t n;
+   size_t offset;
+   size_t length;
+   NetBuffer *buffer;
+   NetInterface *interface;
+   NdpRouterAdvMessage *message;
+   NdpRouterAdvSettings *settings;
+   Ipv6PseudoHeader pseudoHeader;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+   //Point to the router configuration variables
+   settings = &context->settings;
+
+   //The destination address is typically the all-nodes multicast address
+   pseudoHeader.destAddr = IPV6_LINK_LOCAL_ALL_NODES_ADDR;
+
+   //Routers must use their link-local address as the source for Router
+   //Advertisement messages so that hosts can uniquely identify routers
+   error = ipv6SelectSourceAddr(&interface,
+      &pseudoHeader.destAddr, &pseudoHeader.srcAddr);
+
+   //No link-local address assigned to the interface?
+   if(error)
+      return error;
+
+   //Compute the maximum size of the Router Advertisement message
+   length = sizeof(NdpRouterAdvMessage) +
+      sizeof(NdpLinkLayerAddrOption) + sizeof(NdpMtuOption) +
+      settings->prefixListLength * sizeof(NdpPrefixInfoOption) +
+      settings->routeListLength * sizeof(NdpRouteInfoOption) +
+      settings->contextListLength * sizeof(NdpContextOption);
+
+   //Sanity check
+   if((length + sizeof(Ipv6Header)) > IPV6_DEFAULT_MTU)
+      return ERROR_FAILURE;
+
+   //Allocate a memory buffer to hold the Router Advertisement message
+   buffer = ipAllocBuffer(length, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the message
+   message = netBufferAt(buffer, offset);
+
+   //Format Router Advertisement message
+   message->type = ICMPV6_TYPE_ROUTER_ADV;
+   message->code = 0;
+   message->checksum = 0;
+   message->curHopLimit = settings->curHopLimit;
+   message->m = settings->managedFlag;
+   message->o = settings->otherConfigFlag;
+   message->h = settings->homeAgentFlag;
+   message->prf = settings->preference;
+   message->p = settings->proxyFlag;
+   message->reserved = 0;
+   message->routerLifetime = htons(routerLifetime);
+   message->reachableTime = htonl(settings->reachableTime);
+   message->retransTimer = htonl(settings->retransTimer);
+
+   //If the Router Lifetime is zero, the preference value must be
+   //set to zero by the sender
+   if(routerLifetime == 0)
+      message->prf = NDP_ROUTER_SEL_PREFERENCE_MEDIUM;
+
+   //Length of the message, excluding any option
+   length = sizeof(NdpRouterAdvMessage);
+
+#if (ETH_SUPPORT == ENABLED)
+   //Check whether a MAC address has been assigned to the interface
+   if(!macCompAddr(&interface->macAddr, &MAC_UNSPECIFIED_ADDR))
+   {
+      //Add Source Link-Layer Address option
+      ndpAddOption(message, &length, NDP_OPT_SOURCE_LINK_LAYER_ADDR,
+         &interface->macAddr, sizeof(MacAddr));
+   }
+#endif
+
+   //A value of zero indicates that no MTU option is sent
+   if(settings->linkMtu > 0)
+   {
+      NdpMtuOption mtuOption;
+
+      //The MTU option specifies the recommended MTU for the link
+      mtuOption.reserved = 0;
+      mtuOption.mtu = htonl(settings->linkMtu);
+
+      //Add MTU option
+      ndpAddOption(message, &length, NDP_OPT_MTU,
+         (uint8_t *) &mtuOption + sizeof(NdpOption),
+         sizeof(NdpMtuOption) - sizeof(NdpOption));
+   }
+
+   //Loop through the list of IPv6 prefixes
+   for(i = 0; i < settings->prefixListLength; i++)
+   {
+      NdpPrefixInfoOption prefixInfoOption;
+
+      //The Prefix Information option provide hosts with on-link
+      //prefixes and prefixes for Address Autoconfiguration
+      prefixInfoOption.prefixLength = settings->prefixList[i].length;
+      prefixInfoOption.l = settings->prefixList[i].onLinkFlag;
+      prefixInfoOption.a = settings->prefixList[i].autonomousFlag;
+      prefixInfoOption.reserved1 = 0;
+      prefixInfoOption.validLifetime = htonl(settings->prefixList[i].validLifetime);
+      prefixInfoOption.preferredLifetime = htonl(settings->prefixList[i].preferredLifetime);
+      prefixInfoOption.reserved2 = 0;
+      prefixInfoOption.prefix = settings->prefixList[i].prefix;
+
+      //Add Prefix Information option (PIO)
+      ndpAddOption(message, &length, NDP_OPT_PREFIX_INFORMATION,
+         (uint8_t *) &prefixInfoOption + sizeof(NdpOption),
+         sizeof(NdpPrefixInfoOption) - sizeof(NdpOption));
+   }
+
+   //Loop through the list of routes
+   for(i = 0; i < settings->routeListLength; i++)
+   {
+      NdpRouteInfoOption routeInfoOption;
+
+      //The Route Information option specifies prefixes that are
+      //reachable via the router
+      routeInfoOption.prefixLength = settings->routeList[i].length;
+      routeInfoOption.reserved1 = 0;
+      routeInfoOption.prf = settings->routeList[i].preference;
+      routeInfoOption.reserved2 = 0;
+      routeInfoOption.routeLifetime = htonl(settings->routeList[i].routeLifetime);
+      routeInfoOption.prefix = settings->routeList[i].prefix;
+
+      //Add Route Information option (RIO)
+      ndpAddOption(message, &length, NDP_OPT_ROUTE_INFORMATION,
+         (uint8_t *) &routeInfoOption + sizeof(NdpOption),
+         sizeof(NdpRouteInfoOption) - sizeof(NdpOption));
+   }
+
+   //Loop through the list of 6LoWPAN compression contexts
+   for(i = 0; i < settings->contextListLength; i++)
+   {
+      NdpContextOption contextOption;
+
+      //The 6LoWPAN Context option (6CO) carries prefix information for
+      //LoWPAN header compression
+      contextOption.contextLength = settings->contextList[i].length;
+      contextOption.reserved1 = 0;
+      contextOption.c = settings->contextList[i].compression;
+      contextOption.cid = settings->contextList[i].cid;
+      contextOption.reserved2 = 0;
+      contextOption.validLifetime = htons(settings->contextList[i].validLifetime);
+      contextOption.contextPrefix = settings->contextList[i].prefix;
+
+      //Calculate the length of the option in bytes
+      n = sizeof(NdpContextOption) - sizeof(Ipv6Addr) + (contextOption.contextLength / 8);
+
+      //Add 6LoWPAN Context option (6CO)
+      ndpAddOption(message, &length, NDP_OPT_6LOWPAN_CONTEXT,
+         (uint8_t *) &contextOption + sizeof(NdpOption), n - sizeof(NdpOption));
+   }
+
+   //Adjust the length of the multi-part buffer
+   netBufferSetLength(buffer, offset + length);
+
+   //Format IPv6 pseudo header
+   pseudoHeader.length = htonl(length);
+   pseudoHeader.reserved = 0;
+   pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER;
+
+   //Calculate ICMPv6 header checksum
+   message->checksum = ipCalcUpperLayerChecksumEx(&pseudoHeader,
+      sizeof(Ipv6PseudoHeader), buffer, offset, length);
+
+   //Debug message
+   TRACE_INFO("Sending Router Advertisement message (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message contents for debugging purpose
+   ndpDumpRouterAdvMessage(message);
+
+   //Send Router Advertisement message
+   error = ipv6SendDatagram(interface, &pseudoHeader, buffer, offset, NDP_HOP_LIMIT);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/ndp_router_adv.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,153 @@
+/**
+ * @file ndp_router_adv.h
+ * @brief Router advertisement service
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _NDP_ROUTER_ADV_H
+#define _NDP_ROUTER_ADV_H
+
+//Dependencies
+#include "core/net.h"
+#include "ipv6/ipv6.h"
+
+//RA service support
+#ifndef NDP_ROUTER_ADV_SUPPORT
+   #define NDP_ROUTER_ADV_SUPPORT DISABLED
+#elif (NDP_ROUTER_ADV_SUPPORT != ENABLED && NDP_ROUTER_ADV_SUPPORT != DISABLED)
+   #error NDP_ROUTER_ADV_SUPPORT parameter is not valid
+#endif
+
+//RA service tick interval
+#ifndef NDP_ROUTER_ADV_TICK_INTERVAL
+   #define NDP_ROUTER_ADV_TICK_INTERVAL 100
+#elif (NDP_ROUTER_ADV_TICK_INTERVAL < 10)
+   #error NDP_ROUTER_ADV_TICK_INTERVAL parameter is not valid
+#endif
+
+
+/**
+ * @brief IPv6 prefix information
+ **/
+
+typedef struct
+{
+   Ipv6Addr prefix;
+   uint8_t length;
+   bool_t onLinkFlag;
+   bool_t autonomousFlag;
+   uint32_t validLifetime;
+   uint32_t preferredLifetime;
+} NdpRouterAdvPrefixInfo;
+
+
+/**
+ * @brief Route information
+ **/
+
+typedef struct
+{
+   Ipv6Addr prefix;
+   uint8_t length;
+   uint8_t preference;
+   uint32_t routeLifetime;
+} NdpRouterAdvRouteInfo;
+
+
+/**
+ * @brief Context information for 6LoWPAN header compression
+ **/
+
+typedef struct
+{
+   uint8_t cid;
+   Ipv6Addr prefix;
+   uint8_t length;
+   bool_t compression;
+   uint16_t validLifetime;
+} NdpRouterAdvContextInfo;
+
+
+/**
+ * @brief RA service settings
+ **/
+
+typedef struct
+{
+   NetInterface *interface;
+   systime_t maxRtrAdvInterval;
+   systime_t minRtrAdvInterval;
+   uint8_t curHopLimit;
+   bool_t managedFlag;
+   bool_t otherConfigFlag;
+   bool_t homeAgentFlag;
+   uint8_t preference;
+   bool_t proxyFlag;
+   uint16_t defaultLifetime;
+   uint32_t reachableTime;
+   uint32_t retransTimer;
+   uint32_t linkMtu;
+   NdpRouterAdvPrefixInfo *prefixList;
+   uint_t prefixListLength;
+   NdpRouterAdvRouteInfo *routeList;
+   uint_t routeListLength;
+   NdpRouterAdvContextInfo *contextList;
+   uint_t contextListLength;
+} NdpRouterAdvSettings;
+
+
+/**
+ * @brief RA service context
+ **/
+
+typedef struct
+{
+   NdpRouterAdvSettings settings; ///<RA service settings
+   bool_t running;                ///<This flag tells whether the RA service is running
+   systime_t timestamp;           ///<Timestamp to manage retransmissions
+   systime_t timeout;             ///<Timeout value
+   uint_t routerAdvCount;         ///<Router Advertisement message counter
+} NdpRouterAdvContext;
+
+
+//Tick counter to handle periodic operations
+extern systime_t ndpRouterAdvTickCounter;
+
+//RA service related functions
+void ndpRouterAdvGetDefaultSettings(NdpRouterAdvSettings *settings);
+error_t ndpRouterAdvInit(NdpRouterAdvContext *context, const NdpRouterAdvSettings *settings);
+error_t ndpRouterAdvStart(NdpRouterAdvContext *context);
+error_t ndpRouterAdvStop(NdpRouterAdvContext *context);
+
+void ndpRouterAdvTick(NdpRouterAdvContext *context);
+void ndpRouterAdvLinkChangeEvent(NdpRouterAdvContext *context);
+
+void ndpProcessRouterSol(NetInterface *interface, Ipv6PseudoHeader *pseudoHeader,
+   const NetBuffer *buffer, size_t offset, uint8_t hopLimit);
+
+error_t ndpSendRouterAdv(NdpRouterAdvContext *context, uint16_t routerLifetime);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/slaac.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,662 @@
+/**
+ * @file slaac.c
+ * @brief IPv6 Stateless Address Autoconfiguration
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * Stateless Address Autoconfiguration is a facility to allow devices to
+ * configure themselves independently. Refer to the following RFCs for
+ * complete details:
+ * - RFC 4862: IPv6 Stateless Address Autoconfiguration
+ * - RFC 6106: IPv6 Router Advertisement Options for DNS Configuration
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL SLAAC_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "core/ethernet.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_misc.h"
+#include "ipv6/slaac.h"
+#include "ipv6/ndp.h"
+#include "ipv6/ndp_misc.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (IPV6_SUPPORT == ENABLED && SLAAC_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Initialize settings with default values
+ * @param[out] settings Structure that contains SLAAC settings
+ **/
+
+void slaacGetDefaultSettings(SlaacSettings *settings)
+{
+   //Use default interface
+   settings->interface = netGetDefaultInterface();
+
+   //Use the DNS servers specified by the RDNSS option
+   settings->manualDnsConfig = FALSE;
+   //Link state change event
+   settings->linkChangeEvent = NULL;
+   //Router Advertisement parsing callback
+   settings->parseRouterAdvCallback = NULL;
+}
+
+
+/**
+ * @brief SLAAC initialization
+ * @param[in] context Pointer to the SLAAC context
+ * @param[in] settings SLAAC specific settings
+ * @return Error code
+ **/
+
+error_t slaacInit(SlaacContext *context, const SlaacSettings *settings)
+{
+   NetInterface *interface;
+
+   //Debug message
+   TRACE_INFO("Initializing SLAAC...\r\n");
+
+   //Ensure the parameters are valid
+   if(context == NULL || settings == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //A valid pointer to the interface being configured is required
+   if(settings->interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Point to the underlying network interface
+   interface = settings->interface;
+
+   //Clear the SLAAC context
+   memset(context, 0, sizeof(SlaacContext));
+   //Save user settings
+   context->settings = *settings;
+
+   //SLAAC operation is currently suspended
+   context->running = FALSE;
+
+   //Attach the SLAAC context to the network interface
+   interface->slaacContext = context;
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Start SLAAC process
+ * @param[in] context Pointer to the SLAAC context
+ * @return Error code
+ **/
+
+error_t slaacStart(SlaacContext *context)
+{
+   NetInterface *interface;
+
+   //Check parameter
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Starting SLAAC...\r\n");
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Clear the list of IPv6 addresses
+   ipv6FlushAddrList(interface);
+
+   //Automatic DNS server configuration?
+   if(!context->settings.manualDnsConfig)
+   {
+      //Clear the list of DNS servers
+      ipv6FlushDnsServerList(interface);
+   }
+
+   //Check if the link is up?
+   if(interface->linkState)
+   {
+      //A link-local address is formed by combining the well-known
+      //link-local prefix fe80::/10 with the interface identifier
+      slaacGenerateLinkLocalAddr(context);
+   }
+
+   //Start SLAAC operation
+   context->running = TRUE;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Stop SLAAC process
+ * @param[in] context Pointer to the SLAAC context
+ * @return Error code
+ **/
+
+error_t slaacStop(SlaacContext *context)
+{
+   //Check parameter
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Stopping SLAAC...\r\n");
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Suspend SLAAC operation
+   context->running = FALSE;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Callback function for link change event
+ * @param[in] context Pointer to the SLAAC context
+ **/
+
+void slaacLinkChangeEvent(SlaacContext *context)
+{
+   NetInterface *interface;
+
+   //Make sure the SLAAC service has been properly instantiated
+   if(context == NULL)
+      return;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Check whether SLAAC is enabled
+   if(context->running)
+   {
+      //Automatic DNS server configuration?
+      if(!context->settings.manualDnsConfig)
+      {
+         //Clear the list of DNS servers
+         ipv6FlushDnsServerList(interface);
+      }
+
+      //Link-up event?
+      if(interface->linkState)
+      {
+         //A link-local address is formed by combining the well-known
+         //link-local prefix fe80::/10 with the interface identifier
+         slaacGenerateLinkLocalAddr(context);
+      }
+   }
+
+   //Any registered callback?
+   if(context->settings.linkChangeEvent != NULL)
+   {
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+      //Invoke user callback function
+      context->settings.linkChangeEvent(context, interface, interface->linkState);
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+   }
+}
+
+
+/**
+ * @brief Parse Router Advertisement message
+ * @param[in] context Pointer to the SLAAC context
+ * @param[in] message Pointer to the Router Advertisement message
+ * @param[in] length Length of the message, in bytes
+ **/
+
+void slaacParseRouterAdv(SlaacContext *context,
+   NdpRouterAdvMessage *message, size_t length)
+{
+   uint_t i;
+   uint_t n;
+   NetInterface *interface;
+   NdpPrefixInfoOption *prefixInfoOption;
+   NdpRdnssOption *rdnssOption;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Check whether SLAAC is enabled
+   if(!context->running)
+      return;
+
+   //Make sure that a valid link-local address has been assigned to the interface
+   if(ipv6GetLinkLocalAddrState(interface) != IPV6_ADDR_STATE_PREFERRED)
+      return;
+
+   //Calculate the length of the Options field
+   length -= sizeof(NdpRouterAdvMessage);
+
+   //This flag tracks changes in IPv6 configuration
+   context->configUpdated = FALSE;
+
+   //Point to the beginning of the Options field
+   n = 0;
+
+   //Parse Options field
+   while(1)
+   {
+      //Search the Options field for any Prefix Information options
+      prefixInfoOption = ndpGetOption(message->options + n,
+         length - n, NDP_OPT_PREFIX_INFORMATION);
+
+      //No more option of the specified type?
+      if(prefixInfoOption == NULL)
+         break;
+
+      //Parse the Prefix Information Option
+      slaacParsePrefixInfoOption(context, prefixInfoOption);
+
+      //Retrieve the offset to the current position
+      n = (uint8_t *) prefixInfoOption - message->options;
+      //Jump to the next option
+      n += prefixInfoOption->length * 8;
+   }
+
+   //Automatic DNS server configuration?
+   if(!context->settings.manualDnsConfig)
+   {
+      //Search for the Recursive DNS Server (RDNSS) option
+      rdnssOption = ndpGetOption(message->options, length,
+         NDP_OPT_RECURSIVE_DNS_SERVER);
+
+      //RDNSS option found?
+      if(rdnssOption != NULL && rdnssOption->length >= 1)
+      {
+         //Retrieve the number of addresses
+         n = (rdnssOption->length - 1) / 2;
+
+         //Loop through the list of DNS servers
+         for(i = 0; i < n && i < IPV6_DNS_SERVER_LIST_SIZE; i++)
+         {
+            //Record DNS server address
+            interface->ipv6Context.dnsServerList[i] = rdnssOption->address[i];
+         }
+      }
+   }
+
+   //Any registered callback?
+   if(context->settings.parseRouterAdvCallback != NULL)
+   {
+      //Invoke user callback function
+      context->settings.parseRouterAdvCallback(context, message, length);
+   }
+
+   //Check whether a new IPv6 address has been assigned to the interface
+   if(context->configUpdated)
+   {
+      //Dump current IPv6 configuration for debugging purpose
+      slaacDumpConfig(context);
+   }
+}
+
+
+/**
+ * @brief Parse Prefix Information Option
+ * @param[in] context Pointer to the SLAAC context
+ * @param[in] option Pointer to the Prefix Information option
+ **/
+
+void slaacParsePrefixInfoOption(SlaacContext *context,
+   NdpPrefixInfoOption *option)
+{
+   uint_t i;
+   bool_t found;
+   systime_t time;
+   systime_t validLifetime;
+   systime_t preferredLifetime;
+   systime_t remainingLifetime;
+   NetInterface *interface;
+   Ipv6AddrEntry *entry;
+   Ipv6Addr addr;
+
+   //Make sure the Prefix Information option is valid
+   if(option == NULL || option->length != 4)
+      return;
+
+   //If the Autonomous flag is not set, silently ignore the Prefix
+   //Information option
+   if(!option->a)
+      return;
+
+   //If the prefix is the link-local prefix, silently ignore the
+   //Prefix Information option
+   if(ipv6CompPrefix(&option->prefix, &IPV6_LINK_LOCAL_ADDR_PREFIX, 10))
+      return;
+
+   //Check whether the valid lifetime is zero
+   if(ntohl(option->validLifetime) == 0)
+      return;
+
+   //If the preferred lifetime is greater than the valid lifetime,
+   //silently ignore the Prefix Information option
+   if(ntohl(option->preferredLifetime) > ntohl(option->validLifetime))
+      return;
+
+   //If the sum of the prefix length and interface identifier length does
+   //not equal 128 bits, the Prefix Information option must be ignored
+   if(option->prefixLength != 64)
+      return;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Form an address by combining the advertised prefix
+   //with the interface identifier
+   addr.w[0] = option->prefix.w[0];
+   addr.w[1] = option->prefix.w[1];
+   addr.w[2] = option->prefix.w[2];
+   addr.w[3] = option->prefix.w[3];
+   addr.w[4] = interface->eui64.w[0];
+   addr.w[5] = interface->eui64.w[1];
+   addr.w[6] = interface->eui64.w[2];
+   addr.w[7] = interface->eui64.w[3];
+
+   //Convert Valid Lifetime to host byte order
+   validLifetime = ntohl(option->validLifetime);
+
+   //Check the valid lifetime
+   if(validLifetime != NDP_INFINITE_LIFETIME)
+   {
+      //The length of time in seconds that the prefix is valid
+      //for the purpose of on-link determination
+      if(validLifetime < (MAX_DELAY / 1000))
+         validLifetime *= 1000;
+      else
+         validLifetime = MAX_DELAY;
+   }
+   else
+   {
+      //A value of all one bits (0xffffffff) represents infinity
+      validLifetime = INFINITE_DELAY;
+   }
+
+   //Convert Preferred Lifetime to host byte order
+   preferredLifetime = ntohl(option->preferredLifetime);
+
+   //Check the preferred lifetime
+   if(preferredLifetime != NDP_INFINITE_LIFETIME)
+   {
+      //The length of time in seconds that addresses generated from the
+      //prefix via stateless address autoconfiguration remain preferred
+      if(preferredLifetime < (MAX_DELAY / 1000))
+         preferredLifetime *= 1000;
+      else
+         preferredLifetime = MAX_DELAY;
+   }
+   else
+   {
+      //A value of all one bits (0xffffffff) represents infinity
+      preferredLifetime = INFINITE_DELAY;
+   }
+
+   //This flag will be set if the advertised prefix matches an address
+   //assigned to the interface
+   found = FALSE;
+
+   //Loop through the list of IPv6 addresses
+   for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &interface->ipv6Context.addrList[i];
+
+      //Check whether the advertised prefix is equal to the prefix of an
+      //address configured by stateless autoconfiguration in the list
+      if(ipv6CompPrefix(&entry->addr, &option->prefix, option->prefixLength))
+      {
+         //Valid address?
+         if(entry->state == IPV6_ADDR_STATE_PREFERRED ||
+            entry->state == IPV6_ADDR_STATE_DEPRECATED)
+         {
+            //Set flag
+            found = TRUE;
+
+            //The preferred lifetime of the address is reset to the Preferred
+            //Lifetime in the received advertisement
+            entry->preferredLifetime = preferredLifetime;
+
+            //Compute the remaining time to the valid lifetime expiration
+            //of the previously autoconfigured address
+            if(timeCompare(time, entry->timestamp + entry->validLifetime) < 0)
+               remainingLifetime = entry->timestamp + entry->validLifetime - time;
+            else
+               remainingLifetime = 0;
+
+            //The specific action to perform for the valid lifetime of the
+            //address depends on the Valid Lifetime in the received Router
+            //Advertisement and the remaining time
+            if(validLifetime > SLAAC_LIFETIME_2_HOURS ||
+               validLifetime > remainingLifetime)
+            {
+               //If the received Valid Lifetime is greater than 2 hours or
+               //greater than remaining lifetime, set the valid lifetime of
+               //the corresponding address to the advertised Valid Lifetime
+               entry->validLifetime = validLifetime;
+
+               //Save current time
+               entry->timestamp = time;
+               //Update the state of the IPv6 address
+               entry->state = IPV6_ADDR_STATE_PREFERRED;
+            }
+            else if(remainingLifetime <= SLAAC_LIFETIME_2_HOURS)
+            {
+               //If remaining lifetime is less than or equal to 2 hours, ignore
+               //the Prefix Information option with regards to the valid lifetime
+            }
+            else
+            {
+               //Otherwise, reset the valid lifetime of the corresponding
+               //address to 2 hours
+               entry->validLifetime = SLAAC_LIFETIME_2_HOURS;
+
+               //Save current time
+               entry->timestamp = time;
+               //Update the state of the IPv6 address
+               entry->state = IPV6_ADDR_STATE_PREFERRED;
+            }
+         }
+         //Tentative address?
+         else if(entry->state == IPV6_ADDR_STATE_TENTATIVE)
+         {
+            //Do not update the preferred and valid lifetimes of the address
+            //when Duplicate Address Detection is being performed
+            found = TRUE;
+         }
+      }
+   }
+
+   //The IPv6 address is not yet in the list?
+   if(!found)
+   {
+      //Loop through the list of IPv6 addresses
+      for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++)
+      {
+         //Point to the current entry
+         entry = &interface->ipv6Context.addrList[i];
+
+         //Check the state of the IPv6 address
+         if(entry->state == IPV6_ADDR_STATE_INVALID)
+         {
+            //If an address is formed successfully and the address is not yet
+            //in the list, the host adds it to the list of addresses assigned
+            //to the interface, initializing its preferred and valid lifetime
+            //values from the Prefix Information option
+            if(interface->ndpContext.dupAddrDetectTransmits > 0)
+            {
+               //Use the IPv6 address as a tentative address
+               ipv6SetAddr(interface, i, &addr, IPV6_ADDR_STATE_TENTATIVE,
+                  validLifetime, preferredLifetime, FALSE);
+            }
+            else
+            {
+               //The use of the IPv6 address is now unrestricted
+               ipv6SetAddr(interface, i, &addr, IPV6_ADDR_STATE_PREFERRED,
+                  validLifetime, preferredLifetime, FALSE);
+            }
+
+            //A new IPv6 address has just been assigned to the interface
+            context->configUpdated = TRUE;
+            //We are done
+            break;
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Generate a link-local address
+ * @param[in] context Pointer to the SLAAC context
+ * @return Error code
+ **/
+
+error_t slaacGenerateLinkLocalAddr(SlaacContext *context)
+{
+   error_t error;
+   NetInterface *interface;
+   Ipv6Addr addr;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Check whether a link-local address has been manually assigned
+   if(interface->ipv6Context.addrList[0].state != IPV6_ADDR_STATE_INVALID &&
+      interface->ipv6Context.addrList[0].permanent)
+   {
+      //Keep using the current link-local address
+      error = NO_ERROR;
+   }
+   else
+   {
+      //A link-local address is formed by combining the well-known
+      //link-local prefix fe80::/10 with the interface identifier
+      ipv6GenerateLinkLocalAddr(&interface->eui64, &addr);
+
+      //Check whether Duplicate Address Detection should be performed
+      if(interface->ndpContext.dupAddrDetectTransmits > 0)
+      {
+         //Use the link-local address as a tentative address
+         error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_TENTATIVE,
+            NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, FALSE);
+      }
+      else
+      {
+         //The use of the link-local address is now unrestricted
+         error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_PREFERRED,
+            NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, FALSE);
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Dump IPv6 configuration for debugging purpose
+ * @param[in] context Pointer to the SLAAC context
+ **/
+
+void slaacDumpConfig(SlaacContext *context)
+{
+#if (SLAAC_TRACE_LEVEL >= TRACE_LEVEL_INFO)
+   uint_t i;
+   NetInterface *interface;
+   Ipv6Context *ipv6Context;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+   //Point to the IPv6 context
+   ipv6Context = &interface->ipv6Context;
+
+   //Debug message
+   TRACE_INFO("\r\n");
+   TRACE_INFO("SLAAC configuration:\r\n");
+
+   //Link-local address
+   TRACE_INFO("  Link-local Address = %s\r\n",
+      ipv6AddrToString(&ipv6Context->addrList[0].addr, NULL));
+
+   //Global addresses
+   for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++)
+   {
+      TRACE_INFO("  Global Address %u = %s\r\n", i,
+         ipv6AddrToString(&ipv6Context->addrList[i].addr, NULL));
+   }
+
+   //IPv6 prefixes
+   for(i = 0; i < IPV6_PREFIX_LIST_SIZE; i++)
+   {
+      TRACE_INFO("  Prefix %u = %s/%" PRIu8 "\r\n", i + 1,
+         ipv6AddrToString(&ipv6Context->prefixList[i].prefix, NULL),
+         ipv6Context->prefixList[i].prefixLength);
+   }
+
+   //Default routers
+   for(i = 0; i < IPV6_ROUTER_LIST_SIZE; i++)
+   {
+      TRACE_INFO("  Default Router %u = %s\r\n", i + 1,
+         ipv6AddrToString(&ipv6Context->routerList[i].addr, NULL));
+   }
+
+   //DNS servers
+   for(i = 0; i < IPV6_DNS_SERVER_LIST_SIZE; i++)
+   {
+      TRACE_INFO("  DNS Server %u = %s\r\n", i + 1,
+         ipv6AddrToString(&ipv6Context->dnsServerList[i], NULL));
+   }
+
+   //Maximum transmit unit
+   TRACE_INFO("  MTU = %" PRIuSIZE "\r\n", ipv6Context->linkMtu);
+   TRACE_INFO("\r\n");
+#endif
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ipv6/slaac.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,111 @@
+/**
+ * @file slaac.h
+ * @brief IPv6 Stateless Address Autoconfiguration
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SLAAC_H
+#define _SLAAC_H
+
+//Dependencies
+#include "core/net.h"
+#include "ipv6/ndp.h"
+
+//SLAAC support
+#ifndef SLAAC_SUPPORT
+   #define SLAAC_SUPPORT ENABLED
+#elif (SLAAC_SUPPORT != ENABLED && SLAAC_SUPPORT != DISABLED)
+   #error SLAAC_SUPPORT parameter is not valid
+#endif
+
+//Time constant
+#define SLAAC_LIFETIME_2_HOURS (2 * 3600 * 1000)
+
+//Forward declaration of SlaacContext structure
+struct _SlaacContext;
+#define SlaacContext struct _SlaacContext
+
+
+/**
+ * @brief Link state change callback
+ **/
+
+typedef void (*SlaacLinkChangeCallback)(SlaacContext *context,
+   NetInterface *interface, bool_t linkState);
+
+
+/**
+ * @brief Router Advertisement parsing callback
+ **/
+
+typedef void (*SlaacParseRouterAdvCallback)(SlaacContext *context,
+   NdpRouterAdvMessage *message, size_t length);
+
+
+/**
+ * @brief SLAAC settings
+ **/
+
+typedef struct
+{
+   NetInterface *interface;                            ///<Network interface to configure
+   bool_t manualDnsConfig;                             ///<Force manual DNS configuration
+   SlaacLinkChangeCallback linkChangeEvent;            ///<Link state change event
+   SlaacParseRouterAdvCallback parseRouterAdvCallback; ///<Router Advertisement parsing callback
+} SlaacSettings;
+
+
+/**
+ * @brief SLAAC context
+ **/
+
+struct _SlaacContext
+{
+   SlaacSettings settings; ///<SLAAC settings
+   bool_t running;         ///<SLAAC is currently running
+   bool_t configUpdated;   ///<This flag is set when IPv6 configuration has been updated
+};
+
+
+//SLAAC related functions
+void slaacGetDefaultSettings(SlaacSettings *settings);
+error_t slaacInit(SlaacContext *context, const SlaacSettings *settings);
+error_t slaacStart(SlaacContext *context);
+error_t slaacStop(SlaacContext *context);
+
+void slaacLinkChangeEvent(SlaacContext *context);
+
+void slaacParseRouterAdv(SlaacContext *context,
+   NdpRouterAdvMessage *message, size_t length);
+
+void slaacParsePrefixInfoOption(SlaacContext *context,
+   NdpPrefixInfoOption *option);
+
+error_t slaacGenerateLinkLocalAddr(SlaacContext *context);
+
+void slaacDumpConfig(SlaacContext *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mdns/mdns_client.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,345 @@
+/**
+ * @file mdns_client.c
+ * @brief mDNS client (Multicast DNS)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL MDNS_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_misc.h"
+#include "mdns/mdns_client.h"
+#include "mdns/mdns_responder.h"
+#include "mdns/mdns_common.h"
+#include "dns/dns_debug.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (MDNS_CLIENT_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Resolve a host name using mDNS
+ * @param[in] interface Underlying network interface
+ * @param[in] name Name of the host to be resolved
+ * @param[in] type Host type (IPv4 or IPv6)
+ * @param[out] ipAddr IP address corresponding to the specified host name
+ **/
+
+error_t mdnsClientResolve(NetInterface *interface,
+   const char_t *name, HostType type, IpAddr *ipAddr)
+{
+   error_t error;
+   DnsCacheEntry *entry;
+
+#if (NET_RTOS_SUPPORT == ENABLED)
+   systime_t delay;
+
+   //Debug message
+   TRACE_INFO("Resolving host name %s (mDNS resolver)...\r\n", name);
+#endif
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Search the DNS cache for the specified host name
+   entry = dnsFindEntry(interface, name, type, HOST_NAME_RESOLVER_MDNS);
+
+   //Check whether a matching entry has been found
+   if(entry)
+   {
+      //Host name already resolved?
+      if(entry->state == DNS_STATE_RESOLVED ||
+         entry->state == DNS_STATE_PERMANENT)
+      {
+         //Return the corresponding IP address
+         *ipAddr = entry->ipAddr;
+         //Successful host name resolution
+         error = NO_ERROR;
+      }
+      else
+      {
+         //Host name resolution is in progress...
+         error = ERROR_IN_PROGRESS;
+      }
+   }
+   else
+   {
+      //If no entry exists, then create a new one
+      entry = dnsCreateEntry();
+
+      //Record the host name whose IP address is unknown
+      strcpy(entry->name, name);
+
+      //Initialize DNS cache entry
+      entry->type = type;
+      entry->protocol = HOST_NAME_RESOLVER_MDNS;
+      entry->interface = interface;
+
+      //Initialize retransmission counter
+      entry->retransmitCount = MDNS_CLIENT_MAX_RETRIES;
+      //Send mDNS query
+      error = mdnsClientSendQuery(entry);
+
+      //mDNS message successfully sent?
+      if(!error)
+      {
+         //Save the time at which the query message was sent
+         entry->timestamp = osGetSystemTime();
+         //Set timeout value
+         entry->timeout = MDNS_CLIENT_INIT_TIMEOUT;
+         entry->maxTimeout = MDNS_CLIENT_MAX_TIMEOUT;
+         //Decrement retransmission counter
+         entry->retransmitCount--;
+
+         //Switch state
+         entry->state = DNS_STATE_IN_PROGRESS;
+         //Host name resolution is in progress
+         error = ERROR_IN_PROGRESS;
+      }
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+#if (NET_RTOS_SUPPORT == ENABLED)
+   //Set default polling interval
+   delay = DNS_CACHE_INIT_POLLING_INTERVAL;
+
+   //Wait the host name resolution to complete
+   while(error == ERROR_IN_PROGRESS)
+   {
+      //Wait until the next polling period
+      osDelayTask(delay);
+
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+
+      //Search the DNS cache for the specified host name
+      entry = dnsFindEntry(interface, name, type, HOST_NAME_RESOLVER_MDNS);
+
+      //Check whether a matching entry has been found
+      if(entry)
+      {
+         //Host name successfully resolved?
+         if(entry->state == DNS_STATE_RESOLVED)
+         {
+            //Return the corresponding IP address
+            *ipAddr = entry->ipAddr;
+            //Successful host name resolution
+            error = NO_ERROR;
+         }
+      }
+      else
+      {
+         //Host name resolution failed
+         error = ERROR_FAILURE;
+      }
+
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+
+      //Backoff support for less aggressive polling
+      delay = MIN(delay * 2, DNS_CACHE_MAX_POLLING_INTERVAL);
+   }
+
+   //Check status code
+   if(error)
+   {
+      //Failed to resolve host name
+      TRACE_INFO("Host name resolution failed!\r\n");
+   }
+   else
+   {
+      //Successful host name resolution
+      TRACE_INFO("Host name resolved to %s...\r\n", ipAddrToString(ipAddr, NULL));
+   }
+#endif
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send a mDNS query message
+ * @param[in] entry Pointer to a valid DNS cache entry
+ * @return Error code
+ **/
+
+error_t mdnsClientSendQuery(DnsCacheEntry *entry)
+{
+   error_t error;
+   DnsQuestion *dnsQuestion;
+   MdnsMessage message;
+
+   //Create an empty mDNS query message
+   error = mdnsCreateMessage(&message, FALSE);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Encode the host name using the DNS name notation
+   message.length += dnsEncodeName(entry->name, message.dnsHeader->questions);
+
+   //Point to the corresponding question structure
+   dnsQuestion = DNS_GET_QUESTION(message.dnsHeader, message.length);
+
+#if (IPV4_SUPPORT == ENABLED)
+   //An IPv4 address is expected?
+   if(entry->type == HOST_TYPE_IPV4)
+   {
+      //Fill in question structure
+      dnsQuestion->qtype = HTONS(DNS_RR_TYPE_A);
+      dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN);
+   }
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //An IPv6 address is expected?
+   if(entry->type == HOST_TYPE_IPV6)
+   {
+      //Fill in question structure
+      dnsQuestion->qtype = HTONS(DNS_RR_TYPE_AAAA);
+      dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN);
+   }
+#endif
+
+   //Update the length of the mDNS query message
+   message.length += sizeof(DnsQuestion);
+   //Number of questions in the Question Section
+   message.dnsHeader->qdcount = 1;
+
+   //Send mDNS message
+   error = mdnsSendMessage(entry->interface, &message, NULL, MDNS_PORT);
+
+   //Free previously allocated memory
+   mdnsDeleteMessage(&message);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse a resource record from the Answer Section
+ * @param[in] interface Underlying network interface
+ * @param[in] message Pointer to the mDNS message
+ * @param[in] offset Offset to first byte of the resource record
+ * @param[in] record Pointer to the resource record
+ **/
+
+void mdnsClientParseAnRecord(NetInterface *interface,
+   const MdnsMessage *message, size_t offset, const DnsResourceRecord *record)
+{
+   uint_t i;
+   uint16_t rclass;
+   DnsCacheEntry *entry;
+
+   //Loop through DNS cache entries
+   for(i = 0; i < DNS_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &dnsCache[i];
+
+      //mDNS name resolution in progress?
+      if(entry->state == DNS_STATE_IN_PROGRESS &&
+         entry->protocol == HOST_NAME_RESOLVER_MDNS)
+      {
+         //Compare resource record name
+         if(!dnsCompareName(message->dnsHeader, message->length, offset, entry->name, 0))
+         {
+            //Convert the class to host byte order
+            rclass = ntohs(record->rclass);
+            //Discard Cache Flush flag
+            rclass &= ~MDNS_RCLASS_CACHE_FLUSH;
+
+            //Check the class of the resource record
+            if(rclass == DNS_RR_CLASS_IN)
+            {
+#if (IPV4_SUPPORT == ENABLED)
+               //IPv4 address expected?
+               if(entry->type == HOST_TYPE_IPV4)
+               {
+                  //A resource record found?
+                  if(ntohs(record->rtype) == DNS_RR_TYPE_A)
+                  {
+                     //Verify the length of the data field
+                     if(ntohs(record->rdlength) == sizeof(Ipv4Addr))
+                     {
+                        //Copy the IPv4 address
+                        entry->ipAddr.length = sizeof(Ipv4Addr);
+                        ipv4CopyAddr(&entry->ipAddr.ipv4Addr, record->rdata);
+
+                        //Save current time
+                        entry->timestamp = osGetSystemTime();
+                        //Save TTL value
+                        entry->timeout = ntohl(record->ttl) * 1000;
+                        //Limit the lifetime of the mDNS cache entries
+                        entry->timeout = MIN(entry->timeout, MDNS_MAX_LIFETIME);
+
+                        //Host name successfully resolved
+                        entry->state = DNS_STATE_RESOLVED;
+                     }
+                  }
+               }
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+               //IPv6 address expected?
+               if(entry->type == HOST_TYPE_IPV6)
+               {
+                  //AAAA resource record found?
+                  if(ntohs(record->rtype) == DNS_RR_TYPE_AAAA)
+                  {
+                     //Verify the length of the data field
+                     if(ntohs(record->rdlength) == sizeof(Ipv6Addr))
+                     {
+                        //Copy the IPv6 address
+                        entry->ipAddr.length = sizeof(Ipv6Addr);
+                        ipv6CopyAddr(&entry->ipAddr.ipv6Addr, record->rdata);
+
+                        //Save current time
+                        entry->timestamp = osGetSystemTime();
+                        //Save TTL value
+                        entry->timeout = ntohl(record->ttl) * 1000;
+                        //Limit the lifetime of the mDNS cache entries
+                        entry->timeout = MIN(entry->timeout, MDNS_MAX_LIFETIME);
+
+                        //Host name successfully resolved
+                        entry->state = DNS_STATE_RESOLVED;
+                     }
+                  }
+               }
+#endif
+            }
+         }
+      }
+   }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mdns/mdns_client.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,84 @@
+/**
+ * @file mdns_client.h
+ * @brief mDNS client (Multicast DNS)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MDNS_CLIENT_H
+#define _MDNS_CLIENT_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/socket.h"
+#include "core/udp.h"
+#include "dns/dns_cache.h"
+#include "dns/dns_common.h"
+
+//mDNS client support
+#ifndef MDNS_CLIENT_SUPPORT
+   #define MDNS_CLIENT_SUPPORT DISABLED
+#elif (MDNS_CLIENT_SUPPORT != ENABLED && MDNS_CLIENT_SUPPORT != DISABLED)
+   #error MDNS_CLIENT_SUPPORT parameter is not valid
+#endif
+
+//Maximum number of retransmissions of mDNS queries
+#ifndef MDNS_CLIENT_MAX_RETRIES
+   #define MDNS_CLIENT_MAX_RETRIES 3
+#elif (MDNS_CLIENT_MAX_RETRIES < 1)
+   #error MDNS_CLIENT_MAX_RETRIES parameter is not valid
+#endif
+
+//Initial retransmission timeout
+#ifndef MDNS_CLIENT_INIT_TIMEOUT
+   #define MDNS_CLIENT_INIT_TIMEOUT 1000
+#elif (MDNS_CLIENT_INIT_TIMEOUT < 1000)
+   #error MDNS_CLIENT_INIT_TIMEOUT parameter is not valid
+#endif
+
+//Maximum retransmission timeout
+#ifndef MDNS_CLIENT_MAX_TIMEOUT
+   #define MDNS_CLIENT_MAX_TIMEOUT 1000
+#elif (MDNS_CLIENT_MAX_TIMEOUT < 1000)
+   #error MDNS_CLIENT_MAX_TIMEOUT parameter is not valid
+#endif
+
+//Maximum cache lifetime for mDNS entries
+#ifndef MDNS_MAX_LIFETIME
+   #define MDNS_MAX_LIFETIME 60000
+#elif (MDNS_MAX_LIFETIME < 1000)
+   #error MDNS_MAX_LIFETIME parameter is not valid
+#endif
+
+//mDNS related functions
+error_t mdnsClientResolve(NetInterface *interface,
+   const char_t *name, HostType type, IpAddr *ipAddr);
+
+error_t mdnsClientSendQuery(DnsCacheEntry *entry);
+
+void mdnsClientParseAnRecord(NetInterface *interface,
+   const MdnsMessage *message, size_t offset, const DnsResourceRecord *record);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mdns/mdns_common.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,976 @@
+/**
+ * @file mdns_common.c
+ * @brief Functions common to mDNS client and mDNS responder
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * Multicast DNS and its companion technology DNS-Based Service Discovery
+ * were created to provide ease-of-use and autoconfiguration to IP networks.
+ * Refer to the following RFCs for complete details:
+ * - RFC 6762: Multicast DNS
+ * - RFC 6763: DNS-Based Service Discovery
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL MDNS_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "core/net.h"
+#include "ipv6/ipv6_misc.h"
+#include "mdns/mdns_client.h"
+#include "mdns/mdns_responder.h"
+#include "mdns/mdns_common.h"
+#include "dns/dns_debug.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (MDNS_CLIENT_SUPPORT == ENABLED || MDNS_RESPONDER_SUPPORT == ENABLED)
+
+#if (IPV6_SUPPORT == ENABLED)
+
+//mDNS IPv6 multicast group (ff02::fb)
+const Ipv6Addr MDNS_IPV6_MULTICAST_ADDR =
+   IPV6_ADDR(0xFF02, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00FB);
+
+#endif
+
+
+/**
+ * @brief mDNS related initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t mdnsInit(NetInterface *interface)
+{
+   error_t error;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //Join the mDNS IPv4 multicast group
+   error = ipv4JoinMulticastGroup(interface, MDNS_IPV4_MULTICAST_ADDR);
+   //Any error to report?
+   if(error)
+      return error;
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+   //Join the mDNS IPv6 multicast group
+   error = ipv6JoinMulticastGroup(interface, &MDNS_IPV6_MULTICAST_ADDR);
+   //Any error to report?
+   if(error)
+      return error;
+#endif
+
+   //Callback function to be called when a mDNS message is received
+   error = udpAttachRxCallback(interface, MDNS_PORT, mdnsProcessMessage, NULL);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process incoming mDNS message
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader UDP pseudo header
+ * @param[in] udpHeader UDP header
+ * @param[in] buffer Multi-part buffer containing the incoming mDNS message
+ * @param[in] offset Offset to the first byte of the mDNS message
+ * @param[in] params Callback function parameter (not used)
+ **/
+
+void mdnsProcessMessage(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
+   const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, void *params)
+{
+   size_t length;
+   DnsHeader *dnsHeader;
+   MdnsMessage message;
+
+   //Retrieve the length of the mDNS message
+   length = netBufferGetLength(buffer) - offset;
+
+   //Ensure the mDNS message is valid
+   if(length < sizeof(DnsHeader))
+      return;
+   if(length > MDNS_MESSAGE_MAX_SIZE)
+      return;
+
+   //Point to the mDNS message header
+   dnsHeader = netBufferAt(buffer, offset);
+   //Sanity check
+   if(dnsHeader == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("mDNS message received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message
+   dnsDumpMessage(dnsHeader, length);
+
+   //mDNS messages received with an opcode other than zero must be silently ignored
+   if(dnsHeader->opcode != DNS_OPCODE_QUERY)
+      return;
+   //mDNS messages received with non-zero response codes must be silently ignored
+   if(dnsHeader->rcode != DNS_RCODE_NO_ERROR)
+      return;
+
+   //Save mDNS message
+   message.buffer = (NetBuffer *) buffer;
+   message.offset = offset;
+   message.length = length;
+   message.pseudoHeader = pseudoHeader;
+   message.udpHeader = udpHeader;
+   message.dnsHeader = dnsHeader;
+
+   //mDNS query received?
+   if(!dnsHeader->qr)
+   {
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+      //Process incoming mDNS query message
+      mdnsResponderProcessQuery(interface, &message);
+#endif
+   }
+   //mDNS response received?
+   else
+   {
+      //Process incoming mDNS response message
+      mdnsProcessResponse(interface, &message);
+   }
+}
+
+
+/**
+ * @brief Process mDNS response message
+ * @param[in] interface Underlying network interface
+ * @param[in] response Incoming mDNS response message
+ **/
+
+void mdnsProcessResponse(NetInterface *interface, MdnsMessage *response)
+{
+   uint_t i;
+   uint_t k;
+   size_t n;
+   size_t offset;
+   DnsResourceRecord *record;
+
+   //Source address check (refer to RFC 6762 section 11)
+   if(!mdnsCheckSourceAddr(interface, response->pseudoHeader))
+      return;
+
+   //mDNS implementations must silently ignore any mDNS responses they
+   //receive where the source UDP port is not 5353
+   if(ntohs(response->udpHeader->srcPort) != MDNS_PORT)
+      return;
+
+   //Point to the question section
+   offset = sizeof(DnsHeader);
+
+   //Any questions in the question section of a received mDNS response
+   //must be silently ignored
+   for(i = 0; i < ntohs(response->dnsHeader->qdcount); i++)
+   {
+      //Parse domain name
+      offset = dnsParseName(response->dnsHeader, response->length, offset, NULL, 0);
+      //Invalid name?
+      if(!offset)
+         break;
+
+      //Point to the next question
+      offset += sizeof(DnsQuestion);
+      //Make sure the mDNS message is valid
+      if(offset > response->length)
+         break;
+   }
+
+   //Malformed mDNS message?
+   if(i != ntohs(response->dnsHeader->qdcount))
+      return;
+
+   //Compute the total number of resource records
+   k = ntohs(response->dnsHeader->ancount) +
+      ntohs(response->dnsHeader->nscount) +
+      ntohs(response->dnsHeader->arcount);
+
+   //Loop through the resource records
+   for(i = 0; i < k; i++)
+   {
+      //Parse resource record name
+      n = dnsParseName(response->dnsHeader, response->length, offset, NULL, 0);
+      //Invalid name?
+      if(!n)
+         break;
+
+      //Point to the associated resource record
+      record = DNS_GET_RESOURCE_RECORD(response->dnsHeader, n);
+      //Point to the resource data
+      n += sizeof(DnsResourceRecord);
+
+      //Make sure the resource record is valid
+      if(n > response->length)
+         break;
+      if((n + ntohs(record->rdlength)) > response->length)
+         break;
+
+#if (MDNS_CLIENT_SUPPORT == ENABLED)
+      //Parse the resource record
+      mdnsClientParseAnRecord(interface, response, offset, record);
+#endif
+
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+      //Parse the resource record
+      mdnsResponderParseAnRecord(interface, response, offset, record);
+#endif
+
+#if (DNS_SD_SUPPORT == ENABLED)
+      //Parse the resource record
+      dnsSdParseAnRecord(interface, response, offset, record);
+#endif
+
+      //Point to the next resource record
+      offset = n + ntohs(record->rdlength);
+   }
+}
+
+
+/**
+ * @brief Source address check
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader UDP pseudo header
+ * @return TRUE if the source address is valid, else FALSE
+ **/
+
+bool_t mdnsCheckSourceAddr(NetInterface *interface,
+   const IpPseudoHeader *pseudoHeader)
+{
+   bool_t valid;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //IPv4 packet received?
+   if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
+   {
+      //Perform source address check (refer to RFC 6762 section 11)
+      if(pseudoHeader->ipv4Data.destAddr == MDNS_IPV4_MULTICAST_ADDR)
+      {
+         //All responses received with the destination address 224.0.0.251
+         //are necessarily deemed to have originated on the local link,
+         //regardless of source IP address
+         valid = TRUE;
+      }
+      else if(ipv4IsLinkLocalAddr(pseudoHeader->ipv4Data.srcAddr) ||
+         ipv4IsLinkLocalAddr(pseudoHeader->ipv4Data.destAddr))
+      {
+         //Packets with a link-local source or destination address
+         //originate from the local link
+         valid = TRUE;
+      }
+      else if(ipv4IsOnLocalSubnet(interface, pseudoHeader->ipv4Data.srcAddr))
+      {
+         //The source IP address is on the local subnet
+         valid = TRUE;
+      }
+      else
+      {
+         //Only accept responses that originate from the local link, and
+         //silently discard any other response packets
+         valid = FALSE;
+      }
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 packet received?
+   if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
+   {
+      //Perform source address check (refer to RFC 6762 section 11)
+      if(ipv6CompAddr(&pseudoHeader->ipv6Data.destAddr, &MDNS_IPV6_MULTICAST_ADDR))
+      {
+         //All responses received with the destination address ff02::fb
+         //are necessarily deemed to have originated on the local link,
+         //regardless of source IP address
+         valid = TRUE;
+      }
+      else if(ipv6IsOnLink(interface, &pseudoHeader->ipv6Data.srcAddr))
+      {
+         //The source IP address is on the local link
+         valid = TRUE;
+      }
+      else
+      {
+         //Only accept responses that originate from the local link, and
+         //silently discard any other response packets
+         valid = FALSE;
+      }
+   }
+   else
+#endif
+   //Invalid packet received?
+   {
+      //Discard the response packet
+      valid = FALSE;
+   }
+
+   //Return flag value
+   return valid;
+}
+
+
+/**
+ * @brief Create an empty mDNS message
+ * @param[in,out] message Newly created mDNS message
+ * @param[in] queryResponse This flag specifies whether the message is a query or a response
+ * @return Error code
+ **/
+
+error_t mdnsCreateMessage(MdnsMessage *message, bool_t queryResponse)
+{
+   error_t error;
+
+   //Allocate a memory buffer to hold the mDNS message
+   message->buffer = udpAllocBuffer(MDNS_MESSAGE_MAX_SIZE, &message->offset);
+
+   //Successful memory allocation?
+   if(message->buffer != NULL)
+   {
+      //Point to the mDNS message header
+      message->dnsHeader = netBufferAt(message->buffer, message->offset);
+
+      //Sanity check
+      if(message->dnsHeader != NULL)
+      {
+         //Format mDNS message header
+         message->dnsHeader->id = 0;
+         message->dnsHeader->opcode = DNS_OPCODE_QUERY;
+         message->dnsHeader->tc = 0;
+         message->dnsHeader->rd = 0;
+         message->dnsHeader->ra = 0;
+         message->dnsHeader->z = 0;
+         message->dnsHeader->rcode = DNS_RCODE_NO_ERROR;
+         message->dnsHeader->qdcount = 0;
+         message->dnsHeader->ancount = 0;
+         message->dnsHeader->nscount = 0;
+         message->dnsHeader->arcount = 0;
+
+         //Query or response mDNS message?
+         if(!queryResponse)
+         {
+            //In query messages, QR and AA bits must be set to zero
+            message->dnsHeader->qr = 0;
+            message->dnsHeader->aa = 0;
+         }
+         else
+         {
+            //In response messages, QR and AA bits must be set to one
+            message->dnsHeader->qr = 1;
+            message->dnsHeader->aa = 1;
+         }
+
+         //Number of shared resource records
+         message->sharedRecordCount = 0;
+         //Length of the mDNS message
+         message->length = sizeof(DnsHeader);
+
+         //Successful processing
+         error = NO_ERROR;
+      }
+      else
+      {
+         //Clean up side effects
+         mdnsDeleteMessage(message);
+
+         //Report an error
+         error = ERROR_FAILURE;
+      }
+   }
+   else
+   {
+      //Failed to allocate memory
+      error = ERROR_OUT_OF_RESOURCES;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief release a mDNS message
+ * @param[in] message mDNS message to be released
+ **/
+
+void mdnsDeleteMessage(MdnsMessage *message)
+{
+   //Valid mDNS message?
+   if(message->buffer != NULL)
+   {
+      //Free previously allocated memory
+      netBufferFree(message->buffer);
+
+      //The mDNS message is no more valid
+      message->buffer = NULL;
+      message->length = 0;
+   }
+}
+
+
+/**
+ * @brief Send mDNS message
+ * @param[in] interface Underlying network interface
+ * @param[in] message mDNS message to be sent
+ * @param[in] destIpAddr Destination IP address (optional parameter)
+ * @param[in] destPort Destination port
+ * @return Error code
+ **/
+
+error_t mdnsSendMessage(NetInterface *interface, const MdnsMessage *message,
+   const IpAddr *destIpAddr, uint_t destPort)
+{
+   error_t error;
+   IpAddr ipAddr;
+
+   //Make sure the mDNS message is valid
+   if(message->buffer == NULL)
+      return ERROR_FAILURE;
+
+   //Convert 16-bit values to network byte order
+   message->dnsHeader->qdcount = htons(message->dnsHeader->qdcount);
+   message->dnsHeader->nscount = htons(message->dnsHeader->nscount);
+   message->dnsHeader->ancount = htons(message->dnsHeader->ancount);
+   message->dnsHeader->arcount = htons(message->dnsHeader->arcount);
+
+   //Start of exception handling block
+   do
+   {
+      //Adjust the length of the multi-part buffer
+      error = netBufferSetLength(message->buffer, message->offset + message->length);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Debug message
+      TRACE_INFO("Sending mDNS message (%" PRIuSIZE " bytes)...\r\n", message->length);
+      //Dump message
+      dnsDumpMessage(message->dnsHeader, message->length);
+
+      //Check whether the message should be sent to a specific IP address
+      if(destIpAddr != NULL)
+      {
+         //All multicast DNS responses should be sent with an IP TTL set to 255
+         error = udpSendDatagramEx(interface, MDNS_PORT, destIpAddr,
+            destPort, message->buffer, message->offset, MDNS_DEFAULT_IP_TTL);
+         //Any error to report?
+         if(error)
+            break;
+      }
+      else
+      {
+#if (IPV4_SUPPORT == ENABLED)
+         //Select the relevant multicast address (224.0.0.251)
+         ipAddr.length = sizeof(Ipv4Addr);
+         ipAddr.ipv4Addr = MDNS_IPV4_MULTICAST_ADDR;
+
+         //All multicast DNS queries should be sent with an IP TTL set to 255
+         error = udpSendDatagramEx(interface, MDNS_PORT, &ipAddr,
+            MDNS_PORT, message->buffer, message->offset, MDNS_DEFAULT_IP_TTL);
+         //Any error to report?
+         if(error)
+            break;
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+         //Select the relevant multicast address (ff02::fb)
+         ipAddr.length = sizeof(Ipv6Addr);
+         ipAddr.ipv6Addr = MDNS_IPV6_MULTICAST_ADDR;
+
+         //All multicast DNS queries should be sent with an IP TTL set to 255
+         error = udpSendDatagramEx(interface, MDNS_PORT, &ipAddr,
+            MDNS_PORT, message->buffer, message->offset, MDNS_DEFAULT_IP_TTL);
+         //Any error to report?
+         if(error)
+            break;
+#endif
+      }
+
+      //End of exception handling block
+   } while(0);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Encode instance, service and domain names using the DNS name notation
+ * @param[in] instance Instance name
+ * @param[in] service Service name
+ * @param[in] domain Domain name
+ * @param[out] dest Pointer to the encoded name (optional parameter)
+ * @return Length of the encoded domain name
+ **/
+
+size_t mdnsEncodeName(const char_t *instance, const char_t *service,
+   const char_t *domain, uint8_t *dest)
+{
+   size_t n;
+   size_t length;
+
+   //Total length of the encoded name
+   length = 0;
+
+   //Any instance name?
+   if(*instance != '\0')
+   {
+      //Encode instance name
+      n = dnsEncodeName(instance, dest);
+
+      //Failed to encode instance name?
+      if(!n)
+         return 0;
+
+      //Update the length of the encoded name
+      length += n;
+   }
+
+   //Any service name?
+   if(*service != '\0')
+   {
+      //If an instance name precedes the service name, then
+      //remove the null label
+      if(length > 0)
+         length--;
+
+      //Encode service name
+      if(dest != NULL)
+         n = dnsEncodeName(service, dest + length);
+      else
+         n = dnsEncodeName(service, NULL);
+
+      //Failed to encode instance name?
+      if(!n)
+         return 0;
+
+      //Update the length of the encoded name
+      length += n;
+   }
+
+   //Skip the separator that may precede the domain name
+   if(*domain == '.')
+      domain++;
+
+   //Any domain name to encode?
+   if(*domain != '\0')
+   {
+      //If an instance or a service name precedes the domain name, then
+      //remove the null label
+      if(length > 0)
+         length--;
+
+      //Encode domain name
+      if(dest != NULL)
+         n = dnsEncodeName(domain, dest + length);
+      else
+         n = dnsEncodeName(domain, NULL);
+
+      //Failed to encode instance name?
+      if(!n)
+         return 0;
+
+      //Update the length of the encoded name
+      length += n;
+   }
+
+   //Return the length of the encoded string
+   return length;
+}
+
+
+/**
+ * @brief Compare instance, service and domain names
+ * @param[in] message Pointer to the DNS message
+ * @param[in] length Length of the DNS message
+ * @param[in] pos Offset of the encoded name
+ * @param[in] instance Instance name
+ * @param[in] service Service name
+ * @param[in] domain Domain name
+ * @param[in] level Current level of recursion
+ * @return The function returns 0 if the domain names match, -1 if the first
+ *   domain name lexicographically precedes the second name, or 1 if the
+ *   second domain name lexicographically precedes the first name
+ **/
+
+int_t mdnsCompareName(const DnsHeader *message, size_t length, size_t pos,
+   const char_t *instance, const char_t *service, const char_t *domain, uint_t level)
+{
+   int_t res;
+   size_t n;
+   size_t pointer;
+   uint8_t *p;
+
+   //Check parameters
+   if(instance == NULL || service == NULL || domain == NULL)
+      return -2;
+
+   //Recursion limit exceeded?
+   if(level >= DNS_NAME_MAX_RECURSION)
+      return -2;
+
+   //Cast the DNS message to byte array
+   p = (uint8_t *) message;
+
+   //Skip the separator that may precede the domain name
+   if(*domain == '.')
+      domain++;
+
+   //Parse encoded domain name
+   while(pos < length)
+   {
+      //Retrieve the length of the current label
+      n = p[pos];
+
+      //End marker found?
+      if(n == 0)
+      {
+         //The domain name which still has remaining data is deemed
+         //lexicographically later
+         if(*instance != '\0' || *service != '\0' || *domain != '\0')
+            return -1;
+
+         //The domain names match each other
+         return 0;
+      }
+      //Compression tag found?
+      if(n >= DNS_COMPRESSION_TAG)
+      {
+         //Malformed DNS message?
+         if((pos + 1) >= length)
+            return -2;
+
+         //Read the most significant byte of the pointer
+         pointer = (p[pos] & ~DNS_COMPRESSION_TAG) << 8;
+         //Read the least significant byte of the pointer
+         pointer |= p[pos + 1];
+
+         //Compare the remaining part
+         res = mdnsCompareName(message, length, pointer,
+            instance, service, domain, level + 1);
+
+         //Return comparison result
+         return res;
+      }
+      else
+      {
+         //Advance data pointer
+         pos++;
+
+         //Malformed DNS message?
+         if((pos + n) > length)
+            return -2;
+
+         //Compare current label
+         if(*instance != '\0')
+         {
+            //Compare instance name
+            res = strncasecmp((char_t *) p + pos, instance, n);
+            //Any mismatch?
+            if(res)
+               return res;
+
+            //Advance data pointer
+            instance += n;
+
+            //The instance name which still has remaining data is deemed
+            //lexicographically later
+            if(*instance != '\0' && *instance != '.')
+               return -1;
+
+            //Skip the separator character, if any
+            if(*instance == '.')
+               instance++;
+         }
+         else if(*service != '\0')
+         {
+            //Compare service name
+            res = strncasecmp((char_t *) p + pos, service, n);
+            //Any mismatch?
+            if(res)
+               return res;
+
+            //Advance data pointer
+            service += n;
+
+            //The service name which still has remaining data is deemed
+            //lexicographically later
+            if(*service != '\0' && *service != '.')
+               return -1;
+
+            //Any separator in service name?
+            if(*service == '.')
+               service++;
+         }
+         else
+         {
+            //Compare domain name
+            res = strncasecmp((char_t *) p + pos, domain, n);
+            //Any mismatch?
+            if(res)
+               return res;
+
+            //Advance data pointer
+            domain += n;
+
+            //The domain name which still has remaining data is deemed
+            //lexicographically later
+            if(*domain != '\0' && *domain != '.')
+               return -1;
+
+            //Any separator in domain name?
+            if(*domain == '.')
+               domain++;
+         }
+
+         //Advance data pointer
+         pos += n;
+      }
+   }
+
+   //Malformed DNS message
+   return -2;
+}
+
+
+/**
+ * @brief Compare resource records
+ * @param[in] message1 Pointer to the first mDNS message
+ * @param[in] offset1 Offset of the first but of the resource record
+ * @param[in] record1 Pointer the first resource record
+ * @param[in] message2 Pointer to the second mDNS message
+ * @param[in] offset2 Offset of the first but of the resource record
+ * @param[in] record2 Pointer the second resource record
+ * @return The function returns 0 if the resource records match, -1 if the first
+ *   resource record lexicographically precedes the second one, or 1 if the
+ *   second resource record lexicographically precedes the first one
+ **/
+
+int_t mdnsCompareRecord(const MdnsMessage *message1, size_t offset1,
+   const DnsResourceRecord *record1, const MdnsMessage *message2,
+   size_t offset2, const DnsResourceRecord *record2)
+{
+   int_t res;
+   size_t n1;
+   size_t n2;
+   uint16_t value1;
+   uint16_t value2;
+
+   //Convert the record class to host byte order
+   value1 = ntohs(record1->rclass);
+   value2 = ntohs(record2->rclass);
+
+   //Discard cache-flush bit
+   value1 &= ~MDNS_RCLASS_CACHE_FLUSH;
+   value2 &= ~MDNS_RCLASS_CACHE_FLUSH;
+
+   //The determination of lexicographically later record is performed by
+   //first comparing the record class (excluding the cache-flush bit)
+   if(value1 < value2)
+      return -1;
+   else if(value1 > value2)
+      return 1;
+
+   //Convert the record type to host byte order
+   value1 = ntohs(record1->rtype);
+   value2 = ntohs(record2->rtype);
+
+   //Then compare the record type
+   if(value1 < value2)
+      return -1;
+   else if(value1 > value2)
+      return 1;
+
+   //If the rrtype and rrclass both match, then the rdata is compared
+   if(value1 == DNS_RR_TYPE_NS || value1 == DNS_RR_TYPE_SOA ||
+      value1 == DNS_RR_TYPE_CNAME || value1 == DNS_RR_TYPE_PTR)
+   {
+      //Compute the offset of the first byte of the rdata
+      n1 = record1->rdata - (uint8_t *) message1->dnsHeader;
+      n2 = record2->rdata - (uint8_t *) message2->dnsHeader;
+
+      //The names must be uncompressed before comparison
+      res = dnsCompareEncodedName(message1->dnsHeader, message1->length,
+         n1, message2->dnsHeader, message2->length, n2, 0);
+   }
+   else
+   {
+      //Retrieve the length of the rdata fields
+      n1 = htons(record1->rdlength);
+      n2 = htons(record2->rdlength);
+
+      //The bytes of the raw uncompressed rdata are compared in turn, interpreting
+      //the bytes as eight-bit unsigned values, until a byte is found whose value
+      //is greater than that of its counterpart (in which case, the rdata whose
+      //byte has the greater value is deemed lexicographically later) or one of the
+      //resource records runs out of rdata (in which case, the resource record which
+      //still has remaining data first is deemed lexicographically later)
+      if(n1 < n2)
+      {
+         //Raw comparison of the binary content of the rdata
+         res = memcmp(record1->rdata, record2->rdata, n1);
+
+         //Check comparison result
+         if(!res)
+         {
+            //The first resource records runs out of rdata
+            res = -1;
+         }
+      }
+      else if(n1 > n2)
+      {
+         //Raw comparison of the binary content of the rdata
+         res = memcmp(record1->rdata, record2->rdata, n2);
+
+         //Check comparison result
+         if(!res)
+         {
+            //The second resource records runs out of rdata
+            res = 1;
+         }
+      }
+      else
+      {
+         //Raw comparison of the binary content of the rdata
+         res = memcmp(record1->rdata, record2->rdata, n1);
+      }
+   }
+
+   //Return comparison result
+   return res;
+}
+
+
+/**
+ * @brief Check for duplicate resource records
+ * @param[in] message Pointer to the mDNS message
+ * @param[in] instance Instance name
+ * @param[in] service Service name
+ * @param[in] domain Domain name
+ * @param[in] rtype Resource record type
+ * @return The function returns TRUE is the specified resource record is a
+ *   duplicate. Otherwise FALSE is returned
+ **/
+
+bool_t mdnsCheckDuplicateRecord(const MdnsMessage *message, const char_t *instance,
+   const char_t *service, const char_t *domain, uint16_t rtype)
+{
+   uint_t i;
+   uint_t k;
+   size_t n;
+   size_t offset;
+   uint16_t rclass;
+   bool_t duplicate;
+   DnsResourceRecord *record;
+
+   //Clear flag
+   duplicate = FALSE;
+
+   //Point to the first question
+   offset = sizeof(DnsHeader);
+
+   //Parse the Question Section
+   for(i = 0; i < message->dnsHeader->qdcount; i++)
+   {
+      //Parse domain name
+      offset = dnsParseName(message->dnsHeader, message->length, offset, NULL, 0);
+      //Invalid name?
+      if(!offset)
+         break;
+
+      //Point to the next question
+      offset += sizeof(DnsQuestion);
+      //Make sure the mDNS message is valid
+      if(offset > message->length)
+         break;
+   }
+
+   //Successful processing?
+   if(i == message->dnsHeader->qdcount)
+   {
+      //Compute the total number of resource records
+      k = message->dnsHeader->ancount + message->dnsHeader->nscount +
+         message->dnsHeader->arcount;
+
+      //Loop through the resource records
+      for(i = 0; i < k; i++)
+      {
+         //Parse resource record name
+         n = dnsParseName(message->dnsHeader, message->length, offset, NULL, 0);
+         //Invalid name?
+         if(!n)
+            break;
+
+         //Point to the associated resource record
+         record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, n);
+         //Point to the resource data
+         n += sizeof(DnsResourceRecord);
+
+         //Make sure the resource record is valid
+         if(n > message->length)
+            break;
+         if((n + ntohs(record->rdlength)) > message->length)
+            break;
+
+         //Convert the record class to host byte order
+         rclass = ntohs(record->rclass);
+         //Discard cache-flush bit
+         rclass &= ~MDNS_RCLASS_CACHE_FLUSH;
+
+         //Check the class and the type of the resource record
+         if(rclass == DNS_RR_CLASS_IN && ntohs(record->rtype) == rtype)
+         {
+            //Compare resource record name
+            if(!mdnsCompareName(message->dnsHeader, message->length,
+               offset, instance, service, domain, 0))
+            {
+               //The resource record is already present in the Answer Section
+               duplicate = TRUE;
+               //We are done
+               break;
+            }
+         }
+
+         //Point to the next resource record
+         offset = n + ntohs(record->rdlength);
+      }
+   }
+
+   //The function returns TRUE is the specified resource record is a duplicate
+   return duplicate;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mdns/mdns_common.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,118 @@
+/**
+ * @file mdns_common.h
+ * @brief Functions common to mDNS client and mDNS responder
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MDNS_COMMON_H
+#define _MDNS_COMMON_H
+
+//Dependencies
+#include "core/net.h"
+#include "dns/dns_common.h"
+
+//Maximum size of DNS messages
+#ifndef MDNS_MESSAGE_MAX_SIZE
+   #define MDNS_MESSAGE_MAX_SIZE 1024
+#elif (MDNS_MESSAGE_MAX_SIZE < 1)
+   #error MDNS_MESSAGE_MAX_SIZE parameter is not valid
+#endif
+
+//Default resource record TTL (cache lifetime)
+#ifndef MDNS_DEFAULT_RR_TTL
+   #define MDNS_DEFAULT_RR_TTL 120
+#elif (MDNS_DEFAULT_RR_TTL < 1)
+   #error MDNS_DEFAULT_RR_TTL parameter is not valid
+#endif
+
+//mDNS port number
+#define MDNS_PORT 5353
+//Default IP TTL value
+#define MDNS_DEFAULT_IP_TTL 255
+//Maximum RR TTL in legacy unicast responses
+#define MDNS_LEGACY_UNICAST_RR_TTL 10
+
+//QU flag
+#define MDNS_QCLASS_QU 0x8000
+//Cache Flush flag
+#define MDNS_RCLASS_CACHE_FLUSH 0x8000
+
+//mDNS IPv4 multicast group
+#define MDNS_IPV4_MULTICAST_ADDR IPV4_ADDR(224, 0, 0, 251)
+
+
+/**
+ * @brief mDNS message
+ **/
+
+typedef struct
+{
+   NetBuffer *buffer;
+   size_t offset;
+   size_t length;
+   const IpPseudoHeader *pseudoHeader;
+   const UdpHeader *udpHeader;
+   DnsHeader *dnsHeader;
+   systime_t timestamp;
+   systime_t timeout;
+   uint_t sharedRecordCount;
+} MdnsMessage;
+
+
+//mDNS IPv6 multicast group
+extern const Ipv6Addr MDNS_IPV6_MULTICAST_ADDR;
+
+//mDNS related functions
+error_t mdnsInit(NetInterface *interface);
+
+void mdnsProcessMessage(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
+   const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, void *params);
+
+void mdnsProcessResponse(NetInterface *interface, MdnsMessage *response);
+
+bool_t mdnsCheckSourceAddr(NetInterface *interface,
+   const IpPseudoHeader *pseudoHeader);
+
+error_t mdnsCreateMessage(MdnsMessage *message, bool_t queryResponse);
+void mdnsDeleteMessage(MdnsMessage *message);
+
+error_t mdnsSendMessage(NetInterface *interface, const MdnsMessage *message,
+   const IpAddr *destIpAddr, uint_t destPort);
+
+size_t mdnsEncodeName(const char_t *instance, const char_t *service,
+   const char_t *domain, uint8_t *dest);
+
+int_t mdnsCompareName(const DnsHeader *message, size_t length, size_t pos,
+   const char_t *instance, const char_t *service, const char_t *domain, uint_t level);
+
+int_t mdnsCompareRecord(const MdnsMessage *message1, size_t offset1,
+   const DnsResourceRecord *record1, const MdnsMessage *message2,
+   size_t offset2, const DnsResourceRecord *record2);
+
+bool_t mdnsCheckDuplicateRecord(const MdnsMessage *message, const char_t *instance,
+   const char_t *service, const char_t *domain, uint16_t rtype);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mdns/mdns_responder.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,2350 @@
+/**
+ * @file mdns_responder.c
+ * @brief mDNS responder (Multicast DNS)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL MDNS_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include <ctype.h>
+#include "core/net.h"
+#include "mdns/mdns_responder.h"
+#include "mdns/mdns_common.h"
+#include "dns/dns_debug.h"
+#include "dns_sd/dns_sd.h"
+#include "str.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (MDNS_RESPONDER_SUPPORT == ENABLED)
+
+//Tick counter to handle periodic operations
+systime_t mdnsResponderTickCounter;
+
+
+/**
+ * @brief Initialize settings with default values
+ * @param[out] settings Structure that contains mDNS responder settings
+ **/
+
+void mdnsResponderGetDefaultSettings(MdnsResponderSettings *settings)
+{
+   //Use default interface
+   settings->interface = netGetDefaultInterface();
+
+   //Number of announcement packets
+   settings->numAnnouncements = MDNS_ANNOUNCE_NUM;
+   //TTL resource record
+   settings->ttl = MDNS_DEFAULT_RR_TTL;
+   //FSM state change event
+   settings->stateChangeEvent = NULL;
+}
+
+
+/**
+ * @brief mDNS responder initialization
+ * @param[in] context Pointer to the mDNS responder context
+ * @param[in] settings mDNS responder specific settings
+ * @return Error code
+ **/
+
+error_t mdnsResponderInit(MdnsResponderContext *context,
+   const MdnsResponderSettings *settings)
+{
+   NetInterface *interface;
+
+   //Debug message
+   TRACE_INFO("Initializing mDNS responder...\r\n");
+
+   //Ensure the parameters are valid
+   if(context == NULL || settings == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Invalid network interface?
+   if(settings->interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Point to the underlying network interface
+   interface = settings->interface;
+
+   //Clear the mDNS responder context
+   memset(context, 0, sizeof(MdnsResponderContext));
+   //Save user settings
+   context->settings = *settings;
+
+   //mDNS responder is currently suspended
+   context->running = FALSE;
+   //Initialize state machine
+   context->state = MDNS_STATE_INIT;
+
+   //Attach the mDNS responder context to the network interface
+   interface->mdnsResponderContext = context;
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Start mDNS responder
+ * @param[in] context Pointer to the mDNS responder context
+ * @return Error code
+ **/
+
+error_t mdnsResponderStart(MdnsResponderContext *context)
+{
+   //Check parameter
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Starting mDNS responder...\r\n");
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Start mDNS responder
+   context->running = TRUE;
+   //Initialize state machine
+   context->state = MDNS_STATE_INIT;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Stop mDNS responder
+ * @param[in] context Pointer to the mDNS responder context
+ * @return Error code
+ **/
+
+error_t mdnsResponderStop(MdnsResponderContext *context)
+{
+   //Check parameter
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Stopping mDNS responder...\r\n");
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Suspend mDNS responder
+   context->running = FALSE;
+   //Reinitialize state machine
+   context->state = MDNS_STATE_INIT;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Retrieve current state
+ * @param[in] context Pointer to the mDNS responder context
+ * @return Current mDNS responder state
+ **/
+
+MdnsState mdnsResponderGetState(MdnsResponderContext *context)
+{
+   MdnsState state;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+   //Get current state
+   state = context->state;
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return current state
+   return state;
+}
+
+
+/**
+ * @brief Set hostname
+ * @param[in] context Pointer to the mDNS responder context
+ * @param[in] hostname NULL-terminated string that contains the hostname
+ * @return Error code
+ **/
+
+error_t mdnsResponderSetHostname(MdnsResponderContext *context,
+   const char_t *hostname)
+{
+   NetInterface *interface;
+
+   //Check parameters
+   if(context == NULL || hostname == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Check whether a hostname is already assigned
+   if(context->hostname[0] != '\0')
+   {
+      //Check whether the link is up
+      if(interface->linkState)
+      {
+         //Send a goodbye packet
+         mdnsResponderSendGoodbye(context);
+      }
+   }
+
+   //Set hostname
+   strSafeCopy(context->hostname, hostname,
+      MDNS_RESPONDER_MAX_HOSTNAME_LEN);
+
+   //Restart probing process (hostname)
+   mdnsResponderStartProbing(interface->mdnsResponderContext);
+
+#if (DNS_SD_SUPPORT == ENABLED)
+   //Restart probing process (service instance name)
+   dnsSdStartProbing(interface->dnsSdContext);
+#endif
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Generate domain name for reverse DNS lookup (IPv4)
+ * @param[in] context Pointer to the mDNS responder context
+ * @return Error code
+ **/
+
+error_t mdnsResponderSetIpv4ReverseName(MdnsResponderContext *context)
+{
+#if (IPV4_SUPPORT == ENABLED)
+   uint8_t *addr;
+   NetInterface *interface;
+
+   //Check whether the mDNS responder has been properly instantiated
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Check whether the host address is valid
+   if(interface->ipv4Context.addrState == IPV4_ADDR_STATE_VALID)
+   {
+      //Cast the IPv4 address as byte array
+      addr = (uint8_t *) &interface->ipv4Context.addr;
+
+      //Generate the domain name for reverse DNS lookup
+      sprintf(context->ipv4ReverseName, "%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8,
+         addr[3], addr[2], addr[1], addr[0]);
+   }
+   else
+   {
+      //The host address is not valid
+      context->ipv4ReverseName[0] = '\0';
+   }
+#endif
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Generate domain name for reverse DNS lookup (IPv6)
+ * @param[in] context Pointer to the mDNS responder context
+ * @return Error code
+ **/
+
+error_t mdnsResponderSetIpv6ReverseName(MdnsResponderContext *context)
+{
+#if (IPV6_SUPPORT == ENABLED)
+   uint_t i;
+   uint_t m;
+   uint_t n;
+   char_t *p;
+   uint8_t *addr;
+   NetInterface *interface;
+
+   //Check whether the mDNS responder has been properly instantiated
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Point to the buffer where to format the reverse name
+   p = context->ipv6ReverseName;
+
+   //Check whether the link-local address is valid
+   if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED)
+   {
+      //Cast the IPv4 address as byte array
+      addr = interface->ipv6Context.addrList[0].addr.b;
+
+      //Generate the domain name for reverse DNS lookup
+      for(i = 0; i < 32; i++)
+      {
+         //Calculate the shift count
+         n = (31 - i) / 2;
+         m = (i % 2) * 4;
+
+         //Format the current digit
+         p += sprintf(p, "%" PRIx8, (addr[n] >> m) & 0x0F);
+
+         //Add a delimiter character
+         if(i != 31)
+            p += sprintf(p, ".");
+      }
+   }
+   else
+   {
+      //The link-local address is not valid
+      p[0] = '\0';
+   }
+#endif
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Restart probing process
+ * @param[in] context Pointer to the mDNS responder context
+ * @return Error code
+ **/
+
+error_t mdnsResponderStartProbing(MdnsResponderContext *context)
+{
+   //Check whether the mDNS responder has been properly instantiated
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Generate domain names for reverse DNS lookup
+   mdnsResponderSetIpv4ReverseName(context);
+   mdnsResponderSetIpv6ReverseName(context);
+
+   //Force mDNS responder to start probing again
+   context->state = MDNS_STATE_INIT;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief mDNS responder timer handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * manage mDNS operation
+ *
+ * @param[in] context Pointer to the mDNS responder context
+ **/
+
+void mdnsResponderTick(MdnsResponderContext *context)
+{
+   bool_t valid;
+   systime_t time;
+   systime_t delay;
+   NetInterface *interface;
+   IpAddr destIpAddr;
+
+   //Make sure the mDNS responder has been properly instantiated
+   if(context == NULL)
+      return;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Check current state
+   if(context->state == MDNS_STATE_INIT)
+   {
+      //Check whether a hostname has been assigned
+      if(context->hostname[0] != '\0')
+      {
+         //Make sure that the link is up
+         if(interface->linkState)
+         {
+            //Clear flag
+            valid = FALSE;
+
+#if (IPV4_SUPPORT == ENABLED)
+            //Check whether the IPv4 host address is valid
+            if(interface->ipv4Context.addrState == IPV4_ADDR_STATE_VALID)
+               valid = TRUE;
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+            //Check whether the IPv6 link-local address is valid
+            if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED)
+               valid = TRUE;
+#endif
+            //Any valid IP address assigned to the network interface?
+            if(valid)
+            {
+               //Wait until both IPv4 and IPv6 addresses are valid
+               mdnsResponderChangeState(context, MDNS_STATE_WAITING, 0);
+            }
+         }
+      }
+   }
+   else if(context->state == MDNS_STATE_WAITING)
+   {
+      //Set flag
+      valid = TRUE;
+
+      //Check current time
+      if(timeCompare(time, context->timestamp + MDNS_MAX_WAITING_DELAY) < 0)
+      {
+#if (IPV4_SUPPORT == ENABLED)
+         //Check whether the IPv4 host address is valid
+         if(interface->ipv4Context.addrState != IPV4_ADDR_STATE_VALID)
+            valid = FALSE;
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+         //Check whether the IPv6 link-local address is valid
+         if(ipv6GetLinkLocalAddrState(interface) != IPV6_ADDR_STATE_PREFERRED)
+            valid = FALSE;
+#endif
+      }
+
+      //Start probing?
+      if(valid)
+      {
+         //Initial random delay
+         delay = netGetRandRange(MDNS_RAND_DELAY_MIN, MDNS_RAND_DELAY_MAX);
+         //Perform probing
+         mdnsResponderChangeState(context, MDNS_STATE_PROBING, delay);
+      }
+   }
+   else if(context->state == MDNS_STATE_PROBING)
+   {
+      //Probing failed?
+      if(context->conflict && context->retransmitCount > 0)
+      {
+         //Programmatically change the host name
+         mdnsResponderChangeHostname(context);
+         //Probe again, and repeat as necessary until a unique name is found
+         mdnsResponderChangeState(context, MDNS_STATE_PROBING, 0);
+      }
+      //Tie-break lost?
+      else if(context->tieBreakLost && context->retransmitCount > 0)
+      {
+         //The host defers to the winning host by waiting one second, and
+         //then begins probing for this record again
+         mdnsResponderChangeState(context, MDNS_STATE_PROBING, MDNS_PROBE_DEFER);
+      }
+      else
+      {
+         //Check current time
+         if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+         {
+            //Probing is on-going?
+            if(context->retransmitCount < MDNS_PROBE_NUM)
+            {
+               //First probe?
+               if(context->retransmitCount == 0)
+               {
+                  //Apparently conflicting mDNS responses received before the
+                  //first probe packet is sent must be silently ignored
+                  context->conflict = FALSE;
+                  context->tieBreakLost = FALSE;
+               }
+
+               //Send probe packet
+               mdnsResponderSendProbe(context);
+
+               //Save the time at which the packet was sent
+               context->timestamp = time;
+               //Time interval between subsequent probe packets
+               context->timeout = MDNS_PROBE_DELAY;
+               //Increment retransmission counter
+               context->retransmitCount++;
+            }
+            //Probing is complete?
+            else
+            {
+               //The mDNS responder must send unsolicited mDNS responses
+               //containing all of its newly registered resource records
+               if(context->settings.numAnnouncements > 0)
+                  mdnsResponderChangeState(context, MDNS_STATE_ANNOUNCING, 0);
+               else
+                  mdnsResponderChangeState(context, MDNS_STATE_IDLE, 0);
+            }
+         }
+      }
+   }
+   else if(context->state == MDNS_STATE_ANNOUNCING)
+   {
+      //Whenever a mDNS responder receives any mDNS response (solicited or
+      //otherwise) containing a conflicting resource record, the conflict
+      //must be resolved
+      if(context->conflict)
+      {
+         //Probe again, and repeat as necessary until a unique name is found
+         mdnsResponderChangeState(context, MDNS_STATE_PROBING, 0);
+      }
+      else
+      {
+         //Check current time
+         if(timeCompare(time, context->timestamp + context->timeout) >= 0)
+         {
+            //Send announcement packet
+            mdnsResponderSendAnnouncement(context);
+
+            //Save the time at which the packet was sent
+            context->timestamp = time;
+            //Increment retransmission counter
+            context->retransmitCount++;
+
+            //First announcement packet?
+            if(context->retransmitCount == 1)
+            {
+               //The mDNS responder must send at least two unsolicited
+               //responses, one second apart
+               context->timeout = MDNS_ANNOUNCE_DELAY;
+            }
+            else
+            {
+               //To provide increased robustness against packet loss, a mDNS
+               //responder may send up to eight unsolicited responses, provided
+               //that the interval between unsolicited responses increases by
+               //at least a factor of two with every response sent
+               context->timeout *= 2;
+            }
+
+            //Last announcement packet?
+            if(context->retransmitCount >= context->settings.numAnnouncements)
+            {
+               //A mDNS responder must not send regular periodic announcements
+               mdnsResponderChangeState(context, MDNS_STATE_IDLE, 0);
+            }
+         }
+      }
+   }
+   else if(context->state == MDNS_STATE_IDLE)
+   {
+      //Whenever a mDNS responder receives any mDNS response (solicited or
+      //otherwise) containing a conflicting resource record, the conflict
+      //must be resolved
+      if(context->conflict)
+      {
+         //Probe again, and repeat as necessary until a unique name is found
+         mdnsResponderChangeState(context, MDNS_STATE_PROBING, 0);
+      }
+   }
+
+#if (IPV4_SUPPORT == ENABLED)
+   //Any response message pending to be sent?
+   if(context->ipv4Response.buffer != NULL)
+   {
+      //Check whether the time delay has elapsed
+      if(timeCompare(time, context->ipv4Response.timestamp +
+         context->ipv4Response.timeout) >= 0)
+      {
+#if (DNS_SD_SUPPORT == ENABLED)
+         //Additional record generation (DNS-SD)
+         dnsSdGenerateAdditionalRecords(interface,
+            &context->ipv4Response, FALSE);
+#endif
+         //Additional record generation (mDNS)
+         mdnsResponderGenerateAdditionalRecords(interface,
+            &context->ipv4Response, FALSE);
+
+         //Use mDNS IPv4 multicast address
+         destIpAddr.length = sizeof(Ipv4Addr);
+         destIpAddr.ipv4Addr = MDNS_IPV4_MULTICAST_ADDR;
+
+         //Send mDNS response message
+         mdnsSendMessage(interface, &context->ipv4Response,
+            &destIpAddr, MDNS_PORT);
+
+         //Free previously allocated memory
+         mdnsDeleteMessage(&context->ipv4Response);
+      }
+   }
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+   //Any response message pending to be sent?
+   if(context->ipv6Response.buffer != NULL)
+   {
+      //Check whether the time delay has elapsed
+      if(timeCompare(time, context->ipv6Response.timestamp +
+         context->ipv6Response.timeout) >= 0)
+      {
+#if (DNS_SD_SUPPORT == ENABLED)
+         //Additional record generation (DNS-SD)
+         dnsSdGenerateAdditionalRecords(interface,
+            &context->ipv6Response, FALSE);
+#endif
+         //Additional record generation (mDNS)
+         mdnsResponderGenerateAdditionalRecords(interface,
+            &context->ipv6Response, FALSE);
+
+         //Use mDNS IPv6 multicast address
+         destIpAddr.length = sizeof(Ipv6Addr);
+         destIpAddr.ipv6Addr = MDNS_IPV6_MULTICAST_ADDR;
+
+         //Send mDNS response message
+         mdnsSendMessage(interface, &context->ipv6Response,
+            &destIpAddr, MDNS_PORT);
+
+         //Free previously allocated memory
+         mdnsDeleteMessage(&context->ipv6Response);
+      }
+   }
+#endif
+}
+
+
+/**
+ * @brief Callback function for link change event
+ * @param[in] context Pointer to the mDNS responder context
+ **/
+
+void mdnsResponderLinkChangeEvent(MdnsResponderContext *context)
+{
+   //Make sure the mDNS responder has been properly instantiated
+   if(context == NULL)
+      return;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //Free any response message pending to be sent
+   mdnsDeleteMessage(&context->ipv4Response);
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+   //Free any response message pending to be sent
+   mdnsDeleteMessage(&context->ipv6Response);
+#endif
+
+   //Whenever a mDNS responder receives an indication of a link
+   //change event, it must perform probing and announcing
+   mdnsResponderChangeState(context, MDNS_STATE_INIT, 0);
+}
+
+
+/**
+ * @brief Update FSM state
+ * @param[in] context Pointer to the mDNS responder context
+ * @param[in] newState New state to switch to
+ * @param[in] delay Initial delay
+ **/
+
+void mdnsResponderChangeState(MdnsResponderContext *context,
+   MdnsState newState, systime_t delay)
+{
+   NetInterface *interface;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Set time stamp
+   context->timestamp = osGetSystemTime();
+   //Set initial delay
+   context->timeout = delay;
+   //Reset retransmission counter
+   context->retransmitCount = 0;
+   //Switch to the new state
+   context->state = newState;
+
+   //Any registered callback?
+   if(context->settings.stateChangeEvent != NULL)
+   {
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+      //Invoke user callback function
+      context->settings.stateChangeEvent(context, interface, newState);
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+   }
+}
+
+
+/**
+ * @brief Programmatically change the host name
+ * @param[in] context Pointer to the mDNS responder context
+ **/
+
+void mdnsResponderChangeHostname(MdnsResponderContext *context)
+{
+   size_t i;
+   size_t m;
+   size_t n;
+   uint32_t index;
+   char_t s[16];
+
+   //Retrieve the length of the string
+   n = strlen(context->hostname);
+
+   //Parse the string backwards
+   for(i = n; i > 0; i--)
+   {
+      //Check whether the current character is a digit
+      if(!isdigit((uint8_t) context->hostname[i - 1]))
+         break;
+   }
+
+   //Any number following the host name?
+   if(context->hostname[i] != '\0')
+   {
+      //Retrieve the number at the end of the name
+      index = atoi(context->hostname + i);
+      //Increment the value
+      index++;
+
+      //Strip the digits
+      context->hostname[i] = '\0';
+   }
+   else
+   {
+      //Append the digit "2" to the name
+      index = 2;
+   }
+
+   //Convert the number to a string of characters
+   m = sprintf(s, "%" PRIu32, index);
+
+   //Sanity check
+   if((i + m) <= NET_MAX_HOSTNAME_LEN)
+   {
+      //Add padding if necessary
+      while((i + m) < n)
+         context->hostname[i++] = '0';
+
+      //Properly terminate the string
+      context->hostname[i] = '\0';
+      //Programmatically change the host name
+      strcat(context->hostname, s);
+   }
+}
+
+
+/**
+ * @brief Send probe packet
+ * @param[in] context Pointer to the mDNS responder context
+ * @return Error code
+ **/
+
+error_t mdnsResponderSendProbe(MdnsResponderContext *context)
+{
+   error_t error;
+   NetInterface *interface;
+   DnsQuestion *dnsQuestion;
+   MdnsMessage message;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Create an empty mDNS query message
+   error = mdnsCreateMessage(&message, FALSE);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Start of exception handling block
+   do
+   {
+      //Encode the host name using the DNS name notation
+      message.length += mdnsEncodeName(context->hostname, "",
+         ".local", message.dnsHeader->questions);
+
+      //Point to the corresponding question structure
+      dnsQuestion = DNS_GET_QUESTION(message.dnsHeader, message.length);
+
+      //The probes should be sent as QU questions with the unicast-response
+      //bit set, to allow a defending host to respond immediately via unicast
+      dnsQuestion->qtype = HTONS(DNS_RR_TYPE_ANY);
+      dnsQuestion->qclass = HTONS(MDNS_QCLASS_QU | DNS_RR_CLASS_IN);
+
+      //Update the length of the mDNS query message
+      message.length += sizeof(DnsQuestion);
+
+      //Format A resource record
+      error = mdnsResponderAddIpv4AddrRecord(interface,
+         &message, FALSE, MDNS_DEFAULT_RR_TTL);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Format AAAA resource record
+      error = mdnsResponderAddIpv6AddrRecord(interface,
+         &message, FALSE, MDNS_DEFAULT_RR_TTL);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Number of questions in the Question Section
+      message.dnsHeader->qdcount = 1;
+      //Number of resource records in the Authority Section
+      message.dnsHeader->nscount = message.dnsHeader->ancount;
+      //Number of resource records in the Answer Section
+      message.dnsHeader->ancount = 0;
+
+      //Send mDNS message
+      error = mdnsSendMessage(interface, &message, NULL, MDNS_PORT);
+
+      //End of exception handling block
+   } while(0);
+
+   //Free previously allocated memory
+   mdnsDeleteMessage(&message);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send announcement packet
+ * @param[in] context Pointer to the mDNS responder context
+ * @return Error code
+ **/
+
+error_t mdnsResponderSendAnnouncement(MdnsResponderContext *context)
+{
+   error_t error;
+   NetInterface *interface;
+   MdnsMessage message;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Create an empty mDNS response message
+   error = mdnsCreateMessage(&message, TRUE);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Start of exception handling block
+   do
+   {
+      //Format A resource record
+      error = mdnsResponderAddIpv4AddrRecord(interface,
+         &message, TRUE, MDNS_DEFAULT_RR_TTL);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Format reverse address mapping PTR record (IPv4)
+      error = mdnsResponderAddIpv4ReversePtrRecord(interface,
+         &message, TRUE, MDNS_DEFAULT_RR_TTL);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Format AAAA resource record
+      error = mdnsResponderAddIpv6AddrRecord(interface,
+         &message, TRUE, MDNS_DEFAULT_RR_TTL);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Format reverse address mapping PTR record (IPv6)
+      error = mdnsResponderAddIpv6ReversePtrRecord(interface,
+         &message, TRUE, MDNS_DEFAULT_RR_TTL);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Send mDNS message
+      error = mdnsSendMessage(interface, &message, NULL, MDNS_PORT);
+
+      //End of exception handling block
+   } while(0);
+
+   //Free previously allocated memory
+   mdnsDeleteMessage(&message);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send goodbye packet
+ * @param[in] context Pointer to the mDNS responder context
+ * @return Error code
+ **/
+
+error_t mdnsResponderSendGoodbye(MdnsResponderContext *context)
+{
+   error_t error;
+   NetInterface *interface;
+   MdnsMessage message;
+
+   //Point to the underlying network interface
+   interface = context->settings.interface;
+
+   //Create an empty mDNS response message
+   error = mdnsCreateMessage(&message, TRUE);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Start of exception handling block
+   do
+   {
+      //Format A resource record
+      error = mdnsResponderAddIpv4AddrRecord(interface, &message, TRUE, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Format reverse address mapping PTR record (IPv4)
+      error = mdnsResponderAddIpv4ReversePtrRecord(interface, &message, TRUE, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Format AAAA resource record
+      error = mdnsResponderAddIpv6AddrRecord(interface, &message, TRUE, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Format reverse address mapping PTR record (IPv6)
+      error = mdnsResponderAddIpv6ReversePtrRecord(interface, &message, TRUE, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Send mDNS message
+      error = mdnsSendMessage(interface, &message, NULL, MDNS_PORT);
+
+      //End of exception handling block
+   } while(0);
+
+   //Free previously allocated memory
+   mdnsDeleteMessage(&message);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process mDNS query message
+ * @param[in] interface Underlying network interface
+ * @param[in] query Incoming mDNS query message
+ **/
+
+void mdnsResponderProcessQuery(NetInterface *interface, MdnsMessage *query)
+{
+   error_t error;
+   uint_t i;
+   size_t n;
+   size_t offset;
+   DnsQuestion *question;
+   DnsResourceRecord *record;
+   MdnsResponderContext *context;
+   MdnsMessage *response;
+   uint16_t destPort;
+   IpAddr destIpAddr;
+
+   //Point to the mDNS responder context
+   context = interface->mdnsResponderContext;
+   //Make sure the mDNS responder has been properly instantiated
+   if(context == NULL)
+      return;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //IPv4 query received?
+   if(query->pseudoHeader->length == sizeof(Ipv4PseudoHeader))
+   {
+      //If the source UDP port in a received Multicast DNS query is not port 5353,
+      //this indicates that the querier originating the query is a simple resolver
+      if(ntohs(query->udpHeader->srcPort) != MDNS_PORT)
+      {
+         //The mDNS responder must send a UDP response directly back to the querier,
+         //via unicast, to the query packet's source IP address and port
+         destIpAddr.length = sizeof(Ipv4Addr);
+         destIpAddr.ipv4Addr = query->pseudoHeader->ipv4Data.srcAddr;
+      }
+      else
+      {
+         //Use mDNS IPv4 multicast address
+         destIpAddr.length = sizeof(Ipv4Addr);
+         destIpAddr.ipv4Addr = MDNS_IPV4_MULTICAST_ADDR;
+      }
+
+      //Point to the mDNS response message
+      response = &context->ipv4Response;
+   }
+   else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 query received?
+   if(query->pseudoHeader->length == sizeof(Ipv6PseudoHeader))
+   {
+      //If the source UDP port in a received Multicast DNS query is not port 5353,
+      //this indicates that the querier originating the query is a simple resolver
+      if(ntohs(query->udpHeader->srcPort) != MDNS_PORT)
+      {
+         //The mDNS responder must send a UDP response directly back to the querier,
+         //via unicast, to the query packet's source IP address and port
+         destIpAddr.length = sizeof(Ipv6Addr);
+         destIpAddr.ipv6Addr = query->pseudoHeader->ipv6Data.srcAddr;
+      }
+      else
+      {
+         //Use mDNS IPv6 multicast address
+         destIpAddr.length = sizeof(Ipv6Addr);
+         destIpAddr.ipv6Addr = MDNS_IPV6_MULTICAST_ADDR;
+      }
+
+      //Point to the mDNS response message
+      response = &context->ipv6Response;
+   }
+   else
+#endif
+   //Invalid query received?
+   {
+      //Discard the mDNS query message
+      return;
+   }
+
+   //When possible, a responder should, for the sake of network
+   //efficiency, aggregate as many responses as possible into a
+   //single mDNS response message
+   if(response->buffer == NULL)
+   {
+      //Create an empty mDNS response message
+      error = mdnsCreateMessage(response, TRUE);
+      //Any error to report?
+      if(error)
+         return;
+   }
+
+   //Take the identifier from the query message
+   response->dnsHeader->id = query->dnsHeader->id;
+
+   //Point to the first question
+   offset = sizeof(DnsHeader);
+
+   //Start of exception handling block
+   do
+   {
+      //Parse the Question Section
+      for(i = 0; i < ntohs(query->dnsHeader->qdcount); i++)
+      {
+         //Parse resource record name
+         n = dnsParseName(query->dnsHeader, query->length, offset, NULL, 0);
+         //Invalid name?
+         if(!n)
+            break;
+         //Malformed mDNS message?
+         if((n + sizeof(DnsQuestion)) > query->length)
+            break;
+
+         //Point to the corresponding entry
+         question = DNS_GET_QUESTION(query->dnsHeader, n);
+
+         //Parse question
+         error = mdnsResponderParseQuestion(interface, query,
+            offset, question, response);
+         //Any error to report?
+         if(error)
+            break;
+
+#if (DNS_SD_SUPPORT == ENABLED)
+         //Parse resource record
+         error = dnsSdParseQuestion(interface, query, offset,
+            question, response);
+         //Any error to report?
+         if(error)
+            break;
+#endif
+         //Point to the next question
+         offset = n + sizeof(DnsQuestion);
+      }
+
+      //Any error while parsing the Question Section?
+      if(i != ntohs(query->dnsHeader->qdcount))
+         break;
+
+      //Parse the Known-Answer Section
+      for(i = 0; i < ntohs(query->dnsHeader->ancount); i++)
+      {
+         //Parse resource record name
+         n = dnsParseName(query->dnsHeader, query->length, offset, NULL, 0);
+         //Invalid name?
+         if(!n)
+            break;
+
+         //Point to the associated resource record
+         record = DNS_GET_RESOURCE_RECORD(query->dnsHeader, n);
+         //Point to the resource data
+         n += sizeof(DnsResourceRecord);
+
+         //Make sure the resource record is valid
+         if(n > query->length)
+            break;
+         if((n + ntohs(record->rdlength)) > query->length)
+            break;
+
+         //Parse resource record
+         mdnsResponderParseKnownAnRecord(interface, query, offset,
+            record, response);
+
+         //Point to the next resource record
+         offset = n + ntohs(record->rdlength);
+      }
+
+      //Any error while parsing the Answer Section?
+      if(i != ntohs(query->dnsHeader->ancount))
+         break;
+
+      //Parse Authority Section
+      for(i = 0; i < ntohs(query->dnsHeader->nscount); i++)
+      {
+         //Parse resource record name
+         n = dnsParseName(query->dnsHeader, query->length, offset, NULL, 0);
+         //Invalid name?
+         if(!n)
+            break;
+
+         //Point to the associated resource record
+         record = DNS_GET_RESOURCE_RECORD(query->dnsHeader, n);
+         //Point to the resource data
+         n += sizeof(DnsResourceRecord);
+
+         //Make sure the resource record is valid
+         if(n > query->length)
+            break;
+         if((n + ntohs(record->rdlength)) > query->length)
+            break;
+
+         //Check for host name conflict
+         mdnsResponderParseNsRecord(interface, query, offset, record);
+
+#if (DNS_SD_SUPPORT == ENABLED)
+         //Check for service instance name conflict
+         dnsSdParseNsRecord(interface, query, offset, record);
+#endif
+         //Point to the next resource record
+         offset = n + ntohs(record->rdlength);
+      }
+
+      //Any error while parsing the Authority Section?
+      if(i != ntohs(query->dnsHeader->nscount))
+         break;
+
+      //End of exception handling block
+   } while(0);
+
+   //Should a mDNS message be send in response to the query?
+   if(response->dnsHeader->ancount > 0)
+   {
+      //If the source UDP port in a received Multicast DNS query is not port 5353,
+      //this indicates that the querier originating the query is a simple resolver
+      if(ntohs(query->udpHeader->srcPort) != MDNS_PORT)
+      {
+#if (DNS_SD_SUPPORT == ENABLED)
+         //Additional record generation (DNS-SD)
+         dnsSdGenerateAdditionalRecords(interface, response, TRUE);
+#endif
+         //Additional record generation (mDNS)
+         mdnsResponderGenerateAdditionalRecords(interface, response, TRUE);
+
+         //Destination port
+         destPort = ntohs(query->udpHeader->srcPort);
+
+         //Send mDNS response message
+         mdnsSendMessage(interface, response, &destIpAddr, destPort);
+         //Free previously allocated memory
+         mdnsDeleteMessage(response);
+      }
+      else
+      {
+         //Check whether the answer should be delayed
+         if(query->dnsHeader->tc)
+         {
+            //In the case where the query has the TC (truncated) bit set, indicating
+            //that subsequent Known-Answer packets will follow, responders should
+            //delay their responses by a random amount of time selected with uniform
+            //random distribution in the range 400-500 ms
+            response->timeout = netGetRandRange(400, 500);
+
+            //Save current time
+            response->timestamp = osGetSystemTime();
+         }
+         else if(response->sharedRecordCount > 0)
+         {
+            //In any case where there may be multiple responses, such as queries
+            //where the answer is a member of a shared resource record set, each
+            //responder should delay its response by a random amount of time
+            //selected with uniform random distribution in the range 20-120 ms
+            response->timeout = netGetRandRange(20, 120);
+
+            //Save current time
+            response->timestamp = osGetSystemTime();
+         }
+         else
+         {
+#if (DNS_SD_SUPPORT == ENABLED)
+            //Additional record generation (refer to RFC 6763 section 12)
+            dnsSdGenerateAdditionalRecords(interface, response, FALSE);
+#endif
+            //Additional record generation (mDNS)
+            mdnsResponderGenerateAdditionalRecords(interface, response, FALSE);
+
+            //Send mDNS response message
+            mdnsSendMessage(interface, response, &destIpAddr, MDNS_PORT);
+            //Free previously allocated memory
+            mdnsDeleteMessage(response);
+         }
+      }
+   }
+   else
+   {
+      //Free mDNS response message
+      mdnsDeleteMessage(response);
+   }
+}
+
+
+/**
+ * @brief Parse a question
+ * @param[in] interface Underlying network interface
+ * @param[in] query Incoming mDNS query message
+ * @param[in] offset Offset to first byte of the question
+ * @param[in] question Pointer to the question
+ * @param[in,out] response mDNS response message
+ * @return Error code
+ **/
+
+error_t mdnsResponderParseQuestion(NetInterface *interface, const MdnsMessage *query,
+   size_t offset, const DnsQuestion *question, MdnsMessage *response)
+{
+   error_t error;
+   uint16_t qclass;
+   uint16_t qtype;
+   uint32_t ttl;
+   bool_t cacheFlush;
+   MdnsResponderContext *context;
+
+   //Point to the mDNS responder context
+   context = interface->mdnsResponderContext;
+
+   //Check the state of the mDNS responder
+   if(context->state != MDNS_STATE_ANNOUNCING &&
+      context->state != MDNS_STATE_IDLE)
+   {
+      //Do not respond to mDNS queries during probing
+      return NO_ERROR;
+   }
+
+   //Convert the query class to host byte order
+   qclass = ntohs(question->qclass);
+   //Discard QU flag
+   qclass &= ~MDNS_QCLASS_QU;
+
+   //Convert the query type to host byte order
+   qtype = ntohs(question->qtype);
+
+   //Get the TTL resource record
+   ttl = context->settings.ttl;
+
+   //Check whether the querier originating the query is a simple resolver
+   if(ntohs(query->udpHeader->srcPort) != MDNS_PORT)
+   {
+      //The resource record TTL given in a legacy unicast response should
+      //not be greater than ten seconds, even if the true TTL of the mDNS
+      //resource record is higher
+      ttl = MIN(ttl, MDNS_LEGACY_UNICAST_RR_TTL);
+
+      //The cache-flush bit must not be set in legacy unicast responses
+      cacheFlush = FALSE;
+   }
+   else
+   {
+      //The cache-bit should be set for unique resource records
+      cacheFlush = TRUE;
+   }
+
+   //Check the class of the query
+   if(qclass == DNS_RR_CLASS_IN || qclass == DNS_RR_CLASS_ANY)
+   {
+      //Compare domain name
+      if(!mdnsCompareName(query->dnsHeader, query->length,
+         offset, context->hostname, "", ".local", 0))
+      {
+#if (IPV4_SUPPORT == ENABLED)
+         //A query?
+         if(qtype == DNS_RR_TYPE_A)
+         {
+            //Format A resource record
+            error = mdnsResponderAddIpv4AddrRecord(interface,
+               response, cacheFlush, ttl);
+            //Any error to report?
+            if(error)
+               return error;
+         }
+         else
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+         //AAAA query?
+         if(qtype == DNS_RR_TYPE_AAAA)
+         {
+            //Format AAAA resource record
+            error = mdnsResponderAddIpv6AddrRecord(interface,
+               response, cacheFlush, ttl);
+            //Any error to report?
+            if(error)
+               return error;
+         }
+         else
+#endif
+         //ANY query?
+         if(qtype == DNS_RR_TYPE_ANY)
+         {
+            //Format A resource record
+            error = mdnsResponderAddIpv4AddrRecord(interface,
+               response, cacheFlush, ttl);
+            //Any error to report?
+            if(error)
+               return error;
+
+            //Format AAAA resource record
+            error = mdnsResponderAddIpv6AddrRecord(interface,
+               response, cacheFlush, ttl);
+            //Any error to report?
+            if(error)
+               return error;
+
+            //Format NSEC resource record
+            error = mdnsResponderAddNsecRecord(interface,
+               response, cacheFlush, ttl);
+            //Any error to report?
+            if(error)
+               return error;
+         }
+         else
+         {
+            //Format NSEC resource record
+            error = mdnsResponderAddNsecRecord(interface,
+               response, cacheFlush, ttl);
+            //Any error to report?
+            if(error)
+               return error;
+         }
+      }
+
+#if (IPV4_SUPPORT == ENABLED)
+      //Reverse DNS lookup?
+      if(!mdnsCompareName(query->dnsHeader, query->length,
+         offset, context->ipv4ReverseName, "in-addr", ".arpa", 0))
+      {
+         //PTR query?
+         if(qtype == DNS_RR_TYPE_PTR || qtype == DNS_RR_TYPE_ANY)
+         {
+            //Format reverse address mapping PTR record (IPv4)
+            error = mdnsResponderAddIpv4ReversePtrRecord(interface,
+               response, cacheFlush, ttl);
+            //Any error to report?
+            if(error)
+               return error;
+         }
+      }
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+      //Reverse DNS lookup?
+      if(!mdnsCompareName(query->dnsHeader, query->length,
+         offset, context->ipv6ReverseName, "ip6", ".arpa", 0))
+      {
+         //PTR query?
+         if(qtype == DNS_RR_TYPE_PTR || qtype == DNS_RR_TYPE_ANY)
+         {
+            //Format reverse address mapping PTR record (IPv6)
+            error = mdnsResponderAddIpv6ReversePtrRecord(interface,
+               response, cacheFlush, ttl);
+            //Any error to report?
+            if(error)
+               return error;
+         }
+      }
+#endif
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse a resource record from the Known-Answer Section
+ * @param[in] interface Underlying network interface
+ * @param[in] query Incoming mDNS query message
+ * @param[in] queryOffset Offset to first byte of the resource record
+ * @param[in] queryRecord Pointer to the resource record
+ * @param[in,out] response mDNS response message
+ **/
+
+void mdnsResponderParseKnownAnRecord(NetInterface *interface, const MdnsMessage *query,
+   size_t queryOffset, const DnsResourceRecord *queryRecord, MdnsMessage *response)
+{
+   size_t i;
+   size_t n;
+   size_t responseOffset;
+   DnsResourceRecord *responseRecord;
+
+   //mDNS responses must not contain any questions in the Question Section
+   if(response->dnsHeader->qdcount == 0)
+   {
+      //Point to the first resource record
+      responseOffset = sizeof(DnsHeader);
+
+      //Parse the Answer Section of the response
+      for(i = 0; i < response->dnsHeader->ancount; i++)
+      {
+         //Parse resource record name
+         n = dnsParseName(response->dnsHeader, response->length, responseOffset, NULL, 0);
+         //Invalid name?
+         if(!n)
+            break;
+
+         //Point to the associated resource record
+         responseRecord = DNS_GET_RESOURCE_RECORD(response->dnsHeader, n);
+         //Point to the resource data
+         n += sizeof(DnsResourceRecord);
+
+         //Make sure the resource record is valid
+         if(n > response->length)
+            break;
+
+         //Point to the end of the resource record
+         n += ntohs(responseRecord->rdlength);
+
+         //Make sure the resource record is valid
+         if(n > response->length)
+            break;
+
+         //Compare resource record names
+         if(!dnsCompareEncodedName(query->dnsHeader, query->length, queryOffset,
+            response->dnsHeader, response->length, responseOffset, 0))
+         {
+            //Compare the contents of the resource records
+            if(!mdnsCompareRecord(query, queryOffset, queryRecord,
+               response, responseOffset, responseRecord))
+            {
+               //A mDNS responder must not answer a mDNS query if the answer
+               //it would give is already included in the Answer Section with
+               //an RR TTL at least half the correct value
+               if(ntohl(queryRecord->ttl) >= (ntohl(responseRecord->ttl) / 2))
+               {
+                  //Perform Known-Answer Suppression
+                  memmove((uint8_t *) response->dnsHeader + responseOffset,
+                     (uint8_t *) response->dnsHeader + n, response->length - n);
+
+                  //Update the length of the mDNS response message
+                  response->length -= (n - responseOffset);
+                  //Update the number of resource records in the Answer Section
+                  response->dnsHeader->ancount--;
+
+                  //Keep at the same position
+                  n = responseOffset;
+                  i--;
+               }
+            }
+         }
+
+         //Point to the next resource record
+         responseOffset = n;
+      }
+   }
+}
+
+
+/**
+ * @brief Parse a resource record from the Authority Section
+ * @param[in] interface Underlying network interface
+ * @param[in] query Incoming mDNS query message
+ * @param[in] offset Offset to first byte of the resource record
+ * @param[in] record Pointer to the resource record
+ **/
+
+void mdnsResponderParseNsRecord(NetInterface *interface,
+   const MdnsMessage *query, size_t offset, const DnsResourceRecord *record)
+{
+   bool_t tieBreakLost;
+   uint16_t rclass;
+   MdnsResponderContext *context;
+
+   //Point to the mDNS responder context
+   context = interface->mdnsResponderContext;
+
+   //When a host that is probing for a record sees another host issue a query
+   //for the same record, it consults the Authority Section of that query.
+   //If it finds any resource record there which answers the query, then it
+   //compares the data of that resource record with its own tentative data
+   if(!mdnsCompareName(query->dnsHeader, query->length,
+      offset, context->hostname, "", ".local", 0))
+   {
+      //Convert the class to host byte order
+      rclass = ntohs(record->rclass);
+      //Discard Cache Flush flag
+      rclass &= ~MDNS_RCLASS_CACHE_FLUSH;
+
+      //Check the class of the resource record
+      if(rclass == DNS_RR_CLASS_IN)
+      {
+#if (IPV4_SUPPORT == ENABLED)
+         //A resource record found?
+         if(ntohs(record->rtype) == DNS_RR_TYPE_A)
+         {
+            //Apply tie-breaking rules
+            tieBreakLost = TRUE;
+
+            //Verify the length of the data field
+            if(ntohs(record->rdlength) == sizeof(Ipv4Addr))
+            {
+               //Valid host address?
+               if(interface->ipv4Context.addrState == IPV4_ADDR_STATE_VALID)
+               {
+                  //The two records are compared and the lexicographically
+                  //later data wins
+                  if(memcmp(&interface->ipv4Context.addr, record->rdata,
+                     sizeof(Ipv4Addr)) >= 0)
+                  {
+                     tieBreakLost = FALSE;
+                  }
+               }
+            }
+
+            //Check whether the host has lost the tie-break
+            if(tieBreakLost)
+               context->tieBreakLost = TRUE;
+         }
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+         //AAAA resource record found?
+         if(ntohs(record->rtype) == DNS_RR_TYPE_AAAA)
+         {
+            //Apply tie-breaking rules
+            tieBreakLost = TRUE;
+
+            //Verify the length of the data field
+            if(ntohs(record->rdlength) == sizeof(Ipv6Addr))
+            {
+               uint_t i;
+               Ipv6AddrEntry *entry;
+
+               //Loop through the list of IPv6 addresses assigned to the interface
+               for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++)
+               {
+                  //Point to the current entry
+                  entry = &interface->ipv6Context.addrList[i];
+
+                  //Valid IPv6 address
+                  if(entry->state != IPV6_ADDR_STATE_INVALID)
+                  {
+                     //The two records are compared and the lexicographically
+                     //later data wins
+                     if(memcmp(&interface->ipv6Context.addrList[i].addr,
+                        record->rdata, sizeof(Ipv6Addr)) >= 0)
+                     {
+                        tieBreakLost = FALSE;
+                     }
+                  }
+               }
+            }
+
+            //Check whether the host has lost the tie-break
+            if(tieBreakLost)
+               context->tieBreakLost = TRUE;
+         }
+#endif
+      }
+   }
+}
+
+
+/**
+ * @brief Parse a resource record from the Answer Section
+ * @param[in] interface Underlying network interface
+ * @param[in] response Incoming mDNS response message
+ * @param[in] offset Offset to first byte of the resource record to be checked
+ * @param[in] record Pointer to the resource record
+ **/
+
+void mdnsResponderParseAnRecord(NetInterface *interface,
+   const MdnsMessage *response, size_t offset, const DnsResourceRecord *record)
+{
+   bool_t conflict;
+   uint16_t rclass;
+   MdnsResponderContext *context;
+
+   //Point to the mDNS responder context
+   context = interface->mdnsResponderContext;
+
+   //Check for conflicts
+   if(!mdnsCompareName(response->dnsHeader, response->length,
+      offset, context->hostname, "", ".local", 0))
+   {
+      //Convert the class to host byte order
+      rclass = ntohs(record->rclass);
+      //Discard Cache Flush flag
+      rclass &= ~MDNS_RCLASS_CACHE_FLUSH;
+
+      //Check the class of the resource record
+      if(rclass == DNS_RR_CLASS_IN)
+      {
+#if (IPV4_SUPPORT == ENABLED)
+         //A resource record found?
+         if(ntohs(record->rtype) == DNS_RR_TYPE_A)
+         {
+            //A conflict occurs when a mDNS responder has a unique record for
+            //which it is currently authoritative, and it receives a mDNS
+            //response message containing a record with the same name, rrtype
+            //and rrclass, but inconsistent rdata
+            conflict = TRUE;
+
+            //Verify the length of the data field
+            if(ntohs(record->rdlength) == sizeof(Ipv4Addr))
+            {
+               //Valid host address?
+               if(interface->ipv4Context.addrState == IPV4_ADDR_STATE_VALID)
+               {
+                  //Check whether the rdata field is consistent
+                  if(ipv4CompAddr(&interface->ipv4Context.addr, record->rdata))
+                     context->conflict = FALSE;
+               }
+            }
+
+            //Check whether the hostname is already in use by some other host
+            if(conflict)
+               context->conflict = TRUE;
+         }
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+         //AAAA resource record found?
+         if(ntohs(record->rtype) == DNS_RR_TYPE_AAAA)
+         {
+            //A conflict occurs when a mDNS responder has a unique record for
+            //which it is currently authoritative, and it receives a mDNS
+            //response message containing a record with the same name, rrtype
+            //and rrclass, but inconsistent rdata
+            conflict = TRUE;
+
+            //Verify the length of the data field
+            if(ntohs(record->rdlength) == sizeof(Ipv6Addr))
+            {
+               uint_t i;
+               Ipv6AddrEntry *entry;
+
+               //Loop through the list of IPv6 addresses assigned to the interface
+               for(i = 0; i < IPV6_ADDR_LIST_SIZE; i++)
+               {
+                  //Point to the current entry
+                  entry = &interface->ipv6Context.addrList[i];
+
+                  //Valid IPv6 address
+                  if(entry->state != IPV6_ADDR_STATE_INVALID)
+                  {
+                     //Check whether the rdata field is consistent
+                     if(ipv6CompAddr(&interface->ipv6Context.addrList[i].addr, record->rdata))
+                        conflict = FALSE;
+                  }
+               }
+            }
+
+            //Check whether the hostname is already in use by some other host
+            if(conflict)
+               context->conflict = TRUE;
+         }
+#endif
+      }
+   }
+}
+
+
+/**
+ * @brief Additional record generation
+ * @param[in] interface Underlying network interface
+ * @param[in,out] response mDNS response message
+ * @param[in] legacyUnicast This flag is set for legacy unicast responses
+ **/
+
+void mdnsResponderGenerateAdditionalRecords(NetInterface *interface,
+   MdnsMessage *response, bool_t legacyUnicast)
+{
+   error_t error;
+   uint_t i;
+   uint_t k;
+   size_t n;
+   size_t offset;
+   uint_t ancount;
+   uint16_t rclass;
+   uint32_t ttl;
+   bool_t cacheFlush;
+   MdnsResponderContext *context;
+   DnsResourceRecord *record;
+
+   //Point to the mDNS responder context
+   context = interface->mdnsResponderContext;
+
+   //mDNS responses must not contain any questions in the Question Section
+   if(response->dnsHeader->qdcount != 0)
+      return;
+
+   //Get the TTL resource record
+   ttl = context->settings.ttl;
+
+   //Check whether the querier originating the query is a simple resolver
+   if(legacyUnicast)
+   {
+      //The resource record TTL given in a legacy unicast response should
+      //not be greater than ten seconds, even if the true TTL of the mDNS
+      //resource record is higher
+      ttl = MIN(ttl, MDNS_LEGACY_UNICAST_RR_TTL);
+
+      //The cache-flush bit must not be set in legacy unicast responses
+      cacheFlush = FALSE;
+   }
+   else
+   {
+      //The cache-bit should be set for unique resource records
+      cacheFlush = TRUE;
+   }
+
+   //Point to the first resource record
+   offset = sizeof(DnsHeader);
+
+   //Save the number of resource records in the Answer Section
+   ancount = response->dnsHeader->ancount;
+
+   //Compute the total number of resource records
+   k = response->dnsHeader->ancount + response->dnsHeader->nscount +
+      response->dnsHeader->arcount;
+
+   //Loop through the resource records
+   for(i = 0; i < k; i++)
+   {
+      //Parse resource record name
+      n = dnsParseName(response->dnsHeader, response->length, offset, NULL, 0);
+      //Invalid name?
+      if(!n)
+         break;
+
+      //Point to the associated resource record
+      record = DNS_GET_RESOURCE_RECORD(response->dnsHeader, n);
+      //Point to the resource data
+      n += sizeof(DnsResourceRecord);
+
+      //Make sure the resource record is valid
+      if(n > response->length)
+         break;
+      if((n + ntohs(record->rdlength)) > response->length)
+         break;
+
+      //Convert the record class to host byte order
+      rclass = ntohs(record->rclass);
+      //Discard the cache-flush bit
+      rclass &= ~MDNS_RCLASS_CACHE_FLUSH;
+
+      //Check the class of the resource record
+      if(rclass == DNS_RR_CLASS_IN)
+      {
+         //A record?
+         if(ntohs(record->rtype) == DNS_RR_TYPE_A)
+         {
+#if (IPV6_SUPPORT == ENABLED)
+            //When a mDNS responder places an IPv4 address record into a
+            //response message, it should also place any IPv6 address records
+            //with the same name into the Additional Section
+            error = mdnsResponderAddIpv6AddrRecord(interface,
+               response, cacheFlush, ttl);
+#else
+            //In the event that a device has only IPv4 addresses but no IPv6
+            //addresses, then the appropriate NSEC record should be placed
+            //into the Additional Section
+            error = mdnsResponderAddNsecRecord(interface,
+               response, cacheFlush, ttl);
+#endif
+            //Any error to report?
+            if(error)
+               return;
+         }
+         //AAAA record?
+         else if(ntohs(record->rtype) == DNS_RR_TYPE_AAAA)
+         {
+#if (IPV4_SUPPORT == ENABLED)
+            //When a mDNS responder places an IPv6 address record into a
+            //response message, it should also place any IPv4 address records
+            //with the same name into the Additional Section
+            error = mdnsResponderAddIpv4AddrRecord(interface,
+               response, cacheFlush, ttl);
+#else
+            //In the event that a device has only IPv6 addresses but no IPv4
+            //addresses, then the appropriate NSEC record should be placed
+            //into the Additional Section
+            error = mdnsResponderAddNsecRecord(interface,
+               response, cacheFlush, ttl);
+#endif
+            //Any error to report?
+            if(error)
+               return;
+         }
+         //SRV record?
+         else if(ntohs(record->rtype) == DNS_RR_TYPE_SRV)
+         {
+            //Format A resource record
+            error = mdnsResponderAddIpv4AddrRecord(interface,
+               response, cacheFlush, ttl);
+            //Any error to report?
+            if(error)
+               return;
+
+            //Format AAAA resource record
+            error = mdnsResponderAddIpv6AddrRecord(interface,
+               response, cacheFlush, ttl);
+            //Any error to report?
+            if(error)
+               return;
+
+#if (IPV4_SUPPORT == DISABLED || IPV6_SUPPORT == DISABLED)
+            //In the event that a device has only IPv4 addresses but no IPv6
+            //addresses, or vice versa, then the appropriate NSEC record should
+            //be placed into the additional section, so that queriers can know
+            //with certainty that the device has no addresses of that kind
+            error = mdnsResponderAddNsecRecord(interface,
+               response, cacheFlush, ttl);
+            //Any error to report?
+            if(error)
+               return;
+#endif
+         }
+      }
+
+      //Point to the next resource record
+      offset = n + ntohs(record->rdlength);
+   }
+
+   //Number of resource records in the Additional Section
+   response->dnsHeader->arcount += response->dnsHeader->ancount - ancount;
+   //Number of resource records in the Answer Section
+   response->dnsHeader->ancount = ancount;
+}
+
+
+/**
+ * @brief Add A record to a mDNS message
+ * @param[in] interface Underlying network interface
+ * @param[in,out] message Pointer to the mDNS message
+ * @param[in] cacheFlush Cache-flush bit
+ * @param[in] ttl Resource record TTL (cache lifetime)
+ * @return Error code
+ **/
+
+error_t mdnsResponderAddIpv4AddrRecord(NetInterface *interface,
+   MdnsMessage *message, bool_t cacheFlush, uint32_t ttl)
+{
+#if (IPV4_SUPPORT == ENABLED)
+   size_t n;
+   size_t offset;
+   bool_t duplicate;
+   MdnsResponderContext *context;
+   DnsResourceRecord *record;
+
+   //Point to the mDNS responder context
+   context = interface->mdnsResponderContext;
+
+   //Valid IPv4 host address?
+   if(interface->ipv4Context.addrState == IPV4_ADDR_STATE_VALID)
+   {
+      //Check whether the resource record is already present in the Answer
+      //Section of the message
+      duplicate = mdnsCheckDuplicateRecord(message, context->hostname,
+         "", ".local", DNS_RR_TYPE_A);
+
+      //The duplicates should be suppressed and the resource record should
+      //appear only once in the list
+      if(!duplicate)
+      {
+         //Set the position to the end of the buffer
+         offset = message->length;
+
+         //The first pass calculates the length of the DNS encoded host name
+         n = mdnsEncodeName(context->hostname, "", ".local", NULL);
+
+         //Check the length of the resulting mDNS message
+         if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+            return ERROR_MESSAGE_TOO_LONG;
+
+         //The second pass encodes the host name using the DNS name notation
+         offset += mdnsEncodeName(context->hostname, "", ".local",
+            (uint8_t *) message->dnsHeader + offset);
+
+         //Consider the length of the resource record itself
+         n = sizeof(DnsResourceRecord) + sizeof(Ipv4Addr);
+
+         //Check the length of the resulting mDNS message
+         if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+            return ERROR_MESSAGE_TOO_LONG;
+
+         //Point to the corresponding resource record
+         record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset);
+
+         //Fill in resource record
+         record->rtype = HTONS(DNS_RR_TYPE_A);
+         record->rclass = HTONS(DNS_RR_CLASS_IN);
+         record->ttl = htonl(ttl);
+         record->rdlength = HTONS(sizeof(Ipv4Addr));
+
+         //Check whether the cache-flush bit should be set
+         if(cacheFlush)
+            record->rclass |= HTONS(MDNS_RCLASS_CACHE_FLUSH);
+
+         //Copy IPv4 address
+         ipv4CopyAddr(record->rdata, &interface->ipv4Context.addr);
+
+         //Number of resource records in the answer section
+         message->dnsHeader->ancount++;
+         //Update the length of the mDNS response message
+         message->length = offset + n;
+      }
+   }
+#endif
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Add AAAA record to a mDNS message
+ * @param[in] interface Underlying network interface
+ * @param[in,out] message Pointer to the mDNS message
+ * @param[in] cacheFlush Cache-flush bit
+ * @param[in] ttl Resource record TTL (cache lifetime)
+ * @return Error code
+ **/
+
+error_t mdnsResponderAddIpv6AddrRecord(NetInterface *interface,
+   MdnsMessage *message, bool_t cacheFlush, uint32_t ttl)
+{
+#if (IPV6_SUPPORT == ENABLED)
+   size_t n;
+   size_t offset;
+   bool_t duplicate;
+   MdnsResponderContext *context;
+   DnsResourceRecord *record;
+
+   //Point to the mDNS responder context
+   context = interface->mdnsResponderContext;
+
+   //Valid IPv6 link-local address?
+   if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED)
+   {
+      //Check whether the resource record is already present in the Answer
+      //Section of the message
+      duplicate = mdnsCheckDuplicateRecord(message, context->hostname,
+         "", ".local", DNS_RR_TYPE_AAAA);
+
+      //The duplicates should be suppressed and the resource record should
+      //appear only once in the list
+      if(!duplicate)
+      {
+         //Set the position to the end of the buffer
+         offset = message->length;
+
+         //The first pass calculates the length of the DNS encoded host name
+         n = mdnsEncodeName(context->hostname, "", ".local", NULL);
+
+         //Check the length of the resulting mDNS message
+         if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+            return ERROR_MESSAGE_TOO_LONG;
+
+         //The second pass encodes the host name using the DNS name notation
+         offset += mdnsEncodeName(context->hostname, "", ".local",
+            (uint8_t *) message->dnsHeader + offset);
+
+         //Consider the length of the resource record itself
+         n = sizeof(DnsResourceRecord) + sizeof(Ipv6Addr);
+
+         //Check the length of the resulting mDNS message
+         if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+            return ERROR_MESSAGE_TOO_LONG;
+
+         //Point to the corresponding resource record
+         record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset);
+
+         //Fill in resource record
+         record->rtype = HTONS(DNS_RR_TYPE_AAAA);
+         record->rclass = HTONS(DNS_RR_CLASS_IN);
+         record->ttl = htonl(ttl);
+         record->rdlength = HTONS(sizeof(Ipv6Addr));
+
+         //Check whether the cache-flush bit should be set
+         if(cacheFlush)
+            record->rclass |= HTONS(MDNS_RCLASS_CACHE_FLUSH);
+
+         //Copy IPv6 address
+         ipv6CopyAddr(record->rdata, &interface->ipv6Context.addrList[0].addr);
+
+         //Number of resource records in the answer section
+         message->dnsHeader->ancount++;
+         //Update the length of the mDNS response message
+         message->length = offset + n;
+      }
+   }
+#endif
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Add reverse address mapping PTR record (IPv4)
+ * @param[in] interface Underlying network interface
+ * @param[in,out] message Pointer to the mDNS message
+ * @param[in] cacheFlush Cache-flush bit
+ * @param[in] ttl Resource record TTL (cache lifetime)
+ * @return Error code
+ **/
+
+error_t mdnsResponderAddIpv4ReversePtrRecord(NetInterface *interface,
+   MdnsMessage *message, bool_t cacheFlush, uint32_t ttl)
+{
+#if (IPV4_SUPPORT == ENABLED)
+   size_t n;
+   size_t offset;
+   bool_t duplicate;
+   MdnsResponderContext *context;
+   DnsResourceRecord *record;
+
+   //Point to the mDNS responder context
+   context = interface->mdnsResponderContext;
+
+   //Valid reverse name?
+   if(context->ipv4ReverseName[0] != '\0')
+   {
+      //Check whether the resource record is already present in the Answer
+      //Section of the message
+      duplicate = mdnsCheckDuplicateRecord(message, context->ipv4ReverseName,
+         "in-addr", ".arpa", DNS_RR_TYPE_PTR);
+
+      //The duplicates should be suppressed and the resource record should
+      //appear only once in the list
+      if(!duplicate)
+      {
+         //Set the position to the end of the buffer
+         offset = message->length;
+
+         //The first pass calculates the length of the DNS encoded reverse name
+         n = mdnsEncodeName(context->ipv4ReverseName, "in-addr", ".arpa", NULL);
+
+         //Check the length of the resulting mDNS message
+         if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+            return ERROR_MESSAGE_TOO_LONG;
+
+         //The second pass encodes the reverse name using the DNS name notation
+         offset += mdnsEncodeName(context->ipv4ReverseName, "in-addr", ".arpa",
+            (uint8_t *) message->dnsHeader + offset);
+
+         //Consider the length of the resource record itself
+         n = sizeof(DnsResourceRecord) + sizeof(Ipv4Addr);
+
+         //Check the length of the resulting mDNS message
+         if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+            return ERROR_MESSAGE_TOO_LONG;
+
+         //Point to the corresponding resource record
+         record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset);
+
+         //Fill in resource record
+         record->rtype = HTONS(DNS_RR_TYPE_PTR);
+         record->rclass = HTONS(DNS_RR_CLASS_IN);
+         record->ttl = htonl(ttl);
+
+         //Check whether the cache-flush bit should be set
+         if(cacheFlush)
+            record->rclass |= HTONS(MDNS_RCLASS_CACHE_FLUSH);
+
+         //Advance write index
+         offset += sizeof(DnsResourceRecord);
+
+         //The first pass calculates the length of the DNS encoded host name
+         n = mdnsEncodeName("", context->hostname, ".local", NULL);
+
+         //Check the length of the resulting mDNS message
+         if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+            return ERROR_MESSAGE_TOO_LONG;
+
+         //The second pass encodes the host name using DNS notation
+         n = mdnsEncodeName("", context->hostname, ".local", record->rdata);
+
+         //Convert length field to network byte order
+         record->rdlength = htons(n);
+
+         //Number of resource records in the answer section
+         message->dnsHeader->ancount++;
+         //Update the length of the mDNS response message
+         message->length = offset + n;
+      }
+   }
+#endif
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Add reverse address mapping PTR record (IPv6)
+ * @param[in] interface Underlying network interface
+ * @param[in,out] message Pointer to the mDNS message
+ * @param[in] cacheFlush Cache-flush bit
+ * @param[in] ttl Resource record TTL (cache lifetime)
+ * @return Error code
+ **/
+
+error_t mdnsResponderAddIpv6ReversePtrRecord(NetInterface *interface,
+   MdnsMessage *message, bool_t cacheFlush, uint32_t ttl)
+{
+#if (IPV6_SUPPORT == ENABLED)
+   size_t n;
+   size_t offset;
+   bool_t duplicate;
+   MdnsResponderContext *context;
+   DnsResourceRecord *record;
+
+   //Point to the mDNS responder context
+   context = interface->mdnsResponderContext;
+
+   //Valid reverse name?
+   if(context->ipv6ReverseName[0] != '\0')
+   {
+      //Check whether the resource record is already present in the Answer
+      //Section of the message
+      duplicate = mdnsCheckDuplicateRecord(message, context->ipv6ReverseName,
+         "ip6", ".arpa", DNS_RR_TYPE_PTR);
+
+      //The duplicates should be suppressed and the resource record should
+      //appear only once in the list
+      if(!duplicate)
+      {
+         //Set the position to the end of the buffer
+         offset = message->length;
+
+         //The first pass calculates the length of the DNS encoded reverse name
+         n = mdnsEncodeName(context->ipv6ReverseName, "ip6", ".arpa", NULL);
+
+         //Check the length of the resulting mDNS message
+         if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+            return ERROR_MESSAGE_TOO_LONG;
+
+         //The second pass encodes the reverse name using the DNS name notation
+         offset += mdnsEncodeName(context->ipv6ReverseName, "ip6", ".arpa",
+            (uint8_t *) message->dnsHeader + offset);
+
+         //Consider the length of the resource record itself
+         n = sizeof(DnsResourceRecord) + sizeof(Ipv4Addr);
+
+         //Check the length of the resulting mDNS message
+         if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+            return ERROR_MESSAGE_TOO_LONG;
+
+         //Point to the corresponding resource record
+         record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset);
+
+         //Fill in resource record
+         record->rtype = HTONS(DNS_RR_TYPE_PTR);
+         record->rclass = HTONS(DNS_RR_CLASS_IN);
+         record->ttl = htonl(ttl);
+
+         //Check whether the cache-flush bit should be set
+         if(cacheFlush)
+            record->rclass |= HTONS(MDNS_RCLASS_CACHE_FLUSH);
+
+         //Advance write index
+         offset += sizeof(DnsResourceRecord);
+
+         //The first pass calculates the length of the DNS encoded host name
+         n = mdnsEncodeName("", context->hostname, ".local", NULL);
+
+         //Check the length of the resulting mDNS message
+         if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+            return ERROR_MESSAGE_TOO_LONG;
+
+         //The second pass encodes the host name using DNS notation
+         n = mdnsEncodeName("", context->hostname, ".local", record->rdata);
+
+         //Convert length field to network byte order
+         record->rdlength = htons(n);
+
+         //Number of resource records in the answer section
+         message->dnsHeader->ancount++;
+         //Update the length of the mDNS response message
+         message->length = offset + n;
+      }
+   }
+#endif
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Add NSEC record to a mDNS message
+ * @param[in] interface Underlying network interface
+ * @param[in,out] message Pointer to the mDNS message
+ * @param[in] cacheFlush Cache-flush bit
+ * @param[in] ttl Resource record TTL (cache lifetime)
+ * @return Error code
+ **/
+
+error_t mdnsResponderAddNsecRecord(NetInterface *interface,
+   MdnsMessage *message, bool_t cacheFlush, uint32_t ttl)
+{
+   size_t n;
+   size_t offset;
+   bool_t duplicate;
+   size_t bitmapLength;
+   uint8_t bitmap[8];
+   MdnsResponderContext *context;
+   DnsResourceRecord *record;
+
+   //Point to the mDNS responder context
+   context = interface->mdnsResponderContext;
+
+   //Check whether the resource record is already present in the Answer
+   //Section of the message
+   duplicate = mdnsCheckDuplicateRecord(message, context->hostname,
+      "", ".local", DNS_RR_TYPE_NSEC);
+
+   //The duplicates should be suppressed and the resource record should
+   //appear only once in the list
+   if(!duplicate)
+   {
+      //The bitmap identifies the resource record types that exist
+      memset(bitmap, 0, sizeof(bitmap));
+
+#if (IPV4_SUPPORT == ENABLED)
+      //A resource record is supported
+      DNS_SET_NSEC_BITMAP(bitmap, DNS_RR_TYPE_A);
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+      //A resource record is supported
+      DNS_SET_NSEC_BITMAP(bitmap, DNS_RR_TYPE_AAAA);
+#endif
+
+      //Compute the length of the bitmap
+      for(bitmapLength = sizeof(bitmap); bitmapLength > 0; bitmapLength--)
+      {
+         //Trailing zero octets in the bitmap must be omitted...
+         if(bitmap[bitmapLength - 1] != 0x00)
+            break;
+      }
+
+      //Set the position to the end of the buffer
+      offset = message->length;
+
+      //The first pass calculates the length of the DNS encoded host name
+      n = mdnsEncodeName(context->hostname, "", ".local", NULL);
+
+      //Check the length of the resulting mDNS message
+      if((offset + n) > MDNS_MESSAGE_MAX_SIZE)
+         return ERROR_MESSAGE_TOO_LONG;
+
+      //The second pass encodes the host name using the DNS name notation
+      offset += mdnsEncodeName(context->hostname, "", ".local",
+         (uint8_t *) message->dnsHeader + offset);
+
+      //Consider the length of the resource record itself
+      if((offset + sizeof(DnsResourceRecord)) > MDNS_MESSAGE_MAX_SIZE)
+         return ERROR_MESSAGE_TOO_LONG;
+
+      //Point to the corresponding resource record
+      record = DNS_GET_RESOURCE_RECORD(message->dnsHeader, offset);
+
+      //Fill in resource record
+      record->rtype = HTONS(DNS_RR_TYPE_NSEC);
+      record->rclass = HTONS(DNS_RR_CLASS_IN);
+      record->ttl = htonl(ttl);
+
+      //Check whether the cache-flush bit should be set
+      if(cacheFlush)
+         record->rclass |= HTONS(MDNS_RCLASS_CACHE_FLUSH);
+
+      //Advance write index
+      offset += sizeof(DnsResourceRecord);
+
+      //Check the length of the resulting mDNS message
+      if((offset + n + 2 + bitmapLength) > MDNS_MESSAGE_MAX_SIZE)
+         return ERROR_MESSAGE_TOO_LONG;
+
+      //The Next Domain Name field contains the record's own name
+      mdnsEncodeName(context->hostname, "", ".local", record->rdata);
+
+      //DNS NSEC record is limited to Window Block number zero
+      record->rdata[n++] = 0;
+      //The Bitmap Length is a value in the range 1-32
+      record->rdata[n++] = bitmapLength;
+
+      //The Bitmap data identifies the resource record types that exist
+      memcpy(record->rdata + n, bitmap, bitmapLength);
+
+      //Convert length field to network byte order
+      record->rdlength = htons(n + bitmapLength);
+
+      //Number of resource records in the answer section
+      message->dnsHeader->ancount++;
+      //Update the length of the DNS message
+      message->length = offset + n + bitmapLength;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mdns/mdns_responder.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,247 @@
+/**
+ * @file mdns_responder.h
+ * @brief mDNS responder (Multicast DNS)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MDNS_RESPONDER_H
+#define _MDNS_RESPONDER_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/udp.h"
+#include "dns/dns_common.h"
+#include "mdns/mdns_common.h"
+
+//mDNS responder support
+#ifndef MDNS_RESPONDER_SUPPORT
+   #define MDNS_RESPONDER_SUPPORT DISABLED
+#elif (MDNS_RESPONDER_SUPPORT != ENABLED && MDNS_RESPONDER_SUPPORT != DISABLED)
+   #error MDNS_RESPONDER_SUPPORT parameter is not valid
+#endif
+
+//mDNS responder tick interval
+#ifndef MDNS_RESPONDER_TICK_INTERVAL
+   #define MDNS_RESPONDER_TICK_INTERVAL 250
+#elif (MDNS_RESPONDER_TICK_INTERVAL < 10)
+   #error MDNS_RESPONDER_TICK_INTERVAL parameter is not valid
+#endif
+
+//Maximum length of host name
+#ifndef MDNS_RESPONDER_MAX_HOSTNAME_LEN
+   #define MDNS_RESPONDER_MAX_HOSTNAME_LEN 32
+#elif (MDNS_RESPONDER_MAX_HOSTNAME_LEN < 1)
+   #error MDNS_RESPONDER_MAX_HOSTNAME_LEN parameter is not valid
+#endif
+
+//Maximum waiting delay
+#ifndef MDNS_MAX_WAITING_DELAY
+   #define MDNS_MAX_WAITING_DELAY 10000
+#elif (MDNS_MAX_WAITING_DELAY < 0)
+   #error MDNS_MAX_WAITING_DELAY parameter is not valid
+#endif
+
+//Initial random delay (minimum value)
+#ifndef MDNS_RAND_DELAY_MIN
+   #define MDNS_RAND_DELAY_MIN 0
+#elif (MDNS_RAND_DELAY_MIN < 0)
+   #error MDNS_RAND_DELAY_MIN parameter is not valid
+#endif
+
+//Initial random delay (maximum value)
+#ifndef MDNS_RAND_DELAY_MAX
+   #define MDNS_RAND_DELAY_MAX 250
+#elif (MDNS_RAND_DELAY_MAX < 0)
+   #error MDNS_RAND_DELAY_MAX parameter is not valid
+#endif
+
+//Number of probe packets
+#ifndef MDNS_PROBE_NUM
+   #define MDNS_PROBE_NUM 3
+#elif (MDNS_PROBE_NUM < 1)
+   #error MDNS_PROBE_NUM parameter is not valid
+#endif
+
+//Time interval between subsequent probe packets
+#ifndef MDNS_PROBE_DELAY
+   #define MDNS_PROBE_DELAY 250
+#elif (MDNS_PROBE_DELAY < 100)
+   #error MDNS_PROBE_DELAY parameter is not valid
+#endif
+
+//Delay before probing again when deferring to the winning host
+#ifndef MDNS_PROBE_DEFER
+   #define MDNS_PROBE_DEFER 1000
+#elif (MDNS_PROBE_DEFER < 100)
+   #error MDNS_PROBE_DEFER parameter is not valid
+#endif
+
+//Number of announcement packets
+#ifndef MDNS_ANNOUNCE_NUM
+   #define MDNS_ANNOUNCE_NUM 2
+#elif (MDNS_ANNOUNCE_NUM < 1)
+   #error MDNS_ANNOUNCE_NUM parameter is not valid
+#endif
+
+//Time interval between subsequent announcement packets
+#ifndef MDNS_ANNOUNCE_DELAY
+   #define MDNS_ANNOUNCE_DELAY 1000
+#elif (MDNS_ANNOUNCE_DELAY < 100)
+   #error MDNS_ANNOUNCE_DELAY parameter is not valid
+#endif
+
+//Forward declaration of DnsSdContext structure
+struct _MdnsResponderContext;
+#define MdnsResponderContext struct _MdnsResponderContext
+
+
+/**
+ * @brief mDNS responder states
+ **/
+
+typedef enum
+{
+   MDNS_STATE_INIT,
+   MDNS_STATE_WAITING,
+   MDNS_STATE_PROBING,
+   MDNS_STATE_ANNOUNCING,
+   MDNS_STATE_IDLE
+} MdnsState;
+
+
+/**
+ * @brief FSM state change callback
+ **/
+
+typedef void (*MdnsResponderStateChangeCallback)(MdnsResponderContext *context,
+   NetInterface *interface, MdnsState state);
+
+
+/**
+ * @brief mDNS responder settings
+ **/
+
+typedef struct
+{
+   NetInterface *interface;                           ///<Underlying network interface
+   uint_t numAnnouncements;                           ///<Number of announcement packets
+   uint32_t ttl;                                      ///<TTL resource record
+   MdnsResponderStateChangeCallback stateChangeEvent; ///<FSM state change event
+} MdnsResponderSettings;
+
+
+/**
+ * @brief mDNS responder context
+ **/
+
+struct _MdnsResponderContext
+{
+   MdnsResponderSettings settings;                            ///<DNS-SD settings
+   bool_t running;                                            ///<mDNS responder is currently running
+   MdnsState state;                                           ///<FSM state
+   bool_t conflict;                                           ///<Conflict detected
+   bool_t tieBreakLost;                                       ///<Tie-break lost
+   systime_t timestamp;                                       ///<Timestamp to manage retransmissions
+   systime_t timeout;                                         ///<Timeout value
+   uint_t retransmitCount;                                    ///<Retransmission counter
+   char_t hostname[MDNS_RESPONDER_MAX_HOSTNAME_LEN + 1];      ///<Hostname
+#if (IPV4_SUPPORT == ENABLED)
+   char_t ipv4ReverseName[DNS_MAX_IPV4_REVERSE_NAME_LEN + 1]; ///<Reverse DNS lookup for IPv4
+   MdnsMessage ipv4Response;                                  ///<IPv4 response message
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   char_t ipv6ReverseName[DNS_MAX_IPV6_REVERSE_NAME_LEN + 1]; ///<Reverse DNS lookup for IPv6
+   MdnsMessage ipv6Response;                                  ///<IPv6 response message
+#endif
+};
+
+
+//Tick counter to handle periodic operations
+extern systime_t mdnsResponderTickCounter;
+
+//mDNS related functions
+void mdnsResponderGetDefaultSettings(MdnsResponderSettings *settings);
+
+error_t mdnsResponderInit(MdnsResponderContext *context,
+   const MdnsResponderSettings *settings);
+
+error_t mdnsResponderStart(MdnsResponderContext *context);
+error_t mdnsResponderStop(MdnsResponderContext *context);
+MdnsState mdnsResponderGetState(MdnsResponderContext *context);
+
+error_t mdnsResponderSetHostname(MdnsResponderContext *context,
+   const char_t *hostname);
+
+error_t mdnsResponderSetIpv4ReverseName(MdnsResponderContext *context);
+error_t mdnsResponderSetIpv6ReverseName(MdnsResponderContext *context);
+
+error_t mdnsResponderStartProbing(MdnsResponderContext *context);
+
+void mdnsResponderTick(MdnsResponderContext *context);
+void mdnsResponderLinkChangeEvent(MdnsResponderContext *context);
+
+void mdnsResponderChangeState(MdnsResponderContext *context,
+   MdnsState newState, systime_t delay);
+
+void mdnsResponderChangeHostname(MdnsResponderContext *context);
+
+error_t mdnsResponderSendProbe(MdnsResponderContext *context);
+error_t mdnsResponderSendAnnouncement(MdnsResponderContext *context);
+error_t mdnsResponderSendGoodbye(MdnsResponderContext *context);
+
+void mdnsResponderProcessQuery(NetInterface *interface, MdnsMessage *query);
+
+error_t mdnsResponderParseQuestion(NetInterface *interface, const MdnsMessage *query,
+   size_t offset, const DnsQuestion *question, MdnsMessage *response);
+
+void mdnsResponderParseKnownAnRecord(NetInterface *interface, const MdnsMessage *query,
+   size_t queryOffset, const DnsResourceRecord *queryRecord, MdnsMessage *response);
+
+void mdnsResponderParseNsRecord(NetInterface *interface,
+   const MdnsMessage *query, size_t offset, const DnsResourceRecord *record);
+
+void mdnsResponderParseAnRecord(NetInterface *interface,
+   const MdnsMessage *response, size_t offset, const DnsResourceRecord *record);
+
+void mdnsResponderGenerateAdditionalRecords(NetInterface *interface,
+   MdnsMessage *response, bool_t legacyUnicast);
+
+error_t mdnsResponderAddIpv4AddrRecord(NetInterface *interface,
+   MdnsMessage *message, bool_t cacheFlush, uint32_t ttl);
+
+error_t mdnsResponderAddIpv6AddrRecord(NetInterface *interface,
+   MdnsMessage *message, bool_t cacheFlush, uint32_t ttl);
+
+error_t mdnsResponderAddIpv4ReversePtrRecord(NetInterface *interface,
+   MdnsMessage *message, bool_t cacheFlush, uint32_t ttl);
+
+error_t mdnsResponderAddIpv6ReversePtrRecord(NetInterface *interface,
+   MdnsMessage *message, bool_t cacheFlush, uint32_t ttl);
+
+error_t mdnsResponderAddNsecRecord(NetInterface *interface,
+   MdnsMessage *message, bool_t cacheFlush, uint32_t ttl);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mibs/mib2_impl.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1486 @@
+/**
+ * @file mib2_impl.c
+ * @brief MIB-II module implementation
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Dependencies
+#include "core/net.h"
+#include "mibs/mib_common.h"
+#include "mibs/mib2_module.h"
+#include "mibs/mib2_impl.h"
+#include "crypto.h"
+#include "asn1.h"
+#include "oid.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (MIB2_SUPPORT == ENABLED)
+
+
+/**
+ * @brief MIB-II module initialization
+ * @return Error code
+ **/
+
+error_t mib2Init(void)
+{
+   uint_t i;
+   Mib2SysGroup *sysGroup;
+   Mib2IfGroup *ifGroup;
+#if (IPV4_SUPPORT == ENABLED)
+   Mib2IpGroup *ipGroup;
+#endif
+#if (TCP_SUPPORT == ENABLED)
+   Mib2TcpGroup *tcpGroup;
+#endif
+
+   //Debug message
+   TRACE_INFO("Initializing standard MIB-II base...\r\n");
+
+   //Clear MIB-II base
+   memset(&mib2Base, 0, sizeof(mib2Base));
+
+   //Point to the system group
+   sysGroup = &mib2Base.sysGroup;
+
+#if (MIB2_SYS_DESCR_SIZE > 0)
+   //sysDescr object
+   strcpy(sysGroup->sysDescr, "Description");
+   sysGroup->sysDescrLen = strlen(sysGroup->sysDescr);
+#endif
+
+#if (MIB2_SYS_OBJECT_ID_SIZE > 0)
+   //sysObjectID object
+   sysGroup->sysObjectID[0] = 0;
+   sysGroup->sysObjectIDLen = 1;
+#endif
+
+#if (MIB2_SYS_CONTACT_SIZE > 0)
+   //sysContact object
+   strcpy(sysGroup->sysContact, "Contact");
+   sysGroup->sysContactLen = strlen(sysGroup->sysContact);
+#endif
+
+#if (MIB2_SYS_NAME_SIZE > 0)
+   //sysName object
+   strcpy(sysGroup->sysName, "Name");
+   sysGroup->sysNameLen = strlen(sysGroup->sysName);
+#endif
+
+#if (MIB2_SYS_LOCATION_SIZE > 0)
+   //sysLocation object
+   strcpy(sysGroup->sysLocation, "Location");
+   sysGroup->sysLocationLen = strlen(sysGroup->sysLocation);
+#endif
+
+   //sysServices object
+   sysGroup->sysServices = MIB2_SYS_SERVICE_INTERNET;
+
+   //Point to the interfaces group
+   ifGroup = &mib2Base.ifGroup;
+
+   //Interfaces table entry
+   for(i = 0; i < NET_INTERFACE_COUNT; i++)
+   {
+      //ifSpecific object
+      ifGroup->ifTable[i].ifSpecific[0] = 0;
+      ifGroup->ifTable[i].ifSpecificLen = 1;
+   }
+
+#if (IPV4_SUPPORT == ENABLED)
+   //Point to the IP group
+   ipGroup = &mib2Base.ipGroup;
+
+   //ipForwarding object
+   ipGroup->ipForwarding = MIB2_IP_FORWARDING_DISABLED;
+   //ipDefaultTTL object
+   ipGroup->ipDefaultTTL = IPV4_DEFAULT_TTL;
+   //ipReasmTimeout object
+   ipGroup->ipReasmTimeout = IPV4_FRAG_TIME_TO_LIVE / 1000;
+#endif
+
+#if (TCP_SUPPORT == ENABLED)
+   //Point to the TCP group
+   tcpGroup = &mib2Base.tcpGroup;
+
+   //tcpRtoAlgorithm object
+   tcpGroup->tcpRtoAlgorithm = MIB2_TCP_RTO_ALGORITHM_VANJ;
+   //tcpRtoMin object
+   tcpGroup->tcpRtoMin = TCP_MIN_RTO;
+   //tcpRtoMax object
+   tcpGroup->tcpRtoMax = TCP_MAX_RTO;
+   //tcpMaxConn object
+   tcpGroup->tcpMaxConn = SOCKET_MAX_COUNT;
+#endif
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Lock MIB-II base
+ **/
+
+void mib2Lock(void)
+{
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+}
+
+
+/**
+ * @brief Unlock MIB-II base
+ **/
+
+void mib2Unlock(void)
+{
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+}
+
+
+/**
+ * @brief Get sysUpTime object value
+ * @param[in] object Pointer to the MIB object descriptor
+ * @param[in] oid Object identifier (object name and instance identifier)
+ * @param[in] oidLen Length of the OID, in bytes
+ * @param[out] value Object value
+ * @param[in,out] valueLen Length of the object value, in bytes
+ * @return Error code
+ **/
+
+error_t mib2GetSysUpTime(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, MibVariant *value, size_t *valueLen)
+{
+   //Get object value
+   value->timeTicks = osGetSystemTime() / 10;
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Get ifEntry object value
+ * @param[in] object Pointer to the MIB object descriptor
+ * @param[in] oid Object identifier (object name and instance identifier)
+ * @param[in] oidLen Length of the OID, in bytes
+ * @param[out] value Object value
+ * @param[in,out] valueLen Length of the object value, in bytes
+ * @return Error code
+ **/
+
+error_t mib2GetIfEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, MibVariant *value, size_t *valueLen)
+{
+   error_t error;
+   size_t n;
+   uint_t index;
+   Mib2IfEntry *entry;
+
+   //Point to the instance identifier
+   n = object->oidLen;
+
+   //The ifIndex is used as instance identifier
+   error = mibDecodeIndex(oid, oidLen, &n, &index);
+   //Invalid instance identifier?
+   if(error)
+      return error;
+
+   //Sanity check
+   if(n != oidLen)
+      return ERROR_INSTANCE_NOT_FOUND;
+
+   //Check index range
+   if(index < 1 || index > NET_INTERFACE_COUNT)
+      return ERROR_INSTANCE_NOT_FOUND;
+
+   //Point to the interface table entry
+   entry = &mib2Base.ifGroup.ifTable[index - 1];
+
+   //ifIndex object?
+   if(!strcmp(object->name, "ifIndex"))
+   {
+      //Get object value
+      value->integer = entry->ifIndex;
+   }
+   //ifDescr object?
+   else if(!strcmp(object->name, "ifDescr"))
+   {
+      //Make sure the buffer is large enough to hold the entire object
+      if(*valueLen >= entry->ifDescrLen)
+      {
+         //Copy object value
+         memcpy(value->octetString, entry->ifDescr, entry->ifDescrLen);
+         //Return object length
+         *valueLen = entry->ifDescrLen;
+      }
+      else
+      {
+         //Report an error
+         error = ERROR_BUFFER_OVERFLOW;
+      }
+   }
+   //ifType object?
+   else if(!strcmp(object->name, "ifType"))
+   {
+      //Get object value
+      value->integer = entry->ifType;
+   }
+   //ifMtu object?
+   else if(!strcmp(object->name, "ifMtu"))
+   {
+      //Get object value
+      value->integer = entry->ifMtu;
+   }
+   //ifSpeed object?
+   else if(!strcmp(object->name, "ifSpeed"))
+   {
+      //Get object value
+      value->gauge32 = entry->ifSpeed;
+   }
+   //ifPhysAddress object?
+   else if(!strcmp(object->name, "ifPhysAddress"))
+   {
+      //Make sure the buffer is large enough to hold the entire object
+      if(*valueLen >= entry->ifPhysAddressLen)
+      {
+         //Copy object value
+         memcpy(value->octetString, entry->ifPhysAddress, entry->ifPhysAddressLen);
+         //Return object length
+         *valueLen = entry->ifPhysAddressLen;
+      }
+      else
+      {
+         //Report an error
+         error = ERROR_BUFFER_OVERFLOW;
+      }
+   }
+   //ifAdminStatus object?
+   else if(!strcmp(object->name, "ifAdminStatus"))
+   {
+      //Get object value
+      value->integer = entry->ifAdminStatus;
+   }
+   //ifOperStatus object?
+   else if(!strcmp(object->name, "ifOperStatus"))
+   {
+      //Get object value
+      value->integer = entry->ifOperStatus;
+   }
+   //ifLastChange object?
+   else if(!strcmp(object->name, "ifLastChange"))
+   {
+      //Get object value
+      value->timeTicks = entry->ifLastChange;
+   }
+   //ifInOctets object?
+   else if(!strcmp(object->name, "ifInOctets"))
+   {
+      //Get object value
+      value->counter32 = entry->ifInOctets;
+   }
+   //ifInUcastPkts object?
+   else if(!strcmp(object->name, "ifInUcastPkts"))
+   {
+      //Get object value
+      value->counter32 = entry->ifInUcastPkts;
+   }
+   //ifInNUcastPkts object?
+   else if(!strcmp(object->name, "ifInNUcastPkts"))
+   {
+      //Get object value
+      value->counter32 = entry->ifInNUcastPkts;
+   }
+   //ifInDiscards object?
+   else if(!strcmp(object->name, "ifInDiscards"))
+   {
+      //Get object value
+      value->counter32 = entry->ifInDiscards;
+   }
+   //ifInErrors object?
+   else if(!strcmp(object->name, "ifInErrors"))
+   {
+      //Get object value
+      value->counter32 = entry->ifInErrors;
+   }
+   //ifInUnknownProtos object?
+   else if(!strcmp(object->name, "ifInUnknownProtos"))
+   {
+      //Get object value
+      value->counter32 = entry->ifInUnknownProtos;
+   }
+   //ifOutOctets object?
+   else if(!strcmp(object->name, "ifOutOctets"))
+   {
+      //Get object value
+      value->counter32 = entry->ifOutOctets;
+   }
+   //ifOutUcastPkts object?
+   else if(!strcmp(object->name, "ifOutUcastPkts"))
+   {
+      //Get object value
+      value->counter32 = entry->ifOutUcastPkts;
+   }
+   //ifOutNUcastPkts object?
+   else if(!strcmp(object->name, "ifOutNUcastPkts"))
+   {
+      //Get object value
+      value->counter32 = entry->ifOutNUcastPkts;
+   }
+   //ifOutDiscards object?
+   else if(!strcmp(object->name, "ifOutDiscards"))
+   {
+      //Get object value
+      value->counter32 = entry->ifOutDiscards;
+   }
+   //ifOutErrors object?
+   else if(!strcmp(object->name, "ifOutErrors"))
+   {
+      //Get object value
+      value->counter32 = entry->ifOutErrors;
+   }
+   //ifOutQLen object?
+   else if(!strcmp(object->name, "ifOutQLen"))
+   {
+      //Get object value
+      value->gauge32 = entry->ifOutQLen;
+   }
+   //ifSpecific object?
+   else if(!strcmp(object->name, "ifSpecific"))
+   {
+      //Make sure the buffer is large enough to hold the entire object
+      if(*valueLen >= entry->ifSpecificLen)
+      {
+         //Copy object value
+         memcpy(value->oid, entry->ifSpecific, entry->ifSpecificLen);
+         //Return object length
+         *valueLen = entry->ifSpecificLen;
+      }
+      else
+      {
+         //Report an error
+         error = ERROR_BUFFER_OVERFLOW;
+      }
+   }
+   //Unknown object?
+   else
+   {
+      //The specified object does not exist
+      error = ERROR_OBJECT_NOT_FOUND;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Get next ifEntry object
+ * @param[in] object Pointer to the MIB object descriptor
+ * @param[in] oid Object identifier
+ * @param[in] oidLen Length of the OID, in bytes
+ * @param[out] nextOid OID of the next object in the MIB
+ * @param[out] nextOidLen Length of the next object identifier, in bytes
+ * @return Error code
+ **/
+
+error_t mib2GetNextIfEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, uint8_t *nextOid, size_t *nextOidLen)
+{
+   error_t error;
+   size_t n;
+   uint_t index;
+
+   //Make sure the buffer is large enough to hold the OID prefix
+   if(*nextOidLen < object->oidLen)
+      return ERROR_BUFFER_OVERFLOW;
+
+   //Copy OID prefix
+   memcpy(nextOid, object->oid, object->oidLen);
+
+   //Loop through network interfaces
+   for(index = 1; index <= NET_INTERFACE_COUNT; index++)
+   {
+      //Append the instance identifier to the OID prefix
+      n = object->oidLen;
+
+      //The ifIndex is used as instance identifier
+      error = mibEncodeIndex(nextOid, *nextOidLen, &n, index);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Check whether the resulting object identifier lexicographically
+      //follows the specified OID
+      if(oidComp(nextOid, n, oid, oidLen) > 0)
+      {
+         //Save the length of the resulting object identifier
+         *nextOidLen = n;
+         //Next object found
+         return NO_ERROR;
+      }
+   }
+
+   //The specified OID does not lexicographically precede the name
+   //of some object
+   return ERROR_OBJECT_NOT_FOUND;
+}
+
+
+#if (IPV4_SUPPORT == ENABLED)
+
+/**
+ * @brief Get ipAddrEntry object value
+ * @param[in] object Pointer to the MIB object descriptor
+ * @param[in] oid Object identifier (object name and instance identifier)
+ * @param[in] oidLen Length of the OID, in bytes
+ * @param[out] value Object value
+ * @param[in,out] valueLen Length of the object value, in bytes
+ * @return Error code
+ **/
+
+error_t mib2GetIpAddrEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, MibVariant *value, size_t *valueLen)
+{
+   error_t error;
+   uint_t i;
+   size_t n;
+   Ipv4Addr ipAddr;
+   NetInterface *interface;
+
+   //Point to the instance identifier
+   n = object->oidLen;
+
+   //The ipAdEntAddr is used as instance identifier
+   error = mibDecodeIpv4Addr(oid, oidLen, &n, &ipAddr);
+   //Invalid instance identifier?
+   if(error)
+      return error;
+
+   //Sanity check
+   if(n != oidLen)
+      return ERROR_INSTANCE_NOT_FOUND;
+
+   //Loop through network interfaces
+   for(i = 0; i < NET_INTERFACE_COUNT; i++)
+   {
+      //Point to the current interface
+      interface = &netInterface[i];
+
+      //Check address state
+      if(interface->ipv4Context.addrState == IPV4_ADDR_STATE_VALID)
+      {
+         //Compare the current address against the IP address used as
+         //instance identifier
+         if(interface->ipv4Context.addr == ipAddr)
+            break;
+      }
+   }
+
+   //IP address not assigned to any interface?
+   if(i >= NET_INTERFACE_COUNT)
+      return ERROR_INSTANCE_NOT_FOUND;
+
+   //ipAdEntAddr object?
+   if(!strcmp(object->name, "ipAdEntAddr"))
+   {
+      //Get object value
+      ipv4CopyAddr(value->ipAddr, &interface->ipv4Context.addr);
+   }
+   //ipAdEntIfIndex object?
+   else if(!strcmp(object->name, "ipAdEntIfIndex"))
+   {
+      //Get object value
+      value->integer = interface->id + 1;
+   }
+   //ipAdEntNetMask object?
+   else if(!strcmp(object->name, "ipAdEntNetMask"))
+   {
+      //Get object value
+      ipv4CopyAddr(value->ipAddr, &interface->ipv4Context.subnetMask);
+   }
+   //ipAdEntBcastAddr object?
+   else if(!strcmp(object->name, "ipAdEntBcastAddr"))
+   {
+      //Get object value
+      value->integer = 1;
+   }
+   //ipAdEntReasmMaxSize object?
+   else if(!strcmp(object->name, "ipAdEntReasmMaxSize"))
+   {
+      //Get object value
+      value->integer = IPV4_MAX_FRAG_DATAGRAM_SIZE;
+   }
+   //Unknown object?
+   else
+   {
+      //The specified object does not exist
+      error = ERROR_OBJECT_NOT_FOUND;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Get next ipAddrEntry object
+ * @param[in] object Pointer to the MIB object descriptor
+ * @param[in] oid Object identifier
+ * @param[in] oidLen Length of the OID, in bytes
+ * @param[out] nextOid OID of the next object in the MIB
+ * @param[out] nextOidLen Length of the next object identifier, in bytes
+ * @return Error code
+ **/
+
+error_t mib2GetNextIpAddrEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, uint8_t *nextOid, size_t *nextOidLen)
+{
+   error_t error;
+   uint_t i;
+   size_t n;
+   Ipv4Addr ipAddr;
+   NetInterface *interface;
+
+   //Initialize IP address
+   ipAddr = IPV4_UNSPECIFIED_ADDR;
+
+   //Make sure the buffer is large enough to hold the OID prefix
+   if(*nextOidLen < object->oidLen)
+      return ERROR_BUFFER_OVERFLOW;
+
+   //Copy OID prefix
+   memcpy(nextOid, object->oid, object->oidLen);
+
+   //Loop through network interfaces
+   for(i = 0; i < NET_INTERFACE_COUNT; i++)
+   {
+      //Point to the current interface
+      interface = &netInterface[i];
+
+      //Check address state
+      if(interface->ipv4Context.addrState == IPV4_ADDR_STATE_VALID)
+      {
+         //Append the instance identifier to the OID prefix
+         n = object->oidLen;
+
+         //The ipAdEntAddr is used as instance identifier
+         error = mibEncodeIpv4Addr(nextOid, *nextOidLen, &n, interface->ipv4Context.addr);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //Check whether the resulting object identifier lexicographically
+         //follows the specified OID
+         if(oidComp(nextOid, n, oid, oidLen) > 0)
+         {
+            //Save the closest object identifier that follows the specified
+            //OID in lexicographic order
+            if(ipAddr == IPV4_UNSPECIFIED_ADDR)
+            {
+               ipAddr = interface->ipv4Context.addr;
+            }
+            else if(ntohl(interface->ipv4Context.addr) < ntohl(ipAddr))
+            {
+               ipAddr = interface->ipv4Context.addr;
+            }
+         }
+      }
+   }
+
+   //The specified OID does not lexicographically precede the name
+   //of some object?
+   if(ipAddr == IPV4_UNSPECIFIED_ADDR)
+      return ERROR_OBJECT_NOT_FOUND;
+
+   //Append the instance identifier to the OID prefix
+   n = object->oidLen;
+
+   //The ipAdEntAddr is used as instance identifier
+   error = mibEncodeIpv4Addr(nextOid, *nextOidLen, &n, interface->ipv4Context.addr);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Save the length of the resulting object identifier
+   *nextOidLen = n;
+   //Next object found
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set ipNetToMediaEntry object value
+ * @param[in] object Pointer to the MIB object descriptor
+ * @param[in] oid Object identifier (object name and instance identifier)
+ * @param[in] oidLen Length of the OID, in bytes
+ * @param[in] value Object value
+ * @param[in] valueLen Length of the object value, in bytes
+ * @return Error code
+ **/
+
+error_t mib2SetIpNetToMediaEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, const MibVariant *value, size_t valueLen)
+{
+   //Not implemented
+   return ERROR_WRITE_FAILED;
+}
+
+
+/**
+ * @brief Get ipNetToMediaEntry object value
+ * @param[in] object Pointer to the MIB object descriptor
+ * @param[in] oid Object identifier (object name and instance identifier)
+ * @param[in] oidLen Length of the OID, in bytes
+ * @param[out] value Object value
+ * @param[in,out] valueLen Length of the object value, in bytes
+ * @return Error code
+ **/
+
+error_t mib2GetIpNetToMediaEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, MibVariant *value, size_t *valueLen)
+{
+   error_t error;
+   size_t n;
+   uint_t index;
+   Ipv4Addr ipAddr;
+   NetInterface *interface;
+   ArpCacheEntry *entry;
+
+   //Point to the instance identifier
+   n = object->oidLen;
+
+   //The ipNetToMediaIfIndex is used as 1st instance identifier
+   error = mibDecodeIndex(oid, oidLen, &n, &index);
+   //Invalid instance identifier?
+   if(error)
+      return error;
+
+   //The ipNetToMediaNetAddress is used as 2nd instance identifier
+   error = mibDecodeIpv4Addr(oid, oidLen, &n, &ipAddr);
+   //Invalid instance identifier?
+   if(error)
+      return error;
+
+   //Sanity check
+   if(n != oidLen)
+      return ERROR_INSTANCE_NOT_FOUND;
+
+   //Check index range
+   if(index < 1 || index > NET_INTERFACE_COUNT)
+      return ERROR_INSTANCE_NOT_FOUND;
+
+   //Point to the network interface
+   interface = &netInterface[index - 1];
+
+   //Search the ARP cache for the specified IP address
+   entry = arpFindEntry(interface, ipAddr);
+
+   //No matching entry found?
+   if(entry == NULL)
+      return ERROR_INSTANCE_NOT_FOUND;
+
+   //ipNetToMediaIfIndex object?
+   if(!strcmp(object->name, "ipNetToMediaIfIndex"))
+   {
+      //Get object value
+      value->integer = index;
+   }
+   //ipNetToMediaPhysAddress object?
+   else if(!strcmp(object->name, "ipNetToMediaPhysAddress"))
+   {
+      //Make sure the buffer is large enough to hold the entire object
+      if(*valueLen >= MIB2_PHYS_ADDRESS_SIZE)
+      {
+         //Copy object value
+         macCopyAddr(value->octetString, &entry->macAddr);
+         //Return object length
+         *valueLen = MIB2_PHYS_ADDRESS_SIZE;
+      }
+      else
+      {
+         //Report an error
+         error = ERROR_BUFFER_OVERFLOW;
+      }
+   }
+   //ipNetToMediaNetAddress object?
+   else if(!strcmp(object->name, "ipNetToMediaNetAddress"))
+   {
+      //Get object value
+      ipv4CopyAddr(value->ipAddr, &entry->ipAddr);
+   }
+   //ipNetToMediaType object?
+   else if(!strcmp(object->name, "ipNetToMediaType"))
+   {
+      //Get object value
+      value->integer = MIB2_IP_NET_TO_MEDIA_TYPE_DYNAMIC;
+   }
+   //Unknown object?
+   else
+   {
+      //The specified object does not exist
+      error = ERROR_OBJECT_NOT_FOUND;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Get next ipNetToMediaEntry object
+ * @param[in] object Pointer to the MIB object descriptor
+ * @param[in] oid Object identifier
+ * @param[in] oidLen Length of the OID, in bytes
+ * @param[out] nextOid OID of the next object in the MIB
+ * @param[out] nextOidLen Length of the next object identifier, in bytes
+ * @return Error code
+ **/
+
+error_t mib2GetNextIpNetToMediaEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, uint8_t *nextOid, size_t *nextOidLen)
+{
+   error_t error;
+   uint_t i;
+   uint_t j;
+   size_t n;
+   uint32_t index;
+   bool_t acceptable;
+   Ipv4Addr ipAddr;
+   NetInterface *interface;
+   ArpCacheEntry *entry;
+
+   //Initialize variables
+   index = 0;
+   ipAddr = IPV4_UNSPECIFIED_ADDR;
+
+   //Make sure the buffer is large enough to hold the OID prefix
+   if(*nextOidLen < object->oidLen)
+      return ERROR_BUFFER_OVERFLOW;
+
+   //Copy OID prefix
+   memcpy(nextOid, object->oid, object->oidLen);
+
+   //Loop through network interfaces
+   for(i = 1; i <= NET_INTERFACE_COUNT; i++)
+   {
+      //Point to the current interface
+      interface = &netInterface[i - 1];
+
+      //Loop through ARP cache entries
+      for(j = 0; j < ARP_CACHE_SIZE; j++)
+      {
+         //Point to the current entry
+         entry = &interface->arpCache[j];
+
+         //Valid entry?
+         if(entry->state != ARP_STATE_NONE)
+         {
+            //Append the instance identifier to the OID prefix
+            n = object->oidLen;
+
+            //The ipNetToMediaIfIndex is used as 1st instance identifier
+            error = mibEncodeIndex(nextOid, *nextOidLen, &n, i);
+            //Any error to report?
+            if(error)
+               return error;
+
+            //The ipNetToMediaNetAddress is used as 2nd instance identifier
+            error = mibEncodeIpv4Addr(nextOid, *nextOidLen, &n, entry->ipAddr);
+            //Any error to report?
+            if(error)
+               return error;
+
+            //Check whether the resulting object identifier lexicographically
+            //follows the specified OID
+            if(oidComp(nextOid, n, oid, oidLen) > 0)
+            {
+               //Perform lexicographic comparison
+               if(index == 0)
+                  acceptable = TRUE;
+               else if(i < index)
+                  acceptable = TRUE;
+               else if(i > index)
+                  acceptable = FALSE;
+               else if(ntohl(entry->ipAddr) < ntohl(ipAddr))
+                  acceptable = TRUE;
+               else
+                  acceptable = FALSE;
+
+               //Save the closest object identifier that follows the specified
+               //OID in lexicographic order
+               if(acceptable)
+               {
+                  index = i;
+                  ipAddr = entry->ipAddr;
+               }
+            }
+         }
+      }
+   }
+
+   //The specified OID does not lexicographically precede the name
+   //of some object?
+   if(index == 0)
+      return ERROR_OBJECT_NOT_FOUND;
+
+   //Append the instance identifier to the OID prefix
+   n = object->oidLen;
+
+   //The ipNetToMediaIfIndex is used as 1st instance identifier
+   error = mibEncodeIndex(nextOid, *nextOidLen, &n, index);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //The ipNetToMediaNetAddress is used as 2nd instance identifier
+   error = mibEncodeIpv4Addr(nextOid, *nextOidLen, &n, ipAddr);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Save the length of the resulting object identifier
+   *nextOidLen = n;
+   //Next object found
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set tcpConnEntry object value
+ * @param[in] object Pointer to the MIB object descriptor
+ * @param[in] oid Object identifier (object name and instance identifier)
+ * @param[in] oidLen Length of the OID, in bytes
+ * @param[out] value Object value
+ * @param[in] valueLen Length of the object value, in bytes
+ * @return Error code
+ **/
+
+error_t mib2SetTcpConnEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, const MibVariant *value, size_t valueLen)
+{
+   //Not implemented
+   return ERROR_WRITE_FAILED;
+}
+
+
+/**
+ * @brief Get tcpConnEntry object value
+ * @param[in] object Pointer to the MIB object descriptor
+ * @param[in] oid Object identifier (object name and instance identifier)
+ * @param[in] oidLen Length of the OID, in bytes
+ * @param[out] value Object value
+ * @param[in,out] valueLen Length of the object value, in bytes
+ * @return Error code
+ **/
+
+error_t mib2GetTcpConnEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, MibVariant *value, size_t *valueLen)
+{
+   error_t error;
+   uint_t i;
+   size_t n;
+   Ipv4Addr localIpAddr;
+   uint16_t localPort;
+   Ipv4Addr remoteIpAddr;
+   uint16_t remotePort;
+   Socket *socket;
+
+   //Point to the instance identifier
+   n = object->oidLen;
+
+   //The tcpConnLocalAddress is used as 1st instance identifier
+   error = mibDecodeIpv4Addr(oid, oidLen, &n, &localIpAddr);
+   //Invalid instance identifier?
+   if(error)
+      return error;
+
+   //The tcpConnLocalPort is used as 2nd instance identifier
+   error = mibDecodePort(oid, oidLen, &n, &localPort);
+   //Invalid instance identifier?
+   if(error)
+      return error;
+
+   //The tcpConnRemAddress is used as 3rd instance identifier
+   error = mibDecodeIpv4Addr(oid, oidLen, &n, &remoteIpAddr);
+   //Invalid instance identifier?
+   if(error)
+      return error;
+
+   //The tcpConnRemPort is used as 4th instance identifier
+   error = mibDecodePort(oid, oidLen, &n, &remotePort);
+   //Invalid instance identifier?
+   if(error)
+      return error;
+
+   //Sanity check
+   if(n != oidLen)
+      return ERROR_INSTANCE_NOT_FOUND;
+
+   //Loop through socket descriptors
+   for(i = 0; i < SOCKET_MAX_COUNT; i++)
+   {
+      //Point to current socket
+      socket = &socketTable[i];
+
+      //TCP socket?
+      if(socketTable[i].type == SOCKET_TYPE_STREAM)
+      {
+         //Check local IP address
+         if(socket->localIpAddr.length == sizeof(Ipv6Addr))
+            continue;
+         if(socket->localIpAddr.ipv4Addr != localIpAddr)
+            continue;
+         //Check local port number
+         if(socket->localPort != localPort)
+            continue;
+         //Check remote IP address
+         if(socket->remoteIpAddr.length == sizeof(Ipv6Addr))
+            continue;
+         if(socket->remoteIpAddr.ipv4Addr != remoteIpAddr)
+            continue;
+         //Check remote port number
+         if(socket->remotePort != remotePort)
+            continue;
+
+         //A matching socket has been found
+         break;
+      }
+   }
+
+   //No matching connection found in socket table?
+   if(i >= SOCKET_MAX_COUNT)
+      return ERROR_INSTANCE_NOT_FOUND;
+
+   //tcpConnState object?
+   if(!strcmp(object->name, "tcpConnState"))
+   {
+      //Get object value
+      switch(socket->state)
+      {
+      case TCP_STATE_CLOSED:
+         value->integer = MIB2_TCP_CONN_STATE_CLOSED;
+         break;
+      case TCP_STATE_LISTEN:
+         value->integer = MIB2_TCP_CONN_STATE_LISTEN;
+         break;
+      case TCP_STATE_SYN_SENT:
+         value->integer = MIB2_TCP_CONN_STATE_SYN_SENT;
+         break;
+      case TCP_STATE_SYN_RECEIVED:
+         value->integer = MIB2_TCP_CONN_STATE_SYN_RECEIVED;
+         break;
+      case TCP_STATE_ESTABLISHED:
+         value->integer = MIB2_TCP_CONN_STATE_ESTABLISHED;
+         break;
+      case TCP_STATE_FIN_WAIT_1:
+         value->integer = MIB2_TCP_CONN_STATE_FIN_WAIT_1;
+         break;
+      case TCP_STATE_FIN_WAIT_2:
+         value->integer = MIB2_TCP_CONN_STATE_FIN_WAIT_2;
+         break;
+      case TCP_STATE_CLOSE_WAIT:
+         value->integer = MIB2_TCP_CONN_STATE_CLOSE_WAIT;
+         break;
+      case TCP_STATE_LAST_ACK:
+         value->integer = MIB2_TCP_CONN_STATE_LAST_ACK;
+         break;
+      case TCP_STATE_CLOSING:
+         value->integer = MIB2_TCP_CONN_STATE_CLOSING;
+         break;
+      case TCP_STATE_TIME_WAIT:
+         value->integer = MIB2_TCP_CONN_STATE_TIME_WAIT;
+         break;
+      default:
+         value->integer = 0;
+         break;
+      }
+   }
+   //tcpConnLocalAddress object?
+   else if(!strcmp(object->name, "tcpConnLocalAddress"))
+   {
+      //Get object value
+      ipv4CopyAddr(value->ipAddr, &socket->localIpAddr.ipv4Addr);
+   }
+   //tcpConnLocalPort object?
+   else if(!strcmp(object->name, "tcpConnLocalPort"))
+   {
+      //Get object value
+      value->integer = socket->localPort;
+   }
+   //tcpConnRemAddress object?
+   else if(!strcmp(object->name, "tcpConnRemAddress"))
+   {
+      //Get object value
+      ipv4CopyAddr(value->ipAddr, &socket->remoteIpAddr.ipv4Addr);
+   }
+   //tcpConnRemPort object?
+   else if(!strcmp(object->name, "tcpConnRemPort"))
+   {
+      //Get object value
+      value->integer = socket->remotePort;
+   }
+   //Unknown object?
+   else
+   {
+      //The specified object does not exist
+      error = ERROR_OBJECT_NOT_FOUND;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Get next tcpConnEntry object
+ * @param[in] object Pointer to the MIB object descriptor
+ * @param[in] oid Object identifier
+ * @param[in] oidLen Length of the OID, in bytes
+ * @param[out] nextOid OID of the next object in the MIB
+ * @param[out] nextOidLen Length of the next object identifier, in bytes
+ * @return Error code
+ **/
+
+error_t mib2GetNextTcpConnEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, uint8_t *nextOid, size_t *nextOidLen)
+{
+   error_t error;
+   uint_t i;
+   size_t n;
+   bool_t acceptable;
+   Ipv4Addr localIpAddr;
+   uint16_t localPort;
+   Ipv4Addr remoteIpAddr;
+   uint16_t remotePort;
+   Socket *socket;
+
+   //Initialize variables
+   localIpAddr = IPV4_UNSPECIFIED_ADDR;
+   localPort = 0;
+   remoteIpAddr = IPV4_UNSPECIFIED_ADDR;
+   remotePort = 0;
+
+   //Make sure the buffer is large enough to hold the OID prefix
+   if(*nextOidLen < object->oidLen)
+      return ERROR_BUFFER_OVERFLOW;
+
+   //Copy OID prefix
+   memcpy(nextOid, object->oid, object->oidLen);
+
+   //Loop through socket descriptors
+   for(i = 0; i < SOCKET_MAX_COUNT; i++)
+   {
+      //Point to current socket
+      socket = &socketTable[i];
+
+      //TCP socket?
+      if(socketTable[i].type == SOCKET_TYPE_STREAM)
+      {
+         //Filter out IPv6 connections
+         if(socket->localIpAddr.length != sizeof(Ipv6Addr) &&
+            socket->remoteIpAddr.length != sizeof(Ipv6Addr))
+         {
+            //Append the instance identifier to the OID prefix
+            n = object->oidLen;
+
+            //The tcpConnLocalAddress is used as 1st instance identifier
+            error = mibEncodeIpv4Addr(nextOid, *nextOidLen, &n, socket->localIpAddr.ipv4Addr);
+            //Any error to report?
+            if(error)
+               return error;
+
+            //The tcpConnLocalPort is used as 2nd instance identifier
+            error = mibEncodePort(nextOid, *nextOidLen, &n, socket->localPort);
+            //Any error to report?
+            if(error)
+               return error;
+
+            //The tcpConnRemAddress is used as 3rd instance identifier
+            error = mibEncodeIpv4Addr(nextOid, *nextOidLen, &n, socket->remoteIpAddr.ipv4Addr);
+            //Any error to report?
+            if(error)
+               return error;
+
+            //The tcpConnRemPort is used as 4th instance identifier
+            error = mibEncodePort(nextOid, *nextOidLen, &n, socket->remotePort);
+            //Any error to report?
+            if(error)
+               return error;
+
+            //Check whether the resulting object identifier lexicographically
+            //follows the specified OID
+            if(oidComp(nextOid, n, oid, oidLen) > 0)
+            {
+               //Perform lexicographic comparison
+               if(localPort == 0 && remotePort == 0)
+                  acceptable = TRUE;
+               else if(ntohl(socket->localIpAddr.ipv4Addr) < ntohl(localIpAddr))
+                  acceptable = TRUE;
+               else if(ntohl(socket->localIpAddr.ipv4Addr) > ntohl(localIpAddr))
+                  acceptable = FALSE;
+               else if(socket->localPort < localPort)
+                  acceptable = TRUE;
+               else if(socket->localPort > localPort)
+                  acceptable = FALSE;
+               else if(ntohl(socket->remoteIpAddr.ipv4Addr) < ntohl(remoteIpAddr))
+                  acceptable = TRUE;
+               else if(ntohl(socket->remoteIpAddr.ipv4Addr) > ntohl(remoteIpAddr))
+                  acceptable = FALSE;
+               else if(socket->remotePort < remotePort)
+                  acceptable = TRUE;
+               else
+                  acceptable = FALSE;
+
+               //Save the closest object identifier that follows the specified
+               //OID in lexicographic order
+               if(acceptable)
+               {
+                  localIpAddr = socket->localIpAddr.ipv4Addr;
+                  localPort = socket->localPort;
+                  remoteIpAddr = socket->remoteIpAddr.ipv4Addr;
+                  remotePort = socket->remotePort;
+               }
+            }
+         }
+      }
+   }
+
+   //The specified OID does not lexicographically precede the name
+   //of some object?
+   if(localPort == 0 && remotePort == 0)
+      return ERROR_OBJECT_NOT_FOUND;
+
+   //Append the instance identifier to the OID prefix
+   n = object->oidLen;
+
+   //The tcpConnLocalAddress is used as 1st instance identifier
+   error = mibEncodeIpv4Addr(nextOid, *nextOidLen, &n, localIpAddr);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //The tcpConnLocalPort is used as 2nd instance identifier
+   error = mibEncodePort(nextOid, *nextOidLen, &n, localPort);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //The tcpConnRemAddress is used as 3rd instance identifier
+   error = mibEncodeIpv4Addr(nextOid, *nextOidLen, &n, remoteIpAddr);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //The tcpConnRemPort is used as 4th instance identifier
+   error = mibEncodePort(nextOid, *nextOidLen, &n, remotePort);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Save the length of the resulting object identifier
+   *nextOidLen = n;
+   //Next object found
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Get udpEntry object value
+ * @param[in] object Pointer to the MIB object descriptor
+ * @param[in] oid Object identifier (object name and instance identifier)
+ * @param[in] oidLen Length of the OID, in bytes
+ * @param[out] value Object value
+ * @param[in,out] valueLen Length of the object value, in bytes
+ * @return Error code
+ **/
+
+error_t mib2GetUdpEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, MibVariant *value, size_t *valueLen)
+{
+   error_t error;
+   uint_t i;
+   size_t n;
+   Ipv4Addr localIpAddr;
+   uint16_t localPort;
+
+   //Point to the instance identifier
+   n = object->oidLen;
+
+   //The udpLocalAddress is used as 1st instance identifier
+   error = mibDecodeIpv4Addr(oid, oidLen, &n, &localIpAddr);
+   //Invalid instance identifier?
+   if(error)
+      return error;
+
+   //The udpLocalPort is used as 2nd instance identifier
+   error = mibDecodePort(oid, oidLen, &n, &localPort);
+   //Invalid instance identifier?
+   if(error)
+      return error;
+
+   //Sanity check
+   if(n != oidLen)
+      return ERROR_INSTANCE_NOT_FOUND;
+
+   //Loop through socket descriptors
+   for(i = 0; i < SOCKET_MAX_COUNT; i++)
+   {
+      //Point to current socket
+      Socket *socket = &socketTable[i];
+
+      //UDP socket?
+      if(socketTable[i].type == SOCKET_TYPE_DGRAM)
+      {
+         //Check local IP address
+         if(socket->localIpAddr.length == sizeof(Ipv6Addr))
+            continue;
+         if(socket->localIpAddr.ipv4Addr != localIpAddr)
+            continue;
+         //Check local port number
+         if(socket->localPort != localPort)
+            continue;
+
+         //A matching socket has been found
+         break;
+      }
+   }
+
+   //No matching connection found in socket table?
+   if(i >= SOCKET_MAX_COUNT)
+   {
+      //Loop through the UDP callback table
+      for(i = 0; i < UDP_CALLBACK_TABLE_SIZE; i++)
+      {
+         //Point to the current entry
+         UdpRxCallbackDesc *entry = &udpCallbackTable[i];
+
+         //Check whether the entry is currently in used
+         if(entry->callback != NULL)
+         {
+            //Check local port number
+            if(entry->port == localPort)
+               break;
+         }
+      }
+
+      //No matching connection found in UDP callback table?
+      if(i >= UDP_CALLBACK_TABLE_SIZE)
+         return ERROR_INSTANCE_NOT_FOUND;
+   }
+
+   //udpLocalAddress object?
+   if(!strcmp(object->name, "udpLocalAddress"))
+   {
+      //Get object value
+      ipv4CopyAddr(value->ipAddr, &localIpAddr);
+   }
+   //udpLocalPort object?
+   else if(!strcmp(object->name, "udpLocalPort"))
+   {
+      //Get object value
+      value->integer = localPort;
+   }
+   //Unknown object?
+   else
+   {
+      //The specified object does not exist
+      error = ERROR_OBJECT_NOT_FOUND;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Get next udpEntry object
+ * @param[in] object Pointer to the MIB object descriptor
+ * @param[in] oid Object identifier
+ * @param[in] oidLen Length of the OID, in bytes
+ * @param[out] nextOid OID of the next object in the MIB
+ * @param[out] nextOidLen Length of the next object identifier, in bytes
+ * @return Error code
+ **/
+
+error_t mib2GetNextUdpEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, uint8_t *nextOid, size_t *nextOidLen)
+{
+   error_t error;
+   uint_t i;
+   size_t n;
+   bool_t acceptable;
+   Ipv4Addr localIpAddr;
+   uint16_t localPort;
+
+   //Initialize variables
+   localIpAddr = IPV4_UNSPECIFIED_ADDR;
+   localPort = 0;
+
+   //Make sure the buffer is large enough to hold the OID prefix
+   if(*nextOidLen < object->oidLen)
+      return ERROR_BUFFER_OVERFLOW;
+
+   //Copy OID prefix
+   memcpy(nextOid, object->oid, object->oidLen);
+
+   //Loop through socket descriptors
+   for(i = 0; i < SOCKET_MAX_COUNT; i++)
+   {
+      //Point to current socket
+      Socket *socket = &socketTable[i];
+
+      //TCP socket?
+      if(socketTable[i].type == SOCKET_TYPE_DGRAM)
+      {
+         //Filter out IPv6 connections
+         if(socket->localIpAddr.length != sizeof(Ipv6Addr) &&
+            socket->remoteIpAddr.length != sizeof(Ipv6Addr))
+         {
+            //Append the instance identifier to the OID prefix
+            n = object->oidLen;
+
+            //The udpLocalAddress is used as 1st instance identifier
+            error = mibEncodeIpv4Addr(nextOid, *nextOidLen, &n, socket->localIpAddr.ipv4Addr);
+            //Any error to report?
+            if(error)
+               return error;
+
+            //The udpLocalPort is used as 2nd instance identifier
+            error = mibEncodePort(nextOid, *nextOidLen, &n, socket->localPort);
+            //Any error to report?
+            if(error)
+               return error;
+
+            //Check whether the resulting object identifier lexicographically
+            //follows the specified OID
+            if(oidComp(nextOid, n, oid, oidLen) > 0)
+            {
+               //Perform lexicographic comparison
+               if(localPort == 0)
+                  acceptable = TRUE;
+               else if(ntohl(socket->localIpAddr.ipv4Addr) < ntohl(localIpAddr))
+                  acceptable = TRUE;
+               else if(ntohl(socket->localIpAddr.ipv4Addr) > ntohl(localIpAddr))
+                  acceptable = FALSE;
+               else if(socket->localPort < localPort)
+                  acceptable = TRUE;
+               else
+                  acceptable = FALSE;
+
+               //Save the closest object identifier that follows the specified
+               //OID in lexicographic order
+               if(acceptable)
+               {
+                  localIpAddr = socket->localIpAddr.ipv4Addr;
+                  localPort = socket->localPort;
+               }
+            }
+         }
+      }
+   }
+
+   //Loop through the UDP callback table
+   for(i = 0; i < UDP_CALLBACK_TABLE_SIZE; i++)
+   {
+      //Point to the current entry
+      UdpRxCallbackDesc *entry = &udpCallbackTable[i];
+
+      //Check whether the entry is currently in used
+      if(entry->callback != NULL)
+      {
+         //Append the instance identifier to the OID prefix
+         n = object->oidLen;
+
+         //The udpLocalAddress is used as 1st instance identifier
+         error = mibEncodeIpv4Addr(nextOid, *nextOidLen, &n, IPV4_UNSPECIFIED_ADDR);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //The udpLocalPort is used as 2nd instance identifier
+         error = mibEncodePort(nextOid, *nextOidLen, &n, entry->port);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //Check whether the resulting object identifier lexicographically
+         //follows the specified OID
+         if(oidComp(nextOid, n, oid, oidLen) > 0)
+         {
+            //Perform lexicographic comparison
+            if(localPort == 0)
+               acceptable = TRUE;
+            else if(ntohl(IPV4_UNSPECIFIED_ADDR) < ntohl(localIpAddr))
+               acceptable = TRUE;
+            else if(ntohl(IPV4_UNSPECIFIED_ADDR) > ntohl(localIpAddr))
+               acceptable = FALSE;
+            else if(entry->port < localPort)
+               acceptable = TRUE;
+            else
+               acceptable = FALSE;
+
+            //Save the closest object identifier that follows the specified
+            //OID in lexicographic order
+            if(acceptable)
+            {
+               localIpAddr = IPV4_UNSPECIFIED_ADDR;
+               localPort = entry->port;
+            }
+         }
+      }
+   }
+
+   //The specified OID does not lexicographically precede the name
+   //of some object?
+   if(localPort == 0)
+      return ERROR_OBJECT_NOT_FOUND;
+
+   //Append the instance identifier to the OID prefix
+   n = object->oidLen;
+
+   //The udpLocalAddress is used as 1st instance identifier
+   error = mibEncodeIpv4Addr(nextOid, *nextOidLen, &n, localIpAddr);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //The udpLocalPort is used as 2nd instance identifier
+   error = mibEncodePort(nextOid, *nextOidLen, &n, localPort);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Save the length of the resulting object identifier
+   *nextOidLen = n;
+   //Next object found
+   return NO_ERROR;
+}
+
+#endif
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mibs/mib2_impl.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,105 @@
+/**
+ * @file mib2_impl.h
+ * @brief MIB-II module implementation
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MIB2_IMPL_H
+#define _MIB2_IMPL_H
+
+//Dependencies
+#include "core/net.h"
+#include "mibs/mib2_module.h"
+
+//MIB-II related functions
+error_t mib2Init(void);
+void mib2Lock(void);
+void mib2Unlock(void);
+
+error_t mib2GetSysDescr(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, MibVariant *value, size_t *valueLen);
+
+error_t mib2GetSysObjectID(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, MibVariant *value, size_t *valueLen);
+
+error_t mib2GetSysUpTime(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, MibVariant *value, size_t *valueLen);
+
+error_t mib2SetSysContact(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, const MibVariant *value, size_t valueLen);
+
+error_t mib2GetSysContact(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, MibVariant *value, size_t *valueLen);
+
+error_t mib2SetSysName(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, const MibVariant *value, size_t valueLen);
+
+error_t mib2GetSysName(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, MibVariant *value, size_t *valueLen);
+
+error_t mib2SetSysLocation(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, const MibVariant *value, size_t valueLen);
+
+error_t mib2GetSysLocation(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, MibVariant *value, size_t *valueLen);
+
+error_t mib2GetIfEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, MibVariant *value, size_t *valueLen);
+
+error_t mib2GetNextIfEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, uint8_t *nextOid, size_t *nextOidLen);
+
+error_t mib2GetIpAddrEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, MibVariant *value, size_t *valueLen);
+
+error_t mib2GetNextIpAddrEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, uint8_t *nextOid, size_t *nextOidLen);
+
+error_t mib2SetIpNetToMediaEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, const MibVariant *value, size_t valueLen);
+
+error_t mib2GetIpNetToMediaEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, MibVariant *value, size_t *valueLen);
+
+error_t mib2GetNextIpNetToMediaEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, uint8_t *nextOid, size_t *nextOidLen);
+
+error_t mib2SetTcpConnEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, const MibVariant *value, size_t valueLen);
+
+error_t mib2GetTcpConnEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, MibVariant *value, size_t *valueLen);
+
+error_t mib2GetNextTcpConnEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, uint8_t *nextOid, size_t *nextOidLen);
+
+error_t mib2GetUdpEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, MibVariant *value, size_t *valueLen);
+
+error_t mib2GetNextUdpEntry(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, uint8_t *nextOid, size_t *nextOidLen);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mibs/mib2_module.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,2143 @@
+/**
+ * @file mib2_module.c
+ * @brief MIB-II module
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The second version of the Management Information Base (MIB-II) is used to
+ * manage TCP/IP-based hosts. Refer to the following RFCs for complete details:
+ * - RFC 1156: MIB for Network Management of TCP/IP-based internets
+ * - RFC 1213: MIB for Network Management of TCP/IP-based internets (version 2)
+ * - RFC 2863: The Interfaces Group MIB
+ * - RFC 4293: MIB for the Internet Protocol (IP)
+ * - RFC 4292: IP Forwarding Table MIB
+ * - RFC 4022: MIB for the Transmission Control Protocol (TCP)
+ * - RFC 4113: MIB for the User Datagram Protocol (UDP)
+ * - RFC 3418: MIB for the Simple Network Management Protocol (SNMP)
+ * - RFC 4004: Textual Conventions for Internet Network Addresses
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL SNMP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "mibs/mib2_module.h"
+#include "mibs/mib2_impl.h"
+#include "crypto.h"
+#include "asn1.h"
+#include "oid.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (MIB2_SUPPORT == ENABLED)
+
+
+/**
+ * @brief MIB-II base
+ **/
+
+Mib2Base mib2Base;
+
+
+/**
+ * @brief MIB-II objects
+ **/
+
+const MibObject mib2Objects[] =
+{
+   //System group
+   {
+      "sysDescr",
+      {43, 6, 1, 2, 1, 1, 1},
+      7,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_OCTET_STRING,
+      MIB_ACCESS_READ_ONLY,
+#if (MIB2_SYS_DESCR_SIZE > 0)
+      &mib2Base.sysGroup.sysDescr,
+      &mib2Base.sysGroup.sysDescrLen,
+      MIB2_SYS_DESCR_SIZE,
+      NULL,
+      NULL,
+      NULL
+#else
+      NULL,
+      NULL,
+      0,
+      NULL,
+      mib2GetSysDescr,
+      NULL
+#endif
+   },
+   {
+      "sysObjectID",
+      {43, 6, 1, 2, 1, 1, 2},
+      7,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_OBJECT_IDENTIFIER,
+      MIB_ACCESS_READ_ONLY,
+#if (MIB2_SYS_OBJECT_ID_SIZE > 0)
+      &mib2Base.sysGroup.sysObjectID,
+      &mib2Base.sysGroup.sysObjectIDLen,
+      MIB2_SYS_OBJECT_ID_SIZE,
+      NULL,
+      NULL,
+      NULL
+#else
+      NULL,
+      NULL,
+      0,
+      NULL,
+      mib2GetSysObjectID,
+      NULL
+#endif
+   },
+   {
+      "sysUpTime",
+      {43, 6, 1, 2, 1, 1, 3},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_TIME_TICKS,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      mib2GetSysUpTime,
+      NULL
+   },
+   {
+      "sysContact",
+      {43, 6, 1, 2, 1, 1, 4},
+      7,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_OCTET_STRING,
+      MIB_ACCESS_READ_WRITE,
+#if (MIB2_SYS_CONTACT_SIZE > 0)
+      &mib2Base.sysGroup.sysContact,
+      &mib2Base.sysGroup.sysContactLen,
+      MIB2_SYS_CONTACT_SIZE,
+      NULL,
+      NULL,
+      NULL
+#else
+      NULL,
+      NULL,
+      0,
+      mib2SetSysContact,
+      mib2GetSysContact,
+      NULL
+#endif
+   },
+   {
+      "sysName",
+      {43, 6, 1, 2, 1, 1, 5},
+      7,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_OCTET_STRING,
+      MIB_ACCESS_READ_WRITE,
+#if (MIB2_SYS_NAME_SIZE > 0)
+      &mib2Base.sysGroup.sysName,
+      &mib2Base.sysGroup.sysNameLen,
+      MIB2_SYS_NAME_SIZE,
+      NULL,
+      NULL,
+      NULL
+#else
+      NULL,
+      NULL,
+      0,
+      mib2SetSysName,
+      mib2GetSysName,
+      NULL
+#endif
+   },
+   {
+      "sysLocation",
+      {43, 6, 1, 2, 1, 1, 6},
+      7,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_OCTET_STRING,
+      MIB_ACCESS_READ_WRITE,
+#if (MIB2_SYS_LOCATION_SIZE > 0)
+      &mib2Base.sysGroup.sysLocation,
+      &mib2Base.sysGroup.sysLocationLen,
+      MIB2_SYS_LOCATION_SIZE,
+      NULL,
+      NULL,
+      NULL
+#else
+      NULL,
+      NULL,
+      0,
+      mib2SetSysLocation,
+      mib2GetSysLocation,
+      NULL
+#endif
+   },
+   {
+      "sysServices",
+      {43, 6, 1, 2, 1, 1, 7},
+      7,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.sysGroup.sysServices,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   //Interfaces group
+   {
+      "ifNumber",
+      {43, 6, 1, 2, 1, 2, 1},
+      7,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ifGroup.ifNumber,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   //Interfaces table
+   {
+      "ifIndex",
+      {43, 6, 1, 2, 1, 2, 2, 1, 1},
+      9,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifDescr",
+      {43, 6, 1, 2, 1, 2, 2, 1, 2},
+      9,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_OCTET_STRING,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      MIB2_IF_DESCR_SIZE,
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifType",
+      {43, 6, 1, 2, 1, 2, 2, 1, 3},
+      9,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifMtu",
+      {43, 6, 1, 2, 1, 2, 2, 1, 4},
+      9,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifSpeed",
+      {43, 6, 1, 2, 1, 2, 2, 1, 5},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_GAUGE32,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifPhysAddress",
+      {43, 6, 1, 2, 1, 2, 2, 1, 6},
+      9,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_OCTET_STRING,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      MIB2_PHYS_ADDRESS_SIZE,
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifAdminStatus",
+      {43, 6, 1, 2, 1, 2, 2, 1, 7},
+      9,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifOperStatus",
+      {43, 6, 1, 2, 1, 2, 2, 1, 8},
+      9,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifLastChange",
+      {43, 6, 1, 2, 1, 2, 2, 1, 9},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_TIME_TICKS,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifInOctets",
+      {43, 6, 1, 2, 1, 2, 2, 1, 10},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifInUcastPkts",
+      {43, 6, 1, 2, 1, 2, 2, 1, 11},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifInNUcastPkts",
+      {43, 6, 1, 2, 1, 2, 2, 1, 12},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifInDiscards",
+      {43, 6, 1, 2, 1, 2, 2, 1, 13},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifInErrors",
+      {43, 6, 1, 2, 1, 2, 2, 1, 14},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifInUnknownProtos",
+      {43, 6, 1, 2, 1, 2, 2, 1, 15},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifOutOctets",
+      {43, 6, 1, 2, 1, 2, 2, 1, 16},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifOutUcastPkts",
+      {43, 6, 1, 2, 1, 2, 2, 1, 17},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifOutNUcastPkts",
+      {43, 6, 1, 2, 1, 2, 2, 1, 18},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifOutDiscards",
+      {43, 6, 1, 2, 1, 2, 2, 1, 19},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifOutErrors",
+      {43, 6, 1, 2, 1, 2, 2, 1, 20},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifOutQLen",
+      {43, 6, 1, 2, 1, 2, 2, 1, 21},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_GAUGE32,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+   {
+      "ifSpecific",
+      {43, 6, 1, 2, 1, 2, 2, 1, 22},
+      9,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_OBJECT_IDENTIFIER,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      MIB2_IF_SPECIFIC_SIZE,
+      NULL,
+      mib2GetIfEntry,
+      mib2GetNextIfEntry
+   },
+#if (IPV4_SUPPORT == ENABLED)
+   //IP group
+   {
+      "ipForwarding",
+      {43, 6, 1, 2, 1, 4, 1},
+      7,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_WRITE,
+      &mib2Base.ipGroup.ipForwarding,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "ipDefaultTTL",
+      {43, 6, 1, 2, 1, 4, 2},
+      7,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_WRITE,
+      &mib2Base.ipGroup.ipDefaultTTL,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "ipInReceives",
+      {43, 6, 1, 2, 1, 4, 3},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ipGroup.ipInReceives,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "ipInHdrErrors",
+      {43, 6, 1, 2, 1, 4, 4},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ipGroup.ipInHdrErrors,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "ipInAddrErrors",
+      {43, 6, 1, 2, 1, 4, 5},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ipGroup.ipInAddrErrors,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "ipForwDatagrams",
+      {43, 6, 1, 2, 1, 4, 6},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ipGroup.ipForwDatagrams,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "ipInUnknownProtos",
+      {43, 6, 1, 2, 1, 4, 7},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ipGroup.ipInUnknownProtos,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "ipInDiscards",
+      {43, 6, 1, 2, 1, 4, 8},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ipGroup.ipInDiscards,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "ipInDelivers",
+      {43, 6, 1, 2, 1, 4, 9},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ipGroup.ipInDelivers,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "ipOutRequests",
+      {43, 6, 1, 2, 1, 4, 10},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ipGroup.ipOutRequests,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "ipOutDiscards",
+      {43, 6, 1, 2, 1, 4, 11},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ipGroup.ipOutDiscards,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "ipOutNoRoutes",
+      {43, 6, 1, 2, 1, 4, 12},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ipGroup.ipOutNoRoutes,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "ipReasmTimeout",
+      {43, 6, 1, 2, 1, 4, 13},
+      7,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ipGroup.ipReasmTimeout,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "ipReasmReqds",
+      {43, 6, 1, 2, 1, 4, 14},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ipGroup.ipReasmReqds,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "ipReasmOKs",
+      {43, 6, 1, 2, 1, 4, 15},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ipGroup.ipReasmOKs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "ipReasmFails",
+      {43, 6, 1, 2, 1, 4, 16},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ipGroup.ipReasmFails,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "ipFragOKs",
+      {43, 6, 1, 2, 1, 4, 17},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ipGroup.ipFragOKs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "ipFragFails",
+      {43, 6, 1, 2, 1, 4, 18},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ipGroup.ipFragFails,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "ipFragCreates",
+      {43, 6, 1, 2, 1, 4, 19},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ipGroup.ipFragCreates,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   //IP address table
+   {
+      "ipAdEntAddr",
+      {43, 6, 1, 2, 1, 4, 20, 1, 1},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_IP_ADDRESS,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      MIB2_IP_ADDRESS_SIZE,
+      NULL,
+      mib2GetIpAddrEntry,
+      mib2GetNextIpAddrEntry
+   },
+   {
+      "ipAdEntIfIndex",
+      {43, 6, 1, 2, 1, 4, 20, 1, 2},
+      9,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      mib2GetIpAddrEntry,
+      mib2GetNextIpAddrEntry
+   },
+   {
+      "ipAdEntNetMask",
+      {43, 6, 1, 2, 1, 4, 20, 1, 3},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_IP_ADDRESS,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      MIB2_IP_ADDRESS_SIZE,
+      NULL,
+      mib2GetIpAddrEntry,
+      mib2GetNextIpAddrEntry
+   },
+   {
+      "ipAdEntBcastAddr",
+      {43, 6, 1, 2, 1, 4, 20, 1, 4},
+      9,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      mib2GetIpAddrEntry,
+      mib2GetNextIpAddrEntry
+   },
+   {
+      "ipAdEntReasmMaxSize",
+      {43, 6, 1, 2, 1, 4, 20, 1, 5},
+      9,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      mib2GetIpAddrEntry,
+      mib2GetNextIpAddrEntry
+   },
+   //IP address translation table
+   {
+      "ipNetToMediaIfIndex",
+      {43, 6, 1, 2, 1, 4, 22, 1, 1},
+      9,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_WRITE,
+      NULL,
+      NULL,
+      sizeof(int32_t),
+      mib2SetIpNetToMediaEntry,
+      mib2GetIpNetToMediaEntry,
+      mib2GetNextIpNetToMediaEntry
+   },
+   {
+      "ipNetToMediaPhysAddress",
+      {43, 6, 1, 2, 1, 4, 22, 1, 2},
+      9,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_OCTET_STRING,
+      MIB_ACCESS_READ_WRITE,
+      NULL,
+      NULL,
+      MIB2_PHYS_ADDRESS_SIZE,
+      mib2SetIpNetToMediaEntry,
+      mib2GetIpNetToMediaEntry,
+      mib2GetNextIpNetToMediaEntry
+   },
+   {
+      "ipNetToMediaNetAddress",
+      {43, 6, 1, 2, 1, 4, 22, 1, 3},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_IP_ADDRESS,
+      MIB_ACCESS_READ_WRITE,
+      NULL,
+      NULL,
+      MIB2_IP_ADDRESS_SIZE,
+      mib2SetIpNetToMediaEntry,
+      mib2GetIpNetToMediaEntry,
+      mib2GetNextIpNetToMediaEntry
+   },
+   {
+      "ipNetToMediaType",
+      {43, 6, 1, 2, 1, 4, 22, 1, 4},
+      9,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_WRITE,
+      NULL,
+      NULL,
+      sizeof(int32_t),
+      mib2SetIpNetToMediaEntry,
+      mib2GetIpNetToMediaEntry,
+      mib2GetNextIpNetToMediaEntry
+   },
+   //Additional IP objects
+   {
+      "ipRoutingDiscards",
+      {43, 6, 1, 2, 1, 4, 23},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.ipGroup.ipRoutingDiscards,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   //ICMP group
+   {
+      "icmpInMsgs",
+      {43, 6, 1, 2, 1, 5, 1},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpInMsgs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpInErrors",
+      {43, 6, 1, 2, 1, 5, 2},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpInErrors,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpInDestUnreachs",
+      {43, 6, 1, 2, 1, 5, 3},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpInDestUnreachs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpInTimeExcds",
+      {43, 6, 1, 2, 1, 5, 4},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpInTimeExcds,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpInParmProbs",
+      {43, 6, 1, 2, 1, 5, 5},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpInParmProbs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpInSrcQuenchs",
+      {43, 6, 1, 2, 1, 5, 6},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpInSrcQuenchs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpInRedirects",
+      {43, 6, 1, 2, 1, 5, 7},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpInRedirects,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpInEchos",
+      {43, 6, 1, 2, 1, 5, 8},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpInEchos,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpInEchoReps",
+      {43, 6, 1, 2, 1, 5, 9},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpInEchoReps,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpInTimestamps",
+      {43, 6, 1, 2, 1, 5, 10},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpInTimestamps,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpInTimestampReps",
+      {43, 6, 1, 2, 1, 5, 11},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpInTimestampReps,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpInAddrMasks",
+      {43, 6, 1, 2, 1, 5, 12},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpInAddrMasks,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpInAddrMaskReps",
+      {43, 6, 1, 2, 1, 5, 13},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpInAddrMaskReps,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpOutMsgs",
+      {43, 6, 1, 2, 1, 5, 14},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpOutMsgs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpOutErrors",
+      {43, 6, 1, 2, 1, 5, 15},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpOutErrors,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpOutDestUnreachs",
+      {43, 6, 1, 2, 1, 5, 16},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpOutDestUnreachs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpOutTimeExcds",
+      {43, 6, 1, 2, 1, 5, 17},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpOutTimeExcds,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpOutParmProbs",
+      {43, 6, 1, 2, 1, 5, 18},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpOutParmProbs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpOutSrcQuenchs",
+      {43, 6, 1, 2, 1, 5, 19},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpOutSrcQuenchs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpOutRedirects",
+      {43, 6, 1, 2, 1, 5, 20},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpOutRedirects,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpOutEchos",
+      {43, 6, 1, 2, 1, 5, 21},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpOutEchos,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpOutEchoReps",
+      {43, 6, 1, 2, 1, 5, 22},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpOutEchoReps,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpOutTimestamps",
+      {43, 6, 1, 2, 1, 5, 23},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpOutTimestamps,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpOutTimestampReps",
+      {43, 6, 1, 2, 1, 5, 24},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpOutTimestampReps,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpOutAddrMasks",
+      {43, 6, 1, 2, 1, 5, 25},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpOutAddrMasks,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "icmpOutAddrMaskReps",
+      {43, 6, 1, 2, 1, 5, 26},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.icmpGroup.icmpOutAddrMaskReps,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+#endif
+#if (TCP_SUPPORT == ENABLED)
+   //TCP group
+   {
+      "tcpRtoAlgorithm",
+      {43, 6, 1, 2, 1, 6, 1},
+      7,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.tcpGroup.tcpRtoAlgorithm,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "tcpRtoMin",
+      {43, 6, 1, 2, 1, 6, 2},
+      7,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.tcpGroup.tcpRtoMin,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "tcpRtoMax",
+      {43, 6, 1, 2, 1, 6, 3},
+      7,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.tcpGroup.tcpRtoMax,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "tcpMaxConn",
+      {43, 6, 1, 2, 1, 6, 4},
+      7,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.tcpGroup.tcpMaxConn,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "tcpActiveOpens",
+      {43, 6, 1, 2, 1, 6, 5},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.tcpGroup.tcpActiveOpens,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "tcpPassiveOpens",
+      {43, 6, 1, 2, 1, 6, 6},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.tcpGroup.tcpPassiveOpens,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "tcpAttemptFails",
+      {43, 6, 1, 2, 1, 6, 7},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.tcpGroup.tcpAttemptFails,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "tcpEstabResets",
+      {43, 6, 1, 2, 1, 6, 8},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.tcpGroup.tcpEstabResets,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "tcpCurrEstab",
+      {43, 6, 1, 2, 1, 6, 9},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_GAUGE32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.tcpGroup.tcpCurrEstab,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "tcpInSegs",
+      {43, 6, 1, 2, 1, 6, 10},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.tcpGroup.tcpInSegs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "tcpOutSegs",
+      {43, 6, 1, 2, 1, 6, 11},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.tcpGroup.tcpOutSegs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "tcpRetransSegs",
+      {43, 6, 1, 2, 1, 6, 12},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.tcpGroup.tcpRetransSegs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   //TCP connection table
+   {
+      "tcpConnState",
+      {43, 6, 1, 2, 1, 6, 13, 1, 1},
+      9,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_WRITE,
+      NULL,
+      NULL,
+      sizeof(int32_t),
+      mib2SetTcpConnEntry,
+      mib2GetTcpConnEntry,
+      mib2GetNextTcpConnEntry
+   },
+   {
+      "tcpConnLocalAddress",
+      {43, 6, 1, 2, 1, 6, 13, 1, 2},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_IP_ADDRESS,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      MIB2_IP_ADDRESS_SIZE,
+      NULL,
+      mib2GetTcpConnEntry,
+      mib2GetNextTcpConnEntry
+   },
+   {
+      "tcpConnLocalPort",
+      {43, 6, 1, 2, 1, 6, 13, 1, 3},
+      9,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      mib2GetTcpConnEntry,
+      mib2GetNextTcpConnEntry
+   },
+   {
+      "tcpConnRemAddress",
+      {43, 6, 1, 2, 1, 6, 13, 1, 4},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_IP_ADDRESS,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      MIB2_IP_ADDRESS_SIZE,
+      NULL,
+      mib2GetTcpConnEntry,
+      mib2GetNextTcpConnEntry
+   },
+   {
+      "tcpConnRemPort",
+      {43, 6, 1, 2, 1, 6, 13, 1, 5},
+      9,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      mib2GetTcpConnEntry,
+      mib2GetNextTcpConnEntry
+   },
+   //Additional TCP objects
+   {
+      "tcpInErrs",
+      {43, 6, 1, 2, 1, 6, 14},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.tcpGroup.tcpInErrs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "tcpOutRsts",
+      {43, 6, 1, 2, 1, 6, 15},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.tcpGroup.tcpOutRsts,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "tcpHCInSegs",
+      {43, 6, 1, 2, 1, 6, 17},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER64,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.tcpGroup.tcpHCInSegs,
+      NULL,
+      sizeof(uint64_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "tcpHCOutSegs",
+      {43, 6, 1, 2, 1, 6, 18},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER64,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.tcpGroup.tcpHCOutSegs,
+      NULL,
+      sizeof(uint64_t),
+      NULL,
+      NULL,
+      NULL
+   },
+#endif
+#if (UDP_SUPPORT == ENABLED)
+   //UDP group
+   {
+      "udpInDatagrams",
+      {43, 6, 1, 2, 1, 7, 1},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.udpGroup.udpInDatagrams,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "udpNoPorts",
+      {43, 6, 1, 2, 1, 7, 2},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.udpGroup.udpNoPorts,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "udpInErrors",
+      {43, 6, 1, 2, 1, 7, 3},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.udpGroup.udpInErrors,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "udpOutDatagrams",
+      {43, 6, 1, 2, 1, 7, 4},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.udpGroup.udpOutDatagrams,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   //UDP listener table
+   {
+      "udpLocalAddress",
+      {43, 6, 1, 2, 1, 7, 5, 1, 1},
+      9,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_IP_ADDRESS,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      MIB2_IP_ADDRESS_SIZE,
+      NULL,
+      mib2GetUdpEntry,
+      mib2GetNextUdpEntry
+   },
+   {
+      "udpLocalPort",
+      {43, 6, 1, 2, 1, 7, 5, 1, 2},
+      9,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_ONLY,
+      NULL,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      mib2GetUdpEntry,
+      mib2GetNextUdpEntry
+   },
+   //Additional UDP objects
+   {
+      "udpHCInDatagrams",
+      {43, 6, 1, 2, 1, 7, 8},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER64,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.udpGroup.udpHCInDatagrams,
+      NULL,
+      sizeof(uint64_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "udpHCOutDatagrams",
+      {43, 6, 1, 2, 1, 7, 9},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER64,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.udpGroup.udpHCOutDatagrams,
+      NULL,
+      sizeof(uint64_t),
+      NULL,
+      NULL,
+      NULL
+   },
+#endif
+   //SNMP group
+   {
+      "snmpInPkts",
+      {43, 6, 1, 2, 1, 11, 1},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpInPkts,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpOutPkts",
+      {43, 6, 1, 2, 1, 11, 2},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpOutPkts,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpInBadVersions",
+      {43, 6, 1, 2, 1, 11, 3},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpInBadVersions,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpInBadCommunityNames",
+      {43, 6, 1, 2, 1, 11, 4},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpInBadCommunityNames,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpInBadCommunityUses",
+      {43, 6, 1, 2, 1, 11, 5},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpInBadCommunityUses,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpInASNParseErrs",
+      {43, 6, 1, 2, 1, 11, 6},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpInASNParseErrs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpInTooBigs",
+      {43, 6, 1, 2, 1, 11, 8},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpInTooBigs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpInNoSuchNames",
+      {43, 6, 1, 2, 1, 11, 9},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpInNoSuchNames,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpInBadValues",
+      {43, 6, 1, 2, 1, 11, 10},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpInBadValues,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpInReadOnlys",
+      {43, 6, 1, 2, 1, 11, 11},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpInReadOnlys,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpInGenErrs",
+      {43, 6, 1, 2, 1, 11, 12},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpInGenErrs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpInTotalReqVars",
+      {43, 6, 1, 2, 1, 11, 13},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpInTotalReqVars,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpInTotalSetVars",
+      {43, 6, 1, 2, 1, 11, 14},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpInTotalSetVars,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpInGetRequests",
+      {43, 6, 1, 2, 1, 11, 15},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpInGetRequests,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpInGetNexts",
+      {43, 6, 1, 2, 1, 11, 16},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpInGetNexts,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpInSetRequests",
+      {43, 6, 1, 2, 1, 11, 17},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpInSetRequests,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpInGetResponses",
+      {43, 6, 1, 2, 1, 11, 18},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpInGetResponses,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpInTraps",
+      {43, 6, 1, 2, 1, 11, 19},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpInTraps,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpOutTooBigs",
+      {43, 6, 1, 2, 1, 11, 20},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpOutTooBigs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpOutNoSuchNames",
+      {43, 6, 1, 2, 1, 11, 21},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpOutNoSuchNames,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpOutBadValues",
+      {43, 6, 1, 2, 1, 11, 22},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpOutBadValues,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpOutGenErrs",
+      {43, 6, 1, 2, 1, 11, 24},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpOutGenErrs,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpOutGetRequests",
+      {43, 6, 1, 2, 1, 11, 25},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpOutGetRequests,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpOutGetNexts",
+      {43, 6, 1, 2, 1, 11, 26},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpOutGetNexts,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpOutSetRequests",
+      {43, 6, 1, 2, 1, 11, 27},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpOutSetRequests,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpOutGetResponses",
+      {43, 6, 1, 2, 1, 11, 28},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpOutGetResponses,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpOutTraps",
+      {43, 6, 1, 2, 1, 11, 29},
+      7,
+      ASN1_CLASS_APPLICATION,
+      MIB_TYPE_COUNTER32,
+      MIB_ACCESS_READ_ONLY,
+      &mib2Base.snmpGroup.snmpOutTraps,
+      NULL,
+      sizeof(uint32_t),
+      NULL,
+      NULL,
+      NULL
+   },
+   {
+      "snmpEnableAuthenTraps",
+      {43, 6, 1, 2, 1, 11, 30},
+      7,
+      ASN1_CLASS_UNIVERSAL,
+      ASN1_TYPE_INTEGER,
+      MIB_ACCESS_READ_WRITE,
+      &mib2Base.snmpGroup.snmpEnableAuthenTraps,
+      NULL,
+      sizeof(int32_t),
+      NULL,
+      NULL,
+      NULL
+   }
+};
+
+
+/**
+ * @brief MIB-II module
+ **/
+
+const MibModule mib2Module =
+{
+   mib2Objects,
+   arraysize(mib2Objects),
+   mib2Init,
+   mib2Lock,
+   mib2Unlock
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mibs/mib2_module.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,489 @@
+/**
+ * @file mib2_module.h
+ * @brief MIB-II module
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MIB2_MODULE_H
+#define _MIB2_MODULE_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/udp.h"
+#include "core/tcp.h"
+#include "ipv4/ipv4.h"
+#include "mibs/mib_common.h"
+
+//MIB-II module support
+#ifndef MIB2_SUPPORT
+   #define MIB2_SUPPORT DISABLED
+#elif (MIB2_SUPPORT != ENABLED && MIB2_SUPPORT != DISABLED)
+   #error MIB2_SUPPORT parameter is not valid
+#endif
+
+//Size of sysDescr object
+#ifndef MIB2_SYS_DESCR_SIZE
+   #define MIB2_SYS_DESCR_SIZE 16
+#elif (MIB2_SYS_DESCR_SIZE < 0)
+   #error MIB2_SYS_DESCR_SIZE parameter is not valid
+#endif
+
+//Size of sysObjectID object
+#ifndef MIB2_SYS_OBJECT_ID_SIZE
+   #define MIB2_SYS_OBJECT_ID_SIZE 16
+#elif (MIB2_SYS_OBJECT_ID_SIZE < 0)
+   #error MIB2_SYS_OBJECT_ID_SIZE parameter is not valid
+#endif
+
+//Size of sysContact object
+#ifndef MIB2_SYS_CONTACT_SIZE
+   #define MIB2_SYS_CONTACT_SIZE 16
+#elif (MIB2_SYS_CONTACT_SIZE < 0)
+   #error MIB2_SYS_CONTACT_SIZE parameter is not valid
+#endif
+
+//Size of sysName object
+#ifndef MIB2_SYS_NAME_SIZE
+   #define MIB2_SYS_NAME_SIZE 16
+#elif (MIB2_SYS_NAME_SIZE < 0)
+   #error MIB2_SYS_NAME_SIZE parameter is not valid
+#endif
+
+//Size of sysLocation object
+#ifndef MIB2_SYS_LOCATION_SIZE
+   #define MIB2_SYS_LOCATION_SIZE 16
+#elif (MIB2_SYS_LOCATION_SIZE < 0)
+   #error MIB2_SYS_LOCATION_SIZE parameter is not valid
+#endif
+
+//Size of ifDescr object
+#ifndef MIB2_IF_DESCR_SIZE
+   #define MIB2_IF_DESCR_SIZE 16
+#elif (MIB2_IF_DESCR_SIZE < 1)
+   #error MIB2_IF_DESCR_SIZE parameter is not valid
+#endif
+
+//Size of ifSpecific object
+#ifndef MIB2_IF_SPECIFIC_SIZE
+   #define MIB2_IF_SPECIFIC_SIZE 16
+#elif (MIB2_IF_SPECIFIC_SIZE < 1)
+   #error MIB2_IF_SPECIFIC_SIZE parameter is not valid
+#endif
+
+//Size of PhysAddress data type
+#ifndef MIB2_PHYS_ADDRESS_SIZE
+   #define MIB2_PHYS_ADDRESS_SIZE 6
+#elif (MIB2_PHYS_ADDRESS_SIZE < 6)
+   #error MIB2_PHYS_ADDRESS_SIZE parameter is not valid
+#endif
+
+//Size of IpAddress data type
+#ifndef MIB2_IP_ADDRESS_SIZE
+   #define MIB2_IP_ADDRESS_SIZE 4
+#elif (MIB2_IP_ADDRESS_SIZE != 4)
+   #error MIB2_IP_ADDRESS_SIZE parameter is not valid
+#endif
+
+//Macro definitions
+#if (MIB2_SUPPORT == ENABLED)
+   #define MIB2_SET_INTEGER(name, value) name = value
+   #define MIB2_SET_GAUGE32(name, value) name = value
+   #define MIB2_SET_TIME_TICKS(name, value) name = value
+   #define MIB2_SET_OCTET_STRING(name, value, length) memcpy(name, value, length)
+   #define MIB2_SET_OCTET_STRING_LEN(name, length) name = length
+   #define MIB2_INC_COUNTER32(name, value) name += value
+   #define MIB2_INC_COUNTER64(name, value) name += value
+#else
+   #define MIB2_SET_INTEGER(name, value)
+   #define MIB2_SET_GAUGE32(name, value)
+   #define MIB2_SET_TIME_TICKS(name, value)
+   #define MIB2_SET_OCTET_STRING(name, value, length)
+   #define MIB2_SET_OCTET_STRING_LEN(name, length)
+   #define MIB2_INC_COUNTER32(name, value)
+   #define MIB2_INC_COUNTER64(name, value)
+#endif
+
+
+/**
+ * @brief System services
+ **/
+
+typedef enum
+{
+   MIB2_SYS_SERVICE_PHYSICAL     = 0x01,
+   MIB2_SYS_SERVICE_DATALINK     = 0x02,
+   MIB2_SYS_SERVICE_INTERNET     = 0x04,
+   MIB2_SYS_SERVICE_END_TO_END   = 0x08,
+   MIB2_SYS_SERVICE_APPLICATIONS = 0x40
+} Mib2SysService;
+
+
+/**
+ * @brief Interface types
+ **/
+
+typedef enum
+{
+   MIB2_IF_TYPE_OTHER           = 1,
+   MIB2_IF_TYPE_ETHERNET_CSMACD = 6,
+   MIB2_IF_TYPE_PROP_PTP_SERIAL = 22,
+   MIB2_IF_TYPE_PPP             = 23,
+   MIB2_IF_TYPE_SOFT_LOOPBACK   = 24,
+   MIB2_IF_TYPE_SLIP            = 28,
+   MIB2_IF_TYPE_RS232           = 33,
+   MIB2_IF_TYPE_PARA            = 34,
+   MIB2_IF_TYPE_USB             = 160,
+   MIB2_IF_TYPE_IEEE_802_15_4   = 259
+} Mib2IfType;
+
+
+/**
+ * @brief The desired state of the interface
+ **/
+
+typedef enum
+{
+   MIB2_IF_ADMIN_STATUS_UP      = 1,
+   MIB2_IF_ADMIN_STATUS_DOWN    = 2,
+   MIB2_IF_ADMIN_STATUS_TESTING = 3
+} Mib2IfAdminStatus;
+
+
+/**
+ * @brief The operational state of the interface
+ **/
+
+typedef enum
+{
+   MIB2_IF_OPER_STATUS_UP      = 1,
+   MIB2_IF_OPER_STATUS_DOWN    = 2,
+   MIB2_IF_OPER_STATUS_TESTING = 3
+} Mib2IfOperStatus;
+
+
+/**
+ * @brief IP forwarding state
+ **/
+
+typedef enum
+{
+   MIB2_IP_FORWARDING_ENABLED  = 1,
+   MIB2_IP_FORWARDING_DISABLED = 2
+} Mib2IpForwarding;
+
+
+/**
+ * @brief Type of mapping
+ **/
+
+typedef enum
+{
+   MIB2_IP_NET_TO_MEDIA_TYPE_OTHER   = 1,
+   MIB2_IP_NET_TO_MEDIA_TYPE_INVALID = 2,
+   MIB2_IP_NET_TO_MEDIA_TYPE_DYNAMIC = 3,
+   MIB2_IP_NET_TO_MEDIA_TYPE_STATIC  = 4
+} Mib2IpNetToMediaType;
+
+
+/**
+ * @brief RTO calculation algorithm
+ **/
+
+typedef enum
+{
+   MIB2_TCP_RTO_ALGORITHM_OTHER    = 1,
+   MIB2_TCP_RTO_ALGORITHM_CONSTANT = 2,
+   MIB2_TCP_RTO_ALGORITHM_RSRE     = 3,
+   MIB2_TCP_RTO_ALGORITHM_VANJ     = 4
+} Mib2TcpRtoAlgorithm;
+
+
+/**
+ * @brief TCP connection states
+ **/
+
+typedef enum
+{
+   MIB2_TCP_CONN_STATE_CLOSED       = 1,
+   MIB2_TCP_CONN_STATE_LISTEN       = 2,
+   MIB2_TCP_CONN_STATE_SYN_SENT     = 3,
+   MIB2_TCP_CONN_STATE_SYN_RECEIVED = 4,
+   MIB2_TCP_CONN_STATE_ESTABLISHED  = 5,
+   MIB2_TCP_CONN_STATE_FIN_WAIT_1   = 6,
+   MIB2_TCP_CONN_STATE_FIN_WAIT_2   = 7,
+   MIB2_TCP_CONN_STATE_CLOSE_WAIT   = 8,
+   MIB2_TCP_CONN_STATE_LAST_ACK     = 9,
+   MIB2_TCP_CONN_STATE_CLOSING      = 10,
+   MIB2_TCP_CONN_STATE_TIME_WAIT    = 11,
+   MIB2_TCP_CONN_STATE_DELETE_TCB   = 12
+} Mib2TcpConnState;
+
+
+/**
+ * @brief System group
+ **/
+
+typedef struct
+{
+#if (MIB2_SYS_DESCR_SIZE > 0)
+   char_t sysDescr[MIB2_SYS_DESCR_SIZE];
+   size_t sysDescrLen;
+#endif
+#if (MIB2_SYS_OBJECT_ID_SIZE > 0)
+   uint8_t sysObjectID[MIB2_SYS_OBJECT_ID_SIZE];
+   size_t sysObjectIDLen;
+#endif
+   uint32_t sysUpTime;
+#if (MIB2_SYS_CONTACT_SIZE > 0)
+   char_t sysContact[MIB2_SYS_CONTACT_SIZE];
+   size_t sysContactLen;
+#endif
+#if (MIB2_SYS_NAME_SIZE > 0)
+   char_t sysName[MIB2_SYS_NAME_SIZE];
+   size_t sysNameLen;
+#endif
+#if (MIB2_SYS_LOCATION_SIZE > 0)
+   char_t sysLocation[MIB2_SYS_LOCATION_SIZE];
+   size_t sysLocationLen;
+#endif
+   int32_t sysServices;
+} Mib2SysGroup;
+
+
+/**
+ * @brief Interfaces table entry
+ **/
+
+typedef struct
+{
+   int32_t ifIndex;
+   char_t ifDescr[MIB2_IF_DESCR_SIZE];
+   size_t ifDescrLen;
+   int32_t ifType;
+   int32_t ifMtu;
+   uint32_t ifSpeed;
+   uint8_t ifPhysAddress[MIB2_PHYS_ADDRESS_SIZE];
+   size_t ifPhysAddressLen;
+   int32_t ifAdminStatus;
+   int32_t ifOperStatus;
+   uint32_t ifLastChange;
+   uint32_t ifInOctets;
+   uint32_t ifInUcastPkts;
+   uint32_t ifInNUcastPkts;
+   uint32_t ifInDiscards;
+   uint32_t ifInErrors;
+   uint32_t ifInUnknownProtos;
+   uint32_t ifOutOctets;
+   uint32_t ifOutUcastPkts;
+   uint32_t ifOutNUcastPkts;
+   uint32_t ifOutDiscards;
+   uint32_t ifOutErrors;
+   uint32_t ifOutQLen;
+   uint8_t ifSpecific[MIB2_IF_SPECIFIC_SIZE];
+   size_t ifSpecificLen;
+} Mib2IfEntry;
+
+
+/**
+ * @brief Interfaces group
+ **/
+
+typedef struct
+{
+   int32_t ifNumber;
+   Mib2IfEntry ifTable[NET_INTERFACE_COUNT];
+} Mib2IfGroup;
+
+
+/**
+ * @brief IP group
+ **/
+
+typedef struct
+{
+   int32_t ipForwarding;
+   int32_t ipDefaultTTL;
+   uint32_t ipInReceives;
+   uint32_t ipInHdrErrors;
+   uint32_t ipInAddrErrors;
+   uint32_t ipForwDatagrams;
+   uint32_t ipInUnknownProtos;
+   uint32_t ipInDiscards;
+   uint32_t ipInDelivers;
+   uint32_t ipOutRequests;
+   uint32_t ipOutDiscards;
+   uint32_t ipOutNoRoutes;
+   int32_t ipReasmTimeout;
+   uint32_t ipReasmReqds;
+   uint32_t ipReasmOKs;
+   uint32_t ipReasmFails;
+   uint32_t ipFragOKs;
+   uint32_t ipFragFails;
+   uint32_t ipFragCreates;
+   uint32_t ipRoutingDiscards;
+} Mib2IpGroup;
+
+
+/**
+ * @brief ICMP group
+ **/
+
+typedef struct
+{
+   uint32_t icmpInMsgs;
+   uint32_t icmpInErrors;
+   uint32_t icmpInDestUnreachs;
+   uint32_t icmpInTimeExcds;
+   uint32_t icmpInParmProbs;
+   uint32_t icmpInSrcQuenchs;
+   uint32_t icmpInRedirects;
+   uint32_t icmpInEchos;
+   uint32_t icmpInEchoReps;
+   uint32_t icmpInTimestamps;
+   uint32_t icmpInTimestampReps;
+   uint32_t icmpInAddrMasks;
+   uint32_t icmpInAddrMaskReps;
+   uint32_t icmpOutMsgs;
+   uint32_t icmpOutErrors;
+   uint32_t icmpOutDestUnreachs;
+   uint32_t icmpOutTimeExcds;
+   uint32_t icmpOutParmProbs;
+   uint32_t icmpOutSrcQuenchs;
+   uint32_t icmpOutRedirects;
+   uint32_t icmpOutEchos;
+   uint32_t icmpOutEchoReps;
+   uint32_t icmpOutTimestamps;
+   uint32_t icmpOutTimestampReps;
+   uint32_t icmpOutAddrMasks;
+   uint32_t icmpOutAddrMaskReps;
+} Mib2IcmpGroup;
+
+
+/**
+ * @brief TCP group
+ **/
+
+typedef struct
+{
+   int32_t tcpRtoAlgorithm;
+   int32_t tcpRtoMin;
+   int32_t tcpRtoMax;
+   int32_t tcpMaxConn;
+   uint32_t tcpActiveOpens;
+   uint32_t tcpPassiveOpens;
+   uint32_t tcpAttemptFails;
+   uint32_t tcpEstabResets;
+   uint32_t tcpCurrEstab;
+   uint32_t tcpInSegs;
+   uint32_t tcpOutSegs;
+   uint32_t tcpRetransSegs;
+   uint32_t tcpInErrs;
+   uint32_t tcpOutRsts;
+   uint64_t tcpHCInSegs;
+   uint64_t tcpHCOutSegs;
+} Mib2TcpGroup;
+
+
+/**
+ * @brief UDP group
+ **/
+
+typedef struct
+{
+   uint32_t udpInDatagrams;
+   uint32_t udpNoPorts;
+   uint32_t udpInErrors;
+   uint32_t udpOutDatagrams;
+   uint64_t udpHCInDatagrams;
+   uint64_t udpHCOutDatagrams;
+} Mib2UdpGroup;
+
+
+/**
+ * @brief SNMP group
+ **/
+
+typedef struct
+{
+   uint32_t snmpInPkts;
+   uint32_t snmpOutPkts;
+   uint32_t snmpInBadVersions;
+   uint32_t snmpInBadCommunityNames;
+   uint32_t snmpInBadCommunityUses;
+   uint32_t snmpInASNParseErrs;
+   uint32_t snmpInTooBigs;
+   uint32_t snmpInNoSuchNames;
+   uint32_t snmpInBadValues;
+   uint32_t snmpInReadOnlys;
+   uint32_t snmpInGenErrs;
+   uint32_t snmpInTotalReqVars;
+   uint32_t snmpInTotalSetVars;
+   uint32_t snmpInGetRequests;
+   uint32_t snmpInGetNexts;
+   uint32_t snmpInSetRequests;
+   uint32_t snmpInGetResponses;
+   uint32_t snmpInTraps;
+   uint32_t snmpOutTooBigs;
+   uint32_t snmpOutNoSuchNames;
+   uint32_t snmpOutBadValues;
+   uint32_t snmpOutGenErrs;
+   uint32_t snmpOutGetRequests;
+   uint32_t snmpOutGetNexts;
+   uint32_t snmpOutSetRequests;
+   uint32_t snmpOutGetResponses;
+   uint32_t snmpOutTraps;
+   int32_t snmpEnableAuthenTraps;
+} Mib2SnmpGroup;
+
+
+/**
+ * @brief MIB-II base
+ **/
+
+typedef struct
+{
+   Mib2SysGroup sysGroup;
+   Mib2IfGroup ifGroup;
+#if (IPV4_SUPPORT == ENABLED)
+   Mib2IpGroup ipGroup;
+   Mib2IcmpGroup icmpGroup;
+#endif
+#if (TCP_SUPPORT == ENABLED)
+   Mib2TcpGroup tcpGroup;
+#endif
+#if (UDP_SUPPORT == ENABLED)
+   Mib2UdpGroup udpGroup;
+#endif
+   Mib2SnmpGroup snmpGroup;
+} Mib2Base;
+
+
+//MIB-II related constants
+extern Mib2Base mib2Base;
+extern const MibObject mib2Objects[];
+extern const MibModule mib2Module;
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mibs/mib_common.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,200 @@
+/**
+ * @file mib_common.c
+ * @brief Common definitions for MIB modules
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Dependencies
+#include "core/net.h"
+#include "mibs/mib_common.h"
+#include "oid.h"
+#include "debug.h"
+
+
+/**
+ * @brief Encode instance identifier (index)
+ * @param[in] oid Pointer to the object identifier
+ * @param[in] maxOidLen Maximum number of bytes the OID can hold
+ * @param[in,out] pos Offset where to write the instance identifier
+ * @param[in] index Index value
+ * @return Error code
+ **/
+
+error_t mibEncodeIndex(uint8_t *oid, size_t maxOidLen, size_t *pos, uint_t index)
+{
+   //Encode instance identifier
+   return oidEncodeSubIdentifier(oid, maxOidLen, pos, index);
+}
+
+
+/**
+ * @brief Decode instance identifier (index)
+ * @param[in] oid Pointer to the object identifier
+ * @param[in] oidLen Length of the OID, in bytes
+ * @param[in,out] pos Offset where to read the instance identifier
+ * @param[out] index Index value
+ * @return Error code
+ **/
+
+error_t mibDecodeIndex(const uint8_t *oid, size_t oidLen, size_t *pos, uint_t *index)
+{
+   error_t error;
+   uint32_t value;
+
+   //Decode instance identifier
+   error = oidDecodeSubIdentifier(oid, oidLen, pos, &value);
+   //Invalid sub-identifier?
+   if(error)
+      return error;
+
+   //Save index value
+   *index = value;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Encode instance identifier (IPv4 address)
+ * @param[in] oid Pointer to the object identifier
+ * @param[in] maxOidLen Maximum number of bytes the OID can hold
+ * @param[in,out] pos Offset where to write the instance identifier
+ * @param[in] ipAddr IPv4 address
+ * @return Error code
+ **/
+
+error_t mibEncodeIpv4Addr(uint8_t *oid, size_t maxOidLen, size_t *pos, Ipv4Addr ipAddr)
+{
+   error_t error;
+   uint_t i;
+   uint8_t *p;
+
+   //Cast the IPv4 address as a byte array
+   p = (uint8_t *) &ipAddr;
+
+   //The address is encoded as 4 subsequent sub-identifiers
+   for(i = 0; i < 4; i++)
+   {
+      //Encode the current byte
+      error = oidEncodeSubIdentifier(oid, maxOidLen, pos, p[i]);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Decode instance identifier (IPv4 address)
+ * @param[in] oid Pointer to the object identifier
+ * @param[in] oidLen Length of the OID, in bytes
+ * @param[in,out] pos Offset where to read the instance identifier
+ * @param[out] ipAddr IPv4 address
+ * @return Error code
+ **/
+
+error_t mibDecodeIpv4Addr(const uint8_t *oid, size_t oidLen, size_t *pos, Ipv4Addr *ipAddr)
+{
+   error_t error;
+   uint_t i;
+   uint32_t value;
+   uint8_t *p;
+
+   //Cast the IPv4 address as a byte array
+   p = (uint8_t *) ipAddr;
+
+   //The address is encoded as 4 subsequent sub-identifiers
+   for(i = 0; i < 4; i++)
+   {
+      //Decode the current sub-identifier
+      error = oidDecodeSubIdentifier(oid, oidLen, pos, &value);
+      //Invalid sub-identifier?
+      if(error)
+         return error;
+
+      //Each byte of the IPv4 address must be in the range 0-255
+      if(value > 255)
+         return ERROR_INSTANCE_NOT_FOUND;
+
+      //Save the current byte
+      p[i] = value & 0xFF;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Encode instance identifier (port number)
+ * @param[in] oid Pointer to the object identifier
+ * @param[in] maxOidLen Maximum number of bytes the OID can hold
+ * @param[in,out] pos Offset where to write the instance identifier
+ * @param[in] port Port number
+ * @return Error code
+ **/
+
+error_t mibEncodePort(uint8_t *oid, size_t maxOidLen, size_t *pos, uint16_t port)
+{
+   //Encode instance identifier
+   return oidEncodeSubIdentifier(oid, maxOidLen, pos, port);
+}
+
+
+/**
+ * @brief Decode instance identifier (port number)
+ * @param[in] oid Pointer to the object identifier
+ * @param[in] oidLen Length of the OID, in bytes
+ * @param[in,out] pos Offset where to read the instance identifier
+ * @param[out] port Port number
+ * @return Error code
+ **/
+
+error_t mibDecodePort(const uint8_t *oid, size_t oidLen, size_t *pos, uint16_t *port)
+{
+   error_t error;
+   uint32_t value;
+
+   //Decode instance identifier
+   error = oidDecodeSubIdentifier(oid, oidLen, pos, &value);
+   //Invalid sub-identifier?
+   if(error)
+      return error;
+
+   //Port number must be in range 0-65535
+   if(value > 65535)
+      return ERROR_INSTANCE_NOT_FOUND;
+
+   //Save port number
+   *port = value;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mibs/mib_common.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,199 @@
+/**
+ * @file mib_common.h
+ * @brief Common definitions for MIB modules
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MIB_COMMON_H
+#define _MIB_COMMON_H
+
+//Dependencies
+#include "core/net.h"
+
+//Maximum OID size
+#ifndef MIB_MAX_OID_SIZE
+   #define MIB_MAX_OID_SIZE 16
+#elif (MIB_MAX_OID_SIZE < 1)
+   #error MIB_MAX_OID_SIZE parameter is not valid
+#endif
+
+//Forward declaration of MibObject structure
+struct _MibObject;
+#define MibObject struct _MibObject
+
+
+/**
+ * @brief MIB object types
+ **/
+
+typedef enum
+{
+   MIB_TYPE_IP_ADDRESS        = 0,
+   MIB_TYPE_COUNTER32         = 1,
+   MIB_TYPE_GAUGE32           = 2,
+   MIB_TYPE_UNSIGNED32        = 2,
+   MIB_TYPE_TIME_TICKS        = 3,
+   MIB_TYPE_OPAQUE            = 4,
+   MIB_TYPE_COUNTER64         = 6
+} MibType;
+
+
+/**
+ * @brief Access modes
+ **/
+
+typedef enum
+{
+   MIB_ACCESS_NONE       = 0,
+   MIB_ACCESS_READ_ONLY  = 1,
+   MIB_ACCESS_WRITE_ONLY = 2,
+   MIB_ACCESS_READ_WRITE = 3
+} MibAccess;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief Variant data type
+ **/
+
+typedef __start_packed struct
+{
+   __start_packed union
+   {
+      int32_t integer;
+      uint8_t octetString[1];
+      uint8_t oid[1];
+      uint8_t ipAddr[4];
+      uint32_t counter32;
+      uint32_t gauge32;
+      uint32_t timeTicks;
+      uint64_t counter64;
+   };
+} __end_packed MibVariant;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+/**
+ * @brief Set object value
+ **/
+
+typedef error_t (*MibSetValue)(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, const MibVariant *value, size_t valueLen);
+
+
+/**
+ * @brief Get object value
+ **/
+
+typedef error_t (*MibGetValue)(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, MibVariant *value, size_t *valueLen);
+
+
+/**
+ * @brief Get next object
+ **/
+
+typedef error_t (*MibGetNext)(const MibObject *object, const uint8_t *oid,
+   size_t oidLen, uint8_t *nextOid, size_t *nextOidLen);
+
+
+/**
+ * @brief MIB object descriptor
+ **/
+
+struct _MibObject
+{
+   const char_t *name;
+   uint8_t oid[MIB_MAX_OID_SIZE];
+   size_t oidLen;
+   uint_t objClass;
+   uint_t objType;
+   MibAccess access;
+   void *value;
+   size_t *valueLen;
+   size_t valueSize;
+   MibSetValue setValue;
+   MibGetValue getValue;
+   MibGetNext getNext;
+};
+
+
+/**
+ * @brief MIB initialization
+ **/
+
+typedef error_t (*MibInit)(void);
+
+
+/**
+ * @brief Lock MIB
+ **/
+
+typedef void (*MibLock)(void);
+
+
+/**
+ * @brief Unlock MIB
+ **/
+
+typedef void (*MibUnlock)(void);
+
+
+/**
+ * @brief MIB module
+ **/
+
+typedef struct
+{
+   const MibObject *objects;
+   uint_t numObjects;
+   MibInit init;
+   MibLock lock;
+   MibUnlock unlock;
+} MibModule;
+
+
+//MIB related functions
+error_t mibEncodeIndex(uint8_t *oid, size_t maxOidLen, size_t *pos, uint_t index);
+error_t mibDecodeIndex(const uint8_t *oid, size_t oidLen, size_t *pos, uint_t *index);
+
+error_t mibEncodeIpv4Addr(uint8_t *oid, size_t maxOidLen, size_t *pos, Ipv4Addr ipAddr);
+error_t mibDecodeIpv4Addr(const uint8_t *oid, size_t oidLen, size_t *pos, Ipv4Addr *ipAddr);
+
+error_t mibEncodePort(uint8_t *oid, size_t maxOidLen, size_t *pos, uint16_t port);
+error_t mibDecodePort(const uint8_t *oid, size_t oidLen, size_t *pos, uint16_t *port);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mqtt/mqtt_client.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1151 @@
+/**
+ * @file mqtt_client.c
+ * @brief MQTT client
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL MQTT_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "mqtt/mqtt_client.h"
+#include "mqtt/mqtt_client_packet.h"
+#include "mqtt/mqtt_client_transport.h"
+#include "mqtt/mqtt_client_misc.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (MQTT_CLIENT_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Initialize MQTT client context
+ * @param[in] context Pointer to the MQTT client context
+ **/
+
+void mqttClientInit(MqttClientContext *context)
+{
+   //Sanity check
+   if(context != NULL)
+   {
+      //Clear MQTT client context
+      memset(context, 0, sizeof(MqttClientContext));
+
+      //Default protocol version
+      context->settings.protocolLevel = MQTT_PROTOCOL_LEVEL_3_1_1;
+      //Default transport protocol
+      context->settings.transportProtocol = MQTT_TRANSPORT_PROTOCOL_TCP;
+      //Default keep-alive time interval
+      context->settings.keepAlive = MQTT_CLIENT_DEFAULT_KEEP_ALIVE;
+      //Default communication timeout
+      context->settings.timeout = MQTT_CLIENT_DEFAULT_TIMEOUT;
+
+#if (MQTT_CLIENT_WS_SUPPORT == ENABLED)
+      //Default resource name (for WebSocket connections only)
+      strcpy(context->settings.uri, "/");
+#endif
+
+      //Initialize state machine
+      context->state = MQTT_CLIENT_STATE_CLOSED;
+      //Initialize packet identifier
+      context->packetId = 0;
+   }
+}
+
+
+/**
+ * @brief Initialize callback structure
+ * @param[in] callbacks Pointer to a structure that contains callback functions
+ * @return Error code
+ **/
+
+void mqttClientInitCallbacks(MqttClientCallbacks *callbacks)
+{
+   //Initialize callback structure
+   memset(callbacks, 0, sizeof(MqttClientCallbacks));
+}
+
+
+/**
+ * @brief Register MQTT client callbacks
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] callbacks Pointer to a structure that contains callback functions
+ * @return Error code
+ **/
+
+error_t mqttClientRegisterCallbacks(MqttClientContext *context,
+   const MqttClientCallbacks *callbacks)
+{
+   //Make sure the MQTT client context is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Attach callback functions
+   context->callbacks = *callbacks;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set the MQTT protocol version to be used
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] protocolLevel MQTT protocol level (3.1 or 3.1.1)
+ * @return Error code
+ **/
+
+error_t mqttClientSetProtocolLevel(MqttClientContext *context,
+   MqttProtocolLevel protocolLevel)
+{
+   //Make sure the MQTT client context is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save the MQTT protocol version to be used
+   context->settings.protocolLevel = protocolLevel;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set the transport protocol to be used
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] transportProtocol Transport protocol to be used (TCP, TLS,
+ *   WebSocket, or secure WebSocket)
+ * @return Error code
+ **/
+
+error_t mqttClientSetTransportProtocol(MqttClientContext *context,
+   MqttTransportProtocol transportProtocol)
+{
+   //Make sure the MQTT client context is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save the transport protocol to be used
+   context->settings.transportProtocol = transportProtocol;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set keep-alive value
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] keepAlive Maximum time interval that is permitted to elapse
+ *   between the point at which the client finishes transmitting one control
+ *   packet and the point it starts sending the next
+ * @return Error code
+ **/
+
+error_t mqttClientSetKeepAlive(MqttClientContext *context, uint16_t keepAlive)
+{
+   //Make sure the MQTT client context is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save keep-alive value
+   context->settings.keepAlive = keepAlive;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set communication timeout
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] timeout Timeout value, in seconds
+ * @return Error code
+ **/
+
+error_t mqttClientSetTimeout(MqttClientContext *context, uint16_t timeout)
+{
+   //Make sure the MQTT client context is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save timeout value
+   context->settings.timeout = timeout;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set the hostname of the resource being requested
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] host NULL-terminated string containing the hostname
+ * @return Error code
+ **/
+
+error_t mqttClientSetHost(MqttClientContext *context, const char_t *host)
+{
+   //Check parameters
+   if(context == NULL || host == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure the length of the hostname is acceptable
+   if(strlen(host) > MQTT_CLIENT_MAX_HOST_LEN)
+      return ERROR_INVALID_LENGTH;
+
+#if (MQTT_CLIENT_WS_SUPPORT == ENABLED)
+   //Save hostname (for WebSocket connections only)
+   strcpy(context->settings.host, host);
+#endif
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set the name of the resource being requested
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] uri NULL-terminated string containing the URI
+ * @return Error code
+ **/
+
+error_t mqttClientSetUri(MqttClientContext *context, const char_t *uri)
+{
+   //Check parameters
+   if(context == NULL || uri == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure the length of the resource name is acceptable
+   if(strlen(uri) > MQTT_CLIENT_MAX_URI_LEN)
+      return ERROR_INVALID_LENGTH;
+
+#if (MQTT_CLIENT_WS_SUPPORT == ENABLED)
+   //Save resource name (for WebSocket connections only)
+   strcpy(context->settings.uri, uri);
+#endif
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set client identifier
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] clientId NULL-terminated string containing the client identifier
+ * @return Error code
+ **/
+
+error_t mqttClientSetIdentifier(MqttClientContext *context,
+   const char_t *clientId)
+{
+   //Check parameters
+   if(context == NULL || clientId == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure the length of the client identifier is acceptable
+   if(strlen(clientId) > MQTT_CLIENT_MAX_ID_LEN)
+      return ERROR_INVALID_LENGTH;
+
+   //Save client identifier
+   strcpy(context->settings.clientId, clientId);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set authentication information
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] username NULL-terminated string containing the user name to be used
+ * @param[in] password NULL-terminated string containing the password to be used
+ * @return Error code
+ **/
+
+error_t mqttClientSetAuthInfo(MqttClientContext *context,
+   const char_t *username, const char_t *password)
+{
+   //Check parameters
+   if(context == NULL || username == NULL || password == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure the length of the user name is acceptable
+   if(strlen(username) > MQTT_CLIENT_MAX_USERNAME_LEN)
+      return ERROR_INVALID_LENGTH;
+
+   //Save user name
+   strcpy(context->settings.username, username);
+
+   //Make sure the length of the password is acceptable
+   if(strlen(password) > MQTT_CLIENT_MAX_PASSWORD_LEN)
+      return ERROR_INVALID_LENGTH;
+
+   //Save password
+   strcpy(context->settings.password, password);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Specify the Will message
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] topic Will topic name
+ * @param[in] message Will message
+ * @param[in] length Length of the Will message
+ * @param[in] qos QoS level to be used when publishing the Will message
+ * @param[in] retain This flag specifies if the Will message is to be retained
+ * @return Error code
+ **/
+
+error_t mqttClientSetWillMessage(MqttClientContext *context, const char_t *topic,
+   const void *message, size_t length, MqttQosLevel qos, bool_t retain)
+{
+   MqttClientWillMessage *willMessage;
+
+   //Check parameters
+   if(context == NULL || topic == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure the length of the Will topic is acceptable
+   if(strlen(topic) > MQTT_CLIENT_MAX_WILL_TOPIC_LEN)
+      return ERROR_INVALID_LENGTH;
+
+   //Point to the Will message
+   willMessage = &context->settings.willMessage;
+
+   //Save Will topic
+   strcpy(willMessage->topic, topic);
+
+   //Any message payload
+   if(length > 0)
+   {
+      //Sanity check
+      if(message == NULL)
+         return ERROR_INVALID_PARAMETER;
+
+      //Make sure the length of the Will message payload is acceptable
+      if(strlen(message) > MQTT_CLIENT_MAX_WILL_PAYLOAD_LEN)
+         return ERROR_INVALID_LENGTH;
+
+      //Save Will message payload
+      memcpy(willMessage->payload, message, length);
+   }
+
+   //Length of the Will message payload
+   willMessage->length = length;
+   //QoS level to be used when publishing the Will message
+   willMessage->qos = qos;
+   //This flag specifies if the Will message is to be retained
+   willMessage->retain = retain;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Bind the MQTT client to a particular network interface
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] interface Network interface to be used
+ * @return Error code
+ **/
+
+error_t mqttClientBindToInterface(MqttClientContext *context,
+   NetInterface *interface)
+{
+   //Make sure the MQTT client context is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Explicitly associate the MQTT client with the specified interface
+   context->interface = interface;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Establish connection with the MQTT server
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] serverIpAddr IP address of the MQTT server to connect to
+ * @param[in] serverPort TCP port number that will be used to establish the
+ *   connection
+ * @param[in] cleanSession If this flag is set, then the client and server
+ *   must discard any previous session and start a new one
+ * @return Error code
+ **/
+
+error_t mqttClientConnect(MqttClientContext *context,
+   const IpAddr *serverIpAddr, uint16_t serverPort, bool_t cleanSession)
+{
+   error_t error;
+
+   //Check parameters
+   if(context == NULL || serverIpAddr == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Establish network connection
+   while(context->state != MQTT_CLIENT_STATE_IDLE)
+   {
+      //Check current state
+      if(context->state == MQTT_CLIENT_STATE_CLOSED)
+      {
+         //Open network connection
+         error = mqttClientOpenConnection(context);
+
+         //Check status code
+         if(!error)
+         {
+            //Debug message
+            TRACE_INFO("MQTT: Connecting to server %s port %" PRIu16 "...\r\n",
+               ipAddrToString(serverIpAddr, NULL), serverPort);
+
+            //The network connection is open
+            mqttClientChangeState(context, MQTT_CLIENT_STATE_CONNECTING);
+         }
+         else
+         {
+            //Clean up side effects
+            mqttClientCloseConnection(context);
+         }
+      }
+      else if(context->state == MQTT_CLIENT_STATE_CONNECTING)
+      {
+         //Establish network connection
+         error = mqttClientEstablishConnection(context,
+            serverIpAddr, serverPort);
+
+         //Check status code
+         if(!error)
+         {
+            //Debug message
+            TRACE_INFO("MQTT: Connected to server\r\n");
+
+            //The network connection is established
+            mqttClientChangeState(context, MQTT_CLIENT_STATE_CONNECTED);
+         }
+      }
+      else if(context->state == MQTT_CLIENT_STATE_CONNECTED)
+      {
+         //Format CONNECT packet
+         error = mqttClientFormatConnect(context, cleanSession);
+
+         //Check status code
+         if(!error)
+         {
+            //Debug message
+            TRACE_INFO("MQTT: Sending CONNECT packet (%" PRIuSIZE " bytes)...\r\n", context->packetLen);
+            TRACE_DEBUG_ARRAY("  ", context->packet, context->packetLen);
+
+            //Save the type of the MQTT packet to be sent
+            context->packetType = MQTT_PACKET_TYPE_CONNECT;
+            //Point to the beginning of the packet
+            context->packetPos = 0;
+
+            //Send CONNECT packet
+            mqttClientChangeState(context, MQTT_CLIENT_STATE_SENDING_PACKET);
+         }
+      }
+      else if(context->state == MQTT_CLIENT_STATE_SENDING_PACKET)
+      {
+         //Send more data
+         error = mqttClientProcessEvents(context, context->settings.timeout);
+      }
+      else if(context->state == MQTT_CLIENT_STATE_PACKET_SENT)
+      {
+         //Wait for CONNACK packet
+         error = mqttClientProcessEvents(context, context->settings.timeout);
+      }
+      else if(context->state == MQTT_CLIENT_STATE_RECEIVING_PACKET)
+      {
+         //Receive more data
+         error = mqttClientProcessEvents(context, context->settings.timeout);
+      }
+      else if(context->state == MQTT_CLIENT_STATE_PACKET_RECEIVED)
+      {
+         //Reset packet type
+         context->packetType = MQTT_PACKET_TYPE_INVALID;
+         //A CONNACK packet has been received
+         mqttClientChangeState(context, MQTT_CLIENT_STATE_IDLE);
+      }
+      else
+      {
+         //Invalid state
+         error = ERROR_NOT_CONNECTED;
+      }
+
+      //Any error to report?
+      if(error)
+      {
+#if (NET_RTOS_SUPPORT == DISABLED)
+         //Timeout error?
+         if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
+            break;
+#endif
+         //Close connection
+         mqttClientCloseConnection(context);
+         //The connection is closed
+         mqttClientChangeState(context, MQTT_CLIENT_STATE_CLOSED);
+         //Exit immediately
+         break;
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Publish message
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] topic Topic name
+ * @param[in] message Message payload
+ * @param[in] length Length of the message payload
+ * @param[in] qos QoS level to be used when publishing the message
+ * @param[in] retain This flag specifies if the message is to be retained
+ * @param[out] packetId Packet identifier used to send the PUBLISH packet
+ * @return Error code
+ **/
+
+error_t mqttClientPublish(MqttClientContext *context,
+   const char_t *topic, const void *message, size_t length,
+   MqttQosLevel qos, bool_t retain, uint16_t *packetId)
+{
+   error_t error;
+
+   //Make sure the MQTT client context is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Send PUBLISH packet and wait for PUBACK/PUBCOMP packet to be received
+   do
+   {
+      //Check current state
+      if(context->state == MQTT_CLIENT_STATE_IDLE)
+      {
+         //Format PUBLISH packet
+         error = mqttClientFormatPublish(context, topic, message, length, qos, retain);
+
+         //Check status code
+         if(!error)
+         {
+            //Save the packet identifier used to send the PUBLISH packet
+            if(packetId != NULL)
+               *packetId = context->packetId;
+
+            //Debug message
+            TRACE_INFO("MQTT: Sending PUBLISH packet (%" PRIuSIZE " bytes)...\r\n", context->packetLen);
+            TRACE_DEBUG_ARRAY("  ", context->packet, context->packetLen);
+
+            //Save the type of the MQTT packet to be sent
+            context->packetType = MQTT_PACKET_TYPE_PUBLISH;
+            //Point to the beginning of the packet
+            context->packetPos = 0;
+
+            //Send PUBLISH packet
+            mqttClientChangeState(context, MQTT_CLIENT_STATE_SENDING_PACKET);
+         }
+      }
+      else if(context->state == MQTT_CLIENT_STATE_SENDING_PACKET)
+      {
+         //Send more data
+         error = mqttClientProcessEvents(context, context->settings.timeout);
+      }
+      else if(context->state == MQTT_CLIENT_STATE_PACKET_SENT)
+      {
+         //The last parameter is optional
+         if(packetId != NULL)
+         {
+            //Reset packet type
+            context->packetType = MQTT_PACKET_TYPE_INVALID;
+            //Do not wait for PUBACK/PUBCOMP packet
+            mqttClientChangeState(context, MQTT_CLIENT_STATE_IDLE);
+         }
+         else
+         {
+            //Check QoS level
+            if(qos == MQTT_QOS_LEVEL_0)
+            {
+               //Reset packet type
+               context->packetType = MQTT_PACKET_TYPE_INVALID;
+               //No response is sent by the receiver and no retry is performed by the sender
+               mqttClientChangeState(context, MQTT_CLIENT_STATE_IDLE);
+            }
+            else
+            {
+               //Wait for PUBACK/PUBCOMP packet
+               error = mqttClientProcessEvents(context, context->settings.timeout);
+            }
+         }
+      }
+      else if(context->state == MQTT_CLIENT_STATE_RECEIVING_PACKET)
+      {
+         //Receive more data
+         error = mqttClientProcessEvents(context, context->settings.timeout);
+      }
+      else if(context->state == MQTT_CLIENT_STATE_PACKET_RECEIVED)
+      {
+         //Reset packet type
+         context->packetType = MQTT_PACKET_TYPE_INVALID;
+         //A PUBACK/PUBCOMP packet has been received
+         mqttClientChangeState(context, MQTT_CLIENT_STATE_IDLE);
+      }
+      else
+      {
+         //Invalid state
+         error = ERROR_NOT_CONNECTED;
+      }
+
+      //Any error to report?
+      if(error)
+         break;
+
+      //Evaluate the loop condition
+   } while(context->state != MQTT_CLIENT_STATE_IDLE);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Subscribe to topics
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] topic Topic filter
+ * @param[in] qos Maximum QoS level at which the server can send application
+ *   messages to the client
+ * @param[out] packetId Packet identifier used to send the SUBSCRIBE packet
+ * @return Error code
+ **/
+
+error_t mqttClientSubscribe(MqttClientContext *context,
+   const char_t *topic, MqttQosLevel qos, uint16_t *packetId)
+{
+   error_t error;
+
+   //Make sure the MQTT client context is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Send SUBSCRIBE packet and wait for SUBACK packet to be received
+   do
+   {
+      //Check current state
+      if(context->state == MQTT_CLIENT_STATE_IDLE)
+      {
+         //Format SUBSCRIBE packet
+         error = mqttClientFormatSubscribe(context, topic, qos);
+
+         //Check status code
+         if(!error)
+         {
+            //Save the packet identifier used to send the SUBSCRIBE packet
+            if(packetId != NULL)
+               *packetId = context->packetId;
+
+            //Debug message
+            TRACE_INFO("MQTT: Sending SUBSCRIBE packet (%" PRIuSIZE " bytes)...\r\n", context->packetLen);
+            TRACE_DEBUG_ARRAY("  ", context->packet, context->packetLen);
+
+            //Save the type of the MQTT packet to be sent
+            context->packetType = MQTT_PACKET_TYPE_SUBSCRIBE;
+            //Point to the beginning of the packet
+            context->packetPos = 0;
+
+            //Send SUBSCRIBE packet
+            mqttClientChangeState(context, MQTT_CLIENT_STATE_SENDING_PACKET);
+         }
+      }
+      else if(context->state == MQTT_CLIENT_STATE_SENDING_PACKET)
+      {
+         //Send more data
+         error = mqttClientProcessEvents(context, context->settings.timeout);
+      }
+      else if(context->state == MQTT_CLIENT_STATE_PACKET_SENT)
+      {
+         //The last parameter is optional
+         if(packetId != NULL)
+         {
+            //Reset packet type
+            context->packetType = MQTT_PACKET_TYPE_INVALID;
+            //Do not wait for SUBACK packet
+            mqttClientChangeState(context, MQTT_CLIENT_STATE_IDLE);
+         }
+         else
+         {
+            //Wait for SUBACK packet
+            error = mqttClientProcessEvents(context, context->settings.timeout);
+         }
+      }
+      else if(context->state == MQTT_CLIENT_STATE_RECEIVING_PACKET)
+      {
+         //Receive more data
+         error = mqttClientProcessEvents(context, context->settings.timeout);
+      }
+      else if(context->state == MQTT_CLIENT_STATE_PACKET_RECEIVED)
+      {
+         //Reset packet type
+         context->packetType = MQTT_PACKET_TYPE_INVALID;
+         //A SUBACK packet has been received
+         mqttClientChangeState(context, MQTT_CLIENT_STATE_IDLE);
+      }
+      else
+      {
+         //Invalid state
+         error = ERROR_NOT_CONNECTED;
+      }
+
+      //Any error to report?
+      if(error)
+         break;
+
+      //Evaluate the loop condition
+   } while(context->state != MQTT_CLIENT_STATE_IDLE);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Unsubscribe from topics
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] topic Topic filter
+ * @param[out] packetId Packet identifier used to send the UNSUBSCRIBE packet
+ * @return Error code
+ **/
+
+error_t mqttClientUnsubscribe(MqttClientContext *context,
+   const char_t *topic, uint16_t *packetId)
+{
+   error_t error;
+
+   //Make sure the MQTT client context is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Send UNSUBSCRIBE packet and wait for UNSUBACK packet to be received
+   do
+   {
+      //Check current state
+      if(context->state == MQTT_CLIENT_STATE_IDLE)
+      {
+         //Format UNSUBSCRIBE packet
+         error = mqttClientFormatUnsubscribe(context, topic);
+
+         //Check status code
+         if(!error)
+         {
+            //Save the packet identifier used to send the UNSUBSCRIBE packet
+            if(packetId != NULL)
+               *packetId = context->packetId;
+
+            //Debug message
+            TRACE_INFO("MQTT: Sending UNSUBSCRIBE packet (%" PRIuSIZE " bytes)...\r\n", context->packetLen);
+            TRACE_DEBUG_ARRAY("  ", context->packet, context->packetLen);
+
+            //Save the type of the MQTT packet to be sent
+            context->packetType = MQTT_PACKET_TYPE_UNSUBSCRIBE;
+            //Point to the beginning of the packet
+            context->packetPos = 0;
+
+            //Send UNSUBSCRIBE packet
+            mqttClientChangeState(context, MQTT_CLIENT_STATE_SENDING_PACKET);
+         }
+      }
+      else if(context->state == MQTT_CLIENT_STATE_SENDING_PACKET)
+      {
+         //Send more data
+         error = mqttClientProcessEvents(context, context->settings.timeout);
+      }
+      else if(context->state == MQTT_CLIENT_STATE_PACKET_SENT)
+      {
+         //The last parameter is optional
+         if(packetId != NULL)
+         {
+            //Reset packet type
+            context->packetType = MQTT_PACKET_TYPE_INVALID;
+            //Do not wait for UNSUBACK packet
+            mqttClientChangeState(context, MQTT_CLIENT_STATE_IDLE);
+         }
+         else
+         {
+            //Wait for UNSUBACK packet
+            error = mqttClientProcessEvents(context, context->settings.timeout);
+         }
+      }
+      else if(context->state == MQTT_CLIENT_STATE_RECEIVING_PACKET)
+      {
+         //Receive more data
+         error = mqttClientProcessEvents(context, context->settings.timeout);
+      }
+      else if(context->state == MQTT_CLIENT_STATE_PACKET_RECEIVED)
+      {
+         //Reset packet type
+         context->packetType = MQTT_PACKET_TYPE_INVALID;
+         //An UNSUBACK packet has been received
+         mqttClientChangeState(context, MQTT_CLIENT_STATE_IDLE);
+      }
+      else
+      {
+         //Invalid state
+         error = ERROR_NOT_CONNECTED;
+      }
+
+      //Any error to report?
+      if(error)
+         break;
+
+      //Evaluate the loop condition
+   } while(context->state != MQTT_CLIENT_STATE_IDLE);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send ping request
+ * @param[in] context Pointer to the MQTT client context
+ * @param[out] rtt Round-trip time (optional parameter)
+ * @return Error code
+ **/
+
+error_t mqttClientPing(MqttClientContext *context, systime_t *rtt)
+{
+   error_t error;
+
+   //Make sure the MQTT client context is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Send PINGREQ packet and wait for PINGRESP packet to be received
+   do
+   {
+      //Check current state
+      if(context->state == MQTT_CLIENT_STATE_IDLE)
+      {
+         //Format PINGREQ packet
+         error = mqttClientFormatPingReq(context);
+
+         //Check status code
+         if(!error)
+         {
+            //Debug message
+            TRACE_INFO("MQTT: Sending PINGREQ packet (%" PRIuSIZE " bytes)...\r\n", context->packetLen);
+            TRACE_DEBUG_ARRAY("  ", context->packet, context->packetLen);
+
+            //Save the type of the MQTT packet to be sent
+            context->packetType = MQTT_PACKET_TYPE_PINGREQ;
+            //Point to the beginning of the packet
+            context->packetPos = 0;
+
+            //Send PINGREQ packet
+            mqttClientChangeState(context, MQTT_CLIENT_STATE_SENDING_PACKET);
+
+            //Save the time at which the request was sent
+            if(rtt != NULL)
+               context->pingTimestamp = osGetSystemTime();
+         }
+      }
+      else if(context->state == MQTT_CLIENT_STATE_SENDING_PACKET)
+      {
+         //Send more data
+         error = mqttClientProcessEvents(context, context->settings.timeout);
+      }
+      else if(context->state == MQTT_CLIENT_STATE_PACKET_SENT)
+      {
+         //The last parameter is optional
+         if(rtt != NULL)
+         {
+            //Wait for PINGRESP packet
+            error = mqttClientProcessEvents(context, context->settings.timeout);
+         }
+         else
+         {
+            //Reset packet type
+            context->packetType = MQTT_PACKET_TYPE_INVALID;
+            //Do not wait for PINGRESP packet
+            mqttClientChangeState(context, MQTT_CLIENT_STATE_IDLE);
+         }
+      }
+      else if(context->state == MQTT_CLIENT_STATE_RECEIVING_PACKET)
+      {
+         //Receive more data
+         error = mqttClientProcessEvents(context, context->settings.timeout);
+      }
+      else if(context->state == MQTT_CLIENT_STATE_PACKET_RECEIVED)
+      {
+         //The last parameter is optional
+         if(rtt != NULL)
+         {
+            //Compute round-trip time
+            *rtt = osGetSystemTime() - context->pingTimestamp;
+         }
+
+         //Reset packet type
+         context->packetType = MQTT_PACKET_TYPE_INVALID;
+         //A PINGRESP packet has been received
+         mqttClientChangeState(context, MQTT_CLIENT_STATE_IDLE);
+      }
+      else
+      {
+         //Invalid state
+         error = ERROR_NOT_CONNECTED;
+      }
+
+      //Any error to report?
+      if(error)
+         break;
+
+      //Evaluate the loop condition
+   } while(context->state != MQTT_CLIENT_STATE_IDLE);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Gracefully disconnect from the MQTT server
+ * @param[in] context Pointer to the MQTT client context
+ * @return Error code
+ **/
+
+error_t mqttClientDisconnect(MqttClientContext *context)
+{
+   error_t error;
+
+   //Make sure the MQTT client context is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Send DISCONNECT packet and shutdown network connection
+   while(context->state != MQTT_CLIENT_STATE_DISCONNECTED)
+   {
+      //Check current state
+      if(context->state == MQTT_CLIENT_STATE_IDLE)
+      {
+         //Format DISCONNECT packet
+         error = mqttClientFormatDisconnect(context);
+
+         //Check status code
+         if(!error)
+         {
+            //Debug message
+            TRACE_INFO("MQTT: Sending DISCONNECT packet (%" PRIuSIZE " bytes)...\r\n", context->packetLen);
+            TRACE_DEBUG_ARRAY("  ", context->packet, context->packetLen);
+
+            //Save the type of the MQTT packet to be sent
+            context->packetType = MQTT_PACKET_TYPE_DISCONNECT;
+            //Point to the beginning of the packet
+            context->packetPos = 0;
+
+            //Send DISCONNECT packet
+            mqttClientChangeState(context, MQTT_CLIENT_STATE_SENDING_PACKET);
+         }
+      }
+      else if(context->state == MQTT_CLIENT_STATE_SENDING_PACKET)
+      {
+         //Send more data
+         error = mqttClientProcessEvents(context, context->settings.timeout);
+      }
+      else if(context->state == MQTT_CLIENT_STATE_PACKET_SENT)
+      {
+         //Debug message
+         TRACE_INFO("MQTT: Shutting down connection...\r\n");
+
+         //After sending a DISCONNECT packet the client must not send any
+         //more control packets on that network connection
+         mqttClientChangeState(context, MQTT_CLIENT_STATE_DISCONNECTING);
+      }
+      else if(context->state == MQTT_CLIENT_STATE_DISCONNECTING)
+      {
+         //Properly dispose the network connection
+         error = mqttClientShutdownConnection(context);
+
+         //Check status code
+         if(!error)
+         {
+            //The MQTT client is disconnected
+            mqttClientChangeState(context, MQTT_CLIENT_STATE_DISCONNECTED);
+         }
+      }
+      else
+      {
+         //Invalid state
+         error = ERROR_NOT_CONNECTED;
+      }
+
+      //Any error to report?
+      if(error)
+         break;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Close the connection with the MQTT server
+ * @param[in] context Pointer to the MQTT client context
+ * @return Error code
+ **/
+
+error_t mqttClientClose(MqttClientContext *context)
+{
+   //Make sure the MQTT client context is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Close connection
+   mqttClientCloseConnection(context);
+   //The connection is closed
+   mqttClientChangeState(context, MQTT_CLIENT_STATE_CLOSED);
+
+   //Network connection successfully closed
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process MQTT client events
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] timeout Maximum time to wait before returning
+ * @return Error code
+ **/
+
+error_t mqttClientProcessEvents(MqttClientContext *context, systime_t timeout)
+{
+   error_t error;
+   size_t n;
+
+   //It is the responsibility of the client to ensure that the interval
+   //between control packets being sent does not exceed the keep-alive value
+   error = mqttClientCheckKeepAlive(context);
+
+   //Check status code
+   if(!error)
+   {
+      //Check current state
+      if(context->state == MQTT_CLIENT_STATE_IDLE ||
+         context->state == MQTT_CLIENT_STATE_PACKET_SENT)
+      {
+         //Wait for incoming data
+         error = mqttClientWaitForData(context, timeout);
+
+         //Check status code
+         if(!error)
+         {
+            //Initialize context
+            context->packet = context->buffer;
+            context->packetPos = 0;
+            context->packetLen = 0;
+            context->remainingLen = 0;
+
+            //Start receiving the packet
+            mqttClientChangeState(context, MQTT_CLIENT_STATE_RECEIVING_PACKET);
+         }
+      }
+      else if(context->state == MQTT_CLIENT_STATE_RECEIVING_PACKET)
+      {
+         //Receive the incoming packet
+         error = mqttClientReceivePacket(context);
+
+         //Check status code
+         if(!error)
+         {
+            //Process MQTT control packet
+            error = mqttClientProcessPacket(context);
+
+            //Update MQTT client state
+            if(context->state == MQTT_CLIENT_STATE_RECEIVING_PACKET)
+            {
+               if(context->packetType == MQTT_PACKET_TYPE_INVALID)
+                  mqttClientChangeState(context, MQTT_CLIENT_STATE_IDLE);
+               else
+                  mqttClientChangeState(context, MQTT_CLIENT_STATE_PACKET_SENT);
+            }
+         }
+      }
+      else if(context->state == MQTT_CLIENT_STATE_SENDING_PACKET)
+      {
+         //Any remaining data to be sent?
+         if(context->packetPos < context->packetLen)
+         {
+            //Send more data
+            error = mqttClientSendData(context, context->packet + context->packetPos,
+               context->packetLen - context->packetPos, &n, 0);
+
+            //Advance data pointer
+            context->packetPos += n;
+         }
+         else
+         {
+            //Save the time at which the message was sent
+            context->keepAliveTimestamp = osGetSystemTime();
+
+            //Update MQTT client state
+            if(context->packetType == MQTT_PACKET_TYPE_INVALID)
+               mqttClientChangeState(context, MQTT_CLIENT_STATE_IDLE);
+            else
+               mqttClientChangeState(context, MQTT_CLIENT_STATE_PACKET_SENT);
+         }
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mqtt/mqtt_client.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,386 @@
+/**
+ * @file mqtt_client.h
+ * @brief MQTT client
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MQTT_CLIENT_H
+#define _MQTT_CLIENT_H
+
+//Dependencies
+#include "core/net.h"
+#include "mqtt/mqtt_common.h"
+
+//MQTT client support
+#ifndef MQTT_CLIENT_SUPPORT
+   #define MQTT_CLIENT_SUPPORT ENABLED
+#elif (MQTT_CLIENT_SUPPORT != ENABLED && MQTT_CLIENT_SUPPORT != DISABLED)
+   #error MQTT_CLIENT_SUPPORT parameter is not valid
+#endif
+
+//MQTT over SSL/TLS
+#ifndef MQTT_CLIENT_TLS_SUPPORT
+   #define MQTT_CLIENT_TLS_SUPPORT DISABLED
+#elif (MQTT_CLIENT_TLS_SUPPORT != ENABLED && MQTT_CLIENT_TLS_SUPPORT != DISABLED)
+   #error MQTT_CLIENT_TLS_SUPPORT parameter is not valid
+#endif
+
+//MQTT over WebSocket
+#ifndef MQTT_CLIENT_WS_SUPPORT
+   #define MQTT_CLIENT_WS_SUPPORT DISABLED
+#elif (MQTT_CLIENT_WS_SUPPORT != ENABLED && MQTT_CLIENT_WS_SUPPORT != DISABLED)
+   #error MQTT_CLIENT_WS_SUPPORT parameter is not valid
+#endif
+
+//Default keep-alive time interval, in seconds
+#ifndef MQTT_CLIENT_DEFAULT_KEEP_ALIVE
+   #define MQTT_CLIENT_DEFAULT_KEEP_ALIVE 0
+#elif (MQTT_CLIENT_DEFAULT_KEEP_ALIVE < 0)
+   #error MQTT_CLIENT_DEFAULT_KEEP_ALIVE parameter is not valid
+#endif
+
+//Default communication timeout, in milliseconds
+#ifndef MQTT_CLIENT_DEFAULT_TIMEOUT
+   #define MQTT_CLIENT_DEFAULT_TIMEOUT 20000
+#elif (MQTT_CLIENT_DEFAULT_TIMEOUT < 0)
+   #error MQTT_CLIENT_DEFAULT_TIMEOUT parameter is not valid
+#endif
+
+//Maximum length of the hostname
+#ifndef MQTT_CLIENT_MAX_HOST_LEN
+   #define MQTT_CLIENT_MAX_HOST_LEN 32
+#elif (MQTT_CLIENT_MAX_HOST_LEN < 1)
+   #error MQTT_CLIENT_MAX_HOST_LEN parameter is not valid
+#endif
+
+//Maximum length of the resource name
+#ifndef MQTT_CLIENT_MAX_URI_LEN
+   #define MQTT_CLIENT_MAX_URI_LEN 16
+#elif (MQTT_CLIENT_MAX_URI_LEN < 1)
+   #error MQTT_CLIENT_MAX_URI_LEN parameter is not valid
+#endif
+
+//Maximum length of the client identifier
+#ifndef MQTT_CLIENT_MAX_ID_LEN
+   #define MQTT_CLIENT_MAX_ID_LEN 16
+#elif (MQTT_CLIENT_MAX_ID_LEN < 0)
+   #error MQTT_CLIENT_MAX_ID_LEN parameter is not valid
+#endif
+
+//Maximum length of the user name
+#ifndef MQTT_CLIENT_MAX_USERNAME_LEN
+   #define MQTT_CLIENT_MAX_USERNAME_LEN 16
+#elif (MQTT_CLIENT_MAX_USERNAME_LEN < 0)
+   #error MQTT_CLIENT_MAX_USERNAME_LEN parameter is not valid
+#endif
+
+//Maximum length of the password
+#ifndef MQTT_CLIENT_MAX_PASSWORD_LEN
+   #define MQTT_CLIENT_MAX_PASSWORD_LEN 16
+#elif (MQTT_CLIENT_MAX_PASSWORD_LEN < 0)
+   #error MQTT_CLIENT_MAX_PASSWORD_LEN parameter is not valid
+#endif
+
+//Maximum length of the will topic
+#ifndef MQTT_CLIENT_MAX_WILL_TOPIC_LEN
+   #define MQTT_CLIENT_MAX_WILL_TOPIC_LEN 16
+#elif (MQTT_CLIENT_MAX_WILL_TOPIC_LEN < 0)
+   #error MQTT_CLIENT_MAX_WILL_TOPIC_LEN parameter is not valid
+#endif
+
+//Maximum length of the will message payload
+#ifndef MQTT_CLIENT_MAX_WILL_PAYLOAD_LEN
+   #define MQTT_CLIENT_MAX_WILL_PAYLOAD_LEN 16
+#elif (MQTT_CLIENT_MAX_WILL_PAYLOAD_LEN < 0)
+   #error MQTT_CLIENT_MAX_WILL_PAYLOAD_LEN parameter is not valid
+#endif
+
+//Size of the MQTT client buffer
+#ifndef MQTT_CLIENT_BUFFER_SIZE
+   #define MQTT_CLIENT_BUFFER_SIZE 1024
+#elif (MQTT_CLIENT_BUFFER_SIZE < 1)
+   #error MQTT_CLIENT_BUFFER_SIZE parameter is not valid
+#endif
+
+//SSL/TLS supported?
+#if (MQTT_CLIENT_TLS_SUPPORT == ENABLED)
+   #include "crypto.h"
+   #include "tls.h"
+#endif
+
+//WebSocket supported?
+#if (MQTT_CLIENT_WS_SUPPORT == ENABLED)
+   #include "web_socket/web_socket.h"
+#endif
+
+//Forward declaration of MqttClientContext structure
+struct _MqttClientContext;
+#define MqttClientContext struct _MqttClientContext
+
+
+/**
+ * @brief MQTT client states
+ **/
+
+typedef enum
+{
+   MQTT_CLIENT_STATE_CLOSED              = 0,
+   MQTT_CLIENT_STATE_CONNECTING          = 1,
+   MQTT_CLIENT_STATE_CONNECTED           = 2,
+   MQTT_CLIENT_STATE_IDLE                = 3,
+   MQTT_CLIENT_STATE_SENDING_PACKET      = 4,
+   MQTT_CLIENT_STATE_PACKET_SENT         = 5,
+   MQTT_CLIENT_STATE_WAITING_PACKET      = 6,
+   MQTT_CLIENT_STATE_RECEIVING_PACKET    = 7,
+   MQTT_CLIENT_STATE_PACKET_RECEIVED     = 8,
+   MQTT_CLIENT_STATE_DISCONNECTING       = 9,
+   MQTT_CLIENT_STATE_DISCONNECTED        = 10
+} MqttClientState;
+
+
+/**
+ * @brief CONNACK message received callback
+ **/
+
+typedef void (*MqttClientConnAckCallback)(MqttClientContext *context,
+   uint8_t connectAckFlags, uint8_t connectReturnCode);
+
+
+/**
+ * @brief PUBLISH message received callback
+ **/
+
+typedef void (*MqttClientPublishCallback)(MqttClientContext *context,
+   const char_t *topic, const uint8_t *message, size_t length,
+   bool_t dup, MqttQosLevel qos, bool_t retain, uint16_t packetId);
+
+
+/**
+ * @brief PUBACK message received callback
+ **/
+
+typedef void (*MqttClientPubAckCallback)(MqttClientContext *context,
+   uint16_t packetId);
+
+
+/**
+ * @brief PUBREC message received callback
+ **/
+
+typedef void (*MqttClientPubRecCallback)(MqttClientContext *context,
+   uint16_t packetId);
+
+
+/**
+ * @brief PUBREL message received callback
+ **/
+
+typedef void (*MqttClientPubRelCallback)(MqttClientContext *context,
+   uint16_t packetId);
+
+
+/**
+ * @brief PUBCOMP message received callback
+ **/
+
+typedef void (*MqttClientPubCompCallback)(MqttClientContext *context,
+   uint16_t packetId);
+
+
+/**
+ * @brief SUBACK message received callback
+ **/
+
+typedef void (*MqttClientSubAckCallback)(MqttClientContext *context,
+   uint16_t packetId);
+
+
+/**
+ * @brief UNSUBACK message received callback
+ **/
+
+typedef void (*MqttClientUnsubAckCallback)(MqttClientContext *context,
+   uint16_t packetId);
+
+
+/**
+ * @brief PINGRESP message received callback
+ **/
+
+typedef void (*MqttClientPingRespCallback)(MqttClientContext *context);
+
+
+//SSL/TLS supported?
+#if (MQTT_CLIENT_TLS_SUPPORT == ENABLED)
+
+/**
+ * @brief SSL initialization callback
+ **/
+
+typedef error_t (*MqttClientTlsInitCallback)(MqttClientContext *context,
+   TlsContext *tlsContext);
+
+#endif
+
+
+/**
+ * @brief Will message
+ **/
+
+typedef struct
+{
+   char_t topic[MQTT_CLIENT_MAX_WILL_TOPIC_LEN + 1];  ///<Will topic name
+   uint8_t payload[MQTT_CLIENT_MAX_WILL_PAYLOAD_LEN]; ///<Will message payload
+   size_t length;                                     ///<Length of the Will message payload
+   MqttQosLevel qos;                                  ///<QoS level to be used when publishing the Will message
+   bool_t retain;                                     ///<Specifies if the Will message is to be retained
+} MqttClientWillMessage;
+
+
+/**
+ * @brief MQTT client callback functions
+ **/
+
+typedef struct
+{
+   MqttClientConnAckCallback connAckCallback;   ///<CONNACK message received callback
+   MqttClientPublishCallback publishCallback;   ///<PUBLISH message received callback
+   MqttClientPubAckCallback pubAckCallback;     ///<PUBACK message received callback
+   MqttClientPubAckCallback pubRecCallback;     ///<PUBREC message received callback
+   MqttClientPubAckCallback pubRelCallback;     ///<PUBREL message received callback
+   MqttClientPubAckCallback pubCompCallback;    ///<PUBCOMP message received callback
+   MqttClientPubAckCallback subAckCallback;     ///<SUBACK message received callback
+   MqttClientPubAckCallback unsubAckCallback;   ///<UNSUBACK message received callback
+   MqttClientPingRespCallback pingRespCallback; ///<PINGRESP message received callback
+#if (MQTT_CLIENT_TLS_SUPPORT == ENABLED)
+   MqttClientTlsInitCallback tlsInitCallback;   ///<SSL initialization callback
+#endif
+} MqttClientCallbacks;
+
+
+/**
+ * @brief MQTT client settings
+ **/
+
+typedef struct
+{
+   MqttProtocolLevel protocolLevel;                   ///<MQTT protocol version
+   MqttTransportProtocol transportProtocol;           ///<Transport protocol
+   uint16_t keepAlive;                                ///<Keep-alive time interval
+   systime_t timeout;                                 ///<Communication timeout
+#if (MQTT_CLIENT_WS_SUPPORT == ENABLED)
+   char_t host[MQTT_CLIENT_MAX_HOST_LEN + 1];         ///<Hostname of the resource being requested
+   char_t uri[MQTT_CLIENT_MAX_URI_LEN + 1];           ///<Resource name
+#endif
+   char_t clientId[MQTT_CLIENT_MAX_ID_LEN + 1];       ///<Client identifier
+   char_t username[MQTT_CLIENT_MAX_USERNAME_LEN + 1]; ///<User name
+   char_t password[MQTT_CLIENT_MAX_PASSWORD_LEN + 1]; ///<Password
+   MqttClientWillMessage willMessage;                 ///<Will message
+} MqttClientSettings;
+
+
+/**
+ * @brief MQTT client context
+ **/
+
+struct _MqttClientContext
+{
+   MqttClientSettings settings;             ///<MQTT client settings
+   MqttClientCallbacks callbacks;           ///<MQTT client callback functions
+   MqttClientState state;                   ///<MQTT client state
+   systime_t keepAliveTimestamp;            ///<Timestamp used to manage keep-alive
+   systime_t pingTimestamp;                 ///<Timestamp used to measure round-trip time
+   NetInterface *interface;                 ///<Underlying network interface
+   Socket *socket;                          ///<Underlying TCP socket
+#if (MQTT_CLIENT_TLS_SUPPORT == ENABLED)
+   TlsContext *tlsContext;                  ///<SSL context
+   TlsSession tlsSession;                   ///<SSL session
+#endif
+#if (MQTT_CLIENT_WS_SUPPORT == ENABLED)
+   WebSocket *webSocket;                    ///<Underlying WebSocket
+#endif
+   uint8_t buffer[MQTT_CLIENT_BUFFER_SIZE]; ///<Internal buffer
+   uint8_t *packet;                         ///<Pointer to the incoming/outgoing MQTT packet
+   size_t packetPos;                        ///<Current position
+   size_t packetLen;                        ///<Length of the entire MQTT packet
+   MqttPacketType packetType;               ///<Control packet type
+   uint16_t packetId;                       ///<Packet identifier
+   size_t remainingLen;                     ///<Length of the variable header and payload
+};
+
+
+//MQTT client related functions
+void mqttClientInit(MqttClientContext *context);
+void mqttClientInitCallbacks(MqttClientCallbacks *callbacks);
+
+error_t mqttClientRegisterCallbacks(MqttClientContext *context,
+   const MqttClientCallbacks *callbacks);
+
+error_t mqttClientSetProtocolLevel(MqttClientContext *context,
+   MqttProtocolLevel protocolLevel);
+
+error_t mqttClientSetTransportProtocol(MqttClientContext *context,
+   MqttTransportProtocol transportProtocol);
+
+error_t mqttClientSetKeepAlive(MqttClientContext *context, uint16_t keepAlive);
+error_t mqttClientSetTimeout(MqttClientContext *context, uint16_t timeout);
+
+error_t mqttClientSetHost(MqttClientContext *context, const char_t *host);
+error_t mqttClientSetUri(MqttClientContext *context, const char_t *uri);
+
+error_t mqttClientSetIdentifier(MqttClientContext *context,
+   const char_t *clientId);
+
+error_t mqttClientSetAuthInfo(MqttClientContext *context,
+   const char_t *username, const char_t *password);
+
+error_t mqttClientSetWillMessage(MqttClientContext *context, const char_t *topic,
+   const void *message, size_t length, MqttQosLevel qos, bool_t retain);
+
+error_t mqttClientBindToInterface(MqttClientContext *context,
+   NetInterface *interface);
+
+error_t mqttClientConnect(MqttClientContext *context,
+   const IpAddr *serverIpAddr, uint16_t serverPort, bool_t cleanSession);
+
+error_t mqttClientPublish(MqttClientContext *context,
+   const char_t *topic, const void *message, size_t length,
+   MqttQosLevel qos, bool_t retain, uint16_t *packetId);
+
+error_t mqttClientSubscribe(MqttClientContext *context,
+   const char_t *topic, MqttQosLevel qos, uint16_t *packetId);
+
+error_t mqttClientUnsubscribe(MqttClientContext *context,
+   const char_t *topic, uint16_t *packetId);
+
+error_t mqttClientPing(MqttClientContext *context, systime_t *rtt);
+
+error_t mqttClientDisconnect(MqttClientContext *context);
+error_t mqttClientClose(MqttClientContext *context);
+
+error_t mqttClientProcessEvents(MqttClientContext *context, systime_t timeout);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mqtt/mqtt_client_misc.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,509 @@
+/**
+ * @file mqtt_client_misc.c
+ * @brief Helper functions for MQTT client
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL MQTT_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "mqtt/mqtt_client.h"
+#include "mqtt/mqtt_client_packet.h"
+#include "mqtt/mqtt_client_transport.h"
+#include "mqtt/mqtt_client_misc.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (MQTT_CLIENT_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Update MQTT client state
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] newState New state to switch to
+ **/
+
+void mqttClientChangeState(MqttClientContext *context, MqttClientState newState)
+{
+   //Switch to the new state
+   context->state = newState;
+}
+
+
+/**
+ * @brief Check keep-alive time interval
+ * @param[in] context Pointer to the MQTT client context
+ * @return Error code
+ **/
+
+error_t mqttClientCheckKeepAlive(MqttClientContext *context)
+{
+   error_t error;
+   systime_t time;
+   systime_t keepAlive;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //In the absence of sending any other control packets, the client must
+   //send a PINGREQ packet
+   if(context->state == MQTT_CLIENT_STATE_IDLE ||
+      context->state == MQTT_CLIENT_STATE_PACKET_SENT)
+   {
+      //A keep-alive value of zero has the effect of turning off the keep
+      //alive mechanism
+      if(context->settings.keepAlive != 0)
+      {
+         //Get current time
+         time = osGetSystemTime();
+
+         //Convert the keep-alive value to milliseconds
+         keepAlive = context->settings.keepAlive * 1000;
+
+         //It is the responsibility of the client to ensure that the interval
+         //between control packets being sent does not exceed the keep-alive value
+         if(timeCompare(time, context->keepAliveTimestamp + keepAlive) >= 0)
+         {
+            //Format PINGREQ packet
+            error = mqttClientFormatPingReq(context);
+
+            //Check status code
+            if(!error)
+            {
+               //Debug message
+               TRACE_INFO("MQTT: Sending PINGREQ packet (%" PRIuSIZE " bytes)...\r\n", context->packetLen);
+               TRACE_DEBUG_ARRAY("  ", context->packet, context->packetLen);
+
+               //Point to the beginning of the packet
+               context->packetPos = 0;
+
+               //Send PINGREQ packet
+               mqttClientChangeState(context, MQTT_CLIENT_STATE_SENDING_PACKET);
+            }
+         }
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Serialize fixed header
+ * @param[in] buffer Pointer to the output buffer
+ * @param[in,out] pos Current position
+ * @param[in] type MQTT control packet type
+ * @param[in] dup DUP flag
+ * @param[in] qos QoS field
+ * @param[in] retain RETAIN flag
+ * @param[in] remainingLen Length of the variable header and the payload
+ * @return Error code
+ **/
+
+error_t mqttSerializeHeader(uint8_t *buffer, size_t *pos, MqttPacketType type,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen)
+{
+   uint_t i;
+   uint_t k;
+   size_t n;
+   MqttPacketHeader *header;
+
+   //Point to the current position
+   n = *pos;
+
+   //The Remaining Length is encoded using a variable length encoding scheme
+   if(remainingLen < 128)
+      k = 1;
+   else if(remainingLen < 16384)
+      k = 2;
+   else if(remainingLen < 2097152)
+      k = 3;
+   else if(remainingLen < 268435456)
+      k = 4;
+   else
+      return ERROR_INVALID_LENGTH;
+
+   //Sanity check
+   if(n < (sizeof(MqttPacketHeader) + k))
+      return ERROR_BUFFER_OVERFLOW;
+
+   //Position where to format the header
+   n -= sizeof(MqttPacketHeader) + k;
+
+   //Point to the MQTT packet header
+   header = (MqttPacketHeader *) (buffer + n);
+
+   //Encode the first byte of the header
+   header->type = type;
+   header->dup = dup;
+   header->qos = qos;
+   header->retain = retain;
+
+   //Encode the Remaining Length field
+   for(i = 0; i < k; i++)
+   {
+      //The least significant seven bits of each byte encode the data
+      header->length[i] = remainingLen & 0xFF;
+      remainingLen >>= 7;
+
+      //The most significant bit is used to indicate that there are
+      //following bytes in the representation
+      if(remainingLen > 0)
+         header->length[i] |= 0x80;
+   }
+
+   //Update current position
+   *pos = n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write a 8-bit integer to the output buffer
+ * @param[in] buffer Pointer to the output buffer
+ * @param[in] bufferLen Maximum number of bytes the output buffer can hold
+ * @param[in,out] pos Current position
+ * @param[in] value 8-bit integer to be serialized
+ * @return Error code
+ **/
+
+error_t mqttSerializeByte(uint8_t *buffer, size_t bufferLen,
+   size_t *pos, uint8_t value)
+{
+   size_t n;
+
+   //Point to the current position
+   n = *pos;
+
+   //Make sure the output buffer is large enough
+   if((n + sizeof(uint8_t)) > bufferLen)
+      return ERROR_BUFFER_OVERFLOW;
+
+   //Write the byte to the output buffer
+   buffer[n++] = value;
+
+   //Advance current position
+   *pos = n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write a 16-bit integer to the output buffer
+ * @param[in] buffer Pointer to the output buffer
+ * @param[in] bufferLen Maximum number of bytes the output buffer can hold
+ * @param[in,out] pos Current position
+ * @param[in] value 16-bit integer to be serialized
+ * @return Error code
+ **/
+
+error_t mqttSerializeShort(uint8_t *buffer, size_t bufferLen,
+   size_t *pos, uint16_t value)
+{
+   size_t n;
+
+   //Point to the current position
+   n = *pos;
+
+   //Make sure the output buffer is large enough
+   if((n + sizeof(uint16_t)) > bufferLen)
+      return ERROR_BUFFER_OVERFLOW;
+
+   //Write the short integer to the output buffer
+   buffer[n++] = MSB(value);
+   buffer[n++] = LSB(value);
+
+   //Advance current position
+   *pos = n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Serialize string
+ * @param[in] buffer Pointer to the output buffer
+ * @param[in] bufferLen Maximum number of bytes the output buffer can hold
+ * @param[in,out] pos Current position
+ * @param[in] string Pointer to the string to be serialized
+ * @param[in] stringLen Length of the string, in bytes
+ * @return Error code
+ **/
+
+error_t mqttSerializeString(uint8_t *buffer, size_t bufferLen,
+   size_t *pos, const void *string, size_t stringLen)
+{
+   size_t n;
+
+   //Point to the current position
+   n = *pos;
+
+   //Make sure the output buffer is large enough to hold the string
+   if((n + sizeof(uint16_t) + stringLen) > bufferLen)
+      return ERROR_BUFFER_OVERFLOW;
+
+   //Encode the length field
+   buffer[n++] = MSB(stringLen);
+   buffer[n++] = LSB(stringLen);
+
+   //Write the string to the output buffer
+   memcpy(buffer + n, string, stringLen);
+
+   //Advance current position
+   *pos = n + stringLen;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Serialize raw data
+ * @param[in] buffer Pointer to the output buffer
+ * @param[in] bufferLen Maximum number of bytes the output buffer can hold
+ * @param[in,out] pos Current position
+ * @param[in] data Pointer to the raw data to be serialized
+ * @param[in] dataLen Length of the raw data, in bytes
+ * @return Error code
+ **/
+
+error_t mqttSerializeData(uint8_t *buffer, size_t bufferLen,
+   size_t *pos, const void *data, size_t dataLen)
+{
+   size_t n;
+
+   //Point to the current position
+   n = *pos;
+
+   //Make sure the output buffer is large enough to hold the data
+   if((n + dataLen) > bufferLen)
+      return ERROR_BUFFER_OVERFLOW;
+
+   //Write the data to the output buffer
+   memcpy(buffer + n, data, dataLen);
+
+   //Advance current position
+   *pos = n + dataLen;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Deserialize fixed header
+ * @param[in] buffer Pointer to the input buffer
+ * @param[in] bufferLen Length of the input buffer
+ * @param[in,out] pos Current position
+ * @param[out] type MQTT control packet type
+ * @param[out] dup DUP flag from the fixed header
+ * @param[out] qos QoS field from the fixed header
+ * @param[out] retain RETAIN flag from the fixed header
+ * @param[out] remainingLen Length of the variable header and the payload
+ * @return Error code
+ **/
+
+error_t mqttDeserializeHeader(uint8_t *buffer, size_t bufferLen, size_t *pos,
+   MqttPacketType *type, bool_t *dup, MqttQosLevel *qos, bool_t *retain, size_t *remainingLen)
+{
+   uint_t i;
+   size_t n;
+   MqttPacketHeader *header;
+
+   //Point to the current position
+   n = *pos;
+
+   //Make sure the input buffer is large enough
+   if((n + sizeof(MqttPacketHeader)) > bufferLen)
+      return ERROR_INVALID_LENGTH;
+
+   //Point to the MQTT packet header
+   header = (MqttPacketHeader *) (buffer + n);
+
+   //Save MQTT control packet type
+   *type = (MqttPacketType) header->type;
+
+   //Save flags
+   *dup = header->dup;
+   *qos = (MqttQosLevel) header->qos;
+   *retain = header->retain;
+
+   //Advance current position
+   n += sizeof(MqttPacketHeader);
+
+   //Prepare to decode the Remaining Length field
+   *remainingLen = 0;
+
+   //The Remaining Length is encoded using a variable length encoding scheme
+   for(i = 0; i < 4; i++)
+   {
+      //Sanity check
+      if((n + sizeof(uint8_t)) > bufferLen)
+         return ERROR_INVALID_LENGTH;
+
+      //Advance current position
+      n += sizeof(uint8_t);
+
+      //The most significant bit is used to indicate that there are
+      //following bytes in the representation
+      if(header->length[i] & 0x80)
+      {
+         //Applications can send control packets of size up to 256 MB
+         if(i == 3)
+            return ERROR_INVALID_SYNTAX;
+
+         //The least significant seven bits of each byte encode the data
+         *remainingLen |= (header->length[i] & 0x7F) << (7 * i);
+      }
+      else
+      {
+         //The least significant seven bits of each byte encode the data
+         *remainingLen |= header->length[i] << (7 * i);
+         //This is the last byte
+         break;
+      }
+   }
+
+   //Return the current position
+   *pos = n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Read a 8-bit integer from the input buffer
+ * @param[in] buffer Pointer to the input buffer
+ * @param[in] bufferLen Length of the input buffer
+ * @param[in,out] pos Current position
+ * @param[out] value Value of the 8-bit integer
+ * @return Error code
+ **/
+
+error_t mqttDeserializeByte(uint8_t *buffer, size_t bufferLen,
+   size_t *pos, uint8_t *value)
+{
+   size_t n;
+
+   //Point to the current position
+   n = *pos;
+
+   //Make sure the input buffer is large enough
+   if((n + sizeof(uint8_t)) > bufferLen)
+      return ERROR_BUFFER_OVERFLOW;
+
+   //Read the short integer from the input buffer
+   *value = buffer[n];
+
+   //Advance current position
+   *pos = n + sizeof(uint8_t);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Read a 16-bit integer from the input buffer
+ * @param[in] buffer Pointer to the input buffer
+ * @param[in] bufferLen Length of the input buffer
+ * @param[in,out] pos Current position
+ * @param[out] value Value of the 16-bit integer
+ * @return Error code
+ **/
+
+error_t mqttDeserializeShort(uint8_t *buffer, size_t bufferLen,
+   size_t *pos, uint16_t *value)
+{
+   size_t n;
+
+   //Point to the current position
+   n = *pos;
+
+   //Make sure the input buffer is large enough
+   if((n + sizeof(uint16_t)) > bufferLen)
+      return ERROR_BUFFER_OVERFLOW;
+
+   //Read the short integer from the input buffer
+   *value = (buffer[n] << 8) | buffer[n + 1];
+
+   //Advance current position
+   *pos = n + sizeof(uint16_t);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Deserialize string
+ * @param[in] buffer Pointer to the input buffer
+ * @param[in] bufferLen Length of the input buffer
+ * @param[in,out] pos Current position
+ * @param[out] string Pointer to the string
+ * @param[out] stringLen Length of the string, in bytes
+ * @return Error code
+ **/
+
+error_t mqttDeserializeString(uint8_t *buffer, size_t bufferLen,
+   size_t *pos, char_t **string, size_t *stringLen)
+{
+   size_t n;
+
+   //Point to the current position
+   n = *pos;
+
+   //Make sure the input buffer is large enough
+   if((n + sizeof(uint16_t)) > bufferLen)
+      return ERROR_BUFFER_OVERFLOW;
+
+   //Decode the length field
+   *stringLen = (buffer[n] << 8) | buffer[n + 1];
+
+   //Make sure the input buffer is large enough
+   if((n + sizeof(uint16_t) + *stringLen) > bufferLen)
+      return ERROR_BUFFER_OVERFLOW;
+
+   //Read the string from the input buffer
+   *string = (char_t *) buffer + n + 2;
+
+   //Advance current position
+   *pos = n + 2 + *stringLen;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mqtt/mqtt_client_misc.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,69 @@
+/**
+ * @file mqtt_client_misc.h
+ * @brief Helper functions for MQTT client
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MQTT_CLIENT_MISC_H
+#define _MQTT_CLIENT_MISC_H
+
+//Dependencies
+#include "core/net.h"
+#include "mqtt/mqtt_client.h"
+
+//MQTT client related functions
+void mqttClientChangeState(MqttClientContext *context, MqttClientState newState);
+
+error_t mqttClientCheckKeepAlive(MqttClientContext *context);
+
+error_t mqttSerializeHeader(uint8_t *buffer, size_t *pos, MqttPacketType type,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen);
+
+error_t mqttSerializeByte(uint8_t *buffer, size_t bufferLen,
+   size_t *pos, uint8_t value);
+
+error_t mqttSerializeShort(uint8_t *buffer, size_t bufferLen,
+   size_t *pos, uint16_t value);
+
+error_t mqttSerializeString(uint8_t *buffer, size_t bufferLen,
+   size_t *pos, const void *string, size_t stringLen);
+
+error_t mqttSerializeData(uint8_t *buffer, size_t bufferLen,
+   size_t *pos, const void *data, size_t dataLen);
+
+error_t mqttDeserializeHeader(uint8_t *buffer, size_t bufferLen, size_t *pos,
+   MqttPacketType *type, bool_t *dup, MqttQosLevel *qos, bool_t *retain, size_t *remainingLen);
+
+error_t mqttDeserializeByte(uint8_t *buffer, size_t bufferLen,
+   size_t *pos, uint8_t *value);
+
+error_t mqttDeserializeShort(uint8_t *buffer, size_t bufferLen,
+   size_t *pos, uint16_t *value);
+
+error_t mqttDeserializeString(uint8_t *buffer, size_t bufferLen,
+   size_t *pos, char_t **string, size_t *stringLen);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mqtt/mqtt_client_packet.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1406 @@
+/**
+ * @file mqtt_client_packet.c
+ * @brief MQTT packet parsing and formatting
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL MQTT_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "mqtt/mqtt_client.h"
+#include "mqtt/mqtt_client_packet.h"
+#include "mqtt/mqtt_client_transport.h"
+#include "mqtt/mqtt_client_misc.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (MQTT_CLIENT_SUPPORT == ENABLED)
+
+//MQTT control packets
+static const char_t *packetLabel[16] =
+{
+   "Reserved",    //0
+   "CONNECT",     //1
+   "CONNACK",     //2
+   "PUBLISH",     //3
+   "PUBACK",      //4
+   "PUBREC",      //5
+   "PUBREL",      //6
+   "PUBCOMP",     //7
+   "SUBSCRIBE",   //8
+   "SUBACK",      //9
+   "UNSUBSCRIBE", //10
+   "UNSUBACK",    //11
+   "PINGREQ",     //12
+   "PINGRESP",    //13
+   "DISCONNECT",  //14
+   "Reserved"     //15
+};
+
+
+/**
+ * @brief Receive MQTT packet
+ * @param[in] context Pointer to the MQTT client context
+ * @return Error code
+ **/
+
+error_t mqttClientReceivePacket(MqttClientContext *context)
+{
+   error_t error;
+   size_t n;
+   uint8_t value;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Receive incoming packet
+   while(1)
+   {
+      //Packet header is being received?
+      if(context->packetLen == 0)
+      {
+         //Read a single byte
+         error = mqttClientReceiveData(context, &value, sizeof(uint8_t), &n, 0);
+
+         //Any data received?
+         if(!error)
+         {
+            //Save the current byte
+            context->packet[context->packetPos] = value;
+
+            //The Remaining Length is encoded using a variable length encoding scheme
+            if(context->packetPos > 0)
+            {
+               //The most significant bit is used to indicate that there are
+               //following bytes in the representation
+               if(value & 0x80)
+               {
+                  //Applications can send control packets of size up to 256 MB
+                  if(context->packetPos < 4)
+                  {
+                     //The least significant seven bits of each byte encode the data
+                     context->remainingLen |= (value & 0x7F) << (7 * (context->packetPos - 1));
+                  }
+                  else
+                  {
+                     //Report an error
+                     error = ERROR_INVALID_SYNTAX;
+                  }
+               }
+               else
+               {
+                  //The least significant seven bits of each byte encode the data
+                  context->remainingLen |= value << (7 * (context->packetPos - 1));
+                  //Calculate the length of the control packet
+                  context->packetLen = context->packetPos + 1 + context->remainingLen;
+
+                  //Sanity check
+                  if(context->packetLen > MQTT_CLIENT_BUFFER_SIZE)
+                     error = ERROR_INVALID_LENGTH;
+               }
+            }
+
+            //Advance data pointer
+            context->packetPos++;
+         }
+      }
+      //Variable header or payload is being received?
+      else
+      {
+         //Any remaining data?
+         if(context->packetPos < context->packetLen)
+         {
+            //Read more data
+            error = mqttClientReceiveData(context, context->packet + context->packetPos,
+               context->packetLen - context->packetPos, &n, 0);
+
+            //Advance data pointer
+            context->packetPos += n;
+         }
+         else
+         {
+            //The packet has been successfully received
+            break;
+         }
+      }
+
+      //Any error to report?
+      if(error)
+         break;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process incoming MQTT packet
+ * @param[in] context Pointer to the MQTT client context
+ * @return Error code
+ **/
+
+error_t mqttClientProcessPacket(MqttClientContext *context)
+{
+   error_t error;
+   bool_t dup;
+   bool_t retain;
+   size_t remainingLen;
+   MqttQosLevel qos;
+   MqttPacketType type;
+
+   //Point to the first byte of the packet
+   context->packetPos = 0;
+
+   //Read the fixed header from the input buffer
+   error = mqttDeserializeHeader(context->packet, context->packetLen,
+      &context->packetPos, &type, &dup, &qos, &retain, &remainingLen);
+
+   //Failed to deserialize fixed header?
+   if(error)
+      return error;
+
+   //Debug message
+   TRACE_INFO("MQTT: %s packet received (%" PRIuSIZE " bytes)...\r\n",
+      packetLabel[type], context->packetLen);
+
+   //Dump the contents of the packet
+   TRACE_DEBUG_ARRAY("  ", context->packet, context->packetLen);
+
+   //Check MQTT control packet type
+   switch(type)
+   {
+   //CONNACK packet received?
+   case MQTT_PACKET_TYPE_CONNACK:
+      //Process incoming CONNACK packet
+      error = mqttClientProcessConnAck(context, dup, qos, retain, remainingLen);
+      break;
+   //PUBLISH packet received?
+   case MQTT_PACKET_TYPE_PUBLISH:
+      //Process incoming PUBLISH packet
+      error = mqttClientProcessPublish(context, dup, qos, retain, remainingLen);
+      break;
+   //PUBACK packet received?
+   case MQTT_PACKET_TYPE_PUBACK:
+      //Process incoming PUBACK packet
+      error = mqttClientProcessPubAck(context, dup, qos, retain, remainingLen);
+      break;
+   //PUBREC packet received?
+   case MQTT_PACKET_TYPE_PUBREC:
+      //Process incoming PUBREC packet
+      error = mqttClientProcessPubRec(context, dup, qos, retain, remainingLen);
+      break;
+   //PUBREL packet received?
+   case MQTT_PACKET_TYPE_PUBREL:
+      //Process incoming PUBREL packet
+      error = mqttClientProcessPubRel(context, dup, qos, retain, remainingLen);
+      break;
+   //PUBCOMP packet received?
+   case MQTT_PACKET_TYPE_PUBCOMP:
+      //Process incoming PUBCOMP packet
+      error = mqttClientProcessPubComp(context, dup, qos, retain, remainingLen);
+      break;
+   //SUBACK packet received?
+   case MQTT_PACKET_TYPE_SUBACK:
+      //Process incoming SUBACK packet
+      error = mqttClientProcessSubAck(context, dup, qos, retain, remainingLen);
+      break;
+   //UNSUBACK packet received?
+   case MQTT_PACKET_TYPE_UNSUBACK:
+      //Process incoming UNSUBACK packet
+      error = mqttClientProcessUnsubAck(context, dup, qos, retain, remainingLen);
+      break;
+   //PINGRESP packet received?
+   case MQTT_PACKET_TYPE_PINGRESP:
+      //Process incoming PINGRESP packet
+      error = mqttClientProcessPingResp(context, dup, qos, retain, remainingLen);
+      break;
+   //Unknown packet received?
+   default:
+      //Report an error
+      error = ERROR_INVALID_PACKET;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process incoming CONNACK packet
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] dup DUP flag from the fixed header
+ * @param[in] qos QoS field from the fixed header
+ * @param[in] retain RETAIN flag from the fixed header
+ * @param[in] remainingLen Length of the variable header and the payload
+ **/
+
+error_t mqttClientProcessConnAck(MqttClientContext *context,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen)
+{
+   error_t error;
+   uint8_t connectAckFlags;
+   uint8_t connectReturnCode;
+
+   //If invalid flags are received, the receiver must close the network connection
+   if(dup != FALSE && qos != MQTT_QOS_LEVEL_0 && retain != FALSE)
+      return ERROR_INVALID_PACKET;
+
+   //The first byte of the variable header is the Connect Acknowledge Flags
+   error = mqttDeserializeByte(context->packet, context->packetLen,
+      &context->packetPos, &connectAckFlags);
+
+   //Failed to deserialize the Connect Acknowledge Flags?
+   if(error)
+      return error;
+
+   //The second byte of the variable header is the Connect Return Code
+   error = mqttDeserializeByte(context->packet, context->packetLen,
+      &context->packetPos, &connectReturnCode);
+
+   //Failed to deserialize the Connect Return Code?
+   if(error)
+      return error;
+
+   //Any registered callback?
+   if(context->callbacks.connAckCallback != NULL)
+   {
+      //Invoke user callback function
+      context->callbacks.connAckCallback(context,
+         connectAckFlags, connectReturnCode);
+   }
+
+   //Make sure the connection is accepted
+   if(connectReturnCode != MQTT_CONNECT_RET_CODE_ACCEPTED)
+      return ERROR_CONNECTION_REFUSED;
+
+   //Notify the application that a CONNACK packet has been received
+   if(context->packetType == MQTT_PACKET_TYPE_CONNECT)
+      context->state = MQTT_CLIENT_STATE_PACKET_RECEIVED;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process incoming PUBLISH packet
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] dup DUP flag from the fixed header
+ * @param[in] qos QoS field from the fixed header
+ * @param[in] retain RETAIN flag from the fixed header
+ * @param[in] remainingLen Length of the variable header and the payload
+ **/
+
+error_t mqttClientProcessPublish(MqttClientContext *context,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen)
+{
+   error_t error;
+   uint16_t packetId;
+   char_t *topic;
+   size_t topicLen;
+   uint8_t *message;
+   size_t messageLen;
+
+   //The Topic Name must be present as the first field in the PUBLISH
+   //packet variable header
+   error = mqttDeserializeString(context->packet, context->packetLen,
+      &context->packetPos, &topic, &topicLen);
+
+   //Failed to deserialize Topic Name?
+   if(error)
+      return error;
+
+   //Check QoS level
+   if(qos != MQTT_QOS_LEVEL_0)
+   {
+      //The Packet Identifier field is only present in PUBLISH packets
+      //where the QoS level is 1 or 2
+      error = mqttDeserializeShort(context->packet, context->packetLen,
+         &context->packetPos, &packetId);
+
+      //Failed to deserialize Packet Identifier field?
+      if(error)
+         return error;
+   }
+   else
+   {
+      //No packet identifier
+      packetId = 0;
+   }
+
+   //The payload contains the Application Message that is being published
+   message = context->packet + context->packetPos;
+
+   //The length of the payload can be calculated by subtracting the length of the
+   //variable header from the Remaining Length field that is in the fixed header
+   messageLen = context->packetLen - context->packetPos;
+
+   //Make room for the NULL character at the end of the Topic Name
+   memmove(topic - 1, topic, topicLen);
+   //Properly terminate the string with a NULL character
+   topic[topicLen - 1] = '\0';
+   //Point to the first character of the Topic Name
+   topic--;
+
+   //Any registered callback?
+   if(context->callbacks.publishCallback != NULL)
+   {
+      //Invoke user callback function
+      context->callbacks.publishCallback(context, topic,
+         message, messageLen, dup, qos, retain, packetId);
+   }
+
+   //Check QoS level
+   if(qos == MQTT_QOS_LEVEL_1)
+   {
+      //A PUBACK packet is the response to a PUBLISH packet with QoS level 1
+      error = mqttClientFormatPubAck(context, packetId);
+
+      //Check status code
+      if(!error)
+      {
+         //Debug message
+         TRACE_INFO("MQTT: Sending PUBACK packet (%" PRIuSIZE " bytes)...\r\n", context->packetLen);
+         TRACE_DEBUG_ARRAY("  ", context->packet, context->packetLen);
+
+         //Point to the beginning of the packet
+         context->packetPos = 0;
+
+         //Send PUBACK packet
+         mqttClientChangeState(context, MQTT_CLIENT_STATE_SENDING_PACKET);
+      }
+   }
+   else if(qos == MQTT_QOS_LEVEL_2)
+   {
+      //A PUBREC packet is the response to a PUBLISH packet with QoS 2. It is
+      //the second packet of the QoS 2 protocol exchange
+      error = mqttClientFormatPubRec(context, packetId);
+
+      //Check status code
+      if(!error)
+      {
+         //Debug message
+         TRACE_INFO("MQTT: Sending PUBREC packet (%" PRIuSIZE " bytes)...\r\n", context->packetLen);
+         TRACE_DEBUG_ARRAY("  ", context->packet, context->packetLen);
+
+         //Point to the beginning of the packet
+         context->packetPos = 0;
+
+         //Send PUBREC packet
+         mqttClientChangeState(context, MQTT_CLIENT_STATE_SENDING_PACKET);
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process incoming PUBACK packet
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] dup DUP flag from the fixed header
+ * @param[in] qos QoS field from the fixed header
+ * @param[in] retain RETAIN flag from the fixed header
+ * @param[in] remainingLen Length of the variable header and the payload
+ **/
+
+error_t mqttClientProcessPubAck(MqttClientContext *context,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen)
+{
+   error_t error;
+   uint16_t packetId;
+
+   //If invalid flags are received, the receiver must close the network connection
+   if(dup != FALSE && qos != MQTT_QOS_LEVEL_0 && retain != FALSE)
+      return ERROR_INVALID_PACKET;
+
+   //The variable header contains the Packet Identifier from the PUBLISH
+   //packet that is being acknowledged
+   error = mqttDeserializeShort(context->packet, context->packetLen,
+      &context->packetPos, &packetId);
+
+   //Failed to deserialize Packet Identifier field?
+   if(error)
+      return error;
+
+   //Any registered callback?
+   if(context->callbacks.pubAckCallback != NULL)
+   {
+      //Invoke user callback function
+      context->callbacks.pubAckCallback(context, packetId);
+   }
+
+   //Notify the application that a PUBACK packet has been received
+   if(context->packetType == MQTT_PACKET_TYPE_PUBLISH && context->packetId == packetId)
+      context->state = MQTT_CLIENT_STATE_PACKET_RECEIVED;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process incoming PUBREC packet
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] dup DUP flag from the fixed header
+ * @param[in] qos QoS field from the fixed header
+ * @param[in] retain RETAIN flag from the fixed header
+ * @param[in] remainingLen Length of the variable header and the payload
+ **/
+
+error_t mqttClientProcessPubRec(MqttClientContext *context,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen)
+{
+   error_t error;
+   uint16_t packetId;
+
+   //If invalid flags are received, the receiver must close the network connection
+   if(dup != FALSE && qos != MQTT_QOS_LEVEL_0 && retain != FALSE)
+      return ERROR_INVALID_PACKET;
+
+   //The variable header contains the Packet Identifier from the PUBLISH
+   //packet that is being acknowledged
+   error = mqttDeserializeShort(context->packet, context->packetLen,
+      &context->packetPos, &packetId);
+
+   //Failed to deserialize Packet Identifier field?
+   if(error)
+      return error;
+
+   //Any registered callback?
+   if(context->callbacks.pubRecCallback != NULL)
+   {
+      //Invoke user callback function
+      context->callbacks.pubRecCallback(context, packetId);
+   }
+
+   //A PUBREL packet is the response to a PUBREC packet. It is the third
+   //packet of the QoS 2 protocol exchange
+   error = mqttClientFormatPubRel(context, packetId);
+
+   //Check status code
+   if(!error)
+   {
+      //Debug message
+      TRACE_INFO("MQTT: Sending PUBREL packet (%" PRIuSIZE " bytes)...\r\n", context->packetLen);
+      TRACE_DEBUG_ARRAY("  ", context->packet, context->packetLen);
+
+      //Point to the beginning of the packet
+      context->packetPos = 0;
+
+      //Send PUBREL packet
+      mqttClientChangeState(context, MQTT_CLIENT_STATE_SENDING_PACKET);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process incoming PUBREL packet
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] dup DUP flag from the fixed header
+ * @param[in] qos QoS field from the fixed header
+ * @param[in] retain RETAIN flag from the fixed header
+ * @param[in] remainingLen Length of the variable header and the payload
+ **/
+
+error_t mqttClientProcessPubRel(MqttClientContext *context,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen)
+{
+   error_t error;
+   uint16_t packetId;
+
+   //If invalid flags are received, the receiver must close the network connection
+   if(dup != FALSE && qos != MQTT_QOS_LEVEL_1 && retain != FALSE)
+      return ERROR_INVALID_PACKET;
+
+   //The variable header contains the same Packet Identifier as the PUBREC
+   //packet that is being acknowledged
+   error = mqttDeserializeShort(context->packet, context->packetLen,
+      &context->packetPos, &packetId);
+
+   //Failed to deserialize Packet Identifier field?
+   if(error)
+      return error;
+
+   //Any registered callback?
+   if(context->callbacks.pubRelCallback != NULL)
+   {
+      //Invoke user callback function
+      context->callbacks.pubRelCallback(context, packetId);
+   }
+
+   //A PUBCOMP packet is the response to a PUBREL packet. It is the fourth and
+   //final packet of the QoS 2 protocol exchange
+   error = mqttClientFormatPubComp(context, packetId);
+
+   //Check status code
+   if(!error)
+   {
+      //Debug message
+      TRACE_INFO("MQTT: Sending PUBCOMP packet (%" PRIuSIZE " bytes)...\r\n", context->packetLen);
+      TRACE_DEBUG_ARRAY("  ", context->packet, context->packetLen);
+
+      //Point to the beginning of the packet
+      context->packetPos = 0;
+
+      //Send PUBCOMP packet
+      mqttClientChangeState(context, MQTT_CLIENT_STATE_SENDING_PACKET);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process incoming PUBCOMP packet
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] dup DUP flag from the fixed header
+ * @param[in] qos QoS field from the fixed header
+ * @param[in] retain RETAIN flag from the fixed header
+ * @param[in] remainingLen Length of the variable header and the payload
+ **/
+
+error_t mqttClientProcessPubComp(MqttClientContext *context,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen)
+{
+   error_t error;
+   uint16_t packetId;
+
+   //If invalid flags are received, the receiver must close the network connection
+   if(dup != FALSE && qos != MQTT_QOS_LEVEL_0 && retain != FALSE)
+      return ERROR_INVALID_PACKET;
+
+   //The variable header contains the same Packet Identifier as the PUBREL
+   //packet that is being acknowledged
+   error = mqttDeserializeShort(context->packet, context->packetLen,
+      &context->packetPos, &packetId);
+
+   //Failed to deserialize Packet Identifier field?
+   if(error)
+      return error;
+
+   //Any registered callback?
+   if(context->callbacks.pubCompCallback != NULL)
+   {
+      //Invoke user callback function
+      context->callbacks.pubCompCallback(context, packetId);
+   }
+
+   //Notify the application that a PUBCOMP packet has been received
+   if(context->packetType == MQTT_PACKET_TYPE_PUBLISH && context->packetId == packetId)
+      context->state = MQTT_CLIENT_STATE_PACKET_RECEIVED;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process incoming SUBACK packet
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] dup DUP flag from the fixed header
+ * @param[in] qos QoS field from the fixed header
+ * @param[in] retain RETAIN flag from the fixed header
+ * @param[in] remainingLen Length of the variable header and the payload
+ **/
+
+error_t mqttClientProcessSubAck(MqttClientContext *context,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen)
+{
+   error_t error;
+   uint16_t packetId;
+
+   //If invalid flags are received, the receiver must close the network connection
+   if(dup != FALSE && qos != MQTT_QOS_LEVEL_0 && retain != FALSE)
+      return ERROR_INVALID_PACKET;
+
+   //The variable header contains the Packet Identifier from the SUBSCRIBE
+   //packet that is being acknowledged
+   error = mqttDeserializeShort(context->packet, context->packetLen,
+      &context->packetPos, &packetId);
+
+   //Failed to deserialize Packet Identifier field?
+   if(error)
+      return error;
+
+   //Any registered callback?
+   if(context->callbacks.subAckCallback != NULL)
+   {
+      //Invoke user callback function
+      context->callbacks.subAckCallback(context, packetId);
+   }
+
+   //Notify the application that a SUBACK packet has been received
+   if(context->packetType == MQTT_PACKET_TYPE_SUBSCRIBE && context->packetId == packetId)
+      context->state = MQTT_CLIENT_STATE_PACKET_RECEIVED;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process incoming UNSUBACK packet
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] dup DUP flag from the fixed header
+ * @param[in] qos QoS field from the fixed header
+ * @param[in] retain RETAIN flag from the fixed header
+ * @param[in] remainingLen Length of the variable header and the payload
+ **/
+
+error_t mqttClientProcessUnsubAck(MqttClientContext *context,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen)
+{
+   error_t error;
+   uint16_t packetId;
+
+   //If invalid flags are received, the receiver must close the network connection
+   if(dup != FALSE && qos != MQTT_QOS_LEVEL_0 && retain != FALSE)
+      return ERROR_INVALID_PACKET;
+
+   //The variable header contains the Packet Identifier from the UNSUBSCRIBE
+   //packet that is being acknowledged
+   error = mqttDeserializeShort(context->packet, context->packetLen,
+      &context->packetPos, &packetId);
+
+   //Failed to deserialize Packet Identifier field?
+   if(error)
+      return error;
+
+   //Any registered callback?
+   if(context->callbacks.unsubAckCallback != NULL)
+   {
+      //Invoke user callback function
+      context->callbacks.unsubAckCallback(context, packetId);
+   }
+
+   //Notify the application that an UNSUBACK packet has been received
+   if(context->packetType == MQTT_PACKET_TYPE_UNSUBSCRIBE && context->packetId == packetId)
+      context->state = MQTT_CLIENT_STATE_PACKET_RECEIVED;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process incoming PINGRESP packet
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] dup DUP flag from the fixed header
+ * @param[in] qos QoS field from the fixed header
+ * @param[in] retain RETAIN flag from the fixed header
+ * @param[in] remainingLen Length of the variable header and the payload
+ **/
+
+error_t mqttClientProcessPingResp(MqttClientContext *context,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen)
+{
+   //If invalid flags are received, the receiver must close the network connection
+   if(dup != FALSE && qos != MQTT_QOS_LEVEL_0 && retain != FALSE)
+      return ERROR_INVALID_PACKET;
+
+   //Any registered callback?
+   if(context->callbacks.pingRespCallback != NULL)
+   {
+      //Invoke user callback function
+      context->callbacks.pingRespCallback(context);
+   }
+
+   //Notify the application that an PINGRESP packet has been received
+   if(context->packetType == MQTT_PACKET_TYPE_PINGREQ)
+      context->state = MQTT_CLIENT_STATE_PACKET_RECEIVED;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format CONNECT packet
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] cleanSession If this flag is set, then the client and server
+ *   must discard any previous session and start a new one
+ * @return Error code
+ **/
+
+error_t mqttClientFormatConnect(MqttClientContext *context,
+   bool_t cleanSession)
+{
+   error_t error;
+   size_t n;
+   uint8_t connectFlags;
+   MqttClientWillMessage *willMessage;
+
+   //Make room for the fixed header
+   n = MQTT_MAX_HEADER_SIZE;
+
+   //Check protocol version
+   if(context->settings.protocolLevel == MQTT_PROTOCOL_LEVEL_3_1)
+   {
+      //The Protocol Name is a UTF-8 encoded string that represents the
+      //protocol name "MQIsdp"
+      error = mqttSerializeString(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+         &n, MQTT_PROTOCOL_NAME_3_1, strlen(MQTT_PROTOCOL_NAME_3_1));
+   }
+   else if(context->settings.protocolLevel == MQTT_PROTOCOL_LEVEL_3_1_1)
+   {
+      //The Protocol Name is a UTF-8 encoded string that represents the
+      //protocol name "MQTT"
+      error = mqttSerializeString(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+         &n, MQTT_PROTOCOL_NAME_3_1_1, strlen(MQTT_PROTOCOL_NAME_3_1_1));
+   }
+   else
+   {
+      //Invalid protocol level
+      error = ERROR_INVALID_VERSION;
+   }
+
+   //Any error to report?
+   if(error)
+      return error;
+
+   //The Protocol Level represents the revision level of the protocol
+   //used by the client
+   error = mqttSerializeByte(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+      &n, context->settings.protocolLevel);
+
+   //Failed to serialize data?
+   if(error)
+      return error;
+
+   //The Connect Flags byte contains a number of parameters specifying
+   //the behavior of the MQTT connection
+   connectFlags = 0;
+
+   //If CleanSession is set to 1, the client and server must discard any
+   //previous session and start a new one
+   if(cleanSession)
+      connectFlags |= MQTT_CONNECT_FLAG_CLEAN_SESSION;
+
+   //If the client supplies a zero-byte Client Identifier, the client must
+   //also set CleanSession to 1
+   if(context->settings.clientId[0] == '\0')
+      connectFlags |= MQTT_CONNECT_FLAG_CLEAN_SESSION;
+
+   //Point to the Will message
+   willMessage = &context->settings.willMessage;
+
+   //Check whether a valid Will message has been specified
+   if(willMessage->topic[0] != '\0')
+   {
+      //Set the Will flag
+      connectFlags |= MQTT_CONNECT_FLAG_WILL;
+
+      //Check the Will QoS level
+      if(willMessage->qos == MQTT_QOS_LEVEL_1)
+         connectFlags |= MQTT_CONNECT_FLAG_WILL_QOS_1;
+      else if(willMessage->qos == MQTT_QOS_LEVEL_2)
+         connectFlags |= MQTT_CONNECT_FLAG_WILL_QOS_2;
+
+      //The Will Retain flag specifies if the Will Message is to be
+      //retained when it is published
+      if(willMessage->retain)
+         connectFlags |= MQTT_CONNECT_FLAG_WILL_RETAIN;
+   }
+
+   //Check whether a valid user name has been specified
+   if(context->settings.username[0] != '\0')
+      connectFlags |= MQTT_CONNECT_FLAG_USERNAME;
+
+   //Check whether a valid password has been specified
+   if(context->settings.password[0] != '\0')
+      connectFlags |= MQTT_CONNECT_FLAG_PASSWORD;
+
+   //Write the Connect Flags to the output buffer
+   error = mqttSerializeByte(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+      &n, connectFlags);
+
+   //Failed to serialize data?
+   if(error)
+      return error;
+
+   //The Keep Alive is a time interval measured in seconds. It is the maximum
+   //time interval that is permitted to elapse between the point at which the
+   //client finishes transmitting one control packet and the point it starts
+   //sending the next
+   error = mqttSerializeShort(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+      &n, context->settings.keepAlive);
+
+   //Failed to serialize data?
+   if(error)
+      return error;
+
+   //The Client Identifier identifies the client to the server. The Client
+   //Identifier must be present and must be the first field in the CONNECT
+   //packet payload
+   error = mqttSerializeString(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+      &n, context->settings.clientId, strlen(context->settings.clientId));
+
+   //Failed to serialize data?
+   if(error)
+      return error;
+
+   //If the Will Flag is set to 1, the Will Topic is the next field in
+   //the payload
+   if(willMessage->topic[0] != '\0')
+   {
+      //Write the Will Topic to the output buffer
+      error = mqttSerializeString(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+         &n, willMessage->topic, strlen(willMessage->topic));
+
+      //Failed to serialize data?
+      if(error)
+         return error;
+
+      //Write the Will message to the output buffer
+      error = mqttSerializeString(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+         &n, willMessage->payload, willMessage->length);
+
+      //Failed to serialize data?
+      if(error)
+         return error;
+   }
+
+   //If the User Name Flag is set to 1, this is the next field in the payload
+   if(context->settings.username[0] != '\0')
+   {
+      //Write the User Name to the output buffer
+      error = mqttSerializeString(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+         &n, context->settings.username, strlen(context->settings.username));
+
+      //Failed to serialize data?
+      if(error)
+         return error;
+   }
+
+   //If the Password Flag is set to 1, this is the next field in the payload
+   if(context->settings.password[0] != '\0')
+   {
+      //Write the Password to the output buffer
+      error = mqttSerializeString(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+         &n, context->settings.password, strlen(context->settings.password));
+
+      //Failed to serialize data?
+      if(error)
+         return error;
+   }
+
+   //Calculate the length of the variable header and the payload
+   context->packetLen = n - MQTT_MAX_HEADER_SIZE;
+
+   //The fixed header will be encoded in reverse order
+   n = MQTT_MAX_HEADER_SIZE;
+
+   //Prepend the variable header and the payload with the fixed header
+   error = mqttSerializeHeader(context->buffer, &n, MQTT_PACKET_TYPE_CONNECT,
+      FALSE, MQTT_QOS_LEVEL_0, FALSE, context->packetLen);
+
+   //Failed to serialize fixed header?
+   if(error)
+      return error;
+
+   //Point to the first byte of the MQTT packet
+   context->packet = context->buffer + n;
+   //Calculate the length of the MQTT packet
+   context->packetLen += MQTT_MAX_HEADER_SIZE - n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format PUBLISH packet
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] topic Topic name
+ * @param[in] message Message payload
+ * @param[in] length Length of the message payload
+ * @param[in] qos QoS level to be used when publishing the message
+ * @param[in] retain This flag specifies if the message is to be retained
+ * @return Error code
+ **/
+
+error_t mqttClientFormatPublish(MqttClientContext *context, const char_t *topic,
+   const void *message, size_t length, MqttQosLevel qos, bool_t retain)
+{
+   error_t error;
+   size_t n;
+
+   //Make room for the fixed header
+   n = MQTT_MAX_HEADER_SIZE;
+
+   //The Topic Name must be present as the first field in the PUBLISH
+   //packet variable header
+   error = mqttSerializeString(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+      &n, topic, strlen(topic));
+
+   //Failed to serialize Topic Name?
+   if(error)
+      return error;
+
+   //Check QoS level
+   if(qos != MQTT_QOS_LEVEL_0)
+   {
+      //Each time a client sends a new PUBLISH packet it must assign it
+      //a currently unused packet identifier
+      context->packetId++;
+
+      //The Packet Identifier field is only present in PUBLISH packets
+      //where the QoS level is 1 or 2
+      error = mqttSerializeShort(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+         &n, context->packetId);
+
+      //Failed to serialize Packet Identifier field?
+      if(error)
+         return error;
+   }
+
+   //The payload contains the Application Message that is being published
+   error = mqttSerializeData(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+      &n, message, length);
+
+   //Failed to serialize Application Message?
+   if(error)
+      return error;
+
+   //Calculate the length of the variable header and the payload
+   context->packetLen = n - MQTT_MAX_HEADER_SIZE;
+
+   //The fixed header will be encoded in reverse order
+   n = MQTT_MAX_HEADER_SIZE;
+
+   //Prepend the variable header and the payload with the fixed header
+   error = mqttSerializeHeader(context->buffer, &n, MQTT_PACKET_TYPE_PUBLISH,
+      FALSE, qos, retain, context->packetLen);
+
+   //Failed to serialize fixed header?
+   if(error)
+      return error;
+
+   //Point to the first byte of the MQTT packet
+   context->packet = context->buffer + n;
+   //Calculate the length of the MQTT packet
+   context->packetLen += MQTT_MAX_HEADER_SIZE - n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format PUBACK packet
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] packetId Packet identifier
+ * @return Error code
+ **/
+
+error_t mqttClientFormatPubAck(MqttClientContext *context, uint16_t packetId)
+{
+   error_t error;
+   size_t n;
+
+   //Make room for the fixed header
+   n = MQTT_MAX_HEADER_SIZE;
+
+   //The variable header contains the Packet Identifier from the PUBLISH
+   //packet that is being acknowledged
+   error = mqttSerializeShort(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+      &n, packetId);
+
+   //Failed to serialize Packet Identifier field?
+   if(error)
+      return error;
+
+   //Calculate the length of the variable header and the payload
+   context->packetLen = n - MQTT_MAX_HEADER_SIZE;
+
+   //The fixed header will be encoded in reverse order
+   n = MQTT_MAX_HEADER_SIZE;
+
+   //Prepend the variable header and the payload with the fixed header
+   error = mqttSerializeHeader(context->buffer, &n, MQTT_PACKET_TYPE_PUBACK,
+      FALSE, MQTT_QOS_LEVEL_0, FALSE, context->packetLen);
+
+   //Failed to serialize fixed header?
+   if(error)
+      return error;
+
+   //Point to the first byte of the MQTT packet
+   context->packet = context->buffer + n;
+   //Calculate the length of the MQTT packet
+   context->packetLen += MQTT_MAX_HEADER_SIZE - n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format PUBREC packet
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] packetId Packet identifier
+ * @return Error code
+ **/
+
+error_t mqttClientFormatPubRec(MqttClientContext *context, uint16_t packetId)
+{
+   error_t error;
+   size_t n;
+
+   //Make room for the fixed header
+   n = MQTT_MAX_HEADER_SIZE;
+
+   //The variable header contains the Packet Identifier from the PUBLISH
+   //packet that is being acknowledged
+   error = mqttSerializeShort(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+      &n, packetId);
+
+   //Failed to serialize Packet Identifier field?
+   if(error)
+      return error;
+
+   //Calculate the length of the variable header and the payload
+   context->packetLen = n - MQTT_MAX_HEADER_SIZE;
+
+   //The fixed header will be encoded in reverse order
+   n = MQTT_MAX_HEADER_SIZE;
+
+   //Prepend the variable header and the payload with the fixed header
+   error = mqttSerializeHeader(context->buffer, &n, MQTT_PACKET_TYPE_PUBREC,
+      FALSE, MQTT_QOS_LEVEL_0, FALSE, context->packetLen);
+
+   //Failed to serialize fixed header?
+   if(error)
+      return error;
+
+   //Point to the first byte of the MQTT packet
+   context->packet = context->buffer + n;
+   //Calculate the length of the MQTT packet
+   context->packetLen += MQTT_MAX_HEADER_SIZE - n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format PUBREL packet
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] packetId Packet identifier
+ * @return Error code
+ **/
+
+error_t mqttClientFormatPubRel(MqttClientContext *context, uint16_t packetId)
+{
+   error_t error;
+   size_t n;
+
+   //Make room for the fixed header
+   n = MQTT_MAX_HEADER_SIZE;
+
+   //The variable header contains the same Packet Identifier as the PUBREC
+   //packet that is being acknowledged
+   error = mqttSerializeShort(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+      &n, packetId);
+
+   //Failed to serialize Packet Identifier field?
+   if(error)
+      return error;
+
+   //Calculate the length of the variable header and the payload
+   context->packetLen = n - MQTT_MAX_HEADER_SIZE;
+
+   //The fixed header will be encoded in reverse order
+   n = MQTT_MAX_HEADER_SIZE;
+
+   //Prepend the variable header and the payload with the fixed header
+   error = mqttSerializeHeader(context->buffer, &n, MQTT_PACKET_TYPE_PUBREL,
+      FALSE, MQTT_QOS_LEVEL_1, FALSE, context->packetLen);
+
+   //Failed to serialize fixed header?
+   if(error)
+      return error;
+
+   //Point to the first byte of the MQTT packet
+   context->packet = context->buffer + n;
+   //Calculate the length of the MQTT packet
+   context->packetLen += MQTT_MAX_HEADER_SIZE - n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format PUBCOMP packet
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] packetId Packet identifier
+ * @return Error code
+ **/
+
+error_t mqttClientFormatPubComp(MqttClientContext *context, uint16_t packetId)
+{
+   error_t error;
+   size_t n;
+
+   //Make room for the fixed header
+   n = MQTT_MAX_HEADER_SIZE;
+
+   //The variable header contains the same Packet Identifier as the PUBREL
+   //packet that is being acknowledged
+   error = mqttSerializeShort(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+      &n, packetId);
+
+   //Failed to serialize Packet Identifier field?
+   if(error)
+      return error;
+
+   //Calculate the length of the variable header and the payload
+   context->packetLen = n - MQTT_MAX_HEADER_SIZE;
+
+   //The fixed header will be encoded in reverse order
+   n = MQTT_MAX_HEADER_SIZE;
+
+   //Prepend the variable header and the payload with the fixed header
+   error = mqttSerializeHeader(context->buffer, &n, MQTT_PACKET_TYPE_PUBCOMP,
+      FALSE, MQTT_QOS_LEVEL_0, FALSE, context->packetLen);
+
+   //Failed to serialize fixed header?
+   if(error)
+      return error;
+
+   //Point to the first byte of the MQTT packet
+   context->packet = context->buffer + n;
+   //Calculate the length of the MQTT packet
+   context->packetLen += MQTT_MAX_HEADER_SIZE - n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format SUBSCRIBE packet
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] topic Topic filter
+ * @param[in] qos Maximum QoS level at which the server can send application
+ *   messages to the client
+ * @return Error code
+ **/
+
+error_t mqttClientFormatSubscribe(MqttClientContext *context,
+   const char_t *topic, MqttQosLevel qos)
+{
+   error_t error;
+   size_t n;
+
+   //Make room for the fixed header
+   n = MQTT_MAX_HEADER_SIZE;
+
+   //Each time a client sends a new SUBSCRIBE packet it must assign it
+   //a currently unused packet identifier
+   context->packetId++;
+
+   //Write Packet Identifier to the output buffer
+   error = mqttSerializeShort(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+      &n, context->packetId);
+
+   //Failed to serialize data?
+   if(error)
+      return error;
+
+   //Write the Topic Filter to the output buffer
+   error = mqttSerializeString(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+      &n, topic, strlen(topic));
+
+   //Failed to serialize data?
+   if(error)
+      return error;
+
+   //Write the Requested QoS to the output buffer
+   error = mqttSerializeByte(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+      &n, qos);
+
+   //Failed to serialize data?
+   if(error)
+      return error;
+
+   //Calculate the length of the variable header and the payload
+   context->packetLen = n - MQTT_MAX_HEADER_SIZE;
+
+   //The fixed header will be encoded in reverse order
+   n = MQTT_MAX_HEADER_SIZE;
+
+   //Prepend the variable header and the payload with the fixed header
+   error = mqttSerializeHeader(context->buffer, &n, MQTT_PACKET_TYPE_SUBSCRIBE,
+      FALSE, MQTT_QOS_LEVEL_1, FALSE, context->packetLen);
+
+   //Failed to serialize fixed header?
+   if(error)
+      return error;
+
+   //Point to the first byte of the MQTT packet
+   context->packet = context->buffer + n;
+   //Calculate the length of the MQTT packet
+   context->packetLen += MQTT_MAX_HEADER_SIZE - n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format UNSUBSCRIBE packet
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] topic Topic filter
+ * @return Error code
+ **/
+
+error_t mqttClientFormatUnsubscribe(MqttClientContext *context,
+   const char_t *topic)
+{
+   error_t error;
+   size_t n;
+
+   //Make room for the fixed header
+   n = MQTT_MAX_HEADER_SIZE;
+
+   //Each time a client sends a new UNSUBSCRIBE packet it must assign it
+   //a currently unused packet identifier
+   context->packetId++;
+
+   //Write Packet Identifier to the output buffer
+   error = mqttSerializeShort(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+      &n, context->packetId);
+
+   //Failed to serialize data?
+   if(error)
+      return error;
+
+   //Write the Topic Filter to the output buffer
+   error = mqttSerializeString(context->buffer, MQTT_CLIENT_BUFFER_SIZE,
+      &n, topic, strlen(topic));
+
+   //Failed to serialize data?
+   if(error)
+      return error;
+
+   //Calculate the length of the variable header and the payload
+   context->packetLen = n - MQTT_MAX_HEADER_SIZE;
+
+   //The fixed header will be encoded in reverse order
+   n = MQTT_MAX_HEADER_SIZE;
+
+   //Prepend the variable header and the payload with the fixed header
+   error = mqttSerializeHeader(context->buffer, &n, MQTT_PACKET_TYPE_UNSUBSCRIBE,
+      FALSE, MQTT_QOS_LEVEL_1, FALSE, context->packetLen);
+
+   //Failed to serialize fixed header?
+   if(error)
+      return error;
+
+   //Point to the first byte of the MQTT packet
+   context->packet = context->buffer + n;
+   //Calculate the length of the MQTT packet
+   context->packetLen += MQTT_MAX_HEADER_SIZE - n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format PINGREQ packet
+ * @param[in] context Pointer to the MQTT client context
+ * @return Error code
+ **/
+
+error_t mqttClientFormatPingReq(MqttClientContext *context)
+{
+   error_t error;
+   size_t n;
+
+   //The fixed header will be encoded in reverse order
+   n = MQTT_MAX_HEADER_SIZE;
+
+   //The PINGREQ packet does not contain any variable header nor payload
+   error = mqttSerializeHeader(context->buffer, &n, MQTT_PACKET_TYPE_PINGREQ,
+      FALSE, MQTT_QOS_LEVEL_0, FALSE, 0);
+
+   //Failed to serialize fixed header?
+   if(error)
+      return error;
+
+   //Point to the first byte of the MQTT packet
+   context->packet = context->buffer + n;
+   //Calculate the length of the MQTT packet
+   context->packetLen = MQTT_MAX_HEADER_SIZE - n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format DISCONNECT packet
+ * @param[in] context Pointer to the MQTT client context
+ * @return Error code
+ **/
+
+error_t mqttClientFormatDisconnect(MqttClientContext *context)
+{
+   error_t error;
+   size_t n;
+
+   //The fixed header will be encoded in reverse order
+   n = MQTT_MAX_HEADER_SIZE;
+
+   //The DISCONNECT packet does not contain any variable header nor payload
+   error = mqttSerializeHeader(context->buffer, &n, MQTT_PACKET_TYPE_DISCONNECT,
+      FALSE, MQTT_QOS_LEVEL_0, FALSE, 0);
+
+   //Failed to serialize fixed header?
+   if(error)
+      return error;
+
+   //Point to the first byte of the MQTT packet
+   context->packet = context->buffer + n;
+   //Calculate the length of the MQTT packet
+   context->packetLen = MQTT_MAX_HEADER_SIZE - n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mqtt/mqtt_client_packet.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,88 @@
+/**
+ * @file mqtt_client_packet.h
+ * @brief MQTT packet parsing and formatting
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MQTT_CLIENT_PACKET_H
+#define _MQTT_CLIENT_PACKET_H
+
+//Dependencies
+#include "core/net.h"
+#include "mqtt/mqtt_client.h"
+
+//MQTT client related functions
+error_t mqttClientReceivePacket(MqttClientContext *context);
+error_t mqttClientProcessPacket(MqttClientContext *context);
+
+error_t mqttClientProcessConnAck(MqttClientContext *context,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen);
+
+error_t mqttClientProcessPubAck(MqttClientContext *context,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen);
+
+error_t mqttClientProcessPublish(MqttClientContext *context,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen);
+
+error_t mqttClientProcessPubRec(MqttClientContext *context,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen);
+
+error_t mqttClientProcessPubRel(MqttClientContext *context,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen);
+
+error_t mqttClientProcessPubComp(MqttClientContext *context,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen);
+
+error_t mqttClientProcessSubAck(MqttClientContext *context,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen);
+
+error_t mqttClientProcessUnsubAck(MqttClientContext *context,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen);
+
+error_t mqttClientProcessPingResp(MqttClientContext *context,
+   bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen);
+
+error_t mqttClientFormatConnect(MqttClientContext *context,
+   bool_t cleanSession);
+
+error_t mqttClientFormatPublish(MqttClientContext *context, const char_t *topic,
+   const void *message, size_t length, MqttQosLevel qos, bool_t retain);
+
+error_t mqttClientFormatPubAck(MqttClientContext *context, uint16_t packetId);
+error_t mqttClientFormatPubRec(MqttClientContext *context, uint16_t packetId);
+error_t mqttClientFormatPubRel(MqttClientContext *context, uint16_t packetId);
+error_t mqttClientFormatPubComp(MqttClientContext *context, uint16_t packetId);
+
+error_t mqttClientFormatSubscribe(MqttClientContext *context,
+   const char_t *topic, MqttQosLevel qos);
+
+error_t mqttClientFormatUnsubscribe(MqttClientContext *context,
+   const char_t *topic);
+
+error_t mqttClientFormatPingReq(MqttClientContext *context);
+error_t mqttClientFormatDisconnect(MqttClientContext *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mqtt/mqtt_client_transport.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,682 @@
+/**
+ * @file mqtt_client_transport.c
+ * @brief Transport protocol abstraction layer
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL MQTT_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "core/tcp_misc.h"
+#include "mqtt/mqtt_client.h"
+#include "mqtt/mqtt_client_packet.h"
+#include "mqtt/mqtt_client_transport.h"
+#include "mqtt/mqtt_client_misc.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (MQTT_CLIENT_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Open network connection
+ * @param[in] context Pointer to the MQTT client context
+ * @return Error code
+ **/
+
+error_t mqttClientOpenConnection(MqttClientContext *context)
+{
+   error_t error;
+
+   //TCP transport protocol?
+   if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_TCP)
+   {
+      //Open a TCP socket
+      context->socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
+
+      //Valid socket handle?
+      if(context->socket != NULL)
+      {
+         //Associate the socket with the relevant interface
+         error = socketBindToInterface(context->socket, context->interface);
+      }
+      else
+      {
+         //Report an error
+         error = ERROR_OPEN_FAILED;
+      }
+   }
+#if (MQTT_CLIENT_TLS_SUPPORT == ENABLED)
+   //TLS transport protocol?
+   else if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_TLS)
+   {
+      //Open a TCP socket
+      context->socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
+
+      //Valid socket handle?
+      if(context->socket != NULL)
+      {
+         //Associate the socket with the relevant interface
+         error = socketBindToInterface(context->socket, context->interface);
+
+         //Check status code
+         if(!error)
+         {
+            //Allocate SSL/TLS context
+            context->tlsContext = tlsInit();
+
+            //Valid SSL/TLS handle?
+            if(context->tlsContext != NULL)
+            {
+               //Select client operation mode
+               error = tlsSetConnectionEnd(context->tlsContext,
+                  TLS_CONNECTION_END_CLIENT);
+
+               //Check status code
+               if(!error)
+               {
+                  //Bind TLS to the relevant socket
+                  error = tlsSetSocket(context->tlsContext, context->socket);
+               }
+
+               //Check status code
+               if(!error)
+               {
+                  //Restore SSL/TLS session, if any
+                  if(context->tlsSession.idLength > 0)
+                  {
+                     //Restore SSL/TLS session
+                     error = tlsRestoreSession(context->tlsContext,
+                        &context->tlsSession);
+                  }
+               }
+
+               //Check status code
+               if(!error)
+               {
+                  //Invoke user-defined callback, if any
+                  if(context->callbacks.tlsInitCallback != NULL)
+                  {
+                     //Perform SSL/TLS related initialization
+                     error = context->callbacks.tlsInitCallback(context,
+                        context->tlsContext);
+                  }
+               }
+            }
+            else
+            {
+               //Report an error
+               error = ERROR_OPEN_FAILED;
+            }
+         }
+      }
+      else
+      {
+         //Report an error
+         error = ERROR_OPEN_FAILED;
+      }
+   }
+#endif
+#if (MQTT_CLIENT_WS_SUPPORT == ENABLED)
+   //WebSocket transport protocol?
+   else if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_WS)
+   {
+      //Open a WebSocket
+      context->webSocket = webSocketOpen();
+
+      //Valid WebSocket handle?
+      if(context->webSocket != NULL)
+      {
+         //Associate the WebSocket with the relevant interface
+         error = webSocketBindToInterface(context->webSocket,
+            context->interface);
+      }
+      else
+      {
+         //Report an error
+         error = ERROR_OPEN_FAILED;
+      }
+   }
+   //Secure WebSocket transport protocol?
+   else if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_WSS)
+   {
+      //Open a WebSocket
+      context->webSocket = webSocketOpen();
+
+      //Valid WebSocket handle?
+      if(context->webSocket != NULL)
+      {
+         //Associate the WebSocket with the relevant interface
+         error = webSocketBindToInterface(context->webSocket,
+            context->interface);
+
+         //Check status code
+         if(!error)
+         {
+            //Register SSL/TLS initialization callback
+            error = webSocketRegisterTlsInitCallback(context->webSocket,
+               (WebSocketTlsInitCallback) context->callbacks.tlsInitCallback);
+         }
+      }
+      else
+      {
+         //Report an error
+         error = ERROR_OPEN_FAILED;
+      }
+   }
+#endif
+   //Unknown transport protocol?
+   else
+   {
+      //Report an error
+      error = ERROR_INVALID_PROTOCOL;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Establish network connection
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] serverIpAddr IP address of the MQTT server to connect to
+ * @param[in] serverPort TCP port number that will be used to establish the
+ *   connection
+ * @return Error code
+ **/
+
+error_t mqttClientEstablishConnection(MqttClientContext *context,
+   const IpAddr *serverIpAddr, uint16_t serverPort)
+{
+   error_t error;
+
+   //TCP transport protocol?
+   if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_TCP)
+   {
+      //Set timeout
+      error = socketSetTimeout(context->socket, context->settings.timeout);
+
+      //Check status code
+      if(!error)
+      {
+         //Connect to the MQTT server using TCP
+         error = socketConnect(context->socket, serverIpAddr, serverPort);
+      }
+   }
+#if (MQTT_CLIENT_TLS_SUPPORT == ENABLED)
+   //TLS transport protocol?
+   else if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_TLS)
+   {
+      //Set timeout
+      error = socketSetTimeout(context->socket, context->settings.timeout);
+
+      //Check status code
+      if(!error)
+      {
+         //Connect to the MQTT server using TCP
+         error = socketConnect(context->socket, serverIpAddr, serverPort);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Establish a SSL/TLS session
+         error = tlsConnect(context->tlsContext);
+      }
+   }
+#endif
+#if (MQTT_CLIENT_WS_SUPPORT == ENABLED)
+   //WebSocket transport protocol?
+   else if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_WS ||
+      context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_WSS)
+   {
+      //Set timeout
+      error = webSocketSetTimeout(context->webSocket, context->settings.timeout);
+
+      //Check status code
+      if(!error)
+      {
+         //Set the hostname of the remote server
+         error = webSocketSetHost(context->webSocket, context->settings.host);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //The client MUST include "mqtt" in the list of WebSocket
+         //sub-protocols it offers
+         error = webSocketSetSubProtocol(context->webSocket, "mqtt");
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Connect to the MQTT server using WebSocket
+         error = webSocketConnect(context->webSocket, serverIpAddr,
+            serverPort, context->settings.uri);
+      }
+   }
+#endif
+   //Unknown transport protocol?
+   else
+   {
+      //Report an error
+      error = ERROR_INVALID_PROTOCOL;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Shutdown network connection
+ * @param[in] context Pointer to the MQTT client context
+ * @return Error code
+ **/
+
+error_t mqttClientShutdownConnection(MqttClientContext *context)
+{
+   error_t error;
+
+   //TCP transport protocol?
+   if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_TCP)
+   {
+      //Set timeout
+      error = socketSetTimeout(context->socket, context->settings.timeout);
+
+      //Check status code
+      if(!error)
+      {
+         //Shutdown TCP connection
+         error = socketShutdown(context->socket, SOCKET_SD_BOTH);
+      }
+   }
+#if (MQTT_CLIENT_TLS_SUPPORT == ENABLED)
+   //TLS transport protocol?
+   else if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_TLS)
+   {
+      //Set timeout
+      error = socketSetTimeout(context->socket, context->settings.timeout);
+
+      //Check status code
+      if(!error)
+      {
+         //Shutdown SSL/TLS session
+         error = tlsShutdown(context->tlsContext);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Shutdown TCP connection
+         error = socketShutdown(context->socket, SOCKET_SD_BOTH);
+      }
+   }
+#endif
+#if (MQTT_CLIENT_WS_SUPPORT == ENABLED)
+   //WebSocket transport protocol?
+   else if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_WS ||
+      context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_WSS)
+   {
+      //Set timeout
+      error = webSocketSetTimeout(context->webSocket, context->settings.timeout);
+
+      //Check status code
+      if(!error)
+      {
+         //Connect to the MQTT server using WebSocket
+         error = webSocketShutdown(context->webSocket);
+      }
+   }
+#endif
+   //Unknown transport protocol?
+   else
+   {
+      //Report an error
+      error = ERROR_INVALID_PROTOCOL;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Close network connection
+ * @param[in] context Pointer to the MQTT client context
+ **/
+
+void mqttClientCloseConnection(MqttClientContext *context)
+{
+   //TCP transport protocol?
+   if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_TCP)
+   {
+      //Close TCP connection
+      if(context->socket != NULL)
+      {
+         socketClose(context->socket);
+         context->socket = NULL;
+      }
+   }
+#if (MQTT_CLIENT_TLS_SUPPORT == ENABLED)
+   //TLS transport protocol?
+   else if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_TLS)
+   {
+      //Release SSL context
+      if(context->tlsContext != NULL)
+      {
+         tlsFree(context->tlsContext);
+         context->tlsContext = NULL;
+      }
+
+      //Close TCP connection
+      if(context->socket != NULL)
+      {
+         socketClose(context->socket);
+         context->socket = NULL;
+      }
+   }
+#endif
+#if (MQTT_CLIENT_WS_SUPPORT == ENABLED)
+   //WebSocket transport protocol?
+   else if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_WS ||
+      context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_WSS)
+   {
+      //Close WebSocket connection
+      if(context->webSocket != NULL)
+      {
+         webSocketClose(context->webSocket);
+         context->webSocket = NULL;
+      }
+   }
+#endif
+}
+
+
+/**
+ * @brief Send data using the relevant transport protocol
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] data Pointer to a buffer containing the data to be transmitted
+ * @param[in] length Number of bytes to be transmitted
+ * @param[out] written Actual number of bytes written (optional parameter)
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t mqttClientSendData(MqttClientContext *context,
+   const void *data, size_t length, size_t *written, uint_t flags)
+{
+   error_t error;
+
+   //TCP transport protocol?
+   if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_TCP)
+   {
+      //Set timeout
+      error = socketSetTimeout(context->socket, context->settings.timeout);
+
+      //Check status code
+      if(!error)
+      {
+         //Transmit data
+         error = socketSend(context->socket, data, length, written, flags);
+      }
+   }
+#if (MQTT_CLIENT_TLS_SUPPORT == ENABLED)
+   //TLS transport protocol?
+   else if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_TLS)
+   {
+      //Set timeout
+      error = socketSetTimeout(context->socket, context->settings.timeout);
+
+      //Check status code
+      if(!error)
+      {
+         //Transmit data
+         error = tlsWrite(context->tlsContext, data, length, written, flags);
+      }
+   }
+#endif
+#if (MQTT_CLIENT_WS_SUPPORT == ENABLED)
+   //WebSocket transport protocol?
+   else if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_WS ||
+      context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_WSS)
+   {
+      //Set timeout
+      error = webSocketSetTimeout(context->webSocket, context->settings.timeout);
+
+      //Check status code
+      if(!error)
+      {
+         //MQTT control packets must be sent in WebSocket binary data frames
+         error = webSocketSend(context->webSocket, data, length,
+            WS_FRAME_TYPE_BINARY, written);
+      }
+   }
+#endif
+   //Unknown transport protocol?
+   else
+   {
+      //Report an error
+      error = ERROR_INVALID_PROTOCOL;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Receive data using the relevant transport protocol
+ * @param[in] context Pointer to the MQTT client context
+ * @param[out] data Buffer into which received data will be placed
+ * @param[in] size Maximum number of bytes that can be received
+ * @param[out] received Number of bytes that have been received
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t mqttClientReceiveData(MqttClientContext *context,
+   void *data, size_t size, size_t *received, uint_t flags)
+{
+   error_t error;
+
+   //No data has been read yet
+   *received = 0;
+
+   //TCP transport protocol?
+   if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_TCP)
+   {
+      //Set timeout
+      error = socketSetTimeout(context->socket, context->settings.timeout);
+
+      //Check status code
+      if(!error)
+      {
+         //Receive data
+         error = socketReceive(context->socket, data, size, received, flags);
+      }
+   }
+#if (MQTT_CLIENT_TLS_SUPPORT == ENABLED)
+   //TLS transport protocol?
+   else if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_TLS)
+   {
+      //Set timeout
+      error = socketSetTimeout(context->socket, context->settings.timeout);
+
+      //Check status code
+      if(!error)
+      {
+         //Receive data
+         error = tlsRead(context->tlsContext, data, size, received, flags);
+      }
+   }
+#endif
+#if (MQTT_CLIENT_WS_SUPPORT == ENABLED)
+   //WebSocket transport protocol?
+   else if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_WS ||
+      context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_WSS)
+   {
+      WebSocketFrameType type;
+
+      //Set timeout
+      error = webSocketSetTimeout(context->webSocket, context->settings.timeout);
+
+      //Check status code
+      if(!error)
+      {
+         //Receive data
+         error = webSocketReceive(context->webSocket, data, size, &type, received);
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //MQTT control packets must be sent in WebSocket binary data frames. If
+         //any other type of data frame is received the recipient must close the
+         //network connection
+         if(type != WS_FRAME_TYPE_BINARY && type != WS_FRAME_TYPE_CONTINUATION)
+            error = ERROR_INVALID_TYPE;
+      }
+   }
+#endif
+   //Unknown transport protocol?
+   else
+   {
+      //Report an error
+      error = ERROR_INVALID_PROTOCOL;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Wait for incoming data
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] timeout Maximum time to wait before returning
+ * @return Error code
+ **/
+
+error_t mqttClientWaitForData(MqttClientContext *context, systime_t timeout)
+{
+   uint_t event;
+
+   //TCP transport protocol?
+   if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_TCP)
+   {
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+      //Wait for some data to be available for reading
+      event = tcpWaitForEvents(context->socket, SOCKET_EVENT_RX_READY, timeout);
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+   }
+#if (MQTT_CLIENT_TLS_SUPPORT == ENABLED)
+   //TLS transport protocol?
+   else if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_TLS)
+   {
+      //Sanity check
+      if(context->tlsContext == NULL)
+         return ERROR_FAILURE;
+
+      //Check whether some data is pending in the receive buffer
+      if(context->tlsContext->rxBufferLen > 0)
+      {
+         //No need to poll the underlying socket for incoming traffic...
+         event = SOCKET_EVENT_RX_READY;
+      }
+      else
+      {
+         //Get exclusive access
+         osAcquireMutex(&netMutex);
+         //Wait for some data to be available for reading
+         event = tcpWaitForEvents(context->socket, SOCKET_EVENT_RX_READY, timeout);
+         //Release exclusive access
+         osReleaseMutex(&netMutex);
+      }
+   }
+#endif
+#if (MQTT_CLIENT_WS_SUPPORT == ENABLED)
+   //WebSocket transport protocol?
+   else if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_WS)
+   {
+      //Sanity check
+      if(context->webSocket == NULL)
+         return ERROR_FAILURE;
+
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+      //Wait for some data to be available for reading
+      event = tcpWaitForEvents(context->webSocket->socket, SOCKET_EVENT_RX_READY, timeout);
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+   }
+#endif
+#if (MQTT_CLIENT_WS_SUPPORT == ENABLED && WEB_SOCKET_TLS_SUPPORT)
+   //Secure WebSocket transport protocol?
+   else if(context->settings.transportProtocol == MQTT_TRANSPORT_PROTOCOL_WSS)
+   {
+      //Sanity check
+      if(context->webSocket == NULL || context->webSocket->tlsContext == NULL)
+         return ERROR_FAILURE;
+
+      //Check whether some data is pending in the receive buffer
+      if(context->webSocket->tlsContext->rxBufferLen > 0)
+      {
+         //No need to poll the underlying socket for incoming traffic...
+         event = SOCKET_EVENT_RX_READY;
+      }
+      else
+      {
+         //Get exclusive access
+         osAcquireMutex(&netMutex);
+         //Wait for some data to be available for reading
+         event = tcpWaitForEvents(context->webSocket->socket, SOCKET_EVENT_RX_READY, timeout);
+         //Release exclusive access
+         osReleaseMutex(&netMutex);
+      }
+   }
+#endif
+   //Unknown transport protocol?
+   else
+   {
+      //Report an error
+      return ERROR_INVALID_PROTOCOL;
+   }
+
+   //Check whether some data is available for reading
+   if(event == SOCKET_EVENT_RX_READY)
+      return NO_ERROR;
+   else
+      return ERROR_TIMEOUT;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mqtt/mqtt_client_transport.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,51 @@
+/**
+ * @file mqtt_client_transport.h
+ * @brief Transport protocol abstraction layer
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MQTT_CLIENT_IO_H
+#define _MQTT_CLIENT_IO_H
+
+//MQTT client related functions
+error_t mqttClientOpenConnection(MqttClientContext *context);
+
+error_t mqttClientEstablishConnection(MqttClientContext *context,
+   const IpAddr *serverIpAddr, uint16_t serverPort);
+
+error_t mqttClientShutdownConnection(MqttClientContext *context);
+
+void mqttClientCloseConnection(MqttClientContext *context);
+
+error_t mqttClientSendData(MqttClientContext *context,
+   const void *data, size_t length, size_t *written, uint_t flags);
+
+error_t mqttClientReceiveData(MqttClientContext *context,
+   void *data, size_t size, size_t *received, uint_t flags);
+
+error_t mqttClientWaitForData(MqttClientContext *context, systime_t timeout);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/mqtt/mqtt_common.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,197 @@
+/**
+ * @file mqtt_common.h
+ * @brief Definitions common to MQTT client and server
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _MQTT_COMMON_H
+#define _MQTT_COMMON_H
+
+//Dependencies
+#include "core/net.h"
+
+//MQTT port numbers
+#define MQTT_PORT 1883
+//MQTT over SSL/TLS port number
+#define MQTT_TLS_PORT 8883
+
+//MQTT 3.1 protocol name
+#define MQTT_PROTOCOL_NAME_3_1 "MQIsdp"
+//MQTT 3.1.1 protocol name
+#define MQTT_PROTOCOL_NAME_3_1_1 "MQTT"
+
+//Minimum size of MQTT header
+#define MQTT_MIN_HEADER_SIZE 2
+//Maximum size of MQTT header
+#define MQTT_MAX_HEADER_SIZE 5
+
+
+/**
+ * @brief MQTT protocol level
+ */
+
+typedef enum
+{
+   MQTT_PROTOCOL_LEVEL_3_1   = 3, ///<MQTT version 3.1
+   MQTT_PROTOCOL_LEVEL_3_1_1 = 4  ///<MQTT version 3.1.1
+} MqttProtocolLevel;
+
+
+/**
+ * @brief Transport protocol
+ **/
+
+typedef enum
+{
+   MQTT_TRANSPORT_PROTOCOL_TCP = 1, ///TCP protocol
+   MQTT_TRANSPORT_PROTOCOL_TLS = 2, ///SSL protocol
+   MQTT_TRANSPORT_PROTOCOL_WS  = 3, ///WebSocket protocol
+   MQTT_TRANSPORT_PROTOCOL_WSS = 4, ///Secure WebSocket protocol
+} MqttTransportProtocol;
+
+
+/**
+ * @brief Quality of service level
+ **/
+
+typedef enum
+{
+   MQTT_QOS_LEVEL_0 = 0, ///<At most once delivery
+   MQTT_QOS_LEVEL_1 = 1, ///<At least once delivery
+   MQTT_QOS_LEVEL_2 = 2  ///<Exactly once delivery
+} MqttQosLevel;
+
+
+/**
+ * @brief MQTT control packet type
+ **/
+
+typedef enum
+{
+   MQTT_PACKET_TYPE_INVALID     = 0,  ///<Invalid packet
+   MQTT_PACKET_TYPE_CONNECT     = 1,  ///<Client request to connect to server
+   MQTT_PACKET_TYPE_CONNACK     = 2,  ///<Connect acknowledgment
+   MQTT_PACKET_TYPE_PUBLISH     = 3,  ///<Publish message
+   MQTT_PACKET_TYPE_PUBACK      = 4,  ///<Publish acknowledgment
+   MQTT_PACKET_TYPE_PUBREC      = 5,  ///<Publish received (assured delivery part 1)
+   MQTT_PACKET_TYPE_PUBREL      = 6,  ///<Publish release (assured delivery part 2)
+   MQTT_PACKET_TYPE_PUBCOMP     = 7,  ///<Publish complete (assured delivery part 3)
+   MQTT_PACKET_TYPE_SUBSCRIBE   = 8,  ///<Client subscribe request
+   MQTT_PACKET_TYPE_SUBACK      = 9,  ///<Subscribe acknowledgment
+   MQTT_PACKET_TYPE_UNSUBSCRIBE = 10, ///<Unsubscribe request
+   MQTT_PACKET_TYPE_UNSUBACK    = 11, ///<Unsubscribe acknowledgment
+   MQTT_PACKET_TYPE_PINGREQ     = 12, ///<Ping request
+   MQTT_PACKET_TYPE_PINGRESP    = 13, ///<Ping response
+   MQTT_PACKET_TYPE_DISCONNECT  = 14  ///<Client is disconnecting
+} MqttPacketType;
+
+
+/**
+ * @brief Connect flags
+ **/
+
+typedef enum
+{
+   MQTT_CONNECT_FLAG_CLEAN_SESSION = 0x02,
+   MQTT_CONNECT_FLAG_WILL          = 0x04,
+   MQTT_CONNECT_FLAG_WILL_QOS_0    = 0x00,
+   MQTT_CONNECT_FLAG_WILL_QOS_1    = 0x08,
+   MQTT_CONNECT_FLAG_WILL_QOS_2    = 0x10,
+   MQTT_CONNECT_FLAG_WILL_RETAIN   = 0x20,
+   MQTT_CONNECT_FLAG_PASSWORD      = 0x40,
+   MQTT_CONNECT_FLAG_USERNAME      = 0x80
+} MqttConnectFlags;
+
+
+/**
+ * @brief Connect Acknowledge flags
+ **/
+
+typedef enum
+{
+   MQTT_CONNECT_ACK_FLAG_SESSION_PRESENT = 0x01
+} MqttConnectAckFlags;
+
+
+/**
+ * @brief Connect return codes
+ **/
+
+typedef enum
+{
+   MQTT_CONNECT_RET_CODE_ACCEPTED             = 0,
+   MQTT_CONNECT_RET_CODE_UNACCEPTABLE_VERSION = 1,
+   MQTT_CONNECT_RET_CODE_ID_REJECTED          = 2,
+   MQTT_CONNECT_RET_CODE_SERVER_UNAVAILABLE   = 3,
+   MQTT_CONNECT_RET_CODE_BAD_USER_NAME        = 4,
+   MQTT_CONNECT_RET_CODE_NOT_AUTHORIZED       = 5
+} MqttConnectRetCode;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief Fixed header
+ **/
+
+typedef __start_packed struct
+{
+#ifdef _CPU_BIG_ENDIAN
+   uint8_t type : 4;   //0
+   uint8_t dup : 1;
+   uint8_t qos : 2;
+   uint8_t retain: 1;
+#else
+   uint8_t retain : 1; //0
+   uint8_t qos : 2;
+   uint8_t dup : 1;
+   uint8_t type : 4;
+#endif
+   uint8_t length[];   //1
+} __end_packed MqttPacketHeader;
+
+
+/**
+ * @brief UTF-8 encoded string
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t length; //0-1
+   uint8_t data[];  //2
+} __end_packed MqttString;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/netbios/nbns_client.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,353 @@
+/**
+ * @file nbns_client.c
+ * @brief NBNS client (NetBIOS Name Service)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NBNS_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "netbios/nbns_client.h"
+#include "netbios/nbns_common.h"
+#include "dns/dns_debug.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (NBNS_CLIENT_SUPPORT == ENABLED && IPV4_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Resolve a host name using NBNS
+ * @param[in] interface Underlying network interface
+ * @param[in] name Name of the host to be resolved
+ * @param[out] ipAddr IP address corresponding to the specified host name
+ **/
+
+error_t nbnsResolve(NetInterface *interface, const char_t *name, IpAddr *ipAddr)
+{
+   error_t error;
+   DnsCacheEntry *entry;
+
+#if (NET_RTOS_SUPPORT == ENABLED)
+   systime_t delay;
+
+   //Debug message
+   TRACE_INFO("Resolving host name %s (NBNS resolver)...\r\n", name);
+#endif
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Search the DNS cache for the specified host name
+   entry = dnsFindEntry(interface, name, HOST_TYPE_IPV4, HOST_NAME_RESOLVER_NBNS);
+
+   //Check whether a matching entry has been found
+   if(entry)
+   {
+      //Host name already resolved?
+      if(entry->state == DNS_STATE_RESOLVED ||
+         entry->state == DNS_STATE_PERMANENT)
+      {
+         //Return the corresponding IP address
+         *ipAddr = entry->ipAddr;
+         //Successful host name resolution
+         error = NO_ERROR;
+      }
+      else
+      {
+         //Host name resolution is in progress...
+         error = ERROR_IN_PROGRESS;
+      }
+   }
+   else
+   {
+      //If no entry exists, then create a new one
+      entry = dnsCreateEntry();
+
+      //Record the host name whose IP address is unknown
+      strcpy(entry->name, name);
+
+      //Initialize DNS cache entry
+      entry->type = HOST_TYPE_IPV4;
+      entry->protocol = HOST_NAME_RESOLVER_NBNS;
+      entry->interface = interface;
+
+      //Initialize retransmission counter
+      entry->retransmitCount = NBNS_CLIENT_MAX_RETRIES;
+      //Send NBNS query
+      error = nbnsSendQuery(entry);
+
+      //NBNS message successfully sent?
+      if(!error)
+      {
+         //Save the time at which the query message was sent
+         entry->timestamp = osGetSystemTime();
+         //Set timeout value
+         entry->timeout = NBNS_CLIENT_INIT_TIMEOUT;
+         entry->maxTimeout = NBNS_CLIENT_MAX_TIMEOUT;
+         //Decrement retransmission counter
+         entry->retransmitCount--;
+
+         //Switch state
+         entry->state = DNS_STATE_IN_PROGRESS;
+         //Host name resolution is in progress
+         error = ERROR_IN_PROGRESS;
+      }
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+#if (NET_RTOS_SUPPORT == ENABLED)
+   //Set default polling interval
+   delay = DNS_CACHE_INIT_POLLING_INTERVAL;
+
+   //Wait the host name resolution to complete
+   while(error == ERROR_IN_PROGRESS)
+   {
+      //Wait until the next polling period
+      osDelayTask(delay);
+
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+
+      //Search the DNS cache for the specified host name
+      entry = dnsFindEntry(interface, name, HOST_TYPE_IPV4, HOST_NAME_RESOLVER_NBNS);
+
+      //Check whether a matching entry has been found
+      if(entry)
+      {
+         //Host name successfully resolved?
+         if(entry->state == DNS_STATE_RESOLVED)
+         {
+            //Return the corresponding IP address
+            *ipAddr = entry->ipAddr;
+            //Successful host name resolution
+            error = NO_ERROR;
+         }
+      }
+      else
+      {
+         //Host name resolution failed
+         error = ERROR_FAILURE;
+      }
+
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+
+      //Backoff support for less aggressive polling
+      delay = MIN(delay * 2, DNS_CACHE_MAX_POLLING_INTERVAL);
+   }
+
+   //Check status code
+   if(error)
+   {
+      //Failed to resolve host name
+      TRACE_INFO("Host name resolution failed!\r\n");
+   }
+   else
+   {
+      //Successful host name resolution
+      TRACE_INFO("Host name resolved to %s...\r\n", ipAddrToString(ipAddr, NULL));
+   }
+#endif
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send a NBNS query message
+ * @param[in] entry Pointer to a valid DNS cache entry
+ * @return Error code
+ **/
+
+error_t nbnsSendQuery(DnsCacheEntry *entry)
+{
+   error_t error;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   NbnsHeader *message;
+   DnsQuestion *dnsQuestion;
+   IpAddr destIpAddr;
+
+   //Allocate a memory buffer to hold the NBNS query message
+   buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset);
+   //Failed to allocate buffer?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the NBNS header
+   message = netBufferAt(buffer, offset);
+
+   //Format NBNS query message
+   message->id = htons(entry->id);
+   message->qr = 0;
+   message->opcode = DNS_OPCODE_QUERY;
+   message->aa = 0;
+   message->tc = 0;
+   message->rd = 0;
+   message->ra = 0;
+   message->z = 0;
+   message->b = 1;
+   message->rcode = DNS_RCODE_NO_ERROR;
+
+   //The NBNS query contains one question
+   message->qdcount = HTONS(1);
+   message->ancount = 0;
+   message->nscount = 0;
+   message->arcount = 0;
+
+   //Length of the NBNS query message
+   length = sizeof(DnsHeader);
+
+   //Encode the NetBIOS name
+   length += nbnsEncodeName(entry->name, message->questions);
+
+   //Point to the corresponding question structure
+   dnsQuestion = DNS_GET_QUESTION(message, length);
+   //Fill in question structure
+   dnsQuestion->qtype = HTONS(DNS_RR_TYPE_NB);
+   dnsQuestion->qclass = HTONS(DNS_RR_CLASS_IN);
+
+   //Update the length of the NBNS query message
+   length += sizeof(DnsQuestion);
+
+   //Adjust the length of the multi-part buffer
+   netBufferSetLength(buffer, offset + length);
+
+   //Debug message
+   TRACE_INFO("Sending NBNS message (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message
+   dnsDumpMessage((DnsHeader *) message, length);
+
+   //The destination address is the broadcast address
+   destIpAddr.length = sizeof(Ipv4Addr);
+   ipv4GetBroadcastAddr(entry->interface, &destIpAddr.ipv4Addr);
+
+   //A request packet is always sent to the well known port 137
+   error = udpSendDatagramEx(entry->interface, NBNS_PORT,
+      &destIpAddr, NBNS_PORT, buffer, offset, IPV4_DEFAULT_TTL);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process NBNS response message
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader UDP pseudo header
+ * @param[in] udpHeader UDP header
+ * @param[in] message Pointer to the NBNS response message
+ * @param[in] length Length of the message
+ **/
+
+void nbnsProcessResponse(NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader,
+   const UdpHeader *udpHeader, const NbnsHeader *message, size_t length)
+{
+   uint_t i;
+   size_t pos;
+   DnsCacheEntry *entry;
+   DnsResourceRecord *record;
+   NbnsAddrEntry *addrEntry;
+
+   //The NBNS response shall contain one answer
+   if(ntohs(message->qdcount) != 0 && ntohs(message->ancount) != 1)
+      return;
+
+   //Parse NetBIOS name
+   pos = nbnsParseName(message, length, sizeof(DnsHeader), NULL);
+   //Invalid name?
+   if(!pos)
+      return;
+
+   //Point to the associated resource record
+   record = DNS_GET_RESOURCE_RECORD(message, pos);
+   //Point to the resource data
+   pos += sizeof(DnsResourceRecord);
+
+   //Make sure the resource record is valid
+   if(pos > length)
+      return;
+   if((pos + ntohs(record->rdlength)) > length)
+      return;
+
+   //Check the class and the type of the resource record
+   if(ntohs(record->rclass) != DNS_RR_CLASS_IN)
+      return;
+   if(ntohs(record->rtype) != DNS_RR_TYPE_NB)
+      return;
+
+   //Verify the length of the data field
+   if(ntohs(record->rdlength) < sizeof(NbnsAddrEntry))
+      return;
+
+   //Loop through DNS cache entries
+   for(i = 0; i < DNS_CACHE_SIZE; i++)
+   {
+      //Point to the current entry
+      entry = &dnsCache[i];
+
+      //NBNS name resolution in progress?
+      if(entry->state == DNS_STATE_IN_PROGRESS &&
+         entry->protocol == HOST_NAME_RESOLVER_NBNS &&
+         entry->type == HOST_TYPE_IPV4)
+      {
+         //Compare identifiers
+         if(entry->id == ntohs(message->id))
+         {
+            //Compare NetBIOS names
+            if(nbnsCompareName(message, length, sizeof(DnsHeader), entry->name))
+            {
+               //Point to the address entry array
+               addrEntry = (NbnsAddrEntry *) record->rdata;
+               //Copy the IPv4 address
+               entry->ipAddr.length = sizeof(Ipv4Addr);
+               entry->ipAddr.ipv4Addr = addrEntry->addr;
+
+               //Save current time
+               entry->timestamp = osGetSystemTime();
+               //Save TTL value
+               entry->timeout = ntohl(record->ttl) * 1000;
+               //Limit the lifetime of the NBNS cache entries
+               entry->timeout = MIN(entry->timeout, NBNS_MAX_LIFETIME);
+
+               //Host name successfully resolved
+               entry->state = DNS_STATE_RESOLVED;
+            }
+         }
+      }
+   }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/netbios/nbns_client.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,84 @@
+/**
+ * @file nbns_client.h
+ * @brief NBNS client (NetBIOS Name Service)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _NBNS_CLIENT_H
+#define _NBNS_CLIENT_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/socket.h"
+#include "core/udp.h"
+#include "dns/dns_cache.h"
+#include "dns/dns_common.h"
+#include "netbios/nbns_common.h"
+
+//NBNS client support
+#ifndef NBNS_CLIENT_SUPPORT
+   #define NBNS_CLIENT_SUPPORT ENABLED
+#elif (NBNS_CLIENT_SUPPORT != ENABLED && NBNS_CLIENT_SUPPORT != DISABLED)
+   #error NBNS_CLIENT_SUPPORT parameter is not valid
+#endif
+
+//Maximum number of retransmissions of NBNS queries
+#ifndef NBNS_CLIENT_MAX_RETRIES
+   #define NBNS_CLIENT_MAX_RETRIES 3
+#elif (NBNS_CLIENT_MAX_RETRIES < 1)
+   #error NBNS_CLIENT_MAX_RETRIES parameter is not valid
+#endif
+
+//Initial retransmission timeout
+#ifndef NBNS_CLIENT_INIT_TIMEOUT
+   #define NBNS_CLIENT_INIT_TIMEOUT 1000
+#elif (NBNS_CLIENT_INIT_TIMEOUT < 1000)
+   #error NBNS_CLIENT_INIT_TIMEOUT parameter is not valid
+#endif
+
+//Maximum retransmission timeout
+#ifndef NBNS_CLIENT_MAX_TIMEOUT
+   #define NBNS_CLIENT_MAX_TIMEOUT 1000
+#elif (NBNS_CLIENT_MAX_TIMEOUT < 1000)
+   #error NBNS_CLIENT_MAX_TIMEOUT parameter is not valid
+#endif
+
+//Maximum cache lifetime for NBNS entries
+#ifndef NBNS_MAX_LIFETIME
+   #define NBNS_MAX_LIFETIME 60000
+#elif (NBNS_MAX_LIFETIME < 1000)
+   #error NBNS_MAX_LIFETIME parameter is not valid
+#endif
+
+//NBNS related functions
+error_t nbnsResolve(NetInterface *interface, const char_t *name, IpAddr *ipAddr);
+
+error_t nbnsSendQuery(DnsCacheEntry *entry);
+
+void nbnsProcessResponse(NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader,
+   const UdpHeader *udpHeader, const NbnsHeader *message, size_t length);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/netbios/nbns_common.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,352 @@
+/**
+ * @file nbns_common.c
+ * @brief Functions common to NBNS client and NBNS responder
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NBNS_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "core/net.h"
+#include "netbios/nbns_client.h"
+#include "netbios/nbns_responder.h"
+#include "netbios/nbns_common.h"
+#include "dns/dns_debug.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (NBNS_CLIENT_SUPPORT == ENABLED || NBNS_RESPONDER_SUPPORT == ENABLED)
+#if (IPV4_SUPPORT == ENABLED)
+
+
+/**
+ * @brief NBNS related initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t nbnsInit(NetInterface *interface)
+{
+   error_t error;
+
+   //Callback function to be called when a NBNS message is received
+   error = udpAttachRxCallback(interface, NBNS_PORT, nbnsProcessMessage, NULL);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process incoming NBNS message
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader UDP pseudo header
+ * @param[in] udpHeader UDP header
+ * @param[in] buffer Multi-part buffer containing the incoming NBNS message
+ * @param[in] offset Offset to the first byte of the NBNS message
+ * @param[in] params Callback function parameter (not used)
+ **/
+
+void nbnsProcessMessage(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
+   const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, void *params)
+{
+   size_t length;
+   NbnsHeader *message;
+
+   //Make sure the NBNS message was received from an IPv4 peer
+   if(pseudoHeader->length != sizeof(Ipv4PseudoHeader))
+      return;
+
+   //Retrieve the length of the NBNS message
+   length = netBufferGetLength(buffer) - offset;
+
+   //Ensure the NBNS message is valid
+   if(length < sizeof(NbnsHeader))
+      return;
+   if(length > DNS_MESSAGE_MAX_SIZE)
+      return;
+
+   //Point to the NBNS message header
+   message = netBufferAt(buffer, offset);
+   //Sanity check
+   if(message == NULL)
+      return;
+
+   //Debug message
+   TRACE_INFO("NBNS message received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message
+   dnsDumpMessage((DnsHeader *) message, length);
+
+   //NBNS messages received with an opcode other than zero must be silently ignored
+   if(message->opcode != DNS_OPCODE_QUERY)
+      return;
+   //NBNS messages received with non-zero response codes must be silently ignored
+   if(message->rcode != DNS_RCODE_NO_ERROR)
+      return;
+
+   //NBNS query received?
+   if(!message->qr)
+   {
+#if (NBNS_RESPONDER_SUPPORT == ENABLED)
+      //Process incoming NBNS query message
+      nbnsProcessQuery(interface, &pseudoHeader->ipv4Data,
+         udpHeader, message, length);
+#endif
+   }
+   //NBNS response received?
+   else
+   {
+#if (NBNS_CLIENT_SUPPORT == ENABLED)
+      //Process incoming NBNS response message
+      nbnsProcessResponse(interface, &pseudoHeader->ipv4Data,
+         udpHeader, message, length);
+#endif
+   }
+}
+
+
+/**
+ * @brief Encode a NetBIOS name
+ * @param[in] src Pointer to the name to encode
+ * @param[out] dest Pointer to the encoded NetBIOS name
+ * @return Length of the encoded NetBIOS name
+ **/
+
+size_t nbnsEncodeName(const char_t *src, uint8_t *dest)
+{
+   size_t i;
+   size_t j;
+   char_t c;
+
+   //Point to first byte of the output buffer
+   j = 0;
+
+   //NetBIOS names are 32-byte long
+   dest[j++] = 32;
+
+   //Parse input name
+   for(i = 0; i < 15 && src[i] != '\0'; i++)
+   {
+      //Convert current character to uppercase
+      c = toupper((uint8_t) src[i]);
+
+      //Encode character
+      dest[j++] = NBNS_ENCODE_H(c);
+      dest[j++] = NBNS_ENCODE_L(c);
+   }
+
+   //Pad NetBIOS name with space characters
+   for(; i < 15; i++)
+   {
+      //Encoded space character
+      dest[j++] = NBNS_ENCODE_H(' ');
+      dest[j++] = NBNS_ENCODE_L(' ');
+   }
+
+   //The 16th character is the NetBIOS suffix
+   dest[j++] = NBNS_ENCODE_H(0);
+   dest[j++] = NBNS_ENCODE_L(0);
+
+   //Terminate the NetBIOS name with a zero length count
+   dest[j++] = 0;
+
+   //Return the length of the encoded NetBIOS name
+   return j;
+}
+
+
+/**
+ * @brief Decode a NetBIOS name
+ * @param[in] message Pointer to the NBNS message
+ * @param[in] length Length of the NBNS message
+ * @param[in] pos Offset of the name to decode
+ * @param[out] dest Pointer to the decoded name (optional)
+ * @return The position of the resource record that immediately follows the NetBIOS name
+ **/
+
+size_t nbnsParseName(const NbnsHeader *message,
+   size_t length, size_t pos, char_t *dest)
+{
+   size_t i;
+   size_t n;
+   char_t c;
+
+   //Cast the input NBNS message to byte array
+   uint8_t *src = (uint8_t *) message;
+
+   //Malformed NBNS message?
+   if((pos + 34) >= length)
+      return 0;
+
+   //Retrieve the length of the first label
+   n = src[pos++];
+
+   //NetBIOS names must be 32-byte long
+   if(n != 32)
+      return 0;
+
+   //Parse the NetBIOS name
+   for(i = 0; i < 15; i++)
+   {
+      //Make sure the characters of the sequence are valid
+      if(src[pos] < 'A' || src[pos] > 'P')
+         return 0;
+      if(src[pos + 1] < 'A' || src[pos + 1] > 'P')
+         return 0;
+
+      //Combine nibbles to restore the original ASCII character
+      c = ((src[pos] - 'A') << 4) | (src[pos + 1] - 'A');
+
+      //Padding character found?
+      if(c == ' ')
+         break;
+
+      //Save current ASCII character
+      if(dest != NULL)
+         *(dest++) = c;
+
+      //Advance data pointer
+      pos += 2;
+   }
+
+   //Skip padding characters
+   for(; i < 16; i++)
+   {
+      //Make sure the nibbles are valid
+      if(src[pos] < 'A' || src[pos] > 'P')
+         return 0;
+      if(src[pos + 1] < 'A' || src[pos + 1] > 'P')
+         return 0;
+
+      //Advance data pointer
+      pos += 2;
+   }
+
+   //Retrieve the length of the next label
+   n = src[pos++];
+
+   //NetBIOS names are terminated with a zero length count
+   if(n != 0)
+      return 0;
+
+   //Properly terminate the string
+   if(dest != NULL)
+      *(dest++) = '\0';
+
+   //Return the position of the resource record that
+   //is immediately following the NetBIOS name
+   return pos;
+}
+
+
+/**
+ * @brief Compare NetBIOS names
+ * @param[in] message Pointer to the NBNS message
+ * @param[in] length Length of the NBNS message
+ * @param[in] pos Offset of the encoded domain name
+ * @param[in] name NULL-terminated string that holds a domain name
+ * @return TRUE if the NetBIOS names match, else FALSE
+ **/
+
+bool_t nbnsCompareName(const NbnsHeader *message,
+   size_t length, size_t pos, const char_t *name)
+{
+   size_t i;
+   size_t n;
+   char_t c;
+
+   //Cast the input NBNS message to byte array
+   uint8_t *src = (uint8_t *) message;
+
+   //Malformed NBNS message?
+   if((pos + 34) >= length)
+      return FALSE;
+
+   //Retrieve the length of the first label
+   n = src[pos++];
+
+   //NetBIOS names must be 32-byte long
+   if(n != 32)
+      return FALSE;
+
+   //Parse the NetBIOS name
+   for(i = 0; i < 15; i++)
+   {
+      //Make sure the characters of the sequence are valid
+      if(src[pos] < 'A' || src[pos] > 'P')
+         return FALSE;
+      if(src[pos + 1] < 'A' || src[pos + 1] > 'P')
+         return FALSE;
+
+      //Combine nibbles to restore the original ASCII character
+      c = ((src[pos] - 'A') << 4) | (src[pos + 1] - 'A');
+
+      //Padding character found?
+      if(c == ' ' && *name == '\0')
+         break;
+
+      //Perform case insensitive comparison
+      if(toupper((uint8_t) c) != toupper((uint8_t) *name))
+         return FALSE;
+
+      //Advance data pointer
+      pos += 2;
+      name++;
+   }
+
+   //Skip padding characters
+   for(; i < 16; i++)
+   {
+      //Make sure the nibbles are valid
+      if(src[pos] < 'A' || src[pos] > 'P')
+         return FALSE;
+      if(src[pos + 1] < 'A' || src[pos + 1] > 'P')
+         return FALSE;
+
+      //Advance data pointer
+      pos += 2;
+   }
+
+   //Retrieve the length of the next label
+   n = src[pos];
+
+   //NetBIOS names are terminated with a zero length count
+   if(n != 0)
+      return FALSE;
+
+   //The NetBIOS names match
+   return TRUE;
+}
+
+#endif
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/netbios/nbns_common.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,139 @@
+/**
+ * @file nbns_common.h
+ * @brief Functions common to NBNS client and NBNS responder
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _NBNS_COMMON_H
+#define _NBNS_COMMON_H
+
+//Dependencies
+#include "core/net.h"
+#include "dns/dns_common.h"
+
+//Default TTL value for NBNS resource records
+#ifndef NBNS_DEFAULT_RESOURCE_RECORD_TTL
+   #define NBNS_DEFAULT_RESOURCE_RECORD_TTL 120
+#elif (NBNS_DEFAULT_RESOURCE_RECORD_TTL < 1)
+   #error NBNS_DEFAULT_RESOURCE_RECORD_TTL parameter is not valid
+#endif
+
+//NBNS port number
+#define NBNS_PORT 137
+
+//Macro definition
+#define NBNS_ENCODE_H(c) ('A' + (((c) >> 4) & 0x0F))
+#define NBNS_ENCODE_L(c) ('A' + ((c) & 0x0F))
+
+
+/**
+ * @brief NBNS flags
+ **/
+
+typedef enum
+{
+   NBNS_ONT_BNODE = 0x0000,
+   NBNS_ONT_PNODE = 0x2000,
+   NBNS_ONT_MNODE = 0x4000,
+   NBNS_G_UNIQUE  = 0x0000,
+   NBNS_G_GROUP   = 0x8000
+}DnsFlags;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief NBNS message header
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t id;         //0-1
+#ifdef _CPU_BIG_ENDIAN
+   uint16_t qr : 1;     //2
+   uint16_t opcode : 4;
+   uint16_t aa : 1;
+   uint16_t tc : 1;
+   uint16_t rd : 1;
+   uint16_t ra : 1;     //3
+   uint16_t z : 2;
+   uint16_t b : 1;
+   uint16_t rcode : 4;
+#else
+   uint16_t rd : 1;     //2
+   uint16_t tc : 1;
+   uint16_t aa : 1;
+   uint16_t opcode : 4;
+   uint16_t qr : 1;
+   uint16_t rcode : 4;  //3
+   uint16_t b : 1;
+   uint16_t z : 2;
+   uint16_t ra : 1;
+#endif
+   uint16_t qdcount;    //4-5
+   uint16_t ancount;    //6-7
+   uint16_t nscount;    //8-9
+   uint16_t arcount;    //10-11
+   uint8_t questions[]; //12
+} __end_packed NbnsHeader;
+
+
+/**
+ * @brief NBNS address entry
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t flags;
+   Ipv4Addr addr;
+} __end_packed NbnsAddrEntry;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+//NBNS related functions
+error_t nbnsInit(NetInterface *interface);
+
+void nbnsProcessMessage(NetInterface *interface, const IpPseudoHeader *pseudoHeader,
+   const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, void *params);
+
+size_t nbnsEncodeName(const char_t *src, uint8_t *dest);
+
+size_t nbnsParseName(const NbnsHeader *message,
+   size_t length, size_t pos, char_t *dest);
+
+bool_t nbnsCompareName(const NbnsHeader *message,
+   size_t length, size_t pos, const char_t *name);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/netbios/nbns_responder.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,192 @@
+/**
+ * @file nbns_responder.c
+ * @brief NBNS responder (NetBIOS Name Service)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NBNS_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "netbios/nbns_responder.h"
+#include "netbios/nbns_common.h"
+#include "dns/dns_debug.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (NBNS_RESPONDER_SUPPORT == ENABLED && IPV4_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Process NBNS query message
+ * @param[in] interface Underlying network interface
+ * @param[in] pseudoHeader UDP pseudo header
+ * @param[in] udpHeader UDP header
+ * @param[in] message Pointer to the NBNS query message
+ * @param[in] length Length of the message
+ **/
+
+void nbnsProcessQuery(NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader,
+   const UdpHeader *udpHeader, const NbnsHeader *message, size_t length)
+{
+   size_t pos;
+   DnsQuestion *question;
+
+   //The NBNS query shall contain one question
+   if(ntohs(message->qdcount) != 1)
+      return;
+
+   //Parse NetBIOS name
+   pos = nbnsParseName(message, length, sizeof(DnsHeader), NULL);
+
+   //Invalid name?
+   if(!pos)
+      return;
+   //Malformed NBNS query message?
+   if((pos + sizeof(DnsQuestion)) > length)
+      return;
+
+   //Point to the corresponding entry
+   question = DNS_GET_QUESTION(message, pos);
+
+   //Check the class and the type of the request
+   if(ntohs(question->qclass) != DNS_RR_CLASS_IN)
+      return;
+   if(ntohs(question->qtype) != DNS_RR_TYPE_NB)
+      return;
+
+   //Compare NetBIOS names
+   if(nbnsCompareName(message, length, sizeof(DnsHeader), interface->hostname))
+   {
+      uint16_t destPort;
+      IpAddr destIpAddr;
+
+      //A response packet is always sent to the source UDP port and
+      //source IP address of the request packet
+      destIpAddr.length = sizeof(Ipv4Addr);
+      destIpAddr.ipv4Addr = pseudoHeader->srcAddr;
+
+      //Convert the port number to host byte order
+      destPort = ntohs(udpHeader->srcPort);
+
+      //Send NBNS response
+      nbnsSendResponse(interface, &destIpAddr, destPort, message->id);
+   }
+}
+
+
+/**
+ * @brief Send NBNS response message
+ * @param[in] interface Underlying network interface
+ * @param[in] destIpAddr Destination IP address
+ * @param[in] destPort destination port
+ * @param[in] id 16-bit identifier to be used when sending NBNS query
+ **/
+
+error_t nbnsSendResponse(NetInterface *interface,
+   const IpAddr *destIpAddr, uint16_t destPort, uint16_t id)
+{
+   error_t error;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   NbnsHeader *message;
+   NbnsAddrEntry *addrEntry;
+   DnsResourceRecord *record;
+
+   //Allocate a memory buffer to hold the NBNS response message
+   buffer = udpAllocBuffer(DNS_MESSAGE_MAX_SIZE, &offset);
+   //Failed to allocate buffer?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the NBNS header
+   message = netBufferAt(buffer, offset);
+
+   //Take the identifier from the query message
+   message->id = id;
+
+   //Format NBNS response header
+   message->qr = 1;
+   message->opcode = DNS_OPCODE_QUERY;
+   message->aa = 1;
+   message->tc = 0;
+   message->rd = 1;
+   message->ra = 1;
+   message->z = 0;
+   message->b = 0;
+   message->rcode = DNS_RCODE_NO_ERROR;
+
+   //The NBNS response contains 1 answer resource record
+   message->qdcount = 0;
+   message->ancount = HTONS(1);
+   message->nscount = 0;
+   message->arcount = 0;
+
+   //NBNS response message length
+   length = sizeof(DnsHeader);
+
+   //Encode the host name using the NBNS name notation
+   length += nbnsEncodeName(interface->hostname, (uint8_t *) message + length);
+
+   //Point to the corresponding resource record
+   record = DNS_GET_RESOURCE_RECORD(message, length);
+   //Fill in resource record
+   record->rtype = HTONS(DNS_RR_TYPE_NB);
+   record->rclass = HTONS(DNS_RR_CLASS_IN);
+   record->ttl = HTONL(NBNS_DEFAULT_RESOURCE_RECORD_TTL);
+   record->rdlength = HTONS(sizeof(NbnsAddrEntry));
+
+   //Point to the address entry array
+   addrEntry = (NbnsAddrEntry *) record->rdata;
+   //Fill in address entry
+   addrEntry->flags = HTONS(NBNS_G_UNIQUE | NBNS_ONT_BNODE);
+   addrEntry->addr = interface->ipv4Context.addr;
+
+   //Update the length of the NBNS response message
+   length += sizeof(DnsResourceRecord) + sizeof(NbnsAddrEntry);
+
+   //Adjust the length of the multi-part buffer
+   netBufferSetLength(buffer, offset + length);
+
+   //Debug message
+   TRACE_INFO("Sending NBNS message (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump message
+   dnsDumpMessage((DnsHeader *) message, length);
+
+   //A response packet is always sent to the source UDP port and
+   //source IP address of the request packet
+   error = udpSendDatagramEx(interface, NBNS_PORT, destIpAddr,
+      destPort, buffer, offset, IPV4_DEFAULT_TTL);
+
+   //Free previously allocated memory
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/netbios/nbns_responder.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,53 @@
+/**
+ * @file nbns_responder.h
+ * @brief NBNS responder (NetBIOS Name Service)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _NBNS_RESPONDER_H
+#define _NBNS_RESPONDER_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/udp.h"
+#include "dns/dns_common.h"
+#include "netbios/nbns_common.h"
+
+//NBNS responder support
+#ifndef NBNS_RESPONDER_SUPPORT
+   #define NBNS_RESPONDER_SUPPORT ENABLED
+#elif (NBNS_RESPONDER_SUPPORT != ENABLED && NBNS_RESPONDER_SUPPORT != DISABLED)
+   #error NBNS_RESPONDER_SUPPORT parameter is not valid
+#endif
+
+//NBNS related functions
+void nbnsProcessQuery(NetInterface *interface, const Ipv4PseudoHeader *pseudoHeader,
+   const UdpHeader *udpHeader, const NbnsHeader *message, size_t length);
+
+error_t nbnsSendResponse(NetInterface *interface,
+   const IpAddr *destIpAddr, uint16_t destPort, uint16_t id);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/chap.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,735 @@
+/**
+ * @file chap.c
+ * @brief CHAP (Challenge Handshake Authentication Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL PPP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "ppp/ppp_debug.h"
+#include "ppp/lcp.h"
+#include "ppp/ipcp.h"
+#include "ppp/ipv6cp.h"
+#include "ppp/chap.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (PPP_SUPPORT == ENABLED && CHAP_SUPPORT == ENABLED)
+
+//Additional dependencies
+#include "crypto.h"
+#include "md5.h"
+
+
+/**
+ * @brief Start CHAP authentication
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t chapStartAuth(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("\r\nStarting CHAP authentication...\r\n");
+
+   //Check whether the other end of the PPP link is being authenticated
+   if(context->localConfig.authProtocol == PPP_PROTOCOL_CHAP)
+   {
+      //Initialize restart counter
+      context->chapFsm.restartCounter = CHAP_MAX_CHALLENGES;
+      //Send a Challenge packet
+      chapSendChallenge(context);
+      //Switch to the Challenge-Sent state
+      context->chapFsm.localState = CHAP_STATE_2_CHALLENGE_SENT;
+   }
+
+   //Check whether the other end of the PPP link is the authenticator
+   if(context->peerConfig.authProtocol == PPP_PROTOCOL_CHAP)
+   {
+      //Switch to the Started state
+      context->chapFsm.peerState = CHAP_STATE_1_STARTED;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Abort CHAP authentication
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t chapAbortAuth(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("\r\nAborting CHAP authentication...\r\n");
+
+   //Abort CHAP authentication process
+   context->chapFsm.localState = CHAP_STATE_0_INITIAL;
+   context->chapFsm.peerState = CHAP_STATE_0_INITIAL;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief CHAP timer handler
+ * @param[in] context PPP context
+ **/
+
+void chapTick(PppContext *context)
+{
+   //Check whether the restart timer is running
+   if(context->chapFsm.localState == CHAP_STATE_2_CHALLENGE_SENT)
+   {
+      //Get current time
+      systime_t time = osGetSystemTime();
+
+      //Check restart timer
+      if((time - context->chapFsm.timestamp) >= CHAP_RESTART_TIMER)
+      {
+         //Debug message
+         TRACE_INFO("\r\nCHAP Timeout event\r\n");
+
+         //Check whether the restart counter is greater than zero
+         if(context->chapFsm.restartCounter > 0)
+         {
+            //Retransmit the Challenge packet
+            chapSendChallenge(context);
+         }
+         else
+         {
+            //Abort CHAP authentication
+            context->chapFsm.localState = CHAP_STATE_0_INITIAL;
+            //Authentication failed
+            lcpClose(context);
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Process an incoming CHAP packet
+ * @param[in] context PPP context
+ * @param[in] packet CHAP packet received from the peer
+ * @param[in] length Length of the packet, in bytes
+ **/
+
+void chapProcessPacket(PppContext *context,
+   const PppPacket *packet, size_t length)
+{
+   //Ensure the length of the incoming CHAP packet is valid
+   if(length < sizeof(PppPacket))
+      return;
+
+   //Check the length field
+   if(ntohs(packet->length) > length)
+      return;
+   if(ntohs(packet->length) < sizeof(PppPacket))
+      return;
+
+   //Save the length of the CHAP packet
+   length = ntohs(packet->length);
+
+   //Debug message
+   TRACE_INFO("CHAP packet received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump CHAP packet contents for debugging purpose
+   pppDumpPacket(packet, length, PPP_PROTOCOL_CHAP);
+
+   //CHAP is done at initial link establishment, and could also be
+   //requested after link establishment
+   if(context->pppPhase != PPP_PHASE_AUTHENTICATE &&
+      context->pppPhase != PPP_PHASE_NETWORK)
+   {
+      //Any packets received during any other phase must be silently discarded
+      return;
+   }
+
+   //Check CHAP code field
+   switch(packet->code)
+   {
+   //Challenge packet?
+   case CHAP_CODE_CHALLENGE:
+      //Process Challenge packet
+      chapProcessChallenge(context, (ChapChallengePacket *) packet, length);
+      break;
+   //Response packet?
+   case CHAP_CODE_RESPONSE:
+      //Process Response packet
+      chapProcessResponse(context, (ChapResponsePacket *) packet, length);
+      break;
+   //Success packet?
+   case CHAP_CODE_SUCCESS:
+      //Process Success packet
+      chapProcessSuccess(context, (ChapSuccessPacket *) packet, length);
+      break;
+   //Failure packet?
+   case CHAP_CODE_FAILURE:
+      //Process Failure packet
+      chapProcessFailure(context, (ChapFailurePacket *) packet, length);
+      break;
+   //Unknown code field
+   default:
+      //Silently drop the incoming packet
+      break;
+   }
+}
+
+
+/**
+ * @brief Process Challenge packet
+ * @param[in] context PPP context
+ * @param[in] challengePacket Packet received from the peer
+ * @param[in] length Length of the packet, in bytes
+ * @return Error code
+ **/
+
+error_t chapProcessChallenge(PppContext *context,
+   const ChapChallengePacket *challengePacket, size_t length)
+{
+   size_t n;
+   Md5Context md5Context;
+
+   //Debug message
+   TRACE_INFO("\r\nCHAP Challenge packet received\r\n");
+
+   //Make sure the Challenge packet is acceptable
+   if(context->peerConfig.authProtocol != PPP_PROTOCOL_CHAP)
+      return ERROR_FAILURE;
+
+   //Check the length of the packet
+   if(length < sizeof(ChapChallengePacket))
+      return ERROR_INVALID_LENGTH;
+
+   //Malformed Challenge packet?
+   if(length < (sizeof(ChapChallengePacket) + challengePacket->valueSize))
+      return ERROR_INVALID_LENGTH;
+
+   //Save the Identifier field
+   context->chapFsm.peerIdentifier = challengePacket->identifier;
+
+   //Retrieve the length of the password
+   n = strlen(context->password);
+
+   //The response value is the one-way hash calculated over a stream
+   //of octets consisting of the identifier, followed by the secret,
+   //followed by the challenge value
+   md5Init(&md5Context);
+   md5Update(&md5Context, &challengePacket->identifier, sizeof(uint8_t));
+   md5Update(&md5Context, context->password, n);
+   md5Update(&md5Context, challengePacket->value, challengePacket->valueSize);
+   md5Final(&md5Context, NULL);
+
+   //Whenever a Challenge packet is received, the peer must send a Response packet
+   chapSendResponse(context, md5Context.digest);
+
+   //Switch to the Response-Sent state
+   context->chapFsm.peerState = CHAP_STATE_4_RESPONSE_SENT;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Response packet
+ * @param[in] context PPP context
+ * @param[in] responsePacket Packet received from the peer
+ * @param[in] length Length of the packet, in bytes
+ * @return Error code
+ **/
+
+error_t chapProcessResponse(PppContext *context,
+   const ChapResponsePacket *responsePacket, size_t length)
+{
+   bool_t status;
+   const uint8_t *p;
+
+   //Debug message
+   TRACE_INFO("\r\nCHAP Response packet received\r\n");
+
+   //Make sure the Response packet is acceptable
+   if(context->localConfig.authProtocol != PPP_PROTOCOL_CHAP)
+      return ERROR_FAILURE;
+
+   //Check the length of the packet
+   if(length < sizeof(ChapResponsePacket))
+      return ERROR_INVALID_LENGTH;
+
+   //When a packet is received with an invalid Identifier field, the
+   //packet is silently discarded without affecting the automaton
+   if(responsePacket->identifier != context->chapFsm.localIdentifier)
+      return ERROR_WRONG_IDENTIFIER;
+
+   //Malformed Response packet?
+   if(length < (sizeof(ChapResponsePacket) + responsePacket->valueSize))
+      return ERROR_INVALID_LENGTH;
+
+   //The length of the response value depends upon the hash algorithm used
+   if(responsePacket->valueSize != MD5_DIGEST_SIZE)
+      return ERROR_INVALID_LENGTH;
+
+   //Retrieve the response value
+   context->chapFsm.response = responsePacket->value;
+
+   //Point to the Name field
+   p = responsePacket->value + responsePacket->valueSize;
+   //Retrieve the length of the Name field
+   length -= sizeof(ChapResponsePacket) + responsePacket->valueSize;
+
+   //Limit the length of the string
+   length = MIN(length, PPP_MAX_USERNAME_LEN);
+   //Copy the name of the peer to be identified
+   memcpy(context->peerName, p, length);
+   //Properly terminate the string with a NULL character
+   context->peerName[length] = '\0';
+
+   //Invoke user-defined callback, if any
+   if(context->settings.authCallback != NULL)
+   {
+      //Perfom username and password verification
+      status = context->settings.authCallback(context->interface,
+         context->peerName);
+   }
+   else
+   {
+      //Unable to perform authentication...
+      status = FALSE;
+   }
+
+   //Whenever a Response packet is received, the authenticator compares the
+   //Response Value with its own calculation of the expected value. Based on
+   //this comparison, the authenticator must send a Success or Failure packet
+   if(status)
+   {
+      //Send a Success packet
+      chapSendSuccess(context);
+
+      //Switch to the Success-Sent state
+      context->chapFsm.localState = CHAP_STATE_6_SUCCESS_SENT;
+      //The user has been successfully authenticated
+      context->localAuthDone = TRUE;
+
+      //Check whether PPP authentication is complete
+      if(context->localAuthDone && context->peerAuthDone)
+      {
+         //Check current PPP phase
+         if(context->pppPhase == PPP_PHASE_AUTHENTICATE)
+         {
+            //Advance to the Network phase
+            context->pppPhase = PPP_PHASE_NETWORK;
+
+#if (IPV4_SUPPORT == ENABLED)
+            //IPCP Open event
+            ipcpOpen(context);
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+            //IPV6CP Open event
+            ipv6cpOpen(context);
+#endif
+         }
+      }
+   }
+   else
+   {
+      //Send a Failure packet
+      chapSendFailure(context);
+
+      //Switch to the Failure-Sent state
+      context->chapFsm.localState = CHAP_STATE_8_FAILURE_SENT;
+      //The authenticator should take action to terminate the link
+      lcpClose(context);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Success packet
+ * @param[in] context PPP context
+ * @param[in] successPacket Packet received from the peer
+ * @param[in] length Length of the packet, in bytes
+ * @return Error code
+ **/
+
+error_t chapProcessSuccess(PppContext *context,
+   const ChapSuccessPacket *successPacket, size_t length)
+{
+   //Debug message
+   TRACE_INFO("\r\nCHAP Success packet received\r\n");
+
+   //Make sure the Success packet is acceptable
+   if(context->peerConfig.authProtocol != PPP_PROTOCOL_CHAP)
+      return ERROR_FAILURE;
+
+   //Check the length of the packet
+   if(length < sizeof(ChapSuccessPacket))
+      return ERROR_INVALID_LENGTH;
+
+   //When a packet is received with an invalid Identifier field, the
+   //packet is silently discarded without affecting the automaton
+   if(successPacket->identifier != context->chapFsm.peerIdentifier)
+      return ERROR_WRONG_IDENTIFIER;
+
+   //Switch to the Success-Rcvd state
+   context->chapFsm.peerState = CHAP_STATE_7_SUCCESS_RCVD;
+   //The user name has been accepted by the authenticator
+   context->peerAuthDone = TRUE;
+
+   //Check whether PPP authentication is complete
+   if(context->localAuthDone && context->peerAuthDone)
+   {
+      //Check current PPP phase
+      if(context->pppPhase == PPP_PHASE_AUTHENTICATE)
+      {
+         //Advance to the Network phase
+         context->pppPhase = PPP_PHASE_NETWORK;
+
+#if (IPV4_SUPPORT == ENABLED)
+         //IPCP Open event
+         ipcpOpen(context);
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+         //IPV6CP Open event
+         ipv6cpOpen(context);
+#endif
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Failure packet
+ * @param[in] context PPP context
+ * @param[in] failurePacket Packet received from the peer
+ * @param[in] length Length of the packet, in bytes
+ * @return Error code
+ **/
+
+error_t chapProcessFailure(PppContext *context,
+   const ChapFailurePacket *failurePacket, size_t length)
+{
+   //Debug message
+   TRACE_INFO("\r\nCHAP Failure packet received\r\n");
+
+   //Make sure the Failure packet is acceptable
+   if(context->peerConfig.authProtocol != PPP_PROTOCOL_CHAP)
+      return ERROR_FAILURE;
+
+   //Check the length of the packet
+   if(length < sizeof(ChapFailurePacket))
+      return ERROR_INVALID_LENGTH;
+
+   //When a packet is received with an invalid Identifier field, the
+   //packet is silently discarded without affecting the automaton
+   if(failurePacket->identifier != context->chapFsm.peerIdentifier)
+      return ERROR_WRONG_IDENTIFIER;
+
+   //Switch to the Failure-Rcvd state
+   context->chapFsm.peerState = CHAP_STATE_9_FAILURE_RCVD;
+   //Authentication failed
+   lcpClose(context);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Send Challenge packet
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t chapSendChallenge(PppContext *context)
+{
+   error_t error;
+   size_t n;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   ChapChallengePacket *challengePacket;
+
+   //Retrieve the length of the username
+   n = strlen(context->username);
+   //Calculate the length of the Challenge packet
+   length = sizeof(ChapChallengePacket) + MD5_DIGEST_SIZE + n;
+
+   //Allocate a buffer memory to hold the Challenge packet
+   buffer = pppAllocBuffer(length, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the Challenge packet
+   challengePacket = netBufferAt(buffer, offset);
+
+   //Format packet header
+   challengePacket->code = CHAP_CODE_CHALLENGE;
+   challengePacket->identifier = ++context->chapFsm.localIdentifier;
+   challengePacket->length = htons(length);
+   challengePacket->valueSize = MD5_DIGEST_SIZE;
+
+   //Make sure that the callback function has been registered
+   if(context->settings.randCallback != NULL)
+   {
+      //Generate a random challenge value
+      error = context->settings.randCallback(
+         context->chapFsm.challenge, MD5_DIGEST_SIZE);
+   }
+   else
+   {
+      //Report an error
+      error = ERROR_FAILURE;
+   }
+
+   //Check status code
+   if(!error)
+   {
+      //Copy the challenge value
+      memcpy(challengePacket->value, context->chapFsm.challenge, MD5_DIGEST_SIZE);
+
+      //The Name field is one or more octets representing the
+      //identification of the system transmitting the packet
+      memcpy(challengePacket->value + MD5_DIGEST_SIZE, context->username, n);
+
+      //Debug message
+      TRACE_INFO("Sending CHAP Challenge packet (%" PRIuSIZE " bytes)...\r\n", length);
+      //Dump packet contents for debugging purpose
+      pppDumpPacket((PppPacket *) challengePacket, length, PPP_PROTOCOL_CHAP);
+
+      //Send PPP frame
+      error = pppSendFrame(context->interface, buffer, offset, PPP_PROTOCOL_CHAP);
+
+      //The restart counter is decremented each time a Challenge packet is sent
+      if(context->chapFsm.restartCounter > 0)
+         context->chapFsm.restartCounter--;
+
+      //Save the time at which the packet was sent
+      context->chapFsm.timestamp = osGetSystemTime();
+   }
+
+   //Free previously allocated memory block
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send Response packet
+ * @param[in] context PPP context
+ * @param[in] value Response value
+ * @return Error code
+ **/
+
+error_t chapSendResponse(PppContext *context, const uint8_t *value)
+{
+   error_t error;
+   size_t n;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   ChapResponsePacket *responsePacket;
+
+   //Retrieve the length of the username
+   n = strlen(context->username);
+   //Calculate the length of the Response packet
+   length = sizeof(ChapResponsePacket) + MD5_DIGEST_SIZE + n;
+
+   //Allocate a buffer memory to hold the Response packet
+   buffer = pppAllocBuffer(length, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the Response packet
+   responsePacket = netBufferAt(buffer, offset);
+
+   //Format packet header
+   responsePacket->code = CHAP_CODE_RESPONSE;
+   responsePacket->identifier = context->chapFsm.peerIdentifier;
+   responsePacket->length = htons(length);
+   responsePacket->valueSize = MD5_DIGEST_SIZE;
+
+   //Copy the Response value
+   memcpy(responsePacket->value, value, MD5_DIGEST_SIZE);
+
+   //The Name field is one or more octets representing the
+   //identification of the system transmitting the packet
+   memcpy(responsePacket->value + MD5_DIGEST_SIZE, context->username, n);
+
+   //Debug message
+   TRACE_INFO("Sending CHAP Response packet (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump packet contents for debugging purpose
+   pppDumpPacket((PppPacket *) responsePacket, length, PPP_PROTOCOL_CHAP);
+
+   //Send PPP frame
+   error = pppSendFrame(context->interface, buffer, offset, PPP_PROTOCOL_CHAP);
+
+   //Free previously allocated memory block
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send Success packet
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t chapSendSuccess(PppContext *context)
+{
+   error_t error;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   PppPacket *successPacket;
+
+   //Retrieve the length of the Success packet
+   length = sizeof(PppPacket);
+
+   //Allocate a buffer memory to hold the Success packet
+   buffer = pppAllocBuffer(length, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the Success packet
+   successPacket = netBufferAt(buffer, offset);
+
+   //Format packet header
+   successPacket->code = CHAP_CODE_SUCCESS;
+   successPacket->identifier = context->chapFsm.localIdentifier;
+   successPacket->length = htons(length);
+
+   //Debug message
+   TRACE_INFO("Sending CHAP Success packet (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump packet contents for debugging purpose
+   pppDumpPacket((PppPacket *) successPacket, length, PPP_PROTOCOL_CHAP);
+
+   //Send PPP frame
+   error = pppSendFrame(context->interface, buffer, offset, PPP_PROTOCOL_CHAP);
+
+   //Free previously allocated memory block
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send Failure packet
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t chapSendFailure(PppContext *context)
+{
+   error_t error;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   PppPacket *failurePacket;
+
+   //Retrieve the length of the Failure packet
+   length = sizeof(PppPacket);
+
+   //Allocate a buffer memory to hold the Failure packet
+   buffer = pppAllocBuffer(length, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the Failure packet
+   failurePacket = netBufferAt(buffer, offset);
+
+   //Format packet header
+   failurePacket->code = CHAP_CODE_FAILURE;
+   failurePacket->identifier = context->chapFsm.localIdentifier;
+   failurePacket->length = htons(length);
+
+   //Debug message
+   TRACE_INFO("Sending CHAP Failure packet (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump packet contents for debugging purpose
+   pppDumpPacket((PppPacket *) failurePacket, length, PPP_PROTOCOL_CHAP);
+
+   //Send PPP frame
+   error = pppSendFrame(context->interface, buffer, offset, PPP_PROTOCOL_CHAP);
+
+   //Free previously allocated memory block
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Password verification
+ * @param[in] context PPP context
+ * @param[in] password NULL-terminated string containing the password to be checked
+ * @return TRUE if the password is valid, else FALSE
+ **/
+
+bool_t chapCheckPassword(PppContext *context, const char_t *password)
+{
+   size_t n;
+   Md5Context md5Context;
+
+   //Retrieve the length of the password
+   n = strlen(password);
+
+   //The response value is the one-way hash calculated over a stream
+   //of octets consisting of the identifier, followed by the secret,
+   //followed by the challenge value
+   md5Init(&md5Context);
+   md5Update(&md5Context, &context->chapFsm.localIdentifier, sizeof(uint8_t));
+   md5Update(&md5Context, password, n);
+   md5Update(&md5Context, context->chapFsm.challenge, MD5_DIGEST_SIZE);
+   md5Final(&md5Context, NULL);
+
+   //Check the resulting digest value
+   if(!memcmp(md5Context.digest, context->chapFsm.response, MD5_DIGEST_SIZE))
+      return TRUE;
+   else
+      return FALSE;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/chap.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,214 @@
+/**
+ * @file chap.h
+ * @brief CHAP (Challenge Handshake Authentication Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _CHAP_H
+#define _CHAP_H
+
+//Dependencies
+#include "core/net.h"
+#include "ppp/ppp.h"
+
+//CHAP authentication support
+#ifndef CHAP_SUPPORT
+   #define CHAP_SUPPORT DISABLED
+#elif (CHAP_SUPPORT != ENABLED && CHAP_SUPPORT != DISABLED)
+   #error CHAP_SUPPORT parameter is not valid
+#endif
+
+//Restart timer
+#ifndef CHAP_RESTART_TIMER
+   #define CHAP_RESTART_TIMER 3000
+#elif (CHAP_RESTART_TIMER < 1000)
+   #error CHAP_RESTART_TIMER parameter is not valid
+#endif
+
+//Maximum number of retransmissions for Challenge packets
+#ifndef CHAP_MAX_CHALLENGES
+   #define CHAP_MAX_CHALLENGES 5
+#elif (CHAP_MAX_CHALLENGES < 1)
+   #error CHAP_MAX_CHALLENGES parameter is not valid
+#endif
+
+
+/**
+ * @brief CHAP states
+ **/
+
+typedef enum
+{
+   CHAP_STATE_0_INITIAL        = 0,
+   CHAP_STATE_1_STARTED        = 1,
+   CHAP_STATE_2_CHALLENGE_SENT = 2,
+   CHAP_STATE_3_CHALLENGE_RCVD = 3,
+   CHAP_STATE_4_RESPONSE_SENT  = 4,
+   CHAP_STATE_5_RESPONSE_RCVD  = 5,
+   CHAP_STATE_6_SUCCESS_SENT   = 6,
+   CHAP_STATE_7_SUCCESS_RCVD   = 7,
+   CHAP_STATE_8_FAILURE_SENT   = 8,
+   CHAP_STATE_9_FAILURE_RCVD   = 9
+} ChapState;
+
+
+/**
+ * @brief Code field values
+ **/
+
+typedef enum
+{
+   CHAP_CODE_CHALLENGE = 1, ///<Challenge
+   CHAP_CODE_RESPONSE  = 2, ///<Response
+   CHAP_CODE_SUCCESS   = 3, ///<Success
+   CHAP_CODE_FAILURE   = 4  ///<Failure
+} ChapCode;
+
+
+/**
+ * @brief CHAP algorithm identifiers
+ **/
+
+typedef enum
+{
+   CHAP_ALGO_ID_CHAP_MD5   = 5,   //CHAP with MD5
+   CHAP_ALGO_ID_MS_CHAP    = 128, //MS-CHAP
+   CHAP_ALGO_ID_MS_CHAP_V2 = 129  //MS-CHAP-2
+} ChapAlgoId;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief Challenge packet
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t code;       //0
+   uint8_t identifier; //1
+   uint16_t length;    //2-3
+   uint8_t valueSize;  //4
+   uint8_t value[];    //5
+} __end_packed ChapChallengePacket;
+
+
+/**
+ * @brief Response packet
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t code;       //0
+   uint8_t identifier; //1
+   uint16_t length;    //2-3
+   uint8_t valueSize;  //4
+   uint8_t value[];    //5
+} __end_packed ChapResponsePacket;
+
+
+/**
+ * @brief Success packet
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t code;       //0
+   uint8_t identifier; //1
+   uint16_t length;    //2-3
+   uint8_t message[];  //4
+} __end_packed ChapSuccessPacket;
+
+
+/**
+ * @brief Failure packet
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t code;       //0
+   uint8_t identifier; //1
+   uint16_t length;    //2-3
+   uint8_t message[];  //4
+} __end_packed ChapFailurePacket;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+/**
+ * @brief CHAP finite state machine
+ **/
+
+typedef struct
+{
+   uint_t localState;       ///<Local state
+   uint8_t localIdentifier; ///<Identifier used to match requests and replies
+   uint_t peerState;        ///<Peer state
+   uint8_t peerIdentifier;  ///<Identifier used to match requests and replies
+   uint_t restartCounter;   ///<Restart counter
+   systime_t timestamp;     ///<Timestamp to manage retransmissions
+   uint8_t challenge[16];   ///<Challenge value sent to the peer
+   const uint8_t *response; ///<Response value from the peer
+} ChapFsm;
+
+
+//CHAP related functions
+error_t chapStartAuth(PppContext *context);
+error_t chapAbortAuth(PppContext *context);
+
+void chapTick(PppContext *context);
+
+void chapProcessPacket(PppContext *context,
+   const PppPacket *packet, size_t length);
+
+error_t chapProcessChallenge(PppContext *context,
+   const ChapChallengePacket *challengePacket, size_t length);
+
+error_t chapProcessResponse(PppContext *context,
+   const ChapResponsePacket *responsePacket, size_t length);
+
+error_t chapProcessSuccess(PppContext *context,
+   const ChapSuccessPacket *successPacket, size_t length);
+
+error_t chapProcessFailure(PppContext *context,
+   const ChapFailurePacket *failurePacket, size_t length);
+
+error_t chapSendChallenge(PppContext *context);
+error_t chapSendResponse(PppContext *context, const uint8_t *value);
+error_t chapSendSuccess(PppContext *context);
+error_t chapSendFailure(PppContext *context);
+
+bool_t chapCheckPassword(PppContext *context, const char_t *password);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/ipcp.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1088 @@
+/**
+ * @file ipcp.c
+ * @brief IPCP (PPP Internet Protocol Control Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL PPP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "ipv4/ipv4.h"
+#include "ppp/ppp_fsm.h"
+#include "ppp/ppp_misc.h"
+#include "ppp/ppp_debug.h"
+#include "ppp/ipcp.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (PPP_SUPPORT == ENABLED && IPV4_SUPPORT == ENABLED)
+
+
+/**
+ * @brief IPCP FSM callbacks
+ **/
+
+const PppCallbacks ipcpCallbacks =
+{
+   ipcpThisLayerUp,
+   ipcpThisLayerDown,
+   ipcpThisLayerStarted,
+   ipcpThisLayerFinished,
+   ipcpInitRestartCount,
+   ipcpZeroRestartCount,
+   ipcpSendConfigureReq,
+   ipcpSendConfigureAck,
+   ipcpSendConfigureNak,
+   ipcpSendConfigureRej,
+   ipcpSendTerminateReq,
+   ipcpSendTerminateAck,
+   ipcpSendCodeRej,
+   NULL
+};
+
+
+/**
+ * @brief IPCP Open event
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t ipcpOpen(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("\r\nIPCP Open event\r\n");
+
+   //The link is administratively available for traffic
+   pppOpenEvent(context, &context->ipcpFsm, &ipcpCallbacks);
+   //The lower layer is ready to carry packets
+   pppUpEvent(context, &context->ipcpFsm, &ipcpCallbacks);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief IPCP Close event
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t ipcpClose(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("\r\nIPCP Close event\r\n");
+
+   //The lower layer is no longer ready to carry packets
+   pppDownEvent(context, &context->ipcpFsm, &ipcpCallbacks);
+   //The link is no longer available for traffic
+   pppCloseEvent(context, &context->ipcpFsm, &ipcpCallbacks);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief IPCP timer handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * manage retransmissions
+ *
+ * @param[in] context PPP context
+ **/
+
+void ipcpTick(PppContext *context)
+{
+   //Check whether the restart timer is running
+   if(context->ipcpFsm.state >= PPP_STATE_4_CLOSING &&
+      context->ipcpFsm.state <= PPP_STATE_8_ACK_SENT)
+   {
+      //Get current time
+      systime_t time = osGetSystemTime();
+
+      //Check restart timer
+      if((time - context->ipcpFsm.timestamp) >= PPP_RESTART_TIMER)
+      {
+         //Debug message
+         TRACE_INFO("\r\nIPCP Timeout event\r\n");
+
+         //The restart timer is used to retransmit Configure-Request
+         //and Terminate-Request packets
+         pppTimeoutEvent(context, &context->ipcpFsm, &ipcpCallbacks);
+      }
+   }
+}
+
+
+/**
+ * @brief Process an incoming IPCP packet
+ * @param[in] context PPP context
+ * @param[in]  packet IPCP packet received from the peer
+ * @param[in] length Length of the packet, in bytes
+ **/
+
+void ipcpProcessPacket(PppContext *context, const PppPacket *packet, size_t length)
+{
+   //Ensure the length of the incoming IPCP packet is valid
+   if(length < sizeof(PppPacket))
+      return;
+
+   //Check the length field
+   if(ntohs(packet->length) > length)
+      return;
+   if(ntohs(packet->length) < sizeof(PppPacket))
+      return;
+
+   //Save the length of the IPCP packet
+   length = ntohs(packet->length);
+
+   //Debug message
+   TRACE_INFO("IPCP packet received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump IPCP packet contents for debugging purpose
+   pppDumpPacket(packet, length, PPP_PROTOCOL_IPCP);
+
+   //Check IPCP code field
+   switch(packet->code)
+   {
+   //Configure-Request packet?
+   case PPP_CODE_CONFIGURE_REQ:
+      //Process Configure-Request packet
+      ipcpProcessConfigureReq(context, (PppConfigurePacket *) packet);
+      break;
+   //Configure-Ack packet?
+   case PPP_CODE_CONFIGURE_ACK:
+      //Process Configure-Ack packet
+      ipcpProcessConfigureAck(context, (PppConfigurePacket *) packet);
+      break;
+   //Configure-Nak packet?
+   case PPP_CODE_CONFIGURE_NAK:
+      //Process Configure-Nak packet
+      ipcpProcessConfigureNak(context, (PppConfigurePacket *) packet);
+      break;
+   //Configure-Reject packet?
+   case PPP_CODE_CONFIGURE_REJ:
+      //Process Configure-Reject packet
+      ipcpProcessConfigureReject(context, (PppConfigurePacket *) packet);
+      break;
+   //Terminate-Request packet?
+   case PPP_CODE_TERMINATE_REQ:
+      //Process Terminate-Request packet
+      ipcpProcessTerminateReq(context, (PppTerminatePacket *) packet);
+      break;
+   //Terminate-Ack packet?
+   case PPP_CODE_TERMINATE_ACK:
+      //Process Terminate-Ack packet
+      ipcpProcessTerminateAck(context, (PppTerminatePacket *) packet);
+      break;
+   //Code-Reject packet?
+   case PPP_CODE_CODE_REJ:
+      //Process Code-Reject packet
+      ipcpProcessCodeRej(context, (PppCodeRejPacket *) packet);
+      break;
+   //Unknown code field
+   default:
+      //The packet is un-interpretable
+      ipcpProcessUnknownCode(context, packet);
+      break;
+   }
+}
+
+
+/**
+ * @brief Process Configure-Request packet
+ * @param[in] context PPP context
+ * @param[in] configureReqPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipcpProcessConfigureReq(PppContext *context,
+   const PppConfigurePacket *configureReqPacket)
+{
+   error_t error;
+   size_t length;
+   bool_t notRecognizable;
+   bool_t notAcceptable;
+   PppOption *option;
+
+   //Debug message
+   TRACE_INFO("\r\nIPCP Receive-Configure-Request event\r\n");
+
+   //Initialize variables
+   error = NO_ERROR;
+   notRecognizable = FALSE;
+   notAcceptable = FALSE;
+
+   //Retrieve the length of the option list
+   length = ntohs(configureReqPacket->length) - sizeof(PppConfigurePacket);
+   //Point to the first option
+   option = (PppOption *) configureReqPacket->options;
+
+   //Parse configuration options
+   while(length > 0)
+   {
+      //Parse current option
+      error = ipcpParseOption(context, option, length, NULL);
+
+      //Any error to report?
+      if(error == ERROR_INVALID_TYPE)
+      {
+         //Option not recognizable
+         notRecognizable = TRUE;
+         //Catch error
+         error = NO_ERROR;
+      }
+      else if(error == ERROR_INVALID_VALUE)
+      {
+         //Option not acceptable for configuration
+         notAcceptable = TRUE;
+         //Catch error
+         error = NO_ERROR;
+      }
+      else if(error)
+      {
+         //Malformed Configure-Request packet
+         break;
+      }
+
+      //Remaining bytes to process
+      length -= option->length;
+      //Jump to the next option
+      option = (PppOption *) ((uint8_t *) option + option->length);
+   }
+
+   //Valid Configure-Request packet received from the peer?
+   if(!error)
+   {
+      //Check flags
+      if(notRecognizable)
+      {
+         //If some configuration options received in the Configure-Request are not
+         //recognizable or not acceptable for negotiation, then the implementation
+         //must transmit a Configure-Reject
+         pppRcvConfigureReqEvent(context, &context->ipcpFsm, &ipcpCallbacks,
+            configureReqPacket, PPP_CODE_CONFIGURE_REJ);
+      }
+      else if(notAcceptable)
+      {
+         //If all configuration options are recognizable, but some values are not
+         //acceptable, then the implementation must transmit a Configure-Nak
+         pppRcvConfigureReqEvent(context, &context->ipcpFsm, &ipcpCallbacks,
+            configureReqPacket, PPP_CODE_CONFIGURE_NAK);
+      }
+      else
+      {
+         //If every configuration option received in the Configure-Request is
+         //recognizable and all values are acceptable, then the implementation
+         //must transmit a Configure-Ack
+         pppRcvConfigureReqEvent(context, &context->ipcpFsm, &ipcpCallbacks,
+            configureReqPacket, PPP_CODE_CONFIGURE_ACK);
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process Configure-Ack packet
+ * @param[in] context PPP context
+ * @param[in] configureAckPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipcpProcessConfigureAck(PppContext *context,
+   const PppConfigurePacket *configureAckPacket)
+{
+   //Debug message
+   TRACE_INFO("\r\nIPCP Receive-Configure-Ack event\r\n");
+
+   //When a packet is received with an invalid Identifier field, the
+   //packet is silently discarded without affecting the automaton
+   if(configureAckPacket->identifier != context->ipcpFsm.identifier)
+      return ERROR_WRONG_IDENTIFIER;
+
+   //A valid Configure-Ack packet has been received from the peer
+   pppRcvConfigureAckEvent(context, &context->ipcpFsm, &ipcpCallbacks);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Configure-Nak packet
+ * @param[in] context PPP context
+ * @param[in] configureNakPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipcpProcessConfigureNak(PppContext *context,
+   const PppConfigurePacket *configureNakPacket)
+{
+   size_t length;
+   PppOption *option;
+
+   //Debug message
+   TRACE_INFO("IPCP Receive-Configure-Nak event\r\n");
+
+   //When a packet is received with an invalid Identifier field, the
+   //packet is silently discarded without affecting the automaton
+   if(configureNakPacket->identifier != context->ipcpFsm.identifier)
+      return ERROR_WRONG_IDENTIFIER;
+
+   //Retrieve the length of the option list
+   length = ntohs(configureNakPacket->length) - sizeof(PppConfigurePacket);
+   //Point to the first option
+   option = (PppOption *) configureNakPacket->options;
+
+   //Parse configuration options
+   while(length > 0)
+   {
+      //Check option length
+      if(option->length < sizeof(PppOption))
+         return ERROR_INVALID_LENGTH;
+      if(option->length > length)
+         return ERROR_INVALID_LENGTH;
+
+      //IP-Address option?
+      if(option->type == IPCP_OPTION_IP_ADDRESS)
+      {
+         //Cast option
+         IpcpIpAddressOption *ipAddressOption = (IpcpIpAddressOption *) option;
+
+         //Check option length
+         if(ipAddressOption->length != sizeof(IpcpIpAddressOption))
+            return ERROR_INVALID_LENGTH;
+
+         //Save IP address
+         context->localConfig.ipAddr = ipAddressOption->ipAddr;
+      }
+      //Primary-DNS-Server-Address option?
+      else if(option->type == IPCP_OPTION_PRIMARY_DNS)
+      {
+         //Cast option
+         IpcpPrimaryDnsOption *primaryDns = (IpcpPrimaryDnsOption *) option;
+
+         //Check option length
+         if(primaryDns->length != sizeof(IpcpPrimaryDnsOption))
+            return ERROR_INVALID_LENGTH;
+
+         //Save primary DNS server address
+         context->localConfig.primaryDns = primaryDns->ipAddr;
+      }
+      //Secondary-DNS-Server-Address option?
+      else if(option->type == IPCP_OPTION_SECONDARY_DNS)
+      {
+         //Cast option
+         IpcpSecondaryDnsOption *secondaryDns = (IpcpSecondaryDnsOption *) option;
+
+         //Check option length
+         if(secondaryDns->length != sizeof(IpcpSecondaryDnsOption))
+            return ERROR_INVALID_LENGTH;
+
+         //Save secondary DNS server address
+         context->localConfig.secondaryDns = secondaryDns->ipAddr;
+      }
+
+      //Remaining bytes to process
+      length -= option->length;
+      //Jump to the next option
+      option = (PppOption *) ((uint8_t *) option + option->length);
+   }
+
+   //A valid Configure-Nak or Configure-Reject packet has been received from the peer
+   pppRcvConfigureNakEvent(context, &context->ipcpFsm, &ipcpCallbacks);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Configure-Reject packet
+ * @param[in] context PPP context
+ * @param[in] configureRejPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipcpProcessConfigureReject(PppContext *context,
+   const PppConfigurePacket *configureRejPacket)
+{
+   size_t length;
+   PppOption *option;
+
+   //Debug message
+   TRACE_INFO("\r\nIPCP Receive-Configure-Reject event\r\n");
+
+   //When a packet is received with an invalid Identifier field, the
+   //packet is silently discarded without affecting the automaton
+   if(configureRejPacket->identifier != context->ipcpFsm.identifier)
+      return ERROR_WRONG_IDENTIFIER;
+
+   //Retrieve the length of the option list
+   length = ntohs(configureRejPacket->length) - sizeof(PppConfigurePacket);
+   //Point to the first option
+   option = (PppOption *) configureRejPacket->options;
+
+   //Parse configuration options
+   while(length > 0)
+   {
+      //Check option length
+      if(option->length < sizeof(PppOption))
+         return ERROR_INVALID_LENGTH;
+      if(option->length > length)
+         return ERROR_INVALID_LENGTH;
+
+      //IP-Address option?
+      if(option->type == IPCP_OPTION_IP_ADDRESS)
+      {
+         //The option is not recognized by the peer
+         context->localConfig.ipAddrRejected = TRUE;
+      }
+      //Primary-DNS-Server-Address option?
+      else if(option->type == IPCP_OPTION_PRIMARY_DNS)
+      {
+         //The option is not recognized by the peer
+         context->localConfig.primaryDnsRejected = TRUE;
+      }
+      //Secondary-DNS-Server-Address option?
+      else if(option->type == IPCP_OPTION_SECONDARY_DNS)
+      {
+         //The option is not recognized by the peer
+         context->localConfig.secondaryDnsRejected = TRUE;
+      }
+
+      //Remaining bytes to process
+      length -= option->length;
+      //Jump to the next option
+      option = (PppOption *) ((uint8_t *) option + option->length);
+   }
+
+   //A valid Configure-Nak or Configure-Reject packet has been received from the peer
+   pppRcvConfigureNakEvent(context, &context->ipcpFsm, &ipcpCallbacks);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Terminate-Request packet
+ * @param[in] context PPP context
+ * @param[in] terminateReqPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipcpProcessTerminateReq(PppContext *context,
+   const PppTerminatePacket *terminateReqPacket)
+{
+   //Debug message
+   TRACE_INFO("\r\nIPCP Receive-Terminate-Request event\r\n");
+
+   //The Terminate-Request indicates the desire of the peer to close the connection
+   pppRcvTerminateReqEvent(context, &context->ipcpFsm,
+      &ipcpCallbacks, terminateReqPacket);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Terminate-Ack packet
+ * @param[in] context PPP context
+ * @param[in] terminateAckPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipcpProcessTerminateAck(PppContext *context,
+   const PppTerminatePacket *terminateAckPacket)
+{
+   //Debug message
+   TRACE_INFO("\r\nIPCP Receive-Terminate-Ack event\r\n");
+
+   //The Terminate-Ack packet is usually a response to a Terminate-Request
+   //packet. This packet may also indicate that the peer is in Closed or
+   //Stopped states, and serves to re-synchronize the link configuration
+   pppRcvTerminateAckEvent(context, &context->ipcpFsm, &ipcpCallbacks);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Code-Reject packet
+ * @param[in] context PPP context
+ * @param[in] codeRejPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipcpProcessCodeRej(PppContext *context,
+   const PppCodeRejPacket *codeRejPacket)
+{
+   size_t length;
+   PppPacket *packet;
+
+   //Debug message
+   TRACE_INFO("\r\nIPCP Receive-Code-Reject event\r\n");
+
+   //Point to the rejected packet
+   packet = (PppPacket *) codeRejPacket->rejectedPacket;
+   //Retrieve the length of the rejected packet
+   length = ntohs(codeRejPacket->length) - sizeof(PppCodeRejPacket);
+
+   //Make sure the length of the rejected packet is valid
+   if(length < sizeof(PppPacket))
+      return ERROR_INVALID_LENGTH;
+
+   //Check whether the rejected value is acceptable or catastrophic
+   if(packet->code < PPP_CODE_CONFIGURE_REQ ||
+      packet->code > PPP_CODE_CODE_REJ)
+   {
+      //The RXJ+ event arises when the rejected value is acceptable, such
+      //as a Code-Reject of an extended code, or a Protocol-Reject of a
+      //NCP. These are within the scope of normal operation
+      pppRcvCodeRejEvent(context, &context->ipcpFsm, &ipcpCallbacks, TRUE);
+   }
+   else
+   {
+      //The RXJ- event arises when the rejected value is catastrophic, such
+      //as a Code-Reject of Configure-Request! This event communicates an
+      //unrecoverable error that terminates the connection
+      pppRcvCodeRejEvent(context, &context->ipcpFsm, &ipcpCallbacks, FALSE);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process packet with unknown code
+ * @param[in] context PPP context
+ * @param[in] packet Un-interpretable packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipcpProcessUnknownCode(PppContext *context,
+   const PppPacket *packet)
+{
+   //Debug message
+   TRACE_INFO("\r\nIPCP Receive-Unknown-Code event\r\n");
+
+   //This event occurs when an un-interpretable packet is received from
+   //the peer. A Code-Reject packet is sent in response
+   pppRcvUnknownCodeEvent(context, &context->ipcpFsm, &ipcpCallbacks, packet);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief This-Layer-Up callback function
+ * @param[in] context PPP context
+ **/
+
+void ipcpThisLayerUp(PppContext *context)
+{
+   NetInterface *interface;
+
+   //Debug message
+   TRACE_INFO("IPCP This-Layer-Up callback\r\n");
+
+   //Debug message
+   TRACE_INFO("  Local IP Addr = %s\r\n", ipv4AddrToString(context->localConfig.ipAddr, NULL));
+   TRACE_INFO("  Peer IP Addr = %s\r\n", ipv4AddrToString(context->peerConfig.ipAddr, NULL));
+   TRACE_INFO("  Primary DNS = %s\r\n", ipv4AddrToString(context->localConfig.primaryDns, NULL));
+   TRACE_INFO("  Secondary DNS = %s\r\n", ipv4AddrToString(context->localConfig.secondaryDns, NULL));
+
+   //Point to the underlying interface
+   interface = context->interface;
+
+   //Update IPv4 configuration
+   interface->ipv4Context.addr = context->localConfig.ipAddr;
+   interface->ipv4Context.addrState = IPV4_ADDR_STATE_VALID;
+   interface->ipv4Context.defaultGateway = context->peerConfig.ipAddr;
+
+   //Update the list of DNS servers
+   interface->ipv4Context.dnsServerList[0] = context->localConfig.primaryDns;
+#if (IPV4_DNS_SERVER_LIST_SIZE >= 2)
+   interface->ipv4Context.dnsServerList[1] = context->localConfig.secondaryDns;
+#endif
+
+   //All the outgoing traffic will be routed to the other end of the link
+   interface->ipv4Context.subnetMask = IPCP_DEFAULT_SUBNET_MASK;
+
+   //Link is up
+   interface->linkState = TRUE;
+
+   //Disable interrupts
+   interface->nicDriver->disableIrq(interface);
+   //Process link state change event
+   nicNotifyLinkChange(interface);
+   //Re-enable interrupts
+   interface->nicDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief This-Layer-Down callback function
+ * @param[in] context PPP context
+ **/
+
+void ipcpThisLayerDown(PppContext *context)
+{
+   NetInterface *interface;
+
+   //Debug message
+   TRACE_INFO("IPCP This-Layer-Down callback\r\n");
+
+   //Point to the underlying interface
+   interface = context->interface;
+
+   //Link is up
+   interface->linkState = FALSE;
+
+   //Disable interrupts
+   interface->nicDriver->disableIrq(interface);
+   //Process link state change event
+   nicNotifyLinkChange(interface);
+   //Re-enable interrupts
+   interface->nicDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief This-Layer-Started callback function
+ * @param[in] context PPP context
+ **/
+
+void ipcpThisLayerStarted(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("IPCP This-Layer-Started callback\r\n");
+}
+
+
+/**
+ * @brief This-Layer-Finished callback function
+ * @param[in] context PPP context
+ **/
+
+void ipcpThisLayerFinished(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("IPCP This-Layer-Finished callback\r\n");
+}
+
+
+/**
+ * @brief Initialize-Restart-Count callback function
+ * @param[in] context PPP context
+ * @param[in] value Restart counter value
+ **/
+
+void ipcpInitRestartCount(PppContext *context, uint_t value)
+{
+   //Debug message
+   TRACE_INFO("IPCP Initialize-Restart-Count callback\r\n");
+
+   //Initialize restart counter
+   context->ipcpFsm.restartCounter = value;
+}
+
+
+/**
+ * @brief Zero-Restart-Count callback function
+ * @param[in] context PPP context
+ **/
+
+void ipcpZeroRestartCount(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("IPCP Zero-Restart-Count callback\r\n");
+
+   //Zero restart counter
+   context->ipcpFsm.restartCounter = 0;
+
+   //The receiver of a Terminate-Request should wait for the peer to
+   //disconnect, and must not disconnect until at least one Restart
+   //time has passed after sending a Terminate-Ack
+   context->ipcpFsm.timestamp = osGetSystemTime();
+}
+
+
+/**
+ * @brief Send-Configure-Request callback function
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t ipcpSendConfigureReq(PppContext *context)
+{
+   error_t error;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   PppConfigurePacket *configureReqPacket;
+
+   //Debug message
+   TRACE_INFO("IPCP Send-Configure-Request callback\r\n");
+
+   //Allocate a buffer memory to hold the Configure-Request packet
+   buffer = pppAllocBuffer(PPP_MAX_CONF_REQ_SIZE, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the Configure-Request packet
+   configureReqPacket = netBufferAt(buffer, offset);
+
+   //Format packet header
+   configureReqPacket->code = PPP_CODE_CONFIGURE_REQ;
+   configureReqPacket->identifier = ++context->ipcpFsm.identifier;
+   configureReqPacket->length = sizeof(PppConfigurePacket);
+
+   //Make sure the IP-Address option has not been previously rejected
+   if(!context->localConfig.ipAddrRejected)
+   {
+      //Add option
+      pppAddOption(configureReqPacket, IPCP_OPTION_IP_ADDRESS,
+         &context->localConfig.ipAddr, sizeof(Ipv4Addr));
+   }
+
+   //Make sure the Primary-DNS-Server-Address option has not been
+   //previously rejected
+   if(!context->localConfig.primaryDnsRejected)
+   {
+      //Add option
+      pppAddOption(configureReqPacket, IPCP_OPTION_PRIMARY_DNS,
+         &context->localConfig.primaryDns, sizeof(Ipv4Addr));
+   }
+
+   //Make sure the Secondary-DNS-Server-Address option has not been
+   //previously rejected
+   if(!context->localConfig.secondaryDnsRejected)
+   {
+      //Add option
+      pppAddOption(configureReqPacket, IPCP_OPTION_SECONDARY_DNS,
+         &context->localConfig.secondaryDns, sizeof(Ipv4Addr));
+   }
+
+   //Save packet length
+   length = configureReqPacket->length;
+   //Convert length field to network byte order
+   configureReqPacket->length = htons(length);
+
+   //Adjust the length of the multi-part buffer
+   netBufferSetLength(buffer, offset + length);
+
+   //Debug message
+   TRACE_INFO("Sending Configure-Request packet (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump packet contents for debugging purpose
+   pppDumpPacket((PppPacket *) configureReqPacket, length, PPP_PROTOCOL_IPCP);
+
+   //Send PPP frame
+   error = pppSendFrame(context->interface, buffer, offset, PPP_PROTOCOL_IPCP);
+
+   //The restart counter is decremented each time a Configure-Request is sent
+   if(context->ipcpFsm.restartCounter > 0)
+      context->ipcpFsm.restartCounter--;
+
+   //Save the time at which the packet was sent
+   context->ipcpFsm.timestamp = osGetSystemTime();
+
+   //Free previously allocated memory block
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send-Configure-Ack callback function
+ * @param[in] context PPP context
+ * @param[in] configureReqPacket Configure-Request packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipcpSendConfigureAck(PppContext *context,
+   const PppConfigurePacket *configureReqPacket)
+{
+   //Debug message
+   TRACE_INFO("IPCP Send-Configure-Ack callback\r\n");
+
+   //Send Configure-Ack packet
+   return pppSendConfigureAckNak(context, configureReqPacket,
+      PPP_PROTOCOL_IPCP, PPP_CODE_CONFIGURE_ACK);
+}
+
+
+/**
+ * @brief Send-Configure-Nak callback function
+ * @param[in] context PPP context
+ * @param[in] configureReqPacket Configure-Request packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipcpSendConfigureNak(PppContext *context,
+   const PppConfigurePacket *configureReqPacket)
+{
+   //Debug message
+   TRACE_INFO("IPCP Send-Configure-Nak callback\r\n");
+
+   //Send Configure-Nak packet
+   return pppSendConfigureAckNak(context, configureReqPacket,
+      PPP_PROTOCOL_IPCP, PPP_CODE_CONFIGURE_NAK);
+}
+
+
+/**
+ * @brief Send-Configure-Reject callback function
+ * @param[in] context PPP context
+ * @param[in] configureReqPacket Configure-Request packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipcpSendConfigureRej(PppContext *context,
+   const PppConfigurePacket *configureReqPacket)
+{
+   //Debug message
+   TRACE_INFO("IPCP Send-Configure-Reject callback\r\n");
+
+   //Send Configure-Reject packet
+   return pppSendConfigureAckNak(context, configureReqPacket,
+      PPP_PROTOCOL_IPCP, PPP_CODE_CONFIGURE_REJ);
+}
+
+
+/**
+ * @brief Send-Terminate-Request callback function
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t ipcpSendTerminateReq(PppContext *context)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("IPCP Send-Terminate-Request callback\r\n");
+
+   //On transmission, the Identifier field must be changed
+   context->ipcpFsm.identifier++;
+
+   //Send Terminate-Request packet
+   error = pppSendTerminateReq(context, context->ipcpFsm.identifier, PPP_PROTOCOL_IPCP);
+
+   //The restart counter is decremented each time a Terminate-Request is sent
+   if(context->ipcpFsm.restartCounter > 0)
+      context->ipcpFsm.restartCounter--;
+
+   //Save the time at which the packet was sent
+   context->ipcpFsm.timestamp = osGetSystemTime();
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send-Terminate-Ack callback function
+ * @param[in] context PPP context
+ * @param[in] terminateReqPacket Terminate-Request packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipcpSendTerminateAck(PppContext *context,
+   const PppTerminatePacket *terminateReqPacket)
+{
+   uint8_t identifier;
+
+   //Debug message
+   TRACE_INFO("IPCP Send-Terminate-Ack callback\r\n");
+
+   //Check whether this Terminate-Ack acknowledges the reception of a
+   //Terminate-Request packet
+   if(terminateReqPacket != NULL)
+   {
+      //The Identifier field of the Terminate-Request is copied into the
+      //Identifier field of the Terminate-Ack packet
+      identifier = terminateReqPacket->identifier;
+   }
+   else
+   {
+      //This Terminate-Ack packet serves to synchronize the automatons
+      identifier = ++context->ipcpFsm.identifier;
+   }
+
+   //Send Terminate-Ack packet
+   return pppSendTerminateAck(context, identifier, PPP_PROTOCOL_IPCP);
+}
+
+
+/**
+ * @brief Send-Code-Reject callback function
+ * @param[in] context PPP context
+ * @param[in] packet Un-interpretable packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipcpSendCodeRej(PppContext *context, const PppPacket *packet)
+{
+   //Debug message
+   TRACE_INFO("IPCP Send-Code-Reject callback\r\n");
+
+   //The Identifier field must be changed for each Code-Reject sent
+   context->ipcpFsm.identifier++;
+
+   //Send Code-Reject packet
+   return pppSendCodeRej(context, packet, context->ipcpFsm.identifier, PPP_PROTOCOL_IPCP);
+}
+
+
+/**
+ * @brief Parse IPCP configuration option
+ * @param[in] context PPP context
+ * @param[in] option Option to be checked
+ * @param[in] inPacketLen Remaining bytes to process in the incoming packet
+ * @param[out] outPacket Pointer to the Configure-Ack, Nak or Reject packet
+ * @return Error code
+ **/
+
+error_t ipcpParseOption(PppContext *context, PppOption *option,
+   size_t inPacketLen, PppConfigurePacket *outPacket)
+{
+   error_t error;
+
+   //Malformed IPCP packet?
+   if(inPacketLen < sizeof(PppOption))
+      return ERROR_INVALID_LENGTH;
+
+   //Check option length
+   if(option->length < sizeof(PppOption))
+      return ERROR_INVALID_LENGTH;
+   if(option->length > inPacketLen)
+      return ERROR_INVALID_LENGTH;
+
+   //Check option type
+   switch(option->type)
+   {
+   case IPCP_OPTION_IP_ADDRESS:
+      //Check IP-Address option
+      error = ipcpParseIpAddressOption(context, (IpcpIpAddressOption *) option, outPacket);
+      break;
+   default:
+      //If some configuration options received in the Configure-Request are not
+      //recognizable or not acceptable for negotiation, then the implementation
+      //must transmit a Configure-Reject
+      if(outPacket->code == PPP_CODE_CONFIGURE_REJ)
+      {
+         //The options field of the Configure-Reject packet is filled
+         //with the unrecognized options from the Configure-Request
+         pppAddOption(outPacket, option->type, option->data,
+            option->length - sizeof(PppOption));
+      }
+
+      //The option is not acceptable for negotiation
+      error = ERROR_INVALID_TYPE;
+      break;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse IP-Address option
+ * @param[in] context PPP context
+ * @param[in] option Option to be checked
+ * @param[out] outPacket Pointer to the Configure-Nak or Configure-Reject packet
+ * @return Error code
+ **/
+
+error_t ipcpParseIpAddressOption(PppContext *context,
+   IpcpIpAddressOption *option, PppConfigurePacket *outPacket)
+{
+   error_t error;
+
+   //Check length field
+   if(option->length == sizeof(IpcpIpAddressOption))
+   {
+      //Check whether the option value is acceptable
+      if(option->ipAddr != IPV4_UNSPECIFIED_ADDR)
+      {
+         //If every configuration option received in the Configure-Request is
+         //recognizable and all values are acceptable, then the implementation
+         //must transmit a Configure-Ack
+         if(outPacket != NULL && outPacket->code == PPP_CODE_CONFIGURE_ACK)
+         {
+            //Save IP address
+            context->peerConfig.ipAddr = option->ipAddr;
+
+            //The options field of the Configure-Ack packet contains the
+            //configuration options that the sender is acknowledging
+            pppAddOption(outPacket, IPCP_OPTION_IP_ADDRESS,
+               (void *) &option->ipAddr, option->length - sizeof(PppOption));
+         }
+
+         //The value is acceptable
+         error = NO_ERROR;
+      }
+      else
+      {
+         //If all configuration options are recognizable, but some values are not
+         //acceptable, then the implementation must transmit a Configure-Nak
+         if(outPacket != NULL && outPacket->code == PPP_CODE_CONFIGURE_NAK)
+         {
+            //The option must be modified to a value acceptable to the
+            //Configure-Nak sender
+            pppAddOption(outPacket, IPCP_OPTION_IP_ADDRESS,
+               &context->peerConfig.ipAddr, sizeof(Ipv4Addr));
+         }
+
+         //The value is not acceptable
+         error = ERROR_INVALID_VALUE;
+      }
+   }
+   else
+   {
+      //Invalid length field
+      error = ERROR_INVALID_LENGTH;
+   }
+
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/ipcp.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,221 @@
+/**
+ * @file ipcp.h
+ * @brief IPCP (PPP Internet Protocol Control Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _IPCP_H
+#define _IPCP_H
+
+//Dependencies
+#include "core/net.h"
+#include "ppp/ppp.h"
+
+//Subnet mask
+#define IPCP_DEFAULT_SUBNET_MASK IPV4_ADDR(255, 255, 255, 255)
+
+
+/**
+ * @brief IPCP option types
+ **/
+
+typedef enum
+{
+   IPCP_OPTION_IP_ADDRESSES     = 1,   ///<IP-Addresses
+   IPCP_OPTION_IP_COMP_PROTOCOL = 2,   ///<IP-Compression-Protocol
+   IPCP_OPTION_IP_ADDRESS       = 3,   ///<IP-Address
+   IPCP_OPTION_PRIMARY_DNS      = 129, ///<Primary-DNS-Server-Address
+   IPCP_OPTION_PRIMARY_NBNS     = 130, ///<Primary-NBNS-Server-Address
+   IPCP_OPTION_SECONDARY_DNS    = 131, ///<Secondary-DNS-Server-Address
+   IPCP_OPTION_SECONDARY_NBNS   = 132  ///<Secondary-NBNS-Server-Address
+} IpcpOptionType;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief IP-Addresses option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;        //0
+   uint8_t length;      //1
+   Ipv4Addr srcIpAddr;  //2-5
+   Ipv4Addr destIpAddr; //6-9
+} __end_packed IpcpIpAddressesOption;
+
+
+/**
+ * @brief IP-Compression-Protocol option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;      //0
+   uint8_t length;    //1
+   uint16_t protocol; //2-3
+   uint8_t data[];    //4
+} __end_packed IpcpIpCompProtocolOption;
+
+
+/**
+ * @brief IP-Address option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;    //0
+   uint8_t length;  //1
+   Ipv4Addr ipAddr; //2-5
+} __end_packed IpcpIpAddressOption;
+
+
+/**
+ * @brief Primary-DNS-Server-Address option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;    //0
+   uint8_t length;  //1
+   Ipv4Addr ipAddr; //2-5
+} __end_packed IpcpPrimaryDnsOption;
+
+
+/**
+ * @brief Primary-NBNS-Server-Address option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;    //0
+   uint8_t length;  //1
+   Ipv4Addr ipAddr; //2-5
+} __end_packed IpcpPrimaryNbnsOption;
+
+
+/**
+ * @brief Secondary-DNS-Server-Address option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;    //0
+   uint8_t length;  //1
+   Ipv4Addr ipAddr; //2-5
+} __end_packed IpcpSecondaryDnsOption;
+
+
+/**
+ * @brief Secondary-NBNS-Server-Address option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;    //0
+   uint8_t length;  //1
+   Ipv4Addr ipAddr; //2-5
+} __end_packed IpcpSecondaryNbnsOption;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+//IPCP FSM events
+error_t ipcpOpen(PppContext *context);
+error_t ipcpClose(PppContext *context);
+
+void ipcpTick(PppContext *context);
+
+void ipcpProcessPacket(PppContext *context, const PppPacket *packet, size_t length);
+
+error_t ipcpProcessConfigureReq(PppContext *context,
+   const PppConfigurePacket *configureReqPacket);
+
+error_t ipcpProcessConfigureAck(PppContext *context,
+   const PppConfigurePacket *configureAckPacket);
+
+error_t ipcpProcessConfigureNak(PppContext *context,
+   const PppConfigurePacket *configureNakPacket);
+
+error_t ipcpProcessConfigureReject(PppContext *context,
+   const PppConfigurePacket *configureRejPacket);
+
+error_t ipcpProcessTerminateReq(PppContext *context,
+   const PppTerminatePacket *terminateReqPacket);
+
+error_t ipcpProcessTerminateAck(PppContext *context,
+   const PppTerminatePacket *terminateAckPacket);
+
+error_t ipcpProcessCodeRej(PppContext *context,
+   const PppCodeRejPacket *codeRejPacket);
+
+error_t ipcpProcessUnknownCode(PppContext *context,
+   const PppPacket *packet);
+
+//IPCP FSM callback functions
+void ipcpThisLayerUp(PppContext *context);
+void ipcpThisLayerDown(PppContext *context);
+void ipcpThisLayerStarted(PppContext *context);
+void ipcpThisLayerFinished(PppContext *context);
+
+void ipcpInitRestartCount(PppContext *context, uint_t value);
+void ipcpZeroRestartCount(PppContext *context);
+
+error_t ipcpSendConfigureReq(PppContext *context);
+
+error_t ipcpSendConfigureAck(PppContext *context,
+   const PppConfigurePacket *configureReqPacket);
+
+error_t ipcpSendConfigureNak(PppContext *context,
+   const PppConfigurePacket *configureReqPacket);
+
+error_t ipcpSendConfigureRej(PppContext *context,
+   const PppConfigurePacket *configureReqPacket);
+
+error_t ipcpSendTerminateReq(PppContext *context);
+
+error_t ipcpSendTerminateAck(PppContext *context,
+   const PppTerminatePacket *terminateReqPacket);
+
+error_t ipcpSendCodeRej(PppContext *context, const PppPacket *packet);
+
+//IPCP options checking
+error_t ipcpParseOption(PppContext *context, PppOption *option,
+   size_t inPacketLen, PppConfigurePacket *outPacket);
+
+error_t ipcpParseIpAddressOption(PppContext *context,
+   IpcpIpAddressOption *option, PppConfigurePacket *outPacket);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/ipv6cp.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1034 @@
+/**
+ * @file ipv6cp.c
+ * @brief IPV6CP (PPP IPv6 Control Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL PPP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "ipv6/ipv6.h"
+#include "ipv6/ipv6_misc.h"
+#include "ppp/ppp_fsm.h"
+#include "ppp/ppp_misc.h"
+#include "ppp/ppp_debug.h"
+#include "ppp/ipv6cp.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (PPP_SUPPORT == ENABLED && IPV6_SUPPORT == ENABLED)
+
+
+/**
+ * @brief IPV6CP FSM callbacks
+ **/
+
+const PppCallbacks ipv6cpCallbacks =
+{
+   ipv6cpThisLayerUp,
+   ipv6cpThisLayerDown,
+   ipv6cpThisLayerStarted,
+   ipv6cpThisLayerFinished,
+   ipv6cpInitRestartCount,
+   ipv6cpZeroRestartCount,
+   ipv6cpSendConfigureReq,
+   ipv6cpSendConfigureAck,
+   ipv6cpSendConfigureNak,
+   ipv6cpSendConfigureRej,
+   ipv6cpSendTerminateReq,
+   ipv6cpSendTerminateAck,
+   ipv6cpSendCodeRej,
+   NULL
+};
+
+
+/**
+ * @brief IPV6CP Open event
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t ipv6cpOpen(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("\r\nIPV6CP Open event\r\n");
+
+   //The link is administratively available for traffic
+   pppOpenEvent(context, &context->ipv6cpFsm, &ipv6cpCallbacks);
+   //The lower layer is ready to carry packets
+   pppUpEvent(context, &context->ipv6cpFsm, &ipv6cpCallbacks);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief IPV6CP Close event
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t ipv6cpClose(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("\r\nIPV6CP Close event\r\n");
+
+   //The lower layer is no longer ready to carry packets
+   pppDownEvent(context, &context->ipv6cpFsm, &ipv6cpCallbacks);
+   //The link is no longer available for traffic
+   pppCloseEvent(context, &context->ipv6cpFsm, &ipv6cpCallbacks);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief IPV6CP timer handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * manage retransmissions
+ *
+ * @param[in] context PPP context
+ **/
+
+void ipv6cpTick(PppContext *context)
+{
+   //Check whether the restart timer is running
+   if(context->ipv6cpFsm.state >= PPP_STATE_4_CLOSING &&
+      context->ipv6cpFsm.state <= PPP_STATE_8_ACK_SENT)
+   {
+      //Get current time
+      systime_t time = osGetSystemTime();
+
+      //Check restart timer
+      if((time - context->ipv6cpFsm.timestamp) >= PPP_RESTART_TIMER)
+      {
+         //Debug message
+         TRACE_INFO("\r\nIPV6CP Timeout event\r\n");
+
+         //The restart timer is used to retransmit Configure-Request
+         //and Terminate-Request packets
+         pppTimeoutEvent(context, &context->ipv6cpFsm, &ipv6cpCallbacks);
+      }
+   }
+}
+
+
+/**
+ * @brief Process an incoming IPV6CP packet
+ * @param[in] context PPP context
+ * @param[in]  packet IPV6CP packet received from the peer
+ * @param[in] length Length of the packet, in bytes
+ **/
+
+void ipv6cpProcessPacket(PppContext *context, const PppPacket *packet, size_t length)
+{
+   //Ensure the length of the incoming IPV6CP packet is valid
+   if(length < sizeof(PppPacket))
+      return;
+
+   //Check the length field
+   if(ntohs(packet->length) > length)
+      return;
+   if(ntohs(packet->length) < sizeof(PppPacket))
+      return;
+
+   //Save the length of the IPV6CP packet
+   length = ntohs(packet->length);
+
+   //Debug message
+   TRACE_INFO("IPV6CP packet received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump IPV6CP packet contents for debugging purpose
+   pppDumpPacket(packet, length, PPP_PROTOCOL_IPV6CP);
+
+   //Check IPV6CP code field
+   switch(packet->code)
+   {
+   //Configure-Request packet?
+   case PPP_CODE_CONFIGURE_REQ:
+      //Process Configure-Request packet
+      ipv6cpProcessConfigureReq(context, (PppConfigurePacket *) packet);
+      break;
+   //Configure-Ack packet?
+   case PPP_CODE_CONFIGURE_ACK:
+      //Process Configure-Ack packet
+      ipv6cpProcessConfigureAck(context, (PppConfigurePacket *) packet);
+      break;
+   //Configure-Nak packet?
+   case PPP_CODE_CONFIGURE_NAK:
+      //Process Configure-Nak packet
+      ipv6cpProcessConfigureNak(context, (PppConfigurePacket *) packet);
+      break;
+   //Configure-Reject packet?
+   case PPP_CODE_CONFIGURE_REJ:
+      //Process Configure-Reject packet
+      ipv6cpProcessConfigureReject(context, (PppConfigurePacket *) packet);
+      break;
+   //Terminate-Request packet?
+   case PPP_CODE_TERMINATE_REQ:
+      //Process Terminate-Request packet
+      ipv6cpProcessTerminateReq(context, (PppTerminatePacket *) packet);
+      break;
+   //Terminate-Ack packet?
+   case PPP_CODE_TERMINATE_ACK:
+      //Process Terminate-Ack packet
+      ipv6cpProcessTerminateAck(context, (PppTerminatePacket *) packet);
+      break;
+   //Code-Reject packet?
+   case PPP_CODE_CODE_REJ:
+      //Process Code-Reject packet
+      ipv6cpProcessCodeRej(context, (PppCodeRejPacket *) packet);
+      break;
+   //Unknown code field
+   default:
+      //The packet is un-interpretable
+      ipv6cpProcessUnknownCode(context, packet);
+      break;
+   }
+}
+
+
+/**
+ * @brief Process Configure-Request packet
+ * @param[in] context PPP context
+ * @param[in] configureReqPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipv6cpProcessConfigureReq(PppContext *context,
+   const PppConfigurePacket *configureReqPacket)
+{
+   error_t error;
+   size_t length;
+   bool_t notRecognizable;
+   bool_t notAcceptable;
+   PppOption *option;
+
+   //Debug message
+   TRACE_INFO("\r\nIPV6CP Receive-Configure-Request event\r\n");
+
+   //Initialize variables
+   error = NO_ERROR;
+   notRecognizable = FALSE;
+   notAcceptable = FALSE;
+
+   //Retrieve the length of the option list
+   length = ntohs(configureReqPacket->length) - sizeof(PppConfigurePacket);
+   //Point to the first option
+   option = (PppOption *) configureReqPacket->options;
+
+   //Parse configuration options
+   while(length > 0)
+   {
+      //Parse current option
+      error = ipv6cpParseOption(context, option, length, NULL);
+
+      //Any error to report?
+      if(error == ERROR_INVALID_TYPE)
+      {
+         //Option not recognizable
+         notRecognizable = TRUE;
+         //Catch error
+         error = NO_ERROR;
+      }
+      else if(error == ERROR_INVALID_VALUE)
+      {
+         //Option not acceptable for configuration
+         notAcceptable = TRUE;
+         //Catch error
+         error = NO_ERROR;
+      }
+      else if(error)
+      {
+         //Malformed Configure-Request packet
+         break;
+      }
+
+      //Remaining bytes to process
+      length -= option->length;
+      //Jump to the next option
+      option = (PppOption *) ((uint8_t *) option + option->length);
+   }
+
+   //Valid Configure-Request packet received from the peer?
+   if(!error)
+   {
+      //Check flags
+      if(notRecognizable)
+      {
+         //If some configuration options received in the Configure-Request are not
+         //recognizable or not acceptable for negotiation, then the implementation
+         //must transmit a Configure-Reject
+         pppRcvConfigureReqEvent(context, &context->ipv6cpFsm, &ipv6cpCallbacks,
+            configureReqPacket, PPP_CODE_CONFIGURE_REJ);
+      }
+      else if(notAcceptable)
+      {
+         //If all configuration options are recognizable, but some values are not
+         //acceptable, then the implementation must transmit a Configure-Nak
+         pppRcvConfigureReqEvent(context, &context->ipv6cpFsm, &ipv6cpCallbacks,
+            configureReqPacket, PPP_CODE_CONFIGURE_NAK);
+      }
+      else
+      {
+         //If every configuration option received in the Configure-Request is
+         //recognizable and all values are acceptable, then the implementation
+         //must transmit a Configure-Ack
+         pppRcvConfigureReqEvent(context, &context->ipv6cpFsm, &ipv6cpCallbacks,
+            configureReqPacket, PPP_CODE_CONFIGURE_ACK);
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process Configure-Ack packet
+ * @param[in] context PPP context
+ * @param[in] configureAckPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipv6cpProcessConfigureAck(PppContext *context,
+   const PppConfigurePacket *configureAckPacket)
+{
+   //Debug message
+   TRACE_INFO("\r\nIPV6CP Receive-Configure-Ack event\r\n");
+
+   //When a packet is received with an invalid Identifier field, the
+   //packet is silently discarded without affecting the automaton
+   if(configureAckPacket->identifier != context->ipv6cpFsm.identifier)
+      return ERROR_WRONG_IDENTIFIER;
+
+   //A valid Configure-Ack packet has been received from the peer
+   pppRcvConfigureAckEvent(context, &context->ipv6cpFsm, &ipv6cpCallbacks);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Configure-Nak packet
+ * @param[in] context PPP context
+ * @param[in] configureNakPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipv6cpProcessConfigureNak(PppContext *context,
+   const PppConfigurePacket *configureNakPacket)
+{
+   size_t length;
+   PppOption *option;
+
+   //Debug message
+   TRACE_INFO("IPV6CP Receive-Configure-Nak event\r\n");
+
+   //When a packet is received with an invalid Identifier field, the
+   //packet is silently discarded without affecting the automaton
+   if(configureNakPacket->identifier != context->ipv6cpFsm.identifier)
+      return ERROR_WRONG_IDENTIFIER;
+
+   //Retrieve the length of the option list
+   length = ntohs(configureNakPacket->length) - sizeof(PppConfigurePacket);
+   //Point to the first option
+   option = (PppOption *) configureNakPacket->options;
+
+   //Parse configuration options
+   while(length > 0)
+   {
+      //Check option length
+      if(option->length < sizeof(PppOption))
+         return ERROR_INVALID_LENGTH;
+      if(option->length > length)
+         return ERROR_INVALID_LENGTH;
+
+      //Interface-Identifier option?
+      if(option->type == IPV6CP_OPTION_INTERFACE_ID)
+      {
+         //Cast option
+         Ipv6cpInterfaceIdOption *interfaceIdOption = (Ipv6cpInterfaceIdOption *) option;
+
+         //Check option length
+         if(interfaceIdOption->length != sizeof(Ipv6cpInterfaceIdOption))
+            return ERROR_INVALID_LENGTH;
+
+         //Save interface identifier
+         context->localConfig.interfaceId = interfaceIdOption->interfaceId;
+      }
+
+      //Remaining bytes to process
+      length -= option->length;
+      //Jump to the next option
+      option = (PppOption *) ((uint8_t *) option + option->length);
+   }
+
+   //A valid Configure-Nak or Configure-Reject packet has been received from the peer
+   pppRcvConfigureNakEvent(context, &context->ipv6cpFsm, &ipv6cpCallbacks);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Configure-Reject packet
+ * @param[in] context PPP context
+ * @param[in] configureRejPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipv6cpProcessConfigureReject(PppContext *context,
+   const PppConfigurePacket *configureRejPacket)
+{
+   size_t length;
+   PppOption *option;
+
+   //Debug message
+   TRACE_INFO("\r\nIPV6CP Receive-Configure-Reject event\r\n");
+
+   //When a packet is received with an invalid Identifier field, the
+   //packet is silently discarded without affecting the automaton
+   if(configureRejPacket->identifier != context->ipv6cpFsm.identifier)
+      return ERROR_WRONG_IDENTIFIER;
+
+   //Retrieve the length of the option list
+   length = ntohs(configureRejPacket->length) - sizeof(PppConfigurePacket);
+   //Point to the first option
+   option = (PppOption *) configureRejPacket->options;
+
+   //Parse configuration options
+   while(length > 0)
+   {
+      //Check option length
+      if(option->length < sizeof(PppOption))
+         return ERROR_INVALID_LENGTH;
+      if(option->length > length)
+         return ERROR_INVALID_LENGTH;
+
+      //Interface-Identifier option?
+      if(option->type == IPV6CP_OPTION_INTERFACE_ID)
+      {
+         //The option is not recognized by the peer
+         context->localConfig.interfaceIdRejected = TRUE;
+      }
+
+      //Remaining bytes to process
+      length -= option->length;
+      //Jump to the next option
+      option = (PppOption *) ((uint8_t *) option + option->length);
+   }
+
+   //A valid Configure-Nak or Configure-Reject packet has been received from the peer
+   pppRcvConfigureNakEvent(context, &context->ipv6cpFsm, &ipv6cpCallbacks);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Terminate-Request packet
+ * @param[in] context PPP context
+ * @param[in] terminateReqPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipv6cpProcessTerminateReq(PppContext *context,
+   const PppTerminatePacket *terminateReqPacket)
+{
+   //Debug message
+   TRACE_INFO("\r\nIPV6CP Receive-Terminate-Request event\r\n");
+
+   //The Terminate-Request indicates the desire of the peer to close the connection
+   pppRcvTerminateReqEvent(context, &context->ipv6cpFsm,
+      &ipv6cpCallbacks, terminateReqPacket);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Terminate-Ack packet
+ * @param[in] context PPP context
+ * @param[in] terminateAckPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipv6cpProcessTerminateAck(PppContext *context,
+   const PppTerminatePacket *terminateAckPacket)
+{
+   //Debug message
+   TRACE_INFO("\r\nIPV6CP Receive-Terminate-Ack event\r\n");
+
+   //The Terminate-Ack packet is usually a response to a Terminate-Request
+   //packet. This packet may also indicate that the peer is in Closed or
+   //Stopped states, and serves to re-synchronize the link configuration
+   pppRcvTerminateAckEvent(context, &context->ipv6cpFsm, &ipv6cpCallbacks);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Code-Reject packet
+ * @param[in] context PPP context
+ * @param[in] codeRejPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipv6cpProcessCodeRej(PppContext *context,
+   const PppCodeRejPacket *codeRejPacket)
+{
+   size_t length;
+   PppPacket *packet;
+
+   //Debug message
+   TRACE_INFO("\r\nIPV6CP Receive-Code-Reject event\r\n");
+
+   //Point to the rejected packet
+   packet = (PppPacket *) codeRejPacket->rejectedPacket;
+   //Retrieve the length of the rejected packet
+   length = ntohs(codeRejPacket->length) - sizeof(PppCodeRejPacket);
+
+   //Make sure the length of the rejected packet is valid
+   if(length < sizeof(PppPacket))
+      return ERROR_INVALID_LENGTH;
+
+   //Check whether the rejected value is acceptable or catastrophic
+   if(packet->code < PPP_CODE_CONFIGURE_REQ ||
+      packet->code > PPP_CODE_CODE_REJ)
+   {
+      //The RXJ+ event arises when the rejected value is acceptable, such
+      //as a Code-Reject of an extended code, or a Protocol-Reject of a
+      //NCP. These are within the scope of normal operation
+      pppRcvCodeRejEvent(context, &context->ipv6cpFsm, &ipv6cpCallbacks, TRUE);
+   }
+   else
+   {
+      //The RXJ- event arises when the rejected value is catastrophic, such
+      //as a Code-Reject of Configure-Request! This event communicates an
+      //unrecoverable error that terminates the connection
+      pppRcvCodeRejEvent(context, &context->ipv6cpFsm, &ipv6cpCallbacks, FALSE);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process packet with unknown code
+ * @param[in] context PPP context
+ * @param[in] packet Un-interpretable packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipv6cpProcessUnknownCode(PppContext *context,
+   const PppPacket *packet)
+{
+   //Debug message
+   TRACE_INFO("\r\nIPV6CP Receive-Unknown-Code event\r\n");
+
+   //This event occurs when an un-interpretable packet is received from
+   //the peer. A Code-Reject packet is sent in response
+   pppRcvUnknownCodeEvent(context, &context->ipv6cpFsm, &ipv6cpCallbacks, packet);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief This-Layer-Up callback function
+ * @param[in] context PPP context
+ **/
+
+void ipv6cpThisLayerUp(PppContext *context)
+{
+   NetInterface *interface;
+   Ipv6Addr ipAddr;
+
+   //Debug message
+   TRACE_INFO("IPV6CP This-Layer-Up callback\r\n");
+
+   //Debug message
+   TRACE_INFO("  Local Interface Id = %s\r\n",
+      eui64AddrToString(&context->localConfig.interfaceId, NULL));
+   TRACE_INFO("  Peer Interface Id = %s\r\n",
+      eui64AddrToString(&context->peerConfig.interfaceId, NULL));
+
+   //Point to the underlying interface
+   interface = context->interface;
+
+   //Generate an IPv6 address from the local interface identifier
+   ipv6GenerateLinkLocalAddr(&context->localConfig.interfaceId, &ipAddr);
+
+   //Update IPv6 configuration
+   ipv6SetAddr(interface, 0, &ipAddr, IPV6_ADDR_STATE_PREFERRED,
+      NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, TRUE);
+
+   //Generate an IPv6 address from the remote interface identifier
+   ipv6GenerateLinkLocalAddr(&context->peerConfig.interfaceId, &ipAddr);
+
+   //Update IPv6 configuration
+   interface->ipv6Context.routerList[0].addr = ipAddr;
+   interface->ipv6Context.routerList[0].permanent = TRUE;
+
+   //Link is up
+   interface->linkState = TRUE;
+
+   //Disable interrupts
+   interface->nicDriver->disableIrq(interface);
+   //Process link state change event
+   nicNotifyLinkChange(interface);
+   //Re-enable interrupts
+   interface->nicDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief This-Layer-Down callback function
+ * @param[in] context PPP context
+ **/
+
+void ipv6cpThisLayerDown(PppContext *context)
+{
+   NetInterface *interface;
+
+   //Debug message
+   TRACE_INFO("IPV6CP This-Layer-Down callback\r\n");
+
+   //Point to the underlying interface
+   interface = context->interface;
+
+   //Link is up
+   interface->linkState = FALSE;
+
+   //Disable interrupts
+   interface->nicDriver->disableIrq(interface);
+   //Process link state change event
+   nicNotifyLinkChange(interface);
+   //Re-enable interrupts
+   interface->nicDriver->enableIrq(interface);
+}
+
+
+/**
+ * @brief This-Layer-Started callback function
+ * @param[in] context PPP context
+ **/
+
+void ipv6cpThisLayerStarted(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("IPV6CP This-Layer-Started callback\r\n");
+}
+
+
+/**
+ * @brief This-Layer-Finished callback function
+ * @param[in] context PPP context
+ **/
+
+void ipv6cpThisLayerFinished(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("IPV6CP This-Layer-Finished callback\r\n");
+}
+
+
+/**
+ * @brief Initialize-Restart-Count callback function
+ * @param[in] context PPP context
+ * @param[in] value Restart counter value
+ **/
+
+void ipv6cpInitRestartCount(PppContext *context, uint_t value)
+{
+   //Debug message
+   TRACE_INFO("IPV6CP Initialize-Restart-Count callback\r\n");
+
+   //Initialize restart counter
+   context->ipv6cpFsm.restartCounter = value;
+}
+
+
+/**
+ * @brief Zero-Restart-Count callback function
+ * @param[in] context PPP context
+ **/
+
+void ipv6cpZeroRestartCount(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("IPV6CP Zero-Restart-Count callback\r\n");
+
+   //Zero restart counter
+   context->ipv6cpFsm.restartCounter = 0;
+
+   //The receiver of a Terminate-Request should wait for the peer to
+   //disconnect, and must not disconnect until at least one Restart
+   //time has passed after sending a Terminate-Ack
+   context->ipv6cpFsm.timestamp = osGetSystemTime();
+}
+
+
+/**
+ * @brief Send-Configure-Request callback function
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t ipv6cpSendConfigureReq(PppContext *context)
+{
+   error_t error;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   PppConfigurePacket *configureReqPacket;
+
+   //Debug message
+   TRACE_INFO("IPV6CP Send-Configure-Request callback\r\n");
+
+   //Allocate a buffer memory to hold the Configure-Request packet
+   buffer = pppAllocBuffer(PPP_MAX_CONF_REQ_SIZE, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the Configure-Request packet
+   configureReqPacket = netBufferAt(buffer, offset);
+
+   //Format packet header
+   configureReqPacket->code = PPP_CODE_CONFIGURE_REQ;
+   configureReqPacket->identifier = ++context->ipv6cpFsm.identifier;
+   configureReqPacket->length = sizeof(PppConfigurePacket);
+
+   //Make sure the Interface-Identifier option has not been previously rejected
+   if(!context->localConfig.interfaceIdRejected)
+   {
+      //Add option
+      pppAddOption(configureReqPacket, IPV6CP_OPTION_INTERFACE_ID,
+         &context->localConfig.interfaceId, sizeof(Eui64));
+   }
+
+   //Save packet length
+   length = configureReqPacket->length;
+   //Convert length field to network byte order
+   configureReqPacket->length = htons(length);
+
+   //Adjust the length of the multi-part buffer
+   netBufferSetLength(buffer, offset + length);
+
+   //Debug message
+   TRACE_INFO("Sending Configure-Request packet (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump packet contents for debugging purpose
+   pppDumpPacket((PppPacket *) configureReqPacket, length, PPP_PROTOCOL_IPV6CP);
+
+   //Send PPP frame
+   error = pppSendFrame(context->interface, buffer, offset, PPP_PROTOCOL_IPV6CP);
+
+   //The restart counter is decremented each time a Configure-Request is sent
+   if(context->ipv6cpFsm.restartCounter > 0)
+      context->ipv6cpFsm.restartCounter--;
+
+   //Save the time at which the packet was sent
+   context->ipv6cpFsm.timestamp = osGetSystemTime();
+
+   //Free previously allocated memory block
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send-Configure-Ack callback function
+ * @param[in] context PPP context
+ * @param[in] configureReqPacket Configure-Request packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipv6cpSendConfigureAck(PppContext *context,
+   const PppConfigurePacket *configureReqPacket)
+{
+   //Debug message
+   TRACE_INFO("IPV6CP Send-Configure-Ack callback\r\n");
+
+   //Send Configure-Ack packet
+   return pppSendConfigureAckNak(context, configureReqPacket,
+      PPP_PROTOCOL_IPV6CP, PPP_CODE_CONFIGURE_ACK);
+}
+
+
+/**
+ * @brief Send-Configure-Nak callback function
+ * @param[in] context PPP context
+ * @param[in] configureReqPacket Configure-Request packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipv6cpSendConfigureNak(PppContext *context,
+   const PppConfigurePacket *configureReqPacket)
+{
+   //Debug message
+   TRACE_INFO("IPV6CP Send-Configure-Nak callback\r\n");
+
+   //Send Configure-Nak packet
+   return pppSendConfigureAckNak(context, configureReqPacket,
+      PPP_PROTOCOL_IPV6CP, PPP_CODE_CONFIGURE_NAK);
+}
+
+
+/**
+ * @brief Send-Configure-Reject callback function
+ * @param[in] context PPP context
+ * @param[in] configureReqPacket Configure-Request packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipv6cpSendConfigureRej(PppContext *context,
+   const PppConfigurePacket *configureReqPacket)
+{
+   //Debug message
+   TRACE_INFO("IPV6CP Send-Configure-Reject callback\r\n");
+
+   //Send Configure-Reject packet
+   return pppSendConfigureAckNak(context, configureReqPacket,
+      PPP_PROTOCOL_IPV6CP, PPP_CODE_CONFIGURE_REJ);
+}
+
+
+/**
+ * @brief Send-Terminate-Request callback function
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t ipv6cpSendTerminateReq(PppContext *context)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("IPV6CP Send-Terminate-Request callback\r\n");
+
+   //On transmission, the Identifier field must be changed
+   context->ipv6cpFsm.identifier++;
+
+   //Send Terminate-Request packet
+   error = pppSendTerminateReq(context, context->ipv6cpFsm.identifier, PPP_PROTOCOL_IPV6CP);
+
+   //The restart counter is decremented each time a Terminate-Request is sent
+   if(context->ipv6cpFsm.restartCounter > 0)
+      context->ipv6cpFsm.restartCounter--;
+
+   //Save the time at which the packet was sent
+   context->ipv6cpFsm.timestamp = osGetSystemTime();
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send-Terminate-Ack callback function
+ * @param[in] context PPP context
+ * @param[in] terminateReqPacket Terminate-Request packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipv6cpSendTerminateAck(PppContext *context,
+   const PppTerminatePacket *terminateReqPacket)
+{
+   uint8_t identifier;
+
+   //Debug message
+   TRACE_INFO("IPV6CP Send-Terminate-Ack callback\r\n");
+
+   //Check whether this Terminate-Ack acknowledges the reception of a
+   //Terminate-Request packet
+   if(terminateReqPacket != NULL)
+   {
+      //The Identifier field of the Terminate-Request is copied into the
+      //Identifier field of the Terminate-Ack packet
+      identifier = terminateReqPacket->identifier;
+   }
+   else
+   {
+      //This Terminate-Ack packet serves to synchronize the automatons
+      identifier = ++context->ipv6cpFsm.identifier;
+   }
+
+   //Send Terminate-Ack packet
+   return pppSendTerminateAck(context, identifier, PPP_PROTOCOL_IPV6CP);
+}
+
+
+/**
+ * @brief Send-Code-Reject callback function
+ * @param[in] context PPP context
+ * @param[in] packet Un-interpretable packet received from the peer
+ * @return Error code
+ **/
+
+error_t ipv6cpSendCodeRej(PppContext *context, const PppPacket *packet)
+{
+   //Debug message
+   TRACE_INFO("IPV6CP Send-Code-Reject callback\r\n");
+
+   //The Identifier field must be changed for each Code-Reject sent
+   context->ipv6cpFsm.identifier++;
+
+   //Send Code-Reject packet
+   return pppSendCodeRej(context, packet, context->ipv6cpFsm.identifier, PPP_PROTOCOL_IPV6CP);
+}
+
+
+/**
+ * @brief Parse IPV6CP configuration option
+ * @param[in] context PPP context
+ * @param[in] option Option to be checked
+ * @param[in] inPacketLen Remaining bytes to process in the incoming packet
+ * @param[out] outPacket Pointer to the Configure-Ack, Nak or Reject packet
+ * @return Error code
+ **/
+
+error_t ipv6cpParseOption(PppContext *context, PppOption *option,
+   size_t inPacketLen, PppConfigurePacket *outPacket)
+{
+   error_t error;
+
+   //Malformed IPV6CP packet?
+   if(inPacketLen < sizeof(PppOption))
+      return ERROR_INVALID_LENGTH;
+
+   //Check option length
+   if(option->length < sizeof(PppOption))
+      return ERROR_INVALID_LENGTH;
+   if(option->length > inPacketLen)
+      return ERROR_INVALID_LENGTH;
+
+   //Check option type
+   switch(option->type)
+   {
+   case IPV6CP_OPTION_INTERFACE_ID:
+      //Check Interface-Identifier option
+      error = ipv6cpParseInterfaceIdOption(context, (Ipv6cpInterfaceIdOption *) option, outPacket);
+      break;
+   default:
+      //If some configuration options received in the Configure-Request are not
+      //recognizable or not acceptable for negotiation, then the implementation
+      //must transmit a Configure-Reject
+      if(outPacket->code == PPP_CODE_CONFIGURE_REJ)
+      {
+         //The options field of the Configure-Reject packet is filled
+         //with the unrecognized options from the Configure-Request
+         pppAddOption(outPacket, option->type, option->data,
+            option->length - sizeof(PppOption));
+      }
+
+      //The option is not acceptable for negotiation
+      error = ERROR_INVALID_TYPE;
+      break;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse Interface-Identifier option
+ * @param[in] context PPP context
+ * @param[in] option Option to be checked
+ * @param[out] outPacket Pointer to the Configure-Nak or Configure-Reject packet
+ * @return Error code
+ **/
+
+error_t ipv6cpParseInterfaceIdOption(PppContext *context,
+   Ipv6cpInterfaceIdOption *option, PppConfigurePacket *outPacket)
+{
+   error_t error;
+
+   //Check length field
+   if(option->length == sizeof(Ipv6cpInterfaceIdOption))
+   {
+      //Check whether the option value is acceptable
+      if(!eui64CompAddr(&option->interfaceId, &EUI64_UNSPECIFIED_ADDR))
+      {
+         //If every configuration option received in the Configure-Request is
+         //recognizable and all values are acceptable, then the implementation
+         //must transmit a Configure-Ack
+         if(outPacket != NULL && outPacket->code == PPP_CODE_CONFIGURE_ACK)
+         {
+            //Save interface identifier
+            context->peerConfig.interfaceId = option->interfaceId;
+
+            //The options field of the Configure-Ack packet contains the
+            //configuration options that the sender is acknowledging
+            pppAddOption(outPacket, IPV6CP_OPTION_INTERFACE_ID,
+               &option->interfaceId, option->length - sizeof(PppOption));
+         }
+
+         //The value is acceptable
+         error = NO_ERROR;
+      }
+      else
+      {
+         //If all configuration options are recognizable, but some values are not
+         //acceptable, then the implementation must transmit a Configure-Nak
+         if(outPacket != NULL && outPacket->code == PPP_CODE_CONFIGURE_NAK)
+         {
+            //The option must be modified to a value acceptable to the
+            //Configure-Nak sender
+            pppAddOption(outPacket, IPV6CP_OPTION_INTERFACE_ID,
+               &context->peerConfig.interfaceId, sizeof(Eui64));
+         }
+
+         //The value is not acceptable
+         error = ERROR_INVALID_VALUE;
+      }
+   }
+   else
+   {
+      //Invalid length field
+      error = ERROR_INVALID_LENGTH;
+   }
+
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/ipv6cp.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,152 @@
+/**
+ * @file ipv6cp.h
+ * @brief IPV6CP (PPP IPv6 Control Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _IPV6CP_H
+#define _IPV6CP_H
+
+//Dependencies
+#include "core/net.h"
+#include "ppp/ppp.h"
+
+
+/**
+ * @brief IPV6CP option types
+ **/
+
+typedef enum
+{
+   IPV6CP_OPTION_INTERFACE_ID       = 1, ///<Interface-Identifier
+   IPV6CP_OPTION_IPV6_COMP_PROTOCOL = 2  ///<IPv6-Compression-Protocol
+} Ipv6cpOptionType;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief Interface-Identifier option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;       //0
+   uint8_t length;     //1
+   Eui64 interfaceId;  //2-9
+} __end_packed Ipv6cpInterfaceIdOption;
+
+
+/**
+ * @brief IPv6-Compression-Protocol option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;      //0
+   uint8_t length;    //1
+   uint16_t protocol; //2-3
+   uint8_t data[];    //4
+} __end_packed Ipv6cpIpCompProtocolOption;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+//IPV6CP FSM events
+error_t ipv6cpOpen(PppContext *context);
+error_t ipv6cpClose(PppContext *context);
+
+void ipv6cpTick(PppContext *context);
+
+void ipv6cpProcessPacket(PppContext *context, const PppPacket *packet, size_t length);
+
+error_t ipv6cpProcessConfigureReq(PppContext *context,
+   const PppConfigurePacket *configureReqPacket);
+
+error_t ipv6cpProcessConfigureAck(PppContext *context,
+   const PppConfigurePacket *configureAckPacket);
+
+error_t ipv6cpProcessConfigureNak(PppContext *context,
+   const PppConfigurePacket *configureNakPacket);
+
+error_t ipv6cpProcessConfigureReject(PppContext *context,
+   const PppConfigurePacket *configureRejPacket);
+
+error_t ipv6cpProcessTerminateReq(PppContext *context,
+   const PppTerminatePacket *terminateReqPacket);
+
+error_t ipv6cpProcessTerminateAck(PppContext *context,
+   const PppTerminatePacket *terminateAckPacket);
+
+error_t ipv6cpProcessCodeRej(PppContext *context,
+   const PppCodeRejPacket *codeRejPacket);
+
+error_t ipv6cpProcessUnknownCode(PppContext *context,
+   const PppPacket *packet);
+
+//IPV6CP FSM callback functions
+void ipv6cpThisLayerUp(PppContext *context);
+void ipv6cpThisLayerDown(PppContext *context);
+void ipv6cpThisLayerStarted(PppContext *context);
+void ipv6cpThisLayerFinished(PppContext *context);
+
+void ipv6cpInitRestartCount(PppContext *context, uint_t value);
+void ipv6cpZeroRestartCount(PppContext *context);
+
+error_t ipv6cpSendConfigureReq(PppContext *context);
+
+error_t ipv6cpSendConfigureAck(PppContext *context,
+   const PppConfigurePacket *configureReqPacket);
+
+error_t ipv6cpSendConfigureNak(PppContext *context,
+   const PppConfigurePacket *configureReqPacket);
+
+error_t ipv6cpSendConfigureRej(PppContext *context,
+   const PppConfigurePacket *configureReqPacket);
+
+error_t ipv6cpSendTerminateReq(PppContext *context);
+
+error_t ipv6cpSendTerminateAck(PppContext *context,
+   const PppTerminatePacket *terminateReqPacket);
+
+error_t ipv6cpSendCodeRej(PppContext *context, const PppPacket *packet);
+
+//IPV6CP options checking
+error_t ipv6cpParseOption(PppContext *context, PppOption *option,
+   size_t inPacketLen, PppConfigurePacket *outPacket);
+
+error_t ipv6cpParseInterfaceIdOption(PppContext *context,
+   Ipv6cpInterfaceIdOption *option, PppConfigurePacket *outPacket);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/lcp.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1768 @@
+/**
+ * @file lcp.c
+ * @brief LCP (PPP Link Control Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL PPP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "ppp/ppp_fsm.h"
+#include "ppp/ppp_misc.h"
+#include "ppp/ppp_debug.h"
+#include "ppp/lcp.h"
+#include "ppp/ipcp.h"
+#include "ppp/ipv6cp.h"
+#include "ppp/pap.h"
+#include "ppp/chap.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (PPP_SUPPORT == ENABLED)
+
+
+/**
+ * @brief LCP FSM callbacks
+ **/
+
+const PppCallbacks lcpCallbacks =
+{
+   lcpThisLayerUp,
+   lcpThisLayerDown,
+   lcpThisLayerStarted,
+   lcpThisLayerFinished,
+   lcpInitRestartCount,
+   lcpZeroRestartCount,
+   lcpSendConfigureReq,
+   lcpSendConfigureAck,
+   lcpSendConfigureNak,
+   lcpSendConfigureRej,
+   lcpSendTerminateReq,
+   lcpSendTerminateAck,
+   lcpSendCodeRej,
+   lcpSendEchoRep
+};
+
+
+/**
+ * @brief LCP Open event
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t lcpOpen(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("\r\nLCP Open event\r\n");
+
+   //Advance to the Establish phase
+   context->pppPhase = PPP_PHASE_ESTABLISH;
+
+   //The link is administratively available for traffic
+   pppOpenEvent(context, &context->lcpFsm, &lcpCallbacks);
+   //The lower layer is ready to carry packets
+   pppUpEvent(context, &context->lcpFsm, &lcpCallbacks);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief LCP Close event
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t lcpClose(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("\r\nLCP Close event\r\n");
+
+   //The link is no longer available for traffic
+   pppCloseEvent(context, &context->lcpFsm, &lcpCallbacks);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief LCP timer handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * manage retransmissions
+ *
+ * @param[in] context PPP context
+ **/
+
+void lcpTick(PppContext *context)
+{
+   //Check whether the restart timer is running
+   if(context->lcpFsm.state >= PPP_STATE_4_CLOSING &&
+      context->lcpFsm.state <= PPP_STATE_8_ACK_SENT)
+   {
+      //Get current time
+      systime_t time = osGetSystemTime();
+
+      //Check restart timer
+      if((time - context->lcpFsm.timestamp) >= PPP_RESTART_TIMER)
+      {
+         //Debug message
+         TRACE_INFO("\r\nLCP Timeout event\r\n");
+
+         //The restart timer is used to retransmit Configure-Request
+         //and Terminate-Request packets
+         pppTimeoutEvent(context, &context->lcpFsm, &lcpCallbacks);
+      }
+   }
+}
+
+
+/**
+ * @brief Process an incoming LCP packet
+ * @param[in] context PPP context
+ * @param[in] packet LCP packet received from the peer
+ * @param[in] length Length of the packet, in bytes
+ **/
+
+void lcpProcessPacket(PppContext *context, const PppPacket *packet, size_t length)
+{
+   //Ensure the length of the incoming LCP packet is valid
+   if(length < sizeof(PppPacket))
+      return;
+
+   //Check the length field
+   if(ntohs(packet->length) > length)
+      return;
+   if(ntohs(packet->length) < sizeof(PppPacket))
+      return;
+
+   //Save the length of the LCP packet
+   length = ntohs(packet->length);
+
+   //Debug message
+   TRACE_INFO("LCP packet received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump LCP packet contents for debugging purpose
+   pppDumpPacket(packet, length, PPP_PROTOCOL_LCP);
+
+   //Check LCP code field
+   switch(packet->code)
+   {
+   //Configure-Request packet?
+   case PPP_CODE_CONFIGURE_REQ:
+      //Process Configure-Request packet
+      lcpProcessConfigureReq(context, (PppConfigurePacket *) packet);
+      break;
+   //Configure-Ack packet?
+   case PPP_CODE_CONFIGURE_ACK:
+      //Process Configure-Ack packet
+      lcpProcessConfigureAck(context, (PppConfigurePacket *) packet);
+      break;
+   //Configure-Nak packet?
+   case PPP_CODE_CONFIGURE_NAK:
+      //Process Configure-Nak packet
+      lcpProcessConfigureNak(context, (PppConfigurePacket *) packet);
+      break;
+   //Configure-Reject packet?
+   case PPP_CODE_CONFIGURE_REJ:
+      //Process Configure-Reject packet
+      lcpProcessConfigureReject(context, (PppConfigurePacket *) packet);
+      break;
+   //Terminate-Request packet?
+   case PPP_CODE_TERMINATE_REQ:
+      //Process Terminate-Request packet
+      lcpProcessTerminateReq(context, (PppTerminatePacket *) packet);
+      break;
+   //Terminate-Ack packet?
+   case PPP_CODE_TERMINATE_ACK:
+      //Process Terminate-Ack packet
+      lcpProcessTerminateAck(context, (PppTerminatePacket *) packet);
+      break;
+   //Code-Reject packet?
+   case PPP_CODE_CODE_REJ:
+      //Process Code-Reject packet
+      lcpProcessCodeRej(context, (PppCodeRejPacket *) packet);
+      break;
+   //Protocol-Reject packet?
+   case PPP_CODE_PROTOCOL_REJ:
+      //Process Protocol-Reject packet
+      lcpProcessProtocolRej(context, (PppProtocolRejPacket *) packet);
+      break;
+   //Echo-Request packet?
+   case PPP_CODE_ECHO_REQ:
+      //Process Echo-Request packet
+      lcpProcessEchoReq(context, (PppEchoPacket *) packet);
+      break;
+   //Echo-Reply packet?
+   case PPP_CODE_ECHO_REP:
+      //Process Echo-Reply packet
+      lcpProcessEchoRep(context, (PppEchoPacket *) packet);
+      break;
+   //Discard-Request packet?
+   case PPP_CODE_DISCARD_REQ:
+      //Process Discard-Request packet
+      lcpProcessDiscardReq(context, (PppDiscardReqPacket *) packet);
+      break;
+   //Unknown code field
+   default:
+      //The packet is un-interpretable
+      lcpProcessUnknownCode(context, packet);
+      break;
+   }
+}
+
+
+/**
+ * @brief Process Configure-Request packet
+ * @param[in] context PPP context
+ * @param[in] configureReqPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t lcpProcessConfigureReq(PppContext *context,
+   const PppConfigurePacket *configureReqPacket)
+{
+   error_t error;
+   size_t length;
+   bool_t notRecognizable;
+   bool_t notAcceptable;
+   PppOption *option;
+
+   //Debug message
+   TRACE_INFO("\r\nLCP Receive-Configure-Request event\r\n");
+
+   //Initialize variables
+   error = NO_ERROR;
+   notRecognizable = FALSE;
+   notAcceptable = FALSE;
+
+   //Retrieve the length of the option list
+   length = ntohs(configureReqPacket->length) - sizeof(PppConfigurePacket);
+   //Point to the first option
+   option = (PppOption *) configureReqPacket->options;
+
+   //Parse configuration options
+   while(length > 0)
+   {
+      //Parse current option
+      error = lcpParseOption(context, option, length, NULL);
+
+      //Any error to report?
+      if(error == ERROR_INVALID_TYPE)
+      {
+         //Option not recognizable
+         notRecognizable = TRUE;
+         //Catch error
+         error = NO_ERROR;
+      }
+      else if(error == ERROR_INVALID_VALUE)
+      {
+         //Option not acceptable for configuration
+         notAcceptable = TRUE;
+         //Catch error
+         error = NO_ERROR;
+      }
+      else if(error)
+      {
+         //Malformed Configure-Request packet
+         break;
+      }
+
+      //Remaining bytes to process
+      length -= option->length;
+      //Jump to the next option
+      option = (PppOption *) ((uint8_t *) option + option->length);
+   }
+
+   //Valid Configure-Request packet received from the peer?
+   if(!error)
+   {
+      //Check flags
+      if(notRecognizable)
+      {
+         //If some configuration options received in the Configure-Request are not
+         //recognizable or not acceptable for negotiation, then the implementation
+         //must transmit a Configure-Reject
+         pppRcvConfigureReqEvent(context, &context->lcpFsm, &lcpCallbacks,
+            configureReqPacket, PPP_CODE_CONFIGURE_REJ);
+      }
+      else if(notAcceptable)
+      {
+         //If all configuration options are recognizable, but some values are not
+         //acceptable, then the implementation must transmit a Configure-Nak
+         pppRcvConfigureReqEvent(context, &context->lcpFsm, &lcpCallbacks,
+            configureReqPacket, PPP_CODE_CONFIGURE_NAK);
+      }
+      else
+      {
+         //If every configuration option received in the Configure-Request is
+         //recognizable and all values are acceptable, then the implementation
+         //must transmit a Configure-Ack
+         pppRcvConfigureReqEvent(context, &context->lcpFsm, &lcpCallbacks,
+            configureReqPacket, PPP_CODE_CONFIGURE_ACK);
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process Configure-Ack packet
+ * @param[in] context PPP context
+ * @param[in] configureAckPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t lcpProcessConfigureAck(PppContext *context,
+   const PppConfigurePacket *configureAckPacket)
+{
+   //Debug message
+   TRACE_INFO("\r\nLCP Receive-Configure-Ack event\r\n");
+
+   //When a packet is received with an invalid Identifier field, the
+   //packet is silently discarded without affecting the automaton
+   if(configureAckPacket->identifier != context->lcpFsm.identifier)
+      return ERROR_WRONG_IDENTIFIER;
+
+   //A valid Configure-Ack packet has been received from the peer
+   pppRcvConfigureAckEvent(context, &context->lcpFsm, &lcpCallbacks);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Configure-Nak packet
+ * @param[in] context PPP context
+ * @param[in] configureNakPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t lcpProcessConfigureNak(PppContext *context,
+   const PppConfigurePacket *configureNakPacket)
+{
+   size_t length;
+   PppOption *option;
+
+   //Debug message
+   TRACE_INFO("LCP Receive-Configure-Nak event\r\n");
+
+   //When a packet is received with an invalid Identifier field, the
+   //packet is silently discarded without affecting the automaton
+   if(configureNakPacket->identifier != context->lcpFsm.identifier)
+      return ERROR_WRONG_IDENTIFIER;
+
+   //Retrieve the length of the option list
+   length = ntohs(configureNakPacket->length) - sizeof(PppConfigurePacket);
+   //Point to the first option
+   option = (PppOption *) configureNakPacket->options;
+
+   //Parse configuration options
+   while(length > 0)
+   {
+      //Check option length
+      if(option->length < sizeof(PppOption))
+         return ERROR_INVALID_LENGTH;
+      if(option->length > length)
+         return ERROR_INVALID_LENGTH;
+
+      //Maximum-Receive-Unit option?
+      if(option->type == LCP_OPTION_MRU)
+      {
+         //Cast option
+         LcpMruOption *mruOption = (LcpMruOption *) option;
+
+         //Check option length
+         if(mruOption->length != sizeof(LcpMruOption))
+            return ERROR_INVALID_LENGTH;
+
+         //Save value
+         context->localConfig.mru = ntohs(mruOption->mru);
+         //Make sure the MRU is acceptable
+         context->localConfig.mru = MAX(context->localConfig.mru, PPP_MIN_MRU);
+         context->localConfig.mru = MIN(context->localConfig.mru, PPP_MAX_MRU);
+      }
+      else if(option->type == LCP_OPTION_ACCM)
+      {
+         //Cast option
+         LcpAccmOption *accmOption = (LcpAccmOption *) option;
+
+         //Check option length
+         if(accmOption->length != sizeof(LcpAccmOption))
+            return ERROR_INVALID_LENGTH;
+
+         //Save value
+         context->localConfig.accm = ntohl(accmOption->accm);
+      }
+      //Authentication-Protocol option?
+      else if(option->type == LCP_OPTION_AUTH_PROTOCOL)
+      {
+         //Cast option
+         LcpAuthProtocolOption *authProtocolOption = (LcpAuthProtocolOption *) option;
+
+         //Check option length
+         if(authProtocolOption->length < sizeof(LcpAuthProtocolOption))
+            return ERROR_INVALID_LENGTH;
+
+         //Check the value provided by the peer
+         if(ntohs(authProtocolOption->protocol) == PPP_PROTOCOL_PAP)
+         {
+#if (PAP_SUPPORT == ENABLED)
+            //Manage authentication policy
+            if(context->settings.authProtocol & PPP_AUTH_PROTOCOL_PAP)
+            {
+               //Select PAP authentication protocol
+               context->localConfig.authProtocol = PPP_PROTOCOL_PAP;
+            }
+#endif
+         }
+         else if(ntohs(authProtocolOption->protocol) == PPP_PROTOCOL_CHAP)
+         {
+#if (CHAP_SUPPORT == ENABLED)
+            //Make sure that the length of the option is correct
+            if(authProtocolOption->length > sizeof(LcpAuthProtocolOption))
+            {
+               //Check the algorithm identifier
+               if(authProtocolOption->data[0] == CHAP_ALGO_ID_CHAP_MD5)
+               {
+                  //Manage authentication policy
+                  if(context->settings.authProtocol & PPP_AUTH_PROTOCOL_CHAP_MD5)
+                  {
+                     //Select CHAP with MD5 authentication protocol
+                     context->localConfig.authProtocol = PPP_PROTOCOL_CHAP;
+                     context->localConfig.authAlgo = CHAP_ALGO_ID_CHAP_MD5;
+                  }
+               }
+            }
+#endif
+         }
+      }
+
+      //Remaining bytes to process
+      length -= option->length;
+      //Jump to the next option
+      option = (PppOption *) ((uint8_t *) option + option->length);
+   }
+
+   //A valid Configure-Nak or Configure-Reject packet has been received from the peer
+   pppRcvConfigureNakEvent(context, &context->lcpFsm, &lcpCallbacks);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Configure-Reject packet
+ * @param[in] context PPP context
+ * @param[in] configureRejPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t lcpProcessConfigureReject(PppContext *context,
+   const PppConfigurePacket *configureRejPacket)
+{
+   size_t length;
+   PppOption *option;
+
+   //Debug message
+   TRACE_INFO("\r\nLCP Receive-Configure-Reject event\r\n");
+
+   //When a packet is received with an invalid Identifier field, the
+   //packet is silently discarded without affecting the automaton
+   if(configureRejPacket->identifier != context->lcpFsm.identifier)
+      return ERROR_WRONG_IDENTIFIER;
+
+   //Retrieve the length of the option list
+   length = ntohs(configureRejPacket->length) - sizeof(PppConfigurePacket);
+   //Point to the first option
+   option = (PppOption *) configureRejPacket->options;
+
+   //Parse configuration options
+   while(length > 0)
+   {
+      //Check option length
+      if(option->length < sizeof(PppOption))
+         return ERROR_INVALID_LENGTH;
+      if(option->length > length)
+         return ERROR_INVALID_LENGTH;
+
+      //Maximum-Receive-Unit option?
+      if(option->type == LCP_OPTION_MRU)
+      {
+         //The option is not recognized by the peer
+         context->localConfig.mruRejected = TRUE;
+         //Restore default value
+         context->localConfig.mru = PPP_DEFAULT_MRU;
+      }
+      //Async-Control-Character-Map option?
+      else if(option->type == LCP_OPTION_ACCM)
+      {
+         //The option is not recognized by the peer
+         context->localConfig.accmRejected = TRUE;
+         //Restore default value
+         context->localConfig.accm = PPP_DEFAULT_ACCM;
+      }
+      //Authentication-Protocol option?
+      else if(option->type == LCP_OPTION_AUTH_PROTOCOL)
+      {
+         //This is an unrecoverable error that terminates the connection
+         pppRcvCodeRejEvent(context, &context->lcpFsm, &lcpCallbacks, FALSE);
+         //Exit immediately
+         return ERROR_FAILURE;
+      }
+      //Magic-Number option?
+      else if(option->type == LCP_OPTION_MAGIC_NUMBER)
+      {
+         //The option is not recognized by the peer
+         context->localConfig.magicNumberRejected = TRUE;
+         //Restore default value
+         context->localConfig.magicNumber = PPP_DEFAULT_MAGIC_NUMBER;
+      }
+      //Protocol-Field-Compression option?
+      else if(option->type == LCP_OPTION_PFC)
+      {
+         //The option is not recognized by the peer
+         context->localConfig.pfcRejected = TRUE;
+         //Restore default value
+         context->localConfig.pfc = FALSE;
+      }
+      //Address-and-Control-Field-Compression option?
+      else if(option->type == LCP_OPTION_ACFC)
+      {
+         //The option is not recognized by the peer
+         context->localConfig.acfcRejected = TRUE;
+         //Restore default value
+         context->localConfig.acfc = FALSE;
+      }
+
+      //Remaining bytes to process
+      length -= option->length;
+      //Jump to the next option
+      option = (PppOption *) ((uint8_t *) option + option->length);
+   }
+
+   //A valid Configure-Nak or Configure-Reject packet has been received from the peer
+   pppRcvConfigureNakEvent(context, &context->lcpFsm, &lcpCallbacks);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Terminate-Request packet
+ * @param[in] context PPP context
+ * @param[in] terminateReqPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t lcpProcessTerminateReq(PppContext *context,
+   const PppTerminatePacket *terminateReqPacket)
+{
+   //Debug message
+   TRACE_INFO("\r\nLCP Receive-Terminate-Request event\r\n");
+
+   //The Terminate-Request indicates the desire of the peer to close the connection
+   pppRcvTerminateReqEvent(context, &context->lcpFsm,
+      &lcpCallbacks, terminateReqPacket);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Terminate-Ack packet
+ * @param[in] context PPP context
+ * @param[in] terminateAckPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t lcpProcessTerminateAck(PppContext *context,
+   const PppTerminatePacket *terminateAckPacket)
+{
+   //Debug message
+   TRACE_INFO("\r\nLCP Receive-Terminate-Ack event\r\n");
+
+   //The Terminate-Ack packet is usually a response to a Terminate-Request
+   //packet. This packet may also indicate that the peer is in Closed or
+   //Stopped states, and serves to re-synchronize the link configuration
+   pppRcvTerminateAckEvent(context, &context->lcpFsm, &lcpCallbacks);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Code-Reject packet
+ * @param[in] context PPP context
+ * @param[in] codeRejPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t lcpProcessCodeRej(PppContext *context,
+   const PppCodeRejPacket *codeRejPacket)
+{
+   size_t length;
+   PppPacket *packet;
+
+   //Debug message
+   TRACE_INFO("\r\nLCP Receive-Code-Reject event\r\n");
+
+   //Point to the rejected packet
+   packet = (PppPacket *) codeRejPacket->rejectedPacket;
+   //Retrieve the length of the rejected packet
+   length = ntohs(codeRejPacket->length) - sizeof(PppCodeRejPacket);
+
+   //Make sure the length of the rejected packet is valid
+   if(length < sizeof(PppPacket))
+      return ERROR_INVALID_LENGTH;
+
+   //Check whether the rejected value is acceptable or catastrophic
+   if(packet->code < PPP_CODE_CONFIGURE_REQ ||
+      packet->code > PPP_CODE_DISCARD_REQ)
+   {
+      //The RXJ+ event arises when the rejected value is acceptable, such
+      //as a Code-Reject of an extended code, or a Protocol-Reject of a
+      //NCP. These are within the scope of normal operation
+      pppRcvCodeRejEvent(context, &context->lcpFsm, &lcpCallbacks, TRUE);
+   }
+   else
+   {
+      //The RXJ- event arises when the rejected value is catastrophic, such
+      //as a Code-Reject of Configure-Request! This event communicates an
+      //unrecoverable error that terminates the connection
+      pppRcvCodeRejEvent(context, &context->lcpFsm, &lcpCallbacks, FALSE);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Protocol-Reject packet
+ * @param[in] context PPP context
+ * @param[in] protocolRejPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t lcpProcessProtocolRej(PppContext *context,
+   const PppProtocolRejPacket *protocolRejPacket)
+{
+   size_t length;
+   uint16_t protocol;
+
+   //Debug message
+   TRACE_INFO("\r\nLCP Receive-Protocol-Reject event\r\n");
+
+   //Retrieve the length of the packet
+   length = ntohs(protocolRejPacket->length);
+
+   //Make sure the length of the Protocol-Reject packet is valid
+   if(length < sizeof(PppProtocolRejPacket))
+      return ERROR_INVALID_LENGTH;
+
+   //Convert the Rejected-Protocol field to host byte order
+   protocol = ntohs(protocolRejPacket->rejectedProtocol);
+
+   //Check Rejected-Protocol field value
+   switch(protocol)
+   {
+   //LCP protocol?
+   case PPP_PROTOCOL_LCP:
+      //The rejected value is catastrophic. This event communicates
+      //an unrecoverable error that terminates the connection
+      pppRcvCodeRejEvent(context, &context->lcpFsm, &lcpCallbacks, FALSE);
+      break;
+
+   //IPv4 or IPCP protocol?
+   case PPP_PROTOCOL_IP:
+   case PPP_PROTOCOL_IPCP:
+      //The implementation must stop sending the offending packet type
+      context->ipRejected = TRUE;
+      //This is within the scope of normal operation...
+      pppRcvCodeRejEvent(context, &context->lcpFsm, &lcpCallbacks, TRUE);
+      break;
+
+   //IPv6 or IPV6CP protocol?
+   case PPP_PROTOCOL_IPV6:
+   case PPP_PROTOCOL_IPV6CP:
+      //The implementation must stop sending the offending packet type
+      context->ipv6Rejected = TRUE;
+      //This is within the scope of normal operation...
+      pppRcvCodeRejEvent(context, &context->lcpFsm, &lcpCallbacks, TRUE);
+      break;
+
+   //Unknown protocol?
+   default:
+      //Just for sanity's sake...
+      break;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Echo-Request packet
+ * @param[in] context PPP context
+ * @param[in] echoReqPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t lcpProcessEchoReq(PppContext *context,
+   const PppEchoPacket *echoReqPacket)
+{
+   //Debug message
+   TRACE_INFO("\r\nLCP Receive-Echo-Request event\r\n");
+
+   //An Echo-Reply packet is transmitted to acknowledge the
+   //reception of the Echo-Request packet
+   pppRcvEchoReqEvent(context, &context->lcpFsm,
+      &lcpCallbacks, echoReqPacket);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Echo-Reply packet
+ * @param[in] context PPP context
+ * @param[in] echoRepPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t lcpProcessEchoRep(PppContext *context,
+   const PppEchoPacket *echoRepPacket)
+{
+   //Debug message
+   TRACE_INFO("\r\nLCP Receive-Echo-Reply event\r\n");
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Discard-Request packet
+ * @param[in] context PPP context
+ * @param[in] discardReqPacket Packet received from the peer
+ * @return Error code
+ **/
+
+error_t lcpProcessDiscardReq(PppContext *context,
+   const PppDiscardReqPacket *discardReqPacket)
+{
+   //Debug message
+   TRACE_INFO("\r\nLCP Receive-Discard-Request event\r\n");
+
+   //The receiver must silently discard any Discard-Request that it receives
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process packet with unknown code
+ * @param[in] context PPP context
+ * @param[in] packet Un-interpretable packet received from the peer
+ * @return Error code
+ **/
+
+error_t lcpProcessUnknownCode(PppContext *context,
+   const PppPacket *packet)
+{
+   //Debug message
+   TRACE_INFO("\r\nLCP Receive-Unknown-Code event\r\n");
+
+   //This event occurs when an un-interpretable packet is received from
+   //the peer. A Code-Reject packet is sent in response
+   pppRcvUnknownCodeEvent(context, &context->lcpFsm, &lcpCallbacks, packet);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process PPP frame with unknown protocol
+ * @param[in] context PPP context
+ * @param[in] protocol Rejected protocol
+ * @param[in] information Rejected information
+ * @param[in] length Length of the rejected information
+ * @return Error code
+ **/
+
+error_t lcpProcessUnknownProtocol(PppContext *context,
+   uint16_t protocol, const uint8_t *information, size_t length)
+{
+   //Debug message
+   TRACE_INFO("\r\nLCP Receive-Unknown-Protocol event\r\n");
+
+   //The peer is attempting to use a protocol which is unsupported
+   if(context->lcpFsm.state == PPP_STATE_9_OPENED)
+   {
+      //The Identifier field must be changed for each Protocol-Reject sent
+      context->lcpFsm.identifier++;
+
+      //If the LCP automaton is in the Opened state, then this must be
+      //reported back to the peer by transmitting a Protocol-Reject
+      pppSendProtocolRej(context, context->lcpFsm.identifier,
+         protocol, information, length);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief This-Layer-Up callback function
+ * @param[in] context PPP context
+ **/
+
+void lcpThisLayerUp(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("LCP This-Layer-Up callback\r\n");
+
+   //Check whether the other end of the PPP link is being authenticated
+   if(context->localConfig.authProtocol != 0)
+      context->localAuthDone = FALSE;
+   else
+      context->localAuthDone = TRUE;
+
+   //Check whether the other end of the PPP link is the authenticator
+   if(context->peerConfig.authProtocol != 0)
+      context->peerAuthDone = FALSE;
+   else
+      context->peerAuthDone = TRUE;
+
+#if (PAP_SUPPORT == ENABLED)
+   //PAP authentication required?
+   if(context->localConfig.authProtocol == PPP_PROTOCOL_PAP ||
+      context->peerConfig.authProtocol == PPP_PROTOCOL_PAP)
+   {
+      //Advance to the Authentication phase
+      context->pppPhase = PPP_PHASE_AUTHENTICATE;
+      //Start PAP authentication process
+      papStartAuth(context);
+   }
+#endif
+#if (CHAP_SUPPORT == ENABLED)
+   //CHAP authentication required?
+   if(context->localConfig.authProtocol == PPP_PROTOCOL_CHAP ||
+      context->peerConfig.authProtocol == PPP_PROTOCOL_CHAP)
+   {
+      //Advance to the Authentication phase
+      context->pppPhase = PPP_PHASE_AUTHENTICATE;
+      //Start CHAP authentication process
+      chapStartAuth(context);
+   }
+#endif
+
+   //Check whether PPP authentication is complete
+   if(context->localAuthDone && context->peerAuthDone)
+   {
+      //Advance to the Network phase
+      context->pppPhase = PPP_PHASE_NETWORK;
+
+#if (IPV4_SUPPORT == ENABLED)
+      //IPCP Open event
+      ipcpOpen(context);
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+      //IPV6CP Open event
+      ipv6cpOpen(context);
+#endif
+   }
+}
+
+
+/**
+ * @brief This-Layer-Down callback function
+ * @param[in] context PPP context
+ **/
+
+void lcpThisLayerDown(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("LCP This-Layer-Down callback\r\n");
+
+   //Advance to the Terminate phase
+   context->pppPhase = PPP_PHASE_TERMINATE;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //IPCP Close event
+   ipcpClose(context);
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   //IPV6CP Close event
+   ipv6cpClose(context);
+#endif
+
+#if (PAP_SUPPORT == ENABLED)
+   //Abort PAP authentication process
+   papAbortAuth(context);
+#endif
+
+#if (CHAP_SUPPORT == ENABLED)
+   //Abort CHAP authentication process
+   chapAbortAuth(context);
+#endif
+}
+
+
+/**
+ * @brief This-Layer-Started callback function
+ * @param[in] context PPP context
+ **/
+
+void lcpThisLayerStarted(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("LCP This-Layer-Started callback\r\n");
+}
+
+
+/**
+ * @brief This-Layer-Finished callback function
+ * @param[in] context PPP context
+ **/
+
+void lcpThisLayerFinished(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("LCP This-Layer-Finished callback\r\n");
+
+   //The link is no longer available for traffic
+   pppCloseEvent(context, &context->lcpFsm, &lcpCallbacks);
+   //The lower layer is no longer ready to carry packets
+   pppDownEvent(context, &context->lcpFsm, &lcpCallbacks);
+
+   //Advance to the Link Dead phase
+   context->pppPhase = PPP_PHASE_DEAD;
+}
+
+
+/**
+ * @brief Initialize-Restart-Count callback function
+ * @param[in] context PPP context
+ * @param[in] value Restart counter value
+ **/
+
+void lcpInitRestartCount(PppContext *context, uint_t value)
+{
+   //Debug message
+   TRACE_INFO("LCP Initialize-Restart-Count callback\r\n");
+
+   //Initialize restart counter
+   context->lcpFsm.restartCounter = value;
+}
+
+
+/**
+ * @brief Zero-Restart-Count callback function
+ * @param[in] context PPP context
+ **/
+
+void lcpZeroRestartCount(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("LCP Zero-Restart-Count callback\r\n");
+
+   //Zero restart counter
+   context->lcpFsm.restartCounter = 0;
+
+   //The receiver of a Terminate-Request should wait for the peer to
+   //disconnect, and must not disconnect until at least one Restart
+   //time has passed after sending a Terminate-Ack
+   context->lcpFsm.timestamp = osGetSystemTime();
+}
+
+
+/**
+ * @brief Send-Configure-Request callback function
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t lcpSendConfigureReq(PppContext *context)
+{
+   error_t error;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   PppConfigurePacket *configureReqPacket;
+
+   //Debug message
+   TRACE_INFO("LCP Send-Configure-Request callback\r\n");
+
+   //Allocate a buffer memory to hold the Configure-Request packet
+   buffer = pppAllocBuffer(PPP_MAX_CONF_REQ_SIZE, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the Configure-Request packet
+   configureReqPacket = netBufferAt(buffer, offset);
+
+   //Format packet header
+   configureReqPacket->code = PPP_CODE_CONFIGURE_REQ;
+   configureReqPacket->identifier = ++context->lcpFsm.identifier;
+   configureReqPacket->length = sizeof(PppConfigurePacket);
+
+   //Make sure the Maximum-Receive-Unit option has not been
+   //previously rejected
+   if(!context->localConfig.mruRejected)
+   {
+      //Convert MRU to network byte order
+      uint16_t value = htons(context->localConfig.mru);
+      //Add option
+      pppAddOption(configureReqPacket, LCP_OPTION_MRU, &value, sizeof(uint16_t));
+   }
+
+   //Make sure the Async-Control-Character-Map option has not been
+   //previously rejected
+   if(!context->localConfig.accmRejected)
+   {
+      //Convert ACCM to network byte order
+      uint32_t value = htonl(context->localConfig.accm);
+      //Add option
+      pppAddOption(configureReqPacket, LCP_OPTION_ACCM, &value, sizeof(uint32_t));
+   }
+
+   //Make sure the Authentication-Protocol option has not been
+   //previously rejected
+   if(!context->localConfig.authProtocolRejected)
+   {
+      uint8_t value[3];
+
+      //PAP authentication protocol?
+      if(context->localConfig.authProtocol == PPP_PROTOCOL_PAP)
+      {
+         //Format Authentication-Protocol option
+         value[0] = MSB(PPP_PROTOCOL_PAP);
+         value[1] = LSB(PPP_PROTOCOL_PAP);
+
+         //Add option
+         pppAddOption(configureReqPacket, LCP_OPTION_AUTH_PROTOCOL, &value, 2);
+      }
+      //CHAP authentication protocol?
+      else if(context->localConfig.authProtocol == PPP_PROTOCOL_CHAP)
+      {
+         //Format Authentication-Protocol option
+         value[0] = MSB(PPP_PROTOCOL_CHAP);
+         value[1] = LSB(PPP_PROTOCOL_CHAP);
+         value[2] = context->localConfig.authAlgo;
+
+         //Add option
+         pppAddOption(configureReqPacket, LCP_OPTION_AUTH_PROTOCOL, &value, 3);
+      }
+   }
+
+   //Make sure the Protocol-Field-Compression option has not been
+   //previously rejected
+   if(!context->localConfig.pfcRejected)
+   {
+      //Check whether compression of the Protocol field is supported
+      if(context->localConfig.pfc)
+      {
+         //Add option
+         pppAddOption(configureReqPacket, LCP_OPTION_PFC, NULL, 0);
+      }
+   }
+
+   //Make sure the Address-and-Control-Field-Compression option has not been
+   //previously rejected
+   if(!context->localConfig.acfcRejected)
+   {
+      //Check whether compression of the Address and Control fields is supported
+      if(context->localConfig.acfc)
+      {
+         //Add option
+         pppAddOption(configureReqPacket, LCP_OPTION_ACFC, NULL, 0);
+      }
+   }
+
+   //Save packet length
+   length = configureReqPacket->length;
+   //Convert length field to network byte order
+   configureReqPacket->length = htons(length);
+
+   //Adjust the length of the multi-part buffer
+   netBufferSetLength(buffer, offset + length);
+
+   //Debug message
+   TRACE_INFO("Sending Configure-Request packet (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump packet contents for debugging purpose
+   pppDumpPacket((PppPacket *) configureReqPacket, length, PPP_PROTOCOL_LCP);
+
+   //Send PPP frame
+   error = pppSendFrame(context->interface, buffer, offset, PPP_PROTOCOL_LCP);
+
+   //The restart counter is decremented each time a Configure-Request is sent
+   if(context->lcpFsm.restartCounter > 0)
+      context->lcpFsm.restartCounter--;
+
+   //Save the time at which the packet was sent
+   context->lcpFsm.timestamp = osGetSystemTime();
+
+   //Free previously allocated memory block
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send-Configure-Ack callback function
+ * @param[in] context PPP context
+ * @param[in] configureReqPacket Configure-Request packet received from the peer
+ * @return Error code
+ **/
+
+error_t lcpSendConfigureAck(PppContext *context,
+   const PppConfigurePacket *configureReqPacket)
+{
+   //Debug message
+   TRACE_INFO("LCP Send-Configure-Ack callback\r\n");
+
+   //Send Configure-Ack packet
+   return pppSendConfigureAckNak(context, configureReqPacket,
+      PPP_PROTOCOL_LCP, PPP_CODE_CONFIGURE_ACK);
+}
+
+
+/**
+ * @brief Send-Configure-Nak callback function
+ * @param[in] context PPP context
+ * @param[in] configureReqPacket Configure-Request packet received from the peer
+ * @return Error code
+ **/
+
+error_t lcpSendConfigureNak(PppContext *context,
+   const PppConfigurePacket *configureReqPacket)
+{
+   //Debug message
+   TRACE_INFO("LCP Send-Configure-Nak callback\r\n");
+
+   //Send Configure-Nak packet
+   return pppSendConfigureAckNak(context, configureReqPacket,
+      PPP_PROTOCOL_LCP, PPP_CODE_CONFIGURE_NAK);
+}
+
+
+/**
+ * @brief Send-Configure-Reject callback function
+ * @param[in] context PPP context
+ * @param[in] configureReqPacket Configure-Request packet received from the peer
+ * @return Error code
+ **/
+
+error_t lcpSendConfigureRej(PppContext *context,
+   const PppConfigurePacket *configureReqPacket)
+{
+   //Debug message
+   TRACE_INFO("LCP Send-Configure-Reject callback\r\n");
+
+   //Send Configure-Reject packet
+   return pppSendConfigureAckNak(context, configureReqPacket,
+      PPP_PROTOCOL_LCP, PPP_CODE_CONFIGURE_REJ);
+}
+
+
+/**
+ * @brief Send-Terminate-Request callback function
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t lcpSendTerminateReq(PppContext *context)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("LCP Send-Terminate-Request callback\r\n");
+
+   //On transmission, the Identifier field must be changed
+   context->lcpFsm.identifier++;
+
+   //Send Terminate-Request packet
+   error = pppSendTerminateReq(context, context->lcpFsm.identifier, PPP_PROTOCOL_LCP);
+
+   //The restart counter is decremented each time a Terminate-Request is sent
+   if(context->lcpFsm.restartCounter > 0)
+      context->lcpFsm.restartCounter--;
+
+   //Save the time at which the packet was sent
+   context->lcpFsm.timestamp = osGetSystemTime();
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send-Terminate-Ack callback function
+ * @param[in] context PPP context
+ * @param[in] terminateReqPacket Terminate-Request packet received from the peer
+ * @return Error code
+ **/
+
+error_t lcpSendTerminateAck(PppContext *context,
+   const PppTerminatePacket *terminateReqPacket)
+{
+   uint8_t identifier;
+
+   //Debug message
+   TRACE_INFO("LCP Send-Terminate-Ack callback\r\n");
+
+   //Check whether this Terminate-Ack acknowledges the reception of a
+   //Terminate-Request packet
+   if(terminateReqPacket != NULL)
+   {
+      //The Identifier field of the Terminate-Request is copied into the
+      //Identifier field of the Terminate-Ack packet
+      identifier = terminateReqPacket->identifier;
+   }
+   else
+   {
+      //This Terminate-Ack packet serves to synchronize the automatons
+      identifier = ++context->lcpFsm.identifier;
+   }
+
+   //Send Terminate-Ack packet
+   return pppSendTerminateAck(context, identifier, PPP_PROTOCOL_LCP);
+}
+
+
+/**
+ * @brief Send-Code-Reject callback function
+ * @param[in] context PPP context
+ * @param[in] packet Un-interpretable packet received from the peer
+ * @return Error code
+ **/
+
+error_t lcpSendCodeRej(PppContext *context, const PppPacket *packet)
+{
+   //Debug message
+   TRACE_INFO("LCP Send-Code-Reject callback\r\n");
+
+   //The Identifier field must be changed for each Code-Reject sent
+   context->lcpFsm.identifier++;
+
+   //Send Code-Reject packet
+   return pppSendCodeRej(context, packet, context->lcpFsm.identifier, PPP_PROTOCOL_LCP);
+}
+
+
+/**
+ * @brief Send-Echo-Reply callback function
+ * @param[in] context PPP context
+ * @param[in] echoReqPacket Echo-Request packet received from the peer
+ * @return Error code
+ **/
+
+error_t lcpSendEchoRep(PppContext *context, const PppEchoPacket *echoReqPacket)
+{
+   //Debug message
+   TRACE_INFO("LCP Send-Echo-Reply callback\r\n");
+
+   //Send Echo-Reply packet
+   return pppSendEchoRep(context, echoReqPacket, PPP_PROTOCOL_LCP);
+}
+
+
+/**
+ * @brief Parse LCP configuration option
+ * @param[in] context PPP context
+ * @param[in] option Option to be checked
+ * @param[in] inPacketLen Remaining bytes to process in the incoming packet
+ * @param[out] outPacket Pointer to the Configure-Ack, Nak or Reject packet
+ * @return Error code
+ **/
+
+error_t lcpParseOption(PppContext *context, PppOption *option,
+   size_t inPacketLen, PppConfigurePacket *outPacket)
+{
+   error_t error;
+
+   //Malformed LCP packet?
+   if(inPacketLen < sizeof(PppOption))
+      return ERROR_INVALID_LENGTH;
+
+   //Check option length
+   if(option->length < sizeof(PppOption))
+      return ERROR_INVALID_LENGTH;
+   if(option->length > inPacketLen)
+      return ERROR_INVALID_LENGTH;
+
+   //Check option type
+   switch(option->type)
+   {
+   case LCP_OPTION_MRU:
+      //Check Maximum-Receive-Unit option
+      error = lcpParseMruOption(context, (LcpMruOption *) option, outPacket);
+      break;
+   case LCP_OPTION_ACCM:
+      //Check Async-Control-Character-Map option
+      error = lcpParseAccmOption(context, (LcpAccmOption *) option, outPacket);
+      break;
+   case LCP_OPTION_AUTH_PROTOCOL:
+      //Check Authentication-Protocol option
+      error = lcpParseAuthProtocolOption(context, (LcpAuthProtocolOption *) option, outPacket);
+      break;
+   case LCP_OPTION_MAGIC_NUMBER:
+      //Check Magic-Number option
+      error = lcpParseMagicNumberOption(context, (LcpMagicNumberOption *) option, outPacket);
+      break;
+   case LCP_OPTION_PFC:
+      //Check Protocol-Field-Compression option
+      error = lcpParsePfcOption(context, (LcpPfcOption *) option, outPacket);
+      break;
+   case LCP_OPTION_ACFC:
+      //Check Address-and-Control-Field-Compression option
+      error = lcpParseAcfcOption(context, (LcpAcfcOption *) option, outPacket);
+      break;
+   default:
+      //If some configuration options received in the Configure-Request are not
+      //recognizable or not acceptable for negotiation, then the implementation
+      //must transmit a Configure-Reject
+      if(outPacket != NULL && outPacket->code == PPP_CODE_CONFIGURE_REJ)
+      {
+         //The options field of the Configure-Reject packet is filled
+         //with the unrecognized options from the Configure-Request
+         pppAddOption(outPacket, option->type, option->data,
+            option->length - sizeof(PppOption));
+      }
+
+      //The option is not acceptable for negotiation
+      error = ERROR_INVALID_TYPE;
+      break;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse Maximum-Receive-Unit option
+ * @param[in] context PPP context
+ * @param[in] option Option to be checked
+ * @param[out] outPacket Pointer to the Configure-Nak or Configure-Reject packet
+ * @return Error code
+ **/
+
+error_t lcpParseMruOption(PppContext *context,
+   LcpMruOption *option, PppConfigurePacket *outPacket)
+{
+   error_t error;
+   uint16_t value;
+
+   //Check length field
+   if(option->length == sizeof(LcpMruOption))
+   {
+      //Check whether the option value is acceptable
+      if(ntohs(option->mru) >= PPP_MIN_MRU)
+      {
+         //If every configuration option received in the Configure-Request is
+         //recognizable and all values are acceptable, then the implementation
+         //must transmit a Configure-Ack
+         if(outPacket != NULL && outPacket->code == PPP_CODE_CONFIGURE_ACK)
+         {
+            //Save Maximum-Receive-Unit option
+            context->peerConfig.mru = ntohl(option->mru);
+
+            //The options field of the Configure-Ack packet contains the
+            //configuration options that the sender is acknowledging
+            pppAddOption(outPacket, LCP_OPTION_MRU, (void *) &option->mru,
+               option->length - sizeof(PppOption));
+         }
+
+         //The value is acceptable
+         error = NO_ERROR;
+      }
+      else
+      {
+         //If all configuration options are recognizable, but some values are not
+         //acceptable, then the implementation must transmit a Configure-Nak
+         if(outPacket != NULL && outPacket->code == PPP_CODE_CONFIGURE_NAK)
+         {
+            //Use default value
+            value = htons(PPP_DEFAULT_MRU);
+
+            //The option must be modified to a value acceptable to the
+            //Configure-Nak sender
+            pppAddOption(outPacket, LCP_OPTION_MRU, &value, sizeof(uint16_t));
+         }
+
+         //The value is not acceptable
+         error = ERROR_INVALID_VALUE;
+      }
+   }
+   else
+   {
+      //Invalid length field
+      error = ERROR_INVALID_LENGTH;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse Async-Control-Character-Map option
+ * @param[in] context PPP context
+ * @param[in] option Option to be checked
+ * @param[out] outPacket Pointer to the Configure-Nak or Configure-Reject packet
+ * @return Error code
+ **/
+
+error_t lcpParseAccmOption(PppContext *context,
+   LcpAccmOption *option, PppConfigurePacket *outPacket)
+{
+   error_t error;
+
+   //Check length field
+   if(option->length == sizeof(LcpAccmOption))
+   {
+      //If every configuration option received in the Configure-Request is
+      //recognizable and all values are acceptable, then the implementation
+      //must transmit a Configure-Ack
+      if(outPacket != NULL && outPacket->code == PPP_CODE_CONFIGURE_ACK)
+      {
+         //Save Async-Control-Character-Map option
+         context->peerConfig.accm = ntohl(option->accm);
+
+         //The options field of the Configure-Ack packet contains the
+         //configuration options that the sender is acknowledging
+         pppAddOption(outPacket, LCP_OPTION_ACCM, (void *) &option->accm,
+            option->length - sizeof(PppOption));
+      }
+
+      //The value is acceptable
+      error = NO_ERROR;
+   }
+   else
+   {
+      //Invalid length field
+      error = ERROR_INVALID_LENGTH;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse Authentication-Protocol option
+ * @param[in] context PPP context
+ * @param[in] option Option to be checked
+ * @param[out] outPacket Pointer to the Configure-Nak or Configure-Reject packet
+ * @return Error code
+ **/
+
+error_t lcpParseAuthProtocolOption(PppContext *context,
+   LcpAuthProtocolOption *option, PppConfigurePacket *outPacket)
+{
+   error_t error;
+   uint8_t value[3];
+
+   //Assume an error condition...
+   error = ERROR_INVALID_LENGTH;
+
+   //Check the length of the option
+   if(option->length >= sizeof(LcpAuthProtocolOption))
+   {
+      //The Authentication-Protocol option for PAP must be exactly 4 bytes
+      if(ntohs(option->protocol) == PPP_PROTOCOL_PAP)
+      {
+         if(option->length == 4)
+            error = NO_ERROR;
+      }
+      //The Authentication-Protocol option for CHAP must be exactly 5 bytes
+      else if(ntohs(option->protocol) == PPP_PROTOCOL_CHAP)
+      {
+         if(option->length == 5)
+            error = NO_ERROR;
+      }
+   }
+
+   //Make sure the length field is valid
+   if(!error)
+   {
+      //PAP authentication protocol?
+      if(context->settings.authProtocol & PPP_AUTH_PROTOCOL_PAP &&
+         ntohs(option->protocol) == PPP_PROTOCOL_PAP)
+      {
+         //If every configuration option received in the Configure-Request is
+         //recognizable and all values are acceptable, then the implementation
+         //must transmit a Configure-Ack
+         if(outPacket != NULL && outPacket->code == PPP_CODE_CONFIGURE_ACK)
+         {
+            //Save the authentication protocol to be used
+            context->peerConfig.authProtocol = PPP_PROTOCOL_PAP;
+
+            //The options field of the Configure-Ack packet contains the
+            //configuration options that the sender is acknowledging
+            pppAddOption(outPacket, option->type, (void *) &option->protocol,
+               option->length - sizeof(PppOption));
+         }
+
+         //The value is acceptable
+         error = NO_ERROR;
+      }
+      //CHAP with MD5 authentication protocol?
+      else if(context->settings.authProtocol & PPP_AUTH_PROTOCOL_CHAP_MD5 &&
+         ntohs(option->protocol) == PPP_PROTOCOL_CHAP &&
+         option->data[0] == CHAP_ALGO_ID_CHAP_MD5)
+      {
+         //If every configuration option received in the Configure-Request is
+         //recognizable and all values are acceptable, then the implementation
+         //must transmit a Configure-Ack
+         if(outPacket != NULL && outPacket->code == PPP_CODE_CONFIGURE_ACK)
+         {
+            //Save the authentication protocol to be used
+            context->peerConfig.authProtocol = PPP_PROTOCOL_CHAP;
+            context->peerConfig.authAlgo = CHAP_ALGO_ID_CHAP_MD5;
+
+            //The options field of the Configure-Ack packet contains the
+            //configuration options that the sender is acknowledging
+            pppAddOption(outPacket, option->type, (void *) &option->protocol,
+               option->length - sizeof(PppOption));
+         }
+
+         //The value is acceptable
+         error = NO_ERROR;
+      }
+      else
+      {
+         //PAP authentication protocol allowed?
+         if(context->settings.authProtocol & PPP_AUTH_PROTOCOL_PAP)
+         {
+            //If all configuration options are recognizable, but some values are not
+            //acceptable, then the implementation must transmit a Configure-Nak
+            if(outPacket != NULL && outPacket->code == PPP_CODE_CONFIGURE_NAK)
+            {
+               //Format Authentication-Protocol option
+               value[0] = MSB(PPP_PROTOCOL_PAP);
+               value[1] = LSB(PPP_PROTOCOL_PAP);
+
+               //The option must be modified to a value acceptable to the
+               //Configure-Nak sender
+               pppAddOption(outPacket, LCP_OPTION_AUTH_PROTOCOL, value, 2);
+            }
+
+            //The value is not acceptable
+            error = ERROR_INVALID_VALUE;
+         }
+         //CHAP with MD5 authentication protocol allowed?
+         else if(context->settings.authProtocol & PPP_AUTH_PROTOCOL_CHAP_MD5)
+         {
+            //If all configuration options are recognizable, but some values are not
+            //acceptable, then the implementation must transmit a Configure-Nak
+            if(outPacket != NULL && outPacket->code == PPP_CODE_CONFIGURE_NAK)
+            {
+               //Format Authentication-Protocol option
+               value[0] = MSB(PPP_PROTOCOL_CHAP);
+               value[1] = LSB(PPP_PROTOCOL_CHAP);
+               value[2] = CHAP_ALGO_ID_CHAP_MD5;
+
+               //The option must be modified to a value acceptable to the
+               //Configure-Nak sender
+               pppAddOption(outPacket, LCP_OPTION_AUTH_PROTOCOL, value, 3);
+            }
+
+            //The value is not acceptable
+            error = ERROR_INVALID_VALUE;
+         }
+         else
+         {
+            //If some configuration options received in the Configure-Request are not
+            //recognizable or not acceptable for negotiation, then the implementation
+            //must transmit a Configure-Reject
+            if(outPacket != NULL && outPacket->code == PPP_CODE_CONFIGURE_REJ)
+            {
+               //The options field of the Configure-Reject packet is filled
+               //with the unrecognized options from the Configure-Request
+               pppAddOption(outPacket, option->type, (void *) &option->protocol,
+                  option->length - sizeof(PppOption));
+            }
+
+            //The option is not acceptable for negotiation
+            error = ERROR_INVALID_TYPE;
+         }
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse Magic-Number option
+ * @param[in] context PPP context
+ * @param[in] option Option to be checked
+ * @param[out] outPacket Pointer to the Configure-Nak or Configure-Reject packet
+ * @return Error code
+ **/
+
+error_t lcpParseMagicNumberOption(PppContext *context,
+   LcpMagicNumberOption *option, PppConfigurePacket *outPacket)
+{
+   error_t error;
+
+   //Check length field
+   if(option->length == sizeof(LcpMagicNumberOption))
+   {
+      //If every configuration option received in the Configure-Request is
+      //recognizable and all values are acceptable, then the implementation
+      //must transmit a Configure-Ack
+      if(outPacket != NULL && outPacket->code == PPP_CODE_CONFIGURE_ACK)
+      {
+         //Save Magic-Number option
+         context->peerConfig.magicNumber = ntohl(option->magicNumber);
+
+         //The options field of the Configure-Ack packet contains the
+         //configuration options that the sender is acknowledging
+         pppAddOption(outPacket, LCP_OPTION_MAGIC_NUMBER, (void *) &option->magicNumber,
+            option->length - sizeof(PppOption));
+      }
+
+      //The value is acceptable
+      error = NO_ERROR;
+   }
+   else
+   {
+      //Invalid length field
+      error = ERROR_INVALID_LENGTH;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse Protocol-Field-Compression option
+ * @param[in] context PPP context
+ * @param[in] option Option to be checked
+ * @param[out] outPacket Pointer to the Configure-Nak or Configure-Reject packet
+ * @return Error code
+ **/
+
+error_t lcpParsePfcOption(PppContext *context,
+   LcpPfcOption *option, PppConfigurePacket *outPacket)
+{
+   error_t error;
+
+   //Check length field
+   if(option->length == sizeof(LcpPfcOption))
+   {
+      //If every configuration option received in the Configure-Request is
+      //recognizable and all values are acceptable, then the implementation
+      //must transmit a Configure-Ack
+      if(outPacket != NULL && outPacket->code == PPP_CODE_CONFIGURE_ACK)
+      {
+         //Save Protocol-Field-Compression option
+         context->peerConfig.pfc = TRUE;
+
+         //The options field of the Configure-Ack packet contains the
+         //configuration options that the sender is acknowledging
+         pppAddOption(outPacket, LCP_OPTION_PFC, NULL, 0);
+      }
+
+      //The value is acceptable
+      error = NO_ERROR;
+   }
+   else
+   {
+      //Invalid length field
+      error = ERROR_INVALID_LENGTH;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse Address-and-Control-Field-Compression option
+ * @param[in] context PPP context
+ * @param[in] option Option to be checked
+ * @param[out] outPacket Pointer to the Configure-Nak or Configure-Reject packet
+ * @return Error code
+ **/
+
+error_t lcpParseAcfcOption(PppContext *context,
+   LcpAcfcOption *option, PppConfigurePacket *outPacket)
+{
+   error_t error;
+
+   //Check length field
+   if(option->length == sizeof(LcpAcfcOption))
+   {
+      //If every configuration option received in the Configure-Request is
+      //recognizable and all values are acceptable, then the implementation
+      //must transmit a Configure-Ack
+      if(outPacket != NULL && outPacket->code == PPP_CODE_CONFIGURE_ACK)
+      {
+         //Save Address-and-Control-Field-Compression option
+         context->peerConfig.acfc = TRUE;
+
+         //The options field of the Configure-Ack packet contains the
+         //configuration options that the sender is acknowledging
+         pppAddOption(outPacket, LCP_OPTION_ACFC, NULL, 0);
+      }
+
+      //The value is acceptable
+      error = NO_ERROR;
+   }
+   else
+   {
+      //Invalid length field
+      error = ERROR_INVALID_LENGTH;
+   }
+
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/lcp.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,247 @@
+/**
+ * @file lcp.h
+ * @brief LCP (PPP Link Control Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _LCP_H
+#define _LCP_H
+
+//Dependencies
+#include "core/net.h"
+#include "ppp/ppp.h"
+
+
+/**
+ * @brief LCP option types
+ **/
+
+typedef enum
+{
+   LCP_OPTION_MRU              = 1, ///<Maximum-Receive-Unit
+   LCP_OPTION_ACCM             = 2, ///<Async-Control-Character-Map
+   LCP_OPTION_AUTH_PROTOCOL    = 3, ///<Authentication-Protocol
+   LCP_OPTION_QUALITY_PROTOCOL = 4, ///<Quality-Protocol
+   LCP_OPTION_MAGIC_NUMBER     = 5, ///<Magic-Number
+   LCP_OPTION_PFC              = 7, ///<Protocol-Field-Compression
+   LCP_OPTION_ACFC             = 8  ///<Address-and-Control-Field-Compression
+} LcpOptionType;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief Maximum-Receive-Unit option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;   //0
+   uint8_t length; //1
+   uint16_t mru;   //2-3
+} __end_packed LcpMruOption;
+
+
+/**
+ * @brief Async-Control-Character-Map option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;   //0
+   uint8_t length; //1
+   uint32_t accm;  //2-5
+} __end_packed LcpAccmOption;
+
+
+/**
+ * @brief Authentication-Protocol option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;      //0
+   uint8_t length;    //1
+   uint16_t protocol; //2-3
+   uint8_t data[];    //4
+} __end_packed LcpAuthProtocolOption;
+
+
+/**
+ * @brief Quality-Protocol option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;      //0
+   uint8_t length;    //1
+   uint16_t protocol; //2-3
+   uint8_t data[];    //4
+} __end_packed LcpQualityProtocolOption;
+
+
+/**
+ * @brief Magic-Number option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;         //0
+   uint8_t length;       //1
+   uint32_t magicNumber; //2-5
+} __end_packed LcpMagicNumberOption;
+
+
+/**
+ * @brief Protocol-Field-Compression option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;   //0
+   uint8_t length; //1
+} __end_packed LcpPfcOption;
+
+
+/**
+ * @brief Address-and-Control-Field-Compression option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;   //0
+   uint8_t length; //1
+} __end_packed LcpAcfcOption;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+//LCP FSM events
+error_t lcpOpen(PppContext *context);
+error_t lcpClose(PppContext *context);
+
+void lcpTick(PppContext *context);
+
+void lcpProcessPacket(PppContext *context, const PppPacket *packet, size_t length);
+
+error_t lcpProcessConfigureReq(PppContext *context,
+   const PppConfigurePacket *configureReqPacket);
+
+error_t lcpProcessConfigureAck(PppContext *context,
+   const PppConfigurePacket *configureAckPacket);
+
+error_t lcpProcessConfigureNak(PppContext *context,
+   const PppConfigurePacket *configureNakPacket);
+
+error_t lcpProcessConfigureReject(PppContext *context,
+   const PppConfigurePacket *configureRejPacket);
+
+error_t lcpProcessTerminateReq(PppContext *context,
+   const PppTerminatePacket *terminateReqPacket);
+
+error_t lcpProcessTerminateAck(PppContext *context,
+   const PppTerminatePacket *terminateAckPacket);
+
+error_t lcpProcessCodeRej(PppContext *context,
+   const PppCodeRejPacket *codeRejPacket);
+
+error_t lcpProcessProtocolRej(PppContext *context,
+   const PppProtocolRejPacket *protocolRejPacket);
+
+error_t lcpProcessEchoReq(PppContext *context,
+   const PppEchoPacket *echoReqPacket);
+
+error_t lcpProcessEchoRep(PppContext *context,
+   const PppEchoPacket *echoRepPacket);
+
+error_t lcpProcessDiscardReq(PppContext *context,
+   const PppDiscardReqPacket *discardReqPacket);
+
+error_t lcpProcessUnknownCode(PppContext *context,
+   const PppPacket *packet);
+
+error_t lcpProcessUnknownProtocol(PppContext *context,
+   uint16_t protocol, const uint8_t *information, size_t length);
+
+//LCP FSM callback functions
+void lcpThisLayerUp(PppContext *context);
+void lcpThisLayerDown(PppContext *context);
+void lcpThisLayerStarted(PppContext *context);
+void lcpThisLayerFinished(PppContext *context);
+
+void lcpInitRestartCount(PppContext *context, uint_t value);
+void lcpZeroRestartCount(PppContext *context);
+
+error_t lcpSendConfigureReq(PppContext *context);
+
+error_t lcpSendConfigureAck(PppContext *context,
+   const PppConfigurePacket *configureReqPacket);
+
+error_t lcpSendConfigureNak(PppContext *context,
+   const PppConfigurePacket *configureReqPacket);
+
+error_t lcpSendConfigureRej(PppContext *context,
+   const PppConfigurePacket *configureReqPacket);
+
+error_t lcpSendTerminateReq(PppContext *context);
+
+error_t lcpSendTerminateAck(PppContext *context,
+   const PppTerminatePacket *terminateReqPacket);
+
+error_t lcpSendCodeRej(PppContext *context, const PppPacket *packet);
+error_t lcpSendEchoRep(PppContext *context, const PppEchoPacket *echoReqPacket);
+
+//LCP options checking
+error_t lcpParseOption(PppContext *context, PppOption *option,
+   size_t inPacketLen, PppConfigurePacket *outPacket);
+
+error_t lcpParseMruOption(PppContext *context,
+   LcpMruOption *option, PppConfigurePacket *outPacket);
+
+error_t lcpParseAccmOption(PppContext *context,
+   LcpAccmOption *option, PppConfigurePacket *outPacket);
+
+error_t lcpParseAuthProtocolOption(PppContext *context,
+   LcpAuthProtocolOption *option, PppConfigurePacket *outPacket);
+
+error_t lcpParseMagicNumberOption(PppContext *context,
+   LcpMagicNumberOption *option, PppConfigurePacket *outPacket);
+
+error_t lcpParsePfcOption(PppContext *context,
+   LcpPfcOption *option, PppConfigurePacket *outPacket);
+
+error_t lcpParseAcfcOption(PppContext *context,
+   LcpAcfcOption *option, PppConfigurePacket *outPacket);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/pap.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,624 @@
+/**
+ * @file pap.c
+ * @brief PAP (Password Authentication Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL PPP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "ppp/ppp_debug.h"
+#include "ppp/lcp.h"
+#include "ppp/ipcp.h"
+#include "ppp/ipv6cp.h"
+#include "ppp/pap.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (PPP_SUPPORT == ENABLED && PAP_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Start PAP authentication
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t papStartAuth(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("\r\nStarting PAP authentication...\r\n");
+
+   //Check whether the other end of the PPP link is being authenticated
+   if(context->localConfig.authProtocol == PPP_PROTOCOL_PAP)
+   {
+      //Switch to the Started state
+      context->papFsm.localState = PAP_STATE_1_STARTED;
+   }
+
+   //Check whether the other end of the PPP link is the authenticator
+   if(context->peerConfig.authProtocol == PPP_PROTOCOL_PAP)
+   {
+      //Initialize restart counter
+      context->papFsm.restartCounter = PAP_MAX_REQUESTS;
+      //Send Authenticate-Request packet
+      papSendAuthReq(context);
+      //Switch to the Req-Sent state
+      context->papFsm.peerState = PAP_STATE_2_REQ_SENT;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Abort PAP authentication
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t papAbortAuth(PppContext *context)
+{
+   //Debug message
+   TRACE_INFO("\r\nAborting PAP authentication...\r\n");
+
+   //Abort PAP authentication process
+   context->papFsm.localState = PAP_STATE_0_INITIAL;
+   context->papFsm.peerState = PAP_STATE_0_INITIAL;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief PAP timer handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * manage retransmissions
+ *
+ * @param[in] context PPP context
+ **/
+
+void papTick(PppContext *context)
+{
+   //Check whether the restart timer is running
+   if(context->papFsm.peerState == PAP_STATE_2_REQ_SENT)
+   {
+      //Get current time
+      systime_t time = osGetSystemTime();
+
+      //Check restart timer
+      if((time - context->papFsm.timestamp) >= PAP_RESTART_TIMER)
+      {
+         //Debug message
+         TRACE_INFO("\r\nPAP Timeout event\r\n");
+
+         //Check whether the restart counter is greater than zero
+         if(context->papFsm.restartCounter > 0)
+         {
+            //Retransmit the Authenticate-Request packet
+            papSendAuthReq(context);
+         }
+         else
+         {
+            //Abort PAP authentication
+            context->papFsm.peerState = PAP_STATE_0_INITIAL;
+            //Authentication failed
+            lcpClose(context);
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Process an incoming PAP packet
+ * @param[in] context PPP context
+ * @param[in] packet PAP packet received from the peer
+ * @param[in] length Length of the packet, in bytes
+ **/
+
+void papProcessPacket(PppContext *context,
+   const PppPacket *packet, size_t length)
+{
+   //Ensure the length of the incoming PAP packet is valid
+   if(length < sizeof(PppPacket))
+      return;
+
+   //Check the length field
+   if(ntohs(packet->length) > length)
+      return;
+   if(ntohs(packet->length) < sizeof(PppPacket))
+      return;
+
+   //Save the length of the PAP packet
+   length = ntohs(packet->length);
+
+   //Debug message
+   TRACE_INFO("PAP packet received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump PAP packet contents for debugging purpose
+   pppDumpPacket(packet, length, PPP_PROTOCOL_PAP);
+
+   //Because the Authenticate-Ack might be lost, the authenticator must
+   //allow repeated Authenticate-Request packets after completing the
+   //Authentication phase
+   if(context->pppPhase != PPP_PHASE_AUTHENTICATE &&
+      context->pppPhase != PPP_PHASE_NETWORK)
+   {
+      //Any packets received during any other phase must be silently discarded
+      return;
+   }
+
+   //Check PAP code field
+   switch(packet->code)
+   {
+   //Authenticate-Request packet?
+   case PAP_CODE_AUTH_REQ:
+      //Process Authenticate-Request packet
+      papProcessAuthReq(context, (PapAuthReqPacket *) packet, length);
+      break;
+   //Authenticate-Ack packet?
+   case PAP_CODE_AUTH_ACK:
+      //Process Authenticate-Ack packet
+      papProcessAuthAck(context, (PapAuthAckPacket *) packet, length);
+      break;
+   //Authenticate-Nak packet?
+   case PAP_CODE_AUTH_NAK:
+      //Process Authenticate-Nak packet
+      papProcessAuthNak(context, (PapAuthNakPacket *) packet, length);
+      break;
+   //Unknown code field
+   default:
+      //Silently drop the incoming packet
+      break;
+   }
+}
+
+
+/**
+ * @brief Process Authenticate-Request packet
+ * @param[in] context PPP context
+ * @param[in] authReqPacket Packet received from the peer
+ * @param[in] length Length of the packet, in bytes
+ * @return Error code
+ **/
+
+error_t papProcessAuthReq(PppContext *context,
+   const PapAuthReqPacket *authReqPacket, size_t length)
+{
+   bool_t status;
+   size_t usernameLen;
+   const uint8_t *p;
+
+   //Debug message
+   TRACE_INFO("\r\nPAP Authenticate-Request packet received\r\n");
+
+   //Make sure the Authenticate-Request packet is acceptable
+   if(context->localConfig.authProtocol != PPP_PROTOCOL_PAP)
+      return ERROR_FAILURE;
+
+   //Check the length of the packet
+   if(length < sizeof(PapAuthReqPacket))
+      return ERROR_INVALID_LENGTH;
+
+   //Retrieve the length of the Peer-ID field
+   usernameLen = authReqPacket->peerIdLength;
+
+   //Malformed Authenticate-Request packet?
+   if(length < (sizeof(PapAuthReqPacket) + 1 + usernameLen))
+      return ERROR_INVALID_LENGTH;
+
+   //Limit the length of the string
+   usernameLen = MIN(usernameLen, PPP_MAX_USERNAME_LEN);
+   //Copy the name of the peer to be identified
+   memcpy(context->peerName, authReqPacket->peerId, usernameLen);
+   //Properly terminate the string with a NULL character
+   context->peerName[usernameLen] = '\0';
+
+   //Point to the Passwd-Length field
+   p = authReqPacket->peerId + usernameLen;
+
+   //Save the length of Password field
+   context->papFsm.passwordLen = p[0];
+   //Point to the Password field
+   context->papFsm.password = p + 1;
+
+   //Malformed Authenticate-Request packet?
+   if(length < (sizeof(PapAuthReqPacket) + 1 + usernameLen + context->papFsm.passwordLen))
+      return ERROR_INVALID_LENGTH;
+
+   //Invoke user-defined callback, if any
+   if(context->settings.authCallback != NULL)
+   {
+      //Perfom username and password verification
+      status = context->settings.authCallback(context->interface,
+         context->peerName);
+   }
+   else
+   {
+      //Unable to perform authentication...
+      status = FALSE;
+   }
+
+   //Successful authentication?
+   if(status)
+   {
+      //If the Peer-ID/Password pair received in the Authenticate-Request
+      //is both recognizable and acceptable, then the authenticator must
+      //transmit an Authenticate-Ack packet
+      papSendAuthAck(context, authReqPacket->identifier);
+
+      //Switch to the Ack-Sent state
+      context->papFsm.localState = PAP_STATE_4_ACK_SENT;
+      //The user has been successfully authenticated
+      context->localAuthDone = TRUE;
+
+      //Check whether PPP authentication is complete
+      if(context->localAuthDone && context->peerAuthDone)
+      {
+         //Check current PPP phase
+         if(context->pppPhase == PPP_PHASE_AUTHENTICATE)
+         {
+            //Advance to the Network phase
+            context->pppPhase = PPP_PHASE_NETWORK;
+
+#if (IPV4_SUPPORT == ENABLED)
+            //IPCP Open event
+            ipcpOpen(context);
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+            //IPV6CP Open event
+            ipv6cpOpen(context);
+#endif
+         }
+      }
+   }
+   else
+   {
+      //If the Peer-ID/Password pair received in the Authenticate-Request
+      //is not recognizable or acceptable, then the authenticator must
+      //transmit an Authenticate-Nak packet
+      papSendAuthNak(context, authReqPacket->identifier);
+
+      //Switch to the Nak-Sent state
+      context->papFsm.localState = PAP_STATE_6_NAK_SENT;
+      //The authenticator should take action to terminate the link
+      lcpClose(context);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Authenticate-Ack packet
+ * @param[in] context PPP context
+ * @param[in] authAckPacket Packet received from the peer
+ * @param[in] length Length of the packet, in bytes
+ * @return Error code
+ **/
+
+error_t papProcessAuthAck(PppContext *context,
+   const PapAuthAckPacket *authAckPacket, size_t length)
+{
+   //Debug message
+   TRACE_INFO("\r\nPAP Authenticate-Ack packet received\r\n");
+
+   //Make sure the Authenticate-Ack packet is acceptable
+   if(context->peerConfig.authProtocol != PPP_PROTOCOL_PAP)
+      return ERROR_FAILURE;
+
+   //Check the length of the packet
+   if(length < sizeof(PapAuthAckPacket))
+      return ERROR_INVALID_LENGTH;
+
+   //When a packet is received with an invalid Identifier field, the
+   //packet is silently discarded without affecting the automaton
+   if(authAckPacket->identifier != context->papFsm.identifier)
+      return ERROR_WRONG_IDENTIFIER;
+
+   //Switch to the Ack-Rcvd state
+   context->papFsm.peerState = PAP_STATE_5_ACK_RCVD;
+   //The user name has been accepted by the authenticator
+   context->peerAuthDone = TRUE;
+
+   //Check whether PPP authentication is complete
+   if(context->localAuthDone && context->peerAuthDone)
+   {
+      //Check current PPP phase
+      if(context->pppPhase == PPP_PHASE_AUTHENTICATE)
+      {
+         //Advance to the Network phase
+         context->pppPhase = PPP_PHASE_NETWORK;
+
+#if (IPV4_SUPPORT == ENABLED)
+         //IPCP Open event
+         ipcpOpen(context);
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+         //IPV6CP Open event
+         ipv6cpOpen(context);
+#endif
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process Authenticate-Nak packet
+ * @param[in] context PPP context
+ * @param[in] authNakPacket Packet received from the peer
+ * @param[in] length Length of the packet, in bytes
+ * @return Error code
+ **/
+
+error_t papProcessAuthNak(PppContext *context,
+   const PapAuthNakPacket *authNakPacket, size_t length)
+{
+   //Debug message
+   TRACE_INFO("\r\nPAP Authenticate-Nak packet received\r\n");
+
+   //Make sure the Authenticate-Nak packet is acceptable
+   if(context->peerConfig.authProtocol != PPP_PROTOCOL_PAP)
+      return ERROR_FAILURE;
+
+   //Check the length of the packet
+   if(length < sizeof(PapAuthNakPacket))
+      return ERROR_INVALID_LENGTH;
+
+   //When a packet is received with an invalid Identifier field, the
+   //packet is silently discarded without affecting the automaton
+   if(authNakPacket->identifier != context->papFsm.identifier)
+      return ERROR_WRONG_IDENTIFIER;
+
+   //Switch to the Nak-Rcvd state
+   context->papFsm.peerState = PAP_STATE_7_NAK_RCVD;
+   //Authentication failed
+   lcpClose(context);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Send Authenticate-Request packet
+ * @param[in] context PPP context
+ * @return Error code
+ **/
+
+error_t papSendAuthReq(PppContext *context)
+{
+   error_t error;
+   size_t usernameLen;
+   size_t passwordLen;
+   size_t length;
+   size_t offset;
+   uint8_t *p;
+   NetBuffer *buffer;
+   PapAuthReqPacket *authReqPacket;
+
+   //Get the length of the user name
+   usernameLen = strlen(context->username);
+   //Get the length of the password
+   passwordLen = strlen(context->password);
+
+   //Calculate the length of the Authenticate-Request packet
+   length = sizeof(PapAuthReqPacket) + 1 + usernameLen + passwordLen;
+
+   //Allocate a buffer memory to hold the packet
+   buffer = pppAllocBuffer(length, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the Authenticate-Request packet
+   authReqPacket = netBufferAt(buffer, offset);
+
+   //Format packet header
+   authReqPacket->code = PAP_CODE_AUTH_REQ;
+   authReqPacket->identifier = ++context->papFsm.identifier;
+   authReqPacket->length = htons(length);
+
+   //The Peer-ID-Length field indicates the length of Peer-ID field
+   authReqPacket->peerIdLength = usernameLen;
+   //Append Peer-ID
+   memcpy(authReqPacket->peerId, context->username, usernameLen);
+
+   //Point to the Passwd-Length field
+   p = authReqPacket->peerId + usernameLen;
+   //The Passwd-Length field indicates the length of Password field
+   p[0] = passwordLen;
+
+   //Append Password
+   memcpy(p + 1, context->password, passwordLen);
+
+   //Adjust the length of the multi-part buffer
+   netBufferSetLength(buffer, offset + length);
+
+   //Debug message
+   TRACE_INFO("Sending PAP Authenticate-Request packet (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump packet contents for debugging purpose
+   pppDumpPacket((PppPacket *) authReqPacket, length, PPP_PROTOCOL_PAP);
+
+   //Send PPP frame
+   error = pppSendFrame(context->interface, buffer, offset, PPP_PROTOCOL_PAP);
+
+   //The restart counter is decremented each time a Authenticate-Request is sent
+   if(context->papFsm.restartCounter > 0)
+      context->papFsm.restartCounter--;
+
+   //Save the time at which the packet was sent
+   context->papFsm.timestamp = osGetSystemTime();
+
+   //Free previously allocated memory block
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send Authenticate-Ack packet
+ * @param[in] context PPP context
+ * @param[in] identifier Identifier field
+ * @return Error code
+ **/
+
+error_t papSendAuthAck(PppContext *context, uint8_t identifier)
+{
+   error_t error;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   PapAuthAckPacket *authAckPacket;
+
+   //Retrieve the length of the Authenticate-Ack packet
+   length = sizeof(PapAuthAckPacket);
+
+   //Allocate a buffer memory to hold the Authenticate-Ack packet
+   buffer = pppAllocBuffer(length, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the Authenticate-Ack packet
+   authAckPacket = netBufferAt(buffer, offset);
+
+   //Format packet header
+   authAckPacket->code = PAP_CODE_AUTH_ACK;
+   authAckPacket->identifier = identifier;
+   authAckPacket->length = htons(length);
+
+   //The Message field is zero or more octets, and its contents are
+   //implementation dependent
+   authAckPacket->msgLength = 0;
+
+   //Debug message
+   TRACE_INFO("Sending PAP Authenticate-Ack packet (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump packet contents for debugging purpose
+   pppDumpPacket((PppPacket *) authAckPacket, length, PPP_PROTOCOL_PAP);
+
+   //Send PPP frame
+   error = pppSendFrame(context->interface, buffer, offset, PPP_PROTOCOL_PAP);
+
+   //Free previously allocated memory block
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send Authenticate-Nak packet
+ * @param[in] context PPP context
+ * @param[in] identifier Identifier field
+ * @return Error code
+ **/
+
+error_t papSendAuthNak(PppContext *context, uint8_t identifier)
+{
+   error_t error;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   PapAuthNakPacket *authNakPacket;
+
+   //Retrieve the length of the Authenticate-Nak packet
+   length = sizeof(PapAuthNakPacket);
+
+   //Allocate a buffer memory to hold the Authenticate-Nak packet
+   buffer = pppAllocBuffer(length, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the Authenticate-Nak packet
+   authNakPacket = netBufferAt(buffer, offset);
+
+   //Format packet header
+   authNakPacket->code = PAP_CODE_AUTH_NAK;
+   authNakPacket->identifier = identifier;
+   authNakPacket->length = htons(length);
+
+   //The Message field is zero or more octets, and its contents are
+   //implementation dependent
+   authNakPacket->msgLength = 0;
+
+   //Debug message
+   TRACE_INFO("Sending PAP Authenticate-Nak packet (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump packet contents for debugging purpose
+   pppDumpPacket((PppPacket *) authNakPacket, length, PPP_PROTOCOL_PAP);
+
+   //Send PPP frame
+   error = pppSendFrame(context->interface, buffer, offset, PPP_PROTOCOL_PAP);
+
+   //Free previously allocated memory block
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Password verification
+ * @param[in] context PPP context
+ * @param[in] password NULL-terminated string containing the password to be checked
+ * @return TRUE if the password is valid, else FALSE
+ **/
+
+bool_t papCheckPassword(PppContext *context, const char_t *password)
+{
+   size_t n;
+   bool_t status;
+
+   //This flag tells whether the password is valid
+   status = FALSE;
+
+   //Retrieve the length of the password
+   n = strlen(password);
+
+   //Compare the length of the password against the expected value
+   if(n == context->papFsm.passwordLen)
+   {
+      //Check whether the password is valid
+      if(!memcmp(password, context->papFsm.password, n))
+         status = TRUE;
+   }
+
+   //Return TRUE is the password is valid, else FALSE
+   return status;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/pap.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,182 @@
+/**
+ * @file pap.h
+ * @brief PAP (Password Authentication Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _PAP_H
+#define _PAP_H
+
+//Dependencies
+#include "core/net.h"
+#include "ppp/ppp.h"
+
+//PAP authentication support
+#ifndef PAP_SUPPORT
+   #define PAP_SUPPORT ENABLED
+#elif (PAP_SUPPORT != ENABLED && PAP_SUPPORT != DISABLED)
+   #error PAP_SUPPORT parameter is not valid
+#endif
+
+//Restart timer
+#ifndef PAP_RESTART_TIMER
+   #define PAP_RESTART_TIMER 3000
+#elif (PAP_RESTART_TIMER < 1000)
+   #error PAP_RESTART_TIMER parameter is not valid
+#endif
+
+//Maximum number of retransmissions for Authenticate-Request packets
+#ifndef PAP_MAX_REQUESTS
+   #define PAP_MAX_REQUESTS 5
+#elif (PAP_MAX_REQUESTS < 1)
+   #error PAP_MAX_REQUESTS parameter is not valid
+#endif
+
+
+/**
+ * @brief PAP states
+ **/
+
+typedef enum
+{
+   PAP_STATE_0_INITIAL  = 0,
+   PAP_STATE_1_STARTED  = 1,
+   PAP_STATE_2_REQ_SENT = 2,
+   PAP_STATE_3_REQ_RCVD = 3,
+   PAP_STATE_4_ACK_SENT = 4,
+   PAP_STATE_5_ACK_RCVD = 5,
+   PAP_STATE_6_NAK_SENT = 6,
+   PAP_STATE_7_NAK_RCVD = 7
+} PapState;
+
+
+/**
+ * @brief Code field values
+ **/
+
+typedef enum
+{
+   PAP_CODE_AUTH_REQ = 1, ///<Authenticate-Request
+   PAP_CODE_AUTH_ACK = 2, ///<Authenticate-Ack
+   PAP_CODE_AUTH_NAK = 3  ///<Authenticate-Nak
+} PapCode;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief Authenticate-Request packet
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t code;         //0
+   uint8_t identifier;   //1
+   uint16_t length;      //2-3
+   uint8_t peerIdLength; //4
+   uint8_t peerId[];     //5
+} __end_packed PapAuthReqPacket;
+
+
+/**
+ * @brief Authenticate-Ack packet
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t code;       //0
+   uint8_t identifier; //1
+   uint16_t length;    //2-3
+   uint8_t msgLength;  //4
+   uint8_t message[];  //5
+} __end_packed PapAuthAckPacket;
+
+
+/**
+ * @brief Authenticate-Nak packet
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t code;       //0
+   uint8_t identifier; //1
+   uint16_t length;    //2-3
+   uint8_t msgLength;  //4
+   uint8_t message[];  //5
+} __end_packed PapAuthNakPacket;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+/**
+ * @brief PAP finite state machine
+ **/
+
+typedef struct
+{
+   uint_t localState;       ///<Local state
+   uint_t peerState;        ///<Peer state
+   uint8_t identifier;      ///<Identifier used to match requests and replies
+   uint_t restartCounter;   ///<Restart counter
+   systime_t timestamp;     ///<Timestamp to manage retransmissions
+   const uint8_t *password; ///<Peer's password
+   size_t passwordLen;      ///<Length of the password in bytes
+} PapFsm;
+
+
+//PAP related functions
+error_t papStartAuth(PppContext *context);
+error_t papAbortAuth(PppContext *context);
+
+void papTick(PppContext *context);
+
+void papProcessPacket(PppContext *context,
+   const PppPacket *packet, size_t length);
+
+error_t papProcessAuthReq(PppContext *context,
+   const PapAuthReqPacket *authReqPacket, size_t length);
+
+error_t papProcessAuthAck(PppContext *context,
+   const PapAuthAckPacket *authAckPacket, size_t length);
+
+error_t papProcessAuthNak(PppContext *context,
+   const PapAuthNakPacket *authNakPacket, size_t length);
+
+error_t papSendAuthReq(PppContext *context);
+error_t papSendAuthAck(PppContext *context, uint8_t identifier);
+error_t papSendAuthNak(PppContext *context, uint8_t identifier);
+
+bool_t papCheckPassword(PppContext *context, const char_t *password);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/ppp.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1281 @@
+/**
+ * @file ppp.c
+ * @brief PPP (Point-to-Point Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL PPP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "ppp/ppp.h"
+#include "ppp/ppp_hdlc.h"
+#include "ppp/ppp_debug.h"
+#include "ppp/lcp.h"
+#include "ppp/ipcp.h"
+#include "ppp/ipv6cp.h"
+#include "ppp/pap.h"
+#include "ppp/chap.h"
+#include "str.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (PPP_SUPPORT == ENABLED)
+
+//Tick counter to handle periodic operations
+systime_t pppTickCounter;
+
+//FCS lookup table
+static const uint16_t fcsTable[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
+};
+
+
+/**
+ * @brief Initialize settings with default values
+ * @param[out] settings Structure that contains PPP settings
+ **/
+
+void pppGetDefaultSettings(PppSettings *settings)
+{
+   //Use default interface
+   settings->interface = netGetDefaultInterface();
+
+   //Default MRU
+   settings->mru = PPP_DEFAULT_MRU;
+   //Default async control character map
+   settings->accm = PPP_DEFAULT_ACCM;
+   //Allowed authentication protocols
+   settings->authProtocol = PPP_AUTH_PROTOCOL_PAP | PPP_AUTH_PROTOCOL_CHAP_MD5;
+
+   //Random data generation callback function
+   settings->randCallback = NULL;
+   //PPP authentication callback function
+   settings->authCallback = NULL;
+}
+
+
+/**
+ * @brief PPP initialization
+ * @param[in] context Pointer to the PPP context
+ * @param[in] settings PPP specific settings
+ * @return Error code
+ **/
+
+error_t pppInit(PppContext *context, const PppSettings *settings)
+{
+   error_t error;
+   NetInterface *interface;
+
+   //Debug message
+   TRACE_INFO("PPP initialization\r\n");
+
+   //Underlying network interface
+   interface = settings->interface;
+
+   //Initialize PPP context
+   memset(context, 0, sizeof(PppContext));
+
+   //Save user settings
+   context->settings = *settings;
+
+#if (PAP_SUPPORT == DISABLED)
+   //PAP authentication is not supported
+   context->settings.authProtocol &= ~PPP_AUTH_PROTOCOL_PAP;
+#endif
+
+#if (PAP_SUPPORT == DISABLED)
+   //CHAP with MD5 authentication is not supported
+   context->settings.authProtocol &= ~PPP_AUTH_PROTOCOL_CHAP_MD5;
+#endif
+
+   //Attach the PPP context to the network interface
+   interface->pppContext = context;
+
+   //Initialize structure
+   context->interface = interface;
+   context->timeout = INFINITE_DELAY;
+
+   //Initialize PPP finite state machine
+   context->pppPhase = PPP_PHASE_DEAD;
+   context->lcpFsm.state = PPP_STATE_0_INITIAL;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //Initialize IPCP finite state machine
+   context->ipcpFsm.state = PPP_STATE_0_INITIAL;
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+   //Initialize IPV6CP finite state machine
+   context->ipv6cpFsm.state = PPP_STATE_0_INITIAL;
+#endif
+
+#if (PAP_SUPPORT == ENABLED)
+   //Initialize PAP finite state machine
+   context->papFsm.localState = PAP_STATE_0_INITIAL;
+   context->papFsm.peerState = PAP_STATE_0_INITIAL;
+#endif
+
+#if (CHAP_SUPPORT == ENABLED)
+   //Initialize CHAP finite state machine
+   context->chapFsm.localState = CHAP_STATE_0_INITIAL;
+   context->chapFsm.peerState = CHAP_STATE_0_INITIAL;
+#endif
+
+   //Attach PPP HDLC driver
+   error = netSetDriver(interface, &pppHdlcDriver);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Set timeout value for blocking operations
+ * @param[in] interface Underlying network interface
+ * @param[in] timeout Maximum time to wait
+ * @return Error code
+ **/
+
+error_t pppSetTimeout(NetInterface *interface, systime_t timeout)
+{
+   PppContext *context;
+
+   //Check parameters
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Make sure PPP has been properly configured
+   if(interface->pppContext == NULL)
+      return ERROR_NOT_CONFIGURED;
+
+   //Point to the PPP context
+   context = interface->pppContext;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Set timeout value
+   context->timeout = timeout;
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set PPP authentication information
+ * @param[in] interface Underlying network interface
+ * @param[in] username NULL-terminated string containing the user name to be used
+ * @param[in] password NULL-terminated string containing the password to be used
+ * @return Error code
+ **/
+
+error_t pppSetAuthInfo(NetInterface *interface,
+   const char_t *username, const char_t *password)
+{
+   PppContext *context;
+
+   //Check parameters
+   if(interface == NULL || username == NULL || password == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Make sure PPP has been properly configured
+   if(interface->pppContext == NULL)
+      return ERROR_NOT_CONFIGURED;
+
+   //Point to the PPP context
+   context = interface->pppContext;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Save user name
+   strSafeCopy(context->username, username, PPP_MAX_USERNAME_LEN);
+   //Save password
+   strSafeCopy(context->password, password, PPP_MAX_PASSWORD_LEN);
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Password verification
+ * @param[in] interface Underlying network interface
+ * @param[in] password NULL-terminated string containing the password to be checked
+ * @return TRUE if the password is valid, else FALSE
+ **/
+
+bool_t pppCheckPassword(NetInterface *interface, const char_t *password)
+{
+   bool_t status;
+   PppContext *context;
+
+   //Debug message
+   TRACE_DEBUG("PPP password verification...\r\n");
+
+   //The password has not been verified yet
+   status = FALSE;
+
+   //Point to the PPP context
+   context = interface->pppContext;
+
+   //Make sure PPP has been properly configured
+   if(context != NULL)
+   {
+      //Check authentication protocol
+      if(context->localConfig.authProtocol == PPP_PROTOCOL_PAP)
+      {
+#if (PAP_SUPPORT == ENABLED)
+         //PAP authentication protocol
+         status = papCheckPassword(context, password);
+#endif
+      }
+      //CHAP authentication protocol?
+      else if(context->localConfig.authProtocol == PPP_PROTOCOL_CHAP)
+      {
+#if (CHAP_SUPPORT == ENABLED)
+         //CHAP authentication protocol
+         status = chapCheckPassword(context, password);
+#endif
+      }
+   }
+
+   //Return TRUE is the password is valid, else FALSE
+   return status;
+}
+
+
+/**
+ * @brief Send AT command
+ * @param[in] interface Underlying network interface
+ * @param[in] data NULL-terminated string that contains the AT command to be sent
+ * @return Error code
+ **/
+
+error_t pppSendAtCommand(NetInterface *interface, const char_t *data)
+{
+   error_t error;
+   bool_t status;
+   PppContext *context;
+
+   //Check parameters
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Make sure PPP has been properly configured
+   if(interface->pppContext == NULL)
+      return ERROR_NOT_CONFIGURED;
+
+   //Point to the PPP context
+   context = interface->pppContext;
+
+   //Wait for the send buffer to be available for writing
+   status = osWaitForEvent(&interface->nicTxEvent, context->timeout);
+
+   //Check status
+   if(status)
+   {
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+
+      //Check current PPP state
+      if(context->pppPhase == PPP_PHASE_DEAD)
+      {
+         //Purge receive buffer
+         error = pppHdlcDriverPurgeRxBuffer(context);
+
+         //Send AT command
+         if(!error)
+            error = pppHdlcDriverSendAtCommand(interface, data);
+      }
+      else
+      {
+         //Report an error
+         error = ERROR_ALREADY_CONNECTED;
+      }
+
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+   }
+   else
+   {
+      //Timeout error
+      return ERROR_TIMEOUT;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Wait for an incoming AT command
+ * @param[in] interface Underlying network interface
+ * @param[out] data Buffer where to store the incoming AT command
+ * @param[in] size Size of the buffer, in bytes
+ * @return Error code
+ **/
+
+error_t pppReceiveAtCommand(NetInterface *interface, char_t *data, size_t size)
+{
+   error_t error;
+   systime_t time;
+   systime_t start;
+   PppContext *context;
+
+   //Check parameters
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Make sure PPP has been properly configured
+   if(interface->pppContext == NULL)
+      return ERROR_NOT_CONFIGURED;
+
+   //Point to the PPP context
+   context = interface->pppContext;
+   //Save current time
+   start = osGetSystemTime();
+
+   //Wait for an incoming AT command
+   while(1)
+   {
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+
+      //Check current PPP state
+      if(context->pppPhase == PPP_PHASE_DEAD)
+      {
+         //Wait for an incoming AT command
+         error = pppHdlcDriverReceiveAtCommand(interface, data, size);
+      }
+      else
+      {
+         //Report an error
+         error = ERROR_ALREADY_CONNECTED;
+      }
+
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+
+      //No data received?
+      if(error == ERROR_BUFFER_EMPTY || data[0] == '\0')
+      {
+         //Get current time
+         time = osGetSystemTime();
+
+         //Check whether the timeout period has elapsed
+         if(timeCompare(time, start + context->timeout) >= 0)
+         {
+            //Timeout error
+            error = ERROR_TIMEOUT;
+            //Exit immediately
+            break;
+         }
+         else
+         {
+            //Wait for more data to be received
+            osDelayTask(PPP_POLLING_INTERVAL);
+         }
+      }
+      else
+      {
+         //We are done
+         break;
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Establish a PPP connection
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t pppConnect(NetInterface *interface)
+{
+   error_t error;
+   PppContext *context;
+#if (NET_RTOS_SUPPORT == ENABLED)
+   systime_t time;
+   systime_t start;
+#endif
+
+   //Check parameters
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Make sure PPP has been properly configured
+   if(interface->pppContext == NULL)
+      return ERROR_NOT_CONFIGURED;
+
+   //Point to the PPP context
+   context = interface->pppContext;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Default PPP phase
+   context->pppPhase = PPP_PHASE_DEAD;
+
+   //Initialize LCP FSM
+   context->lcpFsm.state = PPP_STATE_0_INITIAL;
+   context->lcpFsm.identifier = 0;
+   context->lcpFsm.restartCounter = 0;
+   context->lcpFsm.failureCounter = 0;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //Initialize IPCP FSM
+   context->ipcpFsm.state = PPP_STATE_0_INITIAL;
+   context->ipcpFsm.identifier = 0;
+   context->ipcpFsm.restartCounter = 0;
+   context->ipcpFsm.failureCounter = 0;
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+   //Initialize IPV6CP FSM
+   context->ipv6cpFsm.state = PPP_STATE_0_INITIAL;
+   context->ipv6cpFsm.identifier = 0;
+   context->ipv6cpFsm.restartCounter = 0;
+   context->ipv6cpFsm.failureCounter = 0;
+#endif
+
+   //Authentication has not been completed
+   context->localAuthDone = FALSE;
+   context->peerAuthDone = FALSE;
+
+#if (PAP_SUPPORT == ENABLED)
+   //Initialize PAP FSM
+   context->papFsm.localState = PAP_STATE_0_INITIAL;
+   context->papFsm.peerState = PAP_STATE_0_INITIAL;
+   context->papFsm.identifier = 0;
+   context->papFsm.restartCounter = 0;
+#endif
+
+#if (CHAP_SUPPORT == ENABLED)
+   //Initialize CHAP FSM
+   context->chapFsm.localState = CHAP_STATE_0_INITIAL;
+   context->chapFsm.localIdentifier = 0;
+   context->chapFsm.peerState = CHAP_STATE_0_INITIAL;
+   context->chapFsm.peerIdentifier = 0;
+#endif
+
+   //Default local configuration
+   context->localConfig.mru = context->settings.mru;
+   context->localConfig.mruRejected = FALSE;
+   context->localConfig.accm = context->settings.accm;
+   context->localConfig.accmRejected = FALSE;
+   context->localConfig.authProtocol = 0;
+   context->localConfig.authAlgo = 0;
+   context->localConfig.authProtocolRejected = FALSE;
+   context->localConfig.magicNumber = PPP_DEFAULT_MAGIC_NUMBER;
+   context->localConfig.magicNumberRejected = FALSE;
+   context->localConfig.pfc = TRUE;
+   context->localConfig.pfcRejected = FALSE;
+   context->localConfig.acfc = TRUE;
+   context->localConfig.acfcRejected = FALSE;
+
+   //Check whether the other end of the PPP link must be authenticated
+   if(context->settings.authCallback != NULL)
+   {
+#if (PAP_SUPPORT == ENABLED)
+      //PAP provides an easy implementation of peer authentication
+      if(context->settings.authProtocol & PPP_AUTH_PROTOCOL_PAP)
+      {
+         //Select PAP authentication protocol
+         context->localConfig.authProtocol = PPP_PROTOCOL_PAP;
+      }
+#endif
+#if (CHAP_SUPPORT == ENABLED)
+      //CHAP with MD5 ensures greater security in the implementation
+      if(context->settings.authProtocol & PPP_AUTH_PROTOCOL_CHAP_MD5)
+      {
+         //Select CHAP with MD5 authentication protocol
+         context->localConfig.authProtocol = PPP_PROTOCOL_CHAP;
+         context->localConfig.authAlgo = CHAP_ALGO_ID_CHAP_MD5;
+      }
+#endif
+   }
+
+   //Default peer's configuration
+   context->peerConfig.mru = PPP_DEFAULT_MRU;
+   context->peerConfig.accm = PPP_DEFAULT_ACCM;
+   context->peerConfig.authProtocol = 0;
+   context->peerConfig.magicNumber = PPP_DEFAULT_MAGIC_NUMBER;
+   context->peerConfig.pfc = FALSE;
+   context->peerConfig.acfc = FALSE;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //Default local configuration
+   context->localConfig.ipAddr = interface->ipv4Context.addr;
+   context->localConfig.ipAddrRejected = FALSE;
+   context->localConfig.primaryDns = interface->ipv4Context.dnsServerList[0];
+   context->localConfig.primaryDnsRejected = FALSE;
+
+#if (IPV4_DNS_SERVER_LIST_SIZE >= 2)
+   context->localConfig.secondaryDns = interface->ipv4Context.dnsServerList[1];
+   context->localConfig.secondaryDnsRejected = FALSE;
+#else
+   context->localConfig.secondaryDns = IPV4_UNSPECIFIED_ADDR;
+   context->localConfig.secondaryDnsRejected = FALSE;
+#endif
+
+   //Manual primary DNS configuration?
+   if(context->localConfig.primaryDns != IPV4_UNSPECIFIED_ADDR)
+      context->localConfig.primaryDnsRejected = TRUE;
+
+   //Manual secondary DNS configuration?
+   if(context->localConfig.secondaryDns != IPV4_UNSPECIFIED_ADDR)
+      context->localConfig.secondaryDnsRejected = TRUE;
+
+   //Default peer's configuration
+   context->peerConfig.ipAddr = interface->ipv4Context.defaultGateway;
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+   //Default local configuration
+   eui64CopyAddr(&context->localConfig.interfaceId,
+      interface->ipv6Context.addrList[0].addr.b + 8);
+
+   context->localConfig.interfaceIdRejected = FALSE;
+
+   //Default peer's configuration
+   eui64CopyAddr(&context->peerConfig.interfaceId,
+      interface->ipv6Context.routerList[0].addr.b + 8);
+#endif
+
+   //The link is available for traffic
+   error = lcpOpen(context);
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Any error to report?
+   if(error)
+      return error;
+
+#if (NET_RTOS_SUPPORT == ENABLED)
+   //Save current time
+   start = osGetSystemTime();
+
+   //Wait for the connection to be established
+   while(1)
+   {
+      //Check current PPP phase
+      if(context->pppPhase == PPP_PHASE_NETWORK)
+      {
+#if (IPV4_SUPPORT == ENABLED)
+         //Check current IPCP state
+         if(context->ipcpFsm.state == PPP_STATE_9_OPENED)
+         {
+            //Connection successfully established
+            error = NO_ERROR;
+            //Exit immediately
+            break;
+         }
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+         //Check current IPV6CP state
+         if(context->ipv6cpFsm.state == PPP_STATE_9_OPENED)
+         {
+            //Connection successfully established
+            error = NO_ERROR;
+            //Exit immediately
+            break;
+         }
+#endif
+      }
+      else if(context->pppPhase == PPP_PHASE_DEAD)
+      {
+         //Failed to establish connection
+         error = ERROR_CONNECTION_FAILED;
+         //Exit immediately
+         break;
+      }
+
+      //Check timeout value
+      if(context->timeout != INFINITE_DELAY)
+      {
+         //Get current time
+         time = osGetSystemTime();
+
+         //Check whether the timeout period has elapsed
+         if(timeCompare(time, start + context->timeout) >= 0)
+         {
+            //Report an error
+            error = ERROR_TIMEOUT;
+            //Exit immediately
+            break;
+         }
+      }
+
+      //Polling delay
+      osDelayTask(PPP_POLLING_INTERVAL);
+   }
+
+   //Failed to establish connection?
+   if(error)
+   {
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+
+      //Abort the PPP connection
+      context->pppPhase = PPP_PHASE_DEAD;
+      context->lcpFsm.state = PPP_STATE_0_INITIAL;
+
+#if (IPV4_SUPPORT == ENABLED)
+      //Reset IPCP finite state machine
+      context->ipcpFsm.state = PPP_STATE_0_INITIAL;
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+      //Reset IPV6CP finite state machine
+      context->ipv6cpFsm.state = PPP_STATE_0_INITIAL;
+#endif
+
+#if (PAP_SUPPORT == ENABLED)
+      //Abort PAP authentication process
+      context->papFsm.localState = PAP_STATE_0_INITIAL;
+      context->papFsm.peerState = PAP_STATE_0_INITIAL;
+#endif
+
+#if (CHAP_SUPPORT == ENABLED)
+      //Abort CHAP authentication process
+      context->chapFsm.localState = CHAP_STATE_0_INITIAL;
+      context->chapFsm.peerState = CHAP_STATE_0_INITIAL;
+#endif
+
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+   }
+#endif
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Close a PPP connection
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t pppClose(NetInterface *interface)
+{
+   error_t error;
+   PppContext *context;
+#if (NET_RTOS_SUPPORT == ENABLED)
+   systime_t time;
+   systime_t start;
+#endif
+
+   //Check parameters
+   if(interface == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Make sure PPP has been properly configured
+   if(interface->pppContext == NULL)
+      return ERROR_NOT_CONFIGURED;
+
+   //Point to the PPP context
+   context = interface->pppContext;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //The link is no longer available for traffic
+   error = lcpClose(context);
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Any error to report?
+   if(error)
+      return error;
+
+#if (NET_RTOS_SUPPORT == ENABLED)
+   //Save current time
+   start = osGetSystemTime();
+
+   //Wait for the connection to be closed
+   while(1)
+   {
+      //Check current PPP phase
+      if(context->pppPhase == PPP_PHASE_DEAD)
+      {
+         //PPP connection is closed
+         error = NO_ERROR;
+         //Exit immediately
+         break;
+      }
+
+      //Check timeout value
+      if(context->timeout != INFINITE_DELAY)
+      {
+         //Get current time
+         time = osGetSystemTime();
+
+         //Check whether the timeout period has elapsed
+         if(timeCompare(time, start + context->timeout) >= 0)
+         {
+            //Report an error
+            error = ERROR_TIMEOUT;
+            //Exit immediately
+            break;
+         }
+      }
+
+      //Poll the state
+      osDelayTask(PPP_POLLING_INTERVAL);
+   }
+
+   //Failed to properly close the connection?
+   if(error)
+   {
+      //Get exclusive access
+      osAcquireMutex(&netMutex);
+
+      //Abort the PPP connection
+      context->pppPhase = PPP_PHASE_DEAD;
+      context->lcpFsm.state = PPP_STATE_0_INITIAL;
+
+#if (IPV4_SUPPORT == ENABLED)
+      //Reset IPCP finite state machine
+      context->ipcpFsm.state = PPP_STATE_0_INITIAL;
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+      //Reset IPV6CP finite state machine
+      context->ipv6cpFsm.state = PPP_STATE_0_INITIAL;
+#endif
+
+#if (PAP_SUPPORT == ENABLED)
+      //Abort PAP authentication process
+      context->papFsm.localState = PAP_STATE_0_INITIAL;
+      context->papFsm.peerState = PAP_STATE_0_INITIAL;
+#endif
+
+#if (CHAP_SUPPORT == ENABLED)
+      //Abort CHAP authentication process
+      context->chapFsm.localState = CHAP_STATE_0_INITIAL;
+      context->chapFsm.peerState = CHAP_STATE_0_INITIAL;
+#endif
+
+      //Release exclusive access
+      osReleaseMutex(&netMutex);
+   }
+#endif
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief PPP timer handler
+ *
+ * This routine must be periodically called by the TCP/IP stack to
+ * manage retransmissions
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void pppTick(NetInterface *interface)
+{
+   PppContext *context;
+
+   //PPP driver?
+   if(interface->nicDriver->type == NIC_TYPE_PPP)
+   {
+      //Point to the PPP context
+      context = interface->pppContext;
+
+      //Handle LCP retransmission timer
+      lcpTick(context);
+
+#if (IPV4_SUPPORT == ENABLED)
+      //Handle IPCP retransmission timer
+      ipcpTick(context);
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+      //Handle IPV6CP retransmission timer
+      ipv6cpTick(context);
+#endif
+
+#if (PAP_SUPPORT == ENABLED)
+      //Handle PAP timer
+      papTick(context);
+#endif
+
+#if (CHAP_SUPPORT == ENABLED)
+      //Handle CHAP timer
+      chapTick(context);
+#endif
+   }
+}
+
+
+/**
+ * @brief Process an incoming PPP frame
+ * @param[in] interface Underlying network interface
+ * @param[in] frame Incoming PPP frame to process
+ * @param[in] length Total frame length
+ **/
+
+void pppProcessFrame(NetInterface *interface, uint8_t *frame, size_t length)
+{
+   size_t n;
+   uint16_t protocol;
+   PppContext *context;
+#if (IPV6_SUPPORT == ENABLED)
+   NetBuffer1 buffer;
+#endif
+
+   //Point to the PPP context
+   context = interface->pppContext;
+
+   //Check the length of the PPP frame
+   if(length < PPP_FCS_SIZE)
+      return;
+
+   //Debug message
+   TRACE_DEBUG("PPP frame received (%" PRIuSIZE " bytes)...\r\n", length);
+
+   //The value of the residue is 0x0F47 when no FCS errors are detected
+   if(pppCalcFcs(frame, length) != 0x0F47)
+   {
+      //Debug message
+      TRACE_WARNING("Wrong FCS detected!\r\n");
+      //Drop the received frame
+      return;
+   }
+
+   //Calculate the length of PPP frame excluding the FCS field
+   length -= PPP_FCS_SIZE;
+
+   //Decompress the frame header
+   n = pppParseFrameHeader(frame, length, &protocol);
+   //Malformed PPP frame?
+   if(!n)
+      return;
+
+   //Point to the payload field
+   frame += n;
+   length -= n;
+
+   //Check protocol field
+   switch(protocol)
+   {
+   //Link control protocol?
+   case PPP_PROTOCOL_LCP:
+      //Process incoming LCP packet
+      lcpProcessPacket(context, (PppPacket *) frame, length);
+      break;
+
+#if (IPV4_SUPPORT == ENABLED)
+   //IP control protocol?
+   case PPP_PROTOCOL_IPCP:
+      //Process incoming IPCP packet
+      ipcpProcessPacket(context, (PppPacket *) frame, length);
+      break;
+   //IP protocol?
+   case PPP_PROTOCOL_IP:
+      //Process incoming IPv4 packet
+      ipv4ProcessPacket(interface, (Ipv4Header *) frame, length);
+      break;
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+   //IPv6 control protocol?
+   case PPP_PROTOCOL_IPV6CP:
+      //Process incoming IPV6CP packet
+      ipv6cpProcessPacket(context, (PppPacket *) frame, length);
+      break;
+   //IPv6 protocol?
+   case PPP_PROTOCOL_IPV6:
+      //The incoming PPP frame fits in a single chunk
+      buffer.chunkCount = 1;
+      buffer.maxChunkCount = 1;
+      buffer.chunk[0].address = frame;
+      buffer.chunk[0].length = length;
+      buffer.chunk[0].size = 0;
+
+      //Process incoming IPv6 packet
+      ipv6ProcessPacket(interface, (NetBuffer *) &buffer, 0);
+      break;
+#endif
+
+#if (PAP_SUPPORT == ENABLED)
+   //PAP protocol?
+   case PPP_PROTOCOL_PAP:
+      //Process incoming PAP packet
+      papProcessPacket(context, (PppPacket *) frame, length);
+      break;
+#endif
+
+#if (CHAP_SUPPORT == ENABLED)
+   //CHAP protocol?
+   case PPP_PROTOCOL_CHAP:
+      //Process incoming CHAP packet
+      chapProcessPacket(context, (PppPacket *) frame, length);
+      break;
+#endif
+
+   //Unknown protocol field
+   default:
+      //The peer is attempting to use a protocol which is unsupported
+      lcpProcessUnknownProtocol(context, protocol, frame, length);
+      break;
+   }
+}
+
+
+/**
+ * @brief Send a PPP frame
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data
+ * @param[in] offset Offset to the first data byte
+ * @param[in] protocol Protocol field value
+ * @return Error code
+ **/
+
+error_t pppSendFrame(NetInterface *interface,
+   NetBuffer *buffer, size_t offset, uint16_t protocol)
+{
+   error_t error;
+   size_t length;
+   uint16_t fcs;
+   uint8_t *p;
+   PppContext *context;
+
+   //Point to the PPP context
+   context = interface->pppContext;
+
+   //Check whether the Protocol field can be compressed
+   if(context->peerConfig.pfc && MSB(protocol) == 0)
+   {
+      //Is there enough space in the buffer to store the compressed
+      //Protocol field?
+      if(offset < 1)
+         return ERROR_FAILURE;
+
+      //Make room for the Protocol field
+      offset--;
+      //Move backward
+      p = netBufferAt(buffer, offset);
+      //Compress the Protocol field
+      p[0] = LSB(protocol);
+   }
+   else
+   {
+      //Is there enough space in the buffer to store the uncompressed
+      //Protocol field?
+      if(offset < 2)
+         return ERROR_FAILURE;
+
+      //Make room for the Protocol field
+      offset -= 2;
+      //Move backward
+      p = netBufferAt(buffer, offset);
+      //Do not compress the Protocol field
+      p[0] = MSB(protocol);
+      p[1] = LSB(protocol);
+   }
+
+   //Check whether the Address and Control fields can be compressed
+   if(context->peerConfig.acfc && protocol != PPP_PROTOCOL_LCP)
+   {
+      //On transmission, compressed Address and Control fields
+      //are simply omitted...
+   }
+   else
+   {
+      //Is there enough space in the buffer to store the uncompressed
+      //Address and Control fields?
+      if(offset < 2)
+         return ERROR_FAILURE;
+
+      //Make room for the Address and Control fields
+      offset -= 2;
+      //Move backward
+      p = netBufferAt(buffer, offset);
+      //Do not compress the Address and Control fields
+      p[0] = PPP_ADDR_FIELD;
+      p[1] = PPP_CTRL_FIELD;
+   }
+
+   //Retrieve the length of the frame
+   length = netBufferGetLength(buffer) - offset;
+
+   //Compute FCS over the header and payload
+   fcs = pppCalcFcsEx(buffer, offset, length);
+   //The FCS is transmitted least significant octet first
+   fcs = htole16(fcs);
+
+   //Append the calculated FCS value
+   error = netBufferAppend(buffer, &fcs, PPP_FCS_SIZE);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Adjust frame length
+   length += PPP_FCS_SIZE;
+
+   //Debug message
+   TRACE_DEBUG("Sending PPP frame (%" PRIuSIZE " bytes)...\r\n", length);
+   TRACE_DEBUG("  Protocol = 0x%04" PRIX16 "\r\n", protocol);
+
+   //Send the resulting frame over the specified link
+   error = nicSendPacket(interface, buffer, offset);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse PPP frame header
+ * @param[in] frame Pointer to the PPP frame
+ * @param[in] length Length of the frame, in bytes
+ * @param[out] protocol Value of the Protocol field
+ * @return If the PPP header was successfully parsed, the function returns the size
+ *   of the PPP header, in bytes. If a parsing error occurred, zero is returned
+ **/
+
+size_t pppParseFrameHeader(const uint8_t *frame, size_t length, uint16_t *protocol)
+{
+   size_t n;
+
+   //Size of the PPP header, in bytes
+   n = 0;
+
+   //On reception, the Address and Control fields are decompressed by
+   //examining the first two octets
+   if(length >= 2)
+   {
+      //If they contain the values 0xff and 0x03, they are assumed to be
+      //the Address and Control fields. If not, it is assumed that the
+      //fields were compressed and were not transmitted
+      if(frame[0] == PPP_ADDR_FIELD && frame[1] == PPP_CTRL_FIELD)
+      {
+         //Move to the Protocol field
+         n = 2;
+      }
+   }
+
+   //Check the length of the PPP frame
+   if(length >= (n + 1))
+   {
+      //PPP Protocol field numbers are chosen such that some values may be
+      //compressed into a single octet form which is clearly distinguishable
+      //from the two octet form
+      if(frame[n] & 0x01)
+      {
+         //The presence of a binary 1 as the LSB marks the last octet of
+         //the Protocol field
+         *protocol = frame[n];
+
+         //Update the length of the header
+         n++;
+      }
+      else
+      {
+         //Check the length of the PPP frame
+         if(length >= (n + 2))
+         {
+            //The Protocol field is not compressed
+            *protocol = (frame[n] << 8) | frame[n + 1];
+
+            //Update the length of the header
+            n += 2;
+         }
+         else
+         {
+            //Malformed PPP frame
+            n = 0;
+         }
+      }
+   }
+   else
+   {
+      //Malformed PPP frame
+      n = 0;
+   }
+
+   //Return the size of the PPP header, in bytes
+   return n;
+}
+
+
+/**
+ * @brief FCS calculation
+ * @param[in] data Pointer to the data over which to calculate the FCS
+ * @param[in] length Number of bytes to process
+ * @return Resulting FCS value
+ **/
+
+uint16_t pppCalcFcs(const uint8_t *data, size_t length)
+{
+   size_t i;
+   uint16_t fcs;
+
+   //FCS preset value
+   fcs = 0xFFFF;
+
+   //Loop through data
+   for(i = 0; i < length; i++)
+   {
+      //The message is processed byte by byte
+      fcs = (fcs >> 8) ^ fcsTable[(fcs & 0xFF) ^ data[i]];
+   }
+
+   //Return 1's complement value
+   return ~fcs;
+}
+
+
+/**
+ * @brief Calculate FCS over a multi-part buffer
+ * @param[in] buffer Pointer to the multi-part buffer
+ * @param[in] offset Offset from the beginning of the buffer
+ * @param[in] length Number of bytes to process
+ * @return Resulting FCS value
+ **/
+
+uint16_t pppCalcFcsEx(const NetBuffer *buffer, size_t offset, size_t length)
+{
+   uint_t i;
+   uint_t n;
+   uint16_t fcs;
+   uint8_t *p;
+
+   //FCS preset value
+   fcs = 0xFFFF;
+
+   //Loop through data chunks
+   for(i = 0; i < buffer->chunkCount && length > 0; i++)
+   {
+      //Is there any data to process in the current chunk?
+      if(offset < buffer->chunk[i].length)
+      {
+         //Point to the first data byte
+         p = (uint8_t *) buffer->chunk[i].address + offset;
+         //Compute the number of bytes to process
+         n = MIN(buffer->chunk[i].length - offset, length);
+         //Adjust byte counter
+         length -= n;
+
+         //Process current chunk
+         while(n > 0)
+         {
+            //The message is processed byte by byte
+            fcs = (fcs >> 8) ^ fcsTable[(fcs & 0xFF) ^ *p];
+
+            //Next byte
+            p++;
+            n--;
+         }
+
+         //Process the next block from the start
+         offset = 0;
+      }
+      else
+      {
+         //Skip the current chunk
+         offset -= buffer->chunk[i].length;
+      }
+   }
+
+   //Return 1's complement value
+   return ~fcs;
+}
+
+
+/**
+ * @brief Allocate a buffer to hold a PPP frame
+ * @param[in] length Desired payload length
+ * @param[out] offset Offset to the first byte of the payload
+ * @return The function returns a pointer to the newly allocated
+ *   buffer. If the system is out of resources, NULL is returned
+ **/
+
+NetBuffer *pppAllocBuffer(size_t length, size_t *offset)
+{
+   NetBuffer *buffer;
+
+   //Allocate a buffer to hold the Ethernet header and the payload
+   buffer = netBufferAlloc(length + PPP_FRAME_HEADER_SIZE);
+   //Failed to allocate buffer?
+   if(buffer == NULL)
+      return NULL;
+
+   //Offset to the first byte of the payload
+   *offset = PPP_FRAME_HEADER_SIZE;
+
+   //Return a pointer to the freshly allocated buffer
+   return buffer;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/ppp.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,518 @@
+/**
+ * @file ppp.h
+ * @brief PPP (Point-to-Point Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _PPP_H
+#define _PPP_H
+
+//Forward declaration of structures
+struct _PppPacket;
+struct _PppContext;
+#define PppPacket struct _PppPacket
+#define PppContext struct _PppContext
+
+//Dependencies
+#include "core/net.h"
+#include "ppp/pap.h"
+#include "ppp/chap.h"
+
+//PPP support
+#ifndef PPP_SUPPORT
+   #define PPP_SUPPORT DISABLED
+#elif (PPP_SUPPORT != ENABLED && PPP_SUPPORT != DISABLED)
+   #error PPP_SUPPORT parameter is not valid
+#endif
+
+//TX buffer size
+#ifndef PPP_TX_BUFFER_SIZE
+   #define PPP_TX_BUFFER_SIZE 4096
+#elif (PPP_TX_BUFFER_SIZE < 3006)
+   #error PPP_TX_BUFFER_SIZE parameter is not valid
+#endif
+
+//RX buffer size
+#ifndef PPP_RX_BUFFER_SIZE
+   #define PPP_RX_BUFFER_SIZE 8192
+#elif (PPP_RX_BUFFER_SIZE < 3006)
+   #error PPP_RX_BUFFER_SIZE parameter is not valid
+#endif
+
+//Maximum user name length
+#ifndef PPP_MAX_USERNAME_LEN
+   #define PPP_MAX_USERNAME_LEN 31
+#elif (PPP_MAX_USERNAME_LEN < 7)
+   #error PPP_MAX_USERNAME_LEN parameter is not valid
+#endif
+
+//Maximum password length
+#ifndef PPP_MAX_PASSWORD_LEN
+   #define PPP_MAX_PASSWORD_LEN 31
+#elif (PPP_MAX_PASSWORD_LEN < 7)
+   #error PPP_MAX_PASSWORD_LEN parameter is not valid
+#endif
+
+//PPP tick interval
+#ifndef PPP_TICK_INTERVAL
+   #define PPP_TICK_INTERVAL 500
+#elif (PPP_TICK_INTERVAL < 10)
+   #error PPP_TICK_INTERVAL parameter is not valid
+#endif
+
+//Polling interval for blocking functions
+#ifndef PPP_POLLING_INTERVAL
+   #define PPP_POLLING_INTERVAL 50
+#elif (PPP_POLLING_INTERVAL < 1)
+   #error PPP_POLLING_INTERVAL parameter is not valid
+#endif
+
+//Restart timer
+#ifndef PPP_RESTART_TIMER
+   #define PPP_RESTART_TIMER 3000
+#elif (PPP_RESTART_TIMER < 1000)
+   #error PPP_RESTART_TIMER parameter is not valid
+#endif
+
+//Maximum number of retransmissions for Configure-Requests
+#ifndef PPP_MAX_CONFIGURE
+   #define PPP_MAX_CONFIGURE 10
+#elif (PPP_MAX_CONFIGURE < 1)
+   #error PPP_MAX_CONFIGURE parameter is not valid
+#endif
+
+//Maximum number of retransmissions for Terminate-Requests
+#ifndef PPP_MAX_TERMINATE
+   #define PPP_MAX_TERMINATE 2
+#elif (PPP_MAX_TERMINATE < 1)
+   #error PPP_MAX_TERMINATE parameter is not valid
+#endif
+
+//Maximum number of Configure-Nak packets sent
+#ifndef PPP_MAX_FAILURE
+   #define PPP_MAX_FAILURE 5
+#elif (PPP_MAX_FAILURE < 1)
+   #error PPP_MAX_FAILURE parameter is not valid
+#endif
+
+//PPP special characters
+#define PPP_MASK_CHAR 0x20
+#define PPP_ESC_CHAR  0x7D
+#define PPP_FLAG_CHAR 0x7E
+
+//PPP default MRU
+#define PPP_DEFAULT_MRU 1500
+//PPP default async control character map
+#define PPP_DEFAULT_ACCM 0xFFFFFFFF
+//PPP default magic number
+#define PPP_DEFAULT_MAGIC_NUMBER 0
+
+//Minimum acceptable value for MRU
+#define PPP_MIN_MRU 32
+//Maximum acceptable value for MRU
+#define PPP_MAX_MRU 1500
+
+//Maximum size of Configure-Request packets
+#define PPP_MAX_CONF_REQ_SIZE 128
+
+//Maximum size of PPP frame header
+#define PPP_FRAME_HEADER_SIZE 4
+//FCS field size
+#define PPP_FCS_SIZE 2
+//Maximum size of PPP frames
+#define PPP_MAX_FRAME_SIZE (PPP_FRAME_HEADER_SIZE + PPP_MAX_MRU + PPP_FCS_SIZE)
+
+//PPP Address field
+#define PPP_ADDR_FIELD 0xFF
+///PPP Control field
+#define PPP_CTRL_FIELD 0x03
+
+
+/**
+ * @brief PPP phases
+ **/
+
+typedef enum
+{
+   PPP_PHASE_DEAD         = 0, ///<Link dead
+   PPP_PHASE_ESTABLISH    = 1, ///<Link establishment phase
+   PPP_PHASE_AUTHENTICATE = 2, ///<Authentication phase
+   PPP_PHASE_NETWORK      = 3, ///<Network-layer protocol phase
+   PPP_PHASE_TERMINATE    = 4  ///<Link termination phase
+} PppPhase;
+
+
+/**
+ * @brief LCP/NCP states
+ **/
+
+typedef enum
+{
+   PPP_STATE_0_INITIAL  = 0,
+   PPP_STATE_1_STARTING = 1,
+   PPP_STATE_2_CLOSED   = 2,
+   PPP_STATE_3_STOPPED  = 3,
+   PPP_STATE_4_CLOSING  = 4,
+   PPP_STATE_5_STOPPING = 5,
+   PPP_STATE_6_REQ_SENT = 6,
+   PPP_STATE_7_ACK_RCVD = 7,
+   PPP_STATE_8_ACK_SENT = 8,
+   PPP_STATE_9_OPENED   = 9
+} PppState;
+
+
+/**
+ * @brief Protocol field values
+ **/
+
+typedef enum
+{
+   PPP_PROTOCOL_IP     = 0x0021, ///<Internet Protocol
+   PPP_PROTOCOL_IPV6   = 0x0057, ///<Internet Protocol version 6
+   PPP_PROTOCOL_IPCP   = 0x8021, ///<IP Control Protocol
+   PPP_PROTOCOL_IPV6CP = 0x8057, ///<IPv6 Control Protocol
+   PPP_PROTOCOL_LCP    = 0xC021, ///<Link Control Protocol
+   PPP_PROTOCOL_PAP    = 0xC023, ///<Password Authentication Protocol
+   PPP_PROTOCOL_LQR    = 0xC025, ///<Link Quality Report
+   PPP_PROTOCOL_CHAP   = 0xC223  ///<Challenge Handshake Authentication Protocol
+} PppProtocol;
+
+
+/**
+ * @brief Code field values
+ **/
+
+typedef enum
+{
+   PPP_CODE_CONFIGURE_REQ = 1,  ///<Configure-Request
+   PPP_CODE_CONFIGURE_ACK = 2,  ///<Configure-Ack
+   PPP_CODE_CONFIGURE_NAK = 3,  ///<Configure-Nak
+   PPP_CODE_CONFIGURE_REJ = 4,  ///<Configure-Reject
+   PPP_CODE_TERMINATE_REQ = 5,  ///<Terminate-Request
+   PPP_CODE_TERMINATE_ACK = 6,  ///<Terminate-Ack
+   PPP_CODE_CODE_REJ      = 7,  ///<Code-Reject
+   PPP_CODE_PROTOCOL_REJ  = 8,  ///<Protocol-Reject
+   PPP_CODE_ECHO_REQ      = 9,  ///<Echo-Request
+   PPP_CODE_ECHO_REP      = 10, ///<Echo-Reply
+   PPP_CODE_DISCARD_REQ   = 11  ///<Discard-Request
+} PppCode;
+
+
+/**
+ * @brief PPP authentication protocols
+ **/
+
+typedef enum
+{
+   PPP_AUTH_PROTOCOL_PAP       = 0x01, //PAP
+   PPP_AUTH_PROTOCOL_CHAP_MD5  = 0x02, //CHAP with MD5
+   PPP_AUTH_PROTOCOL_MS_CHAP   = 0x04, //MS-CHAP
+   PPP_AUTH_PROTOCOL_MS_CHAP_2 = 0x08  //MS-CHAP-2
+} PppAuthProtocol;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief LCP/NCP packet header
+ **/
+
+__start_packed struct _PppPacket
+{
+   uint8_t code;       //0
+   uint8_t identifier; //1
+   uint16_t length;    //2-3
+   uint8_t data[];     //4
+} __end_packed;
+
+
+/**
+ * @brief Configure-Request, Configure-Ack, Configure-Nak and Configure-Reject packets
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t code;       //0
+   uint8_t identifier; //1
+   uint16_t length;    //2-3
+   uint8_t options[];  //4
+} __end_packed PppConfigurePacket;
+
+
+/**
+ * @brief Terminate-Request and Terminate-Ack packet
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t code;       //0
+   uint8_t identifier; //1
+   uint16_t length;    //2-3
+   uint8_t data[];     //4
+} __end_packed PppTerminatePacket;
+
+
+/**
+ * @brief Code-Reject packet
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t code;             //0
+   uint8_t identifier;       //1
+   uint16_t length;          //2-3
+   uint8_t rejectedPacket[]; //4
+} __end_packed PppCodeRejPacket;
+
+
+/**
+ * @brief Protocol-Reject packet
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t code;              //0
+   uint8_t identifier;        //1
+   uint16_t length;           //2-3
+   uint16_t rejectedProtocol; //4-5
+   uint8_t rejectedInfo[];    //6
+} __end_packed PppProtocolRejPacket;
+
+
+/**
+ * @brief Echo-Request and Echo-Reply packet
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t code;         //0
+   uint8_t identifier;   //1
+   uint16_t length;      //2-3
+   uint32_t magicNumber; //4-7
+   uint8_t data[];       //8
+} __end_packed PppEchoPacket;
+
+
+/**
+ * @brief PPP Discard-Request packet
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t code;         //0
+   uint8_t identifier;   //1
+   uint16_t length;      //2-3
+   uint32_t magicNumber; //4-7
+   uint8_t data[];       //8
+} __end_packed PppDiscardReqPacket;
+
+
+/**
+ * @brief LCP/NCP option
+ **/
+
+typedef __start_packed struct
+{
+   uint8_t type;   //0
+   uint8_t length; //1
+   uint8_t data[]; //2
+} __end_packed PppOption;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+/**
+ * @brief Random data generation callback function
+ **/
+
+typedef error_t (*PppRandCallback)(uint8_t *data, size_t length);
+
+
+/**
+ * @brief PPP authentication callback function
+ **/
+
+typedef bool_t (*PppAuthCallback)(NetInterface *interface,
+   const char_t *username);
+
+
+/**
+ * @brief PPP settings
+ **/
+
+typedef struct
+{
+   NetInterface *interface;      ///<Underlying network interface
+   uint16_t mru;                 ///<Default MRU
+   uint32_t accm;                ///<Default async control character map
+   uint_t authProtocol;          ///<Allowed authentication protocols
+   PppRandCallback randCallback; ///<Random data generation callback function
+   PppAuthCallback authCallback; ///<PPP authentication callback function
+} PppSettings;
+
+
+/**
+ * @brief PPP finite state machine
+ **/
+
+typedef struct
+{
+   uint_t state;          ///<FSM state
+   uint8_t identifier;    ///<Identifier used to match requests and replies
+   uint_t restartCounter; ///<Restart counter
+   uint_t failureCounter; ///<Failure counter
+   systime_t timestamp;   ///<Timestamp to manage retransmissions
+} PppFsm;
+
+
+/**
+ * @brief PPP configuration options
+ **/
+
+typedef struct
+{
+   uint16_t mru;
+   bool_t mruRejected;
+   uint32_t accm;
+   bool_t accmRejected;
+   uint16_t authProtocol;
+   uint8_t authAlgo;
+   bool_t authProtocolRejected;
+   uint32_t magicNumber;
+   bool_t magicNumberRejected;
+   bool_t pfc;
+   bool_t pfcRejected;
+   bool_t acfc;
+   bool_t acfcRejected;
+#if (IPV4_SUPPORT == ENABLED)
+   Ipv4Addr ipAddr;
+   bool_t ipAddrRejected;
+   Ipv4Addr primaryDns;
+   bool_t primaryDnsRejected;
+   Ipv4Addr secondaryDns;
+   bool_t secondaryDnsRejected;
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   Eui64 interfaceId;
+   bool_t interfaceIdRejected;
+#endif
+} PppConfig;
+
+
+/**
+ * @brief PPP context
+ **/
+
+struct _PppContext
+{
+   PppSettings settings;    ///PPP settings
+   NetInterface *interface; ///<Underlying network interface
+   systime_t timeout;       ///<Timeout for blocking operations
+
+   char_t username[PPP_MAX_USERNAME_LEN + 1]; ///<User name
+   char_t password[PPP_MAX_PASSWORD_LEN + 1]; ///<Password
+   char_t peerName[PPP_MAX_USERNAME_LEN + 1]; ///<Peer's name
+
+   bool_t localAuthDone;
+   bool_t peerAuthDone;
+
+   PppPhase pppPhase;       ///<PPP phase
+   PppFsm lcpFsm;           ///<LCP finite state machine
+#if (IPV4_SUPPORT == ENABLED)
+   PppFsm ipcpFsm;          ///<IPCP finite state machine
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   PppFsm ipv6cpFsm;        ///<IPV6CP finite state machine
+#endif
+#if (PAP_SUPPORT == ENABLED)
+   PapFsm papFsm;           ///<PAP finite state machine
+#endif
+#if (CHAP_SUPPORT == ENABLED)
+   ChapFsm chapFsm;         ///<CHAP finite state machine
+#endif
+   PppConfig localConfig;   ///<Local configuration options
+   PppConfig peerConfig;    ///<Peer configuration options
+   bool_t ipRejected;       ///<IPv4 protocol is not supported by the peer
+   bool_t ipv6Rejected;     ///<IPv6 protocol is not support by the peer
+
+   uint8_t frame[PPP_MAX_FRAME_SIZE]; ///<Incoming PPP frame
+
+   uint8_t txBuffer[PPP_TX_BUFFER_SIZE]; ///<Transmit buffer
+   uint_t txBufferLen;
+   uint_t txWriteIndex;
+   uint_t txReadIndex;
+
+   uint8_t rxBuffer[PPP_RX_BUFFER_SIZE]; ///<Receive buffer
+   uint_t rxBufferLen;
+   uint_t rxWriteIndex;
+   uint_t rxReadIndex;
+   uint_t rxFrameCount;
+};
+
+
+//Tick counter to handle periodic operations
+extern systime_t pppTickCounter;
+
+//PPP related functions
+void pppGetDefaultSettings(PppSettings *settings);
+error_t pppInit(PppContext *context, const PppSettings *settings);
+
+error_t pppSetTimeout(NetInterface *interface, systime_t timeout);
+
+error_t pppSetAuthInfo(NetInterface *interface,
+   const char_t *username, const char_t *password);
+
+bool_t pppCheckPassword(NetInterface *interface, const char_t *password);
+
+error_t pppSendAtCommand(NetInterface *interface, const char_t *data);
+error_t pppReceiveAtCommand(NetInterface *interface, char_t *data, size_t size);
+
+error_t pppConnect(NetInterface *interface);
+error_t pppClose(NetInterface *interface);
+
+void pppTick(NetInterface *interface);
+
+void pppProcessFrame(NetInterface *interface, uint8_t *frame, size_t length);
+
+error_t pppSendFrame(NetInterface *interface,
+   NetBuffer *buffer, size_t offset, uint16_t protocol);
+
+size_t pppParseFrameHeader(const uint8_t *frame, size_t length, uint16_t *protocol);
+
+uint16_t pppCalcFcs(const uint8_t *data, size_t length);
+uint16_t pppCalcFcsEx(const NetBuffer *buffer, size_t offset, size_t length);
+
+NetBuffer *pppAllocBuffer(size_t length, size_t *offset);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/ppp_debug.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,886 @@
+/**
+ * @file ppp_debug.c
+ * @brief Data logging functions for debugging purpose (PPP)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Dependencies
+#include "core/net.h"
+#include "ppp/ppp_debug.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (PPP_SUPPORT == ENABLED && PPP_TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+
+//LCP codes
+static const char_t *lcpCodeLabel[] =
+{
+   "",                  //0
+   "Configure-Request", //1
+   "Configure-Ack",     //2
+   "Configure-Nak",     //3
+   "Configure-Reject",  //4
+   "Terminate-Request", //5
+   "Terminate-Ack",     //6
+   "Code-Reject",       //7
+   "Protocol-Reject",   //8
+   "Echo-Request",      //9
+   "Echo-Reply",        //10
+   "Discard-Request"    //11
+};
+
+//NCP codes
+static const char_t *ncpCodeLabel[] =
+{
+   "",                  //0
+   "Configure-Request", //1
+   "Configure-Ack",     //2
+   "Configure-Nak",     //3
+   "Configure-Reject",  //4
+   "Terminate-Request", //5
+   "Terminate-Ack",     //6
+   "Code-Reject"        //7
+};
+
+//PAP codes
+static const char_t *papCodeLabel[] =
+{
+   "",                     //0
+   "Authenticate-Request", //1
+   "Authenticate-Ack",     //2
+   "Authenticate-Nak"      //3
+};
+
+//CHAP codes
+static const char_t *chapCodeLabel[] =
+{
+   "",          //0
+   "Challenge", //1
+   "Response",  //2
+   "Success",   //3
+   "Failure"    //4
+};
+
+//LCP options
+static const char_t *lcpOptionLabel[] =
+{
+   "",                                      //0
+   "Maximum-Receive-Unit",                  //1
+   "Async-Control-Character-Map",           //2
+   "Authentication-Protocol",               //3
+   "Quality-Protocol",                      //4
+   "Magic-Number",                          //5
+   "",                                      //6
+   "Protocol-Field-Compression",            //7
+   "Address-and-Control-Field-Compression", //8
+   "FCS-Alternatives",                      //9
+   "Self-Describing-Pad",                   //10
+   "Numbered-Mode",                         //11
+   "",                                      //12
+   "Callback"                               //13
+};
+
+//IPCP options
+static const char_t *ipcpOptionLabel[] =
+{
+   "",                        //0
+   "IP-Addresses",            //1
+   "IP-Compression-Protocol", //2
+   "IP-Address",              //3
+   "Mobile-IPv4",             //4
+};
+
+static const char_t *ipcpOptionLabel2[] =
+{
+   "",                             //128
+   "Primary-DNS-Server-Address",   //129
+   "Primary-NBNS-Server-Address",  //130
+   "Secondary-DNS-Server-Address", //131
+   "Secondary-NBNS-Server-Address" //132
+};
+
+//IPV6CP options
+static const char_t *ipv6cpOptionLabel[] =
+{
+   "",                         //0
+   "Interface-Identifier",     //1
+   "IPv6-Compression-Protocol" //2
+};
+
+
+/**
+ * @brief Dump LCP/NCP packet for debugging purpose
+ * @param[in] packet Pointer to the LCP packet
+ * @param[in] length Length of the packet, in bytes
+ * @param[in] protocol Protocol field
+ * @return Error code
+ **/
+
+error_t pppDumpPacket(const PppPacket *packet, size_t length, PppProtocol protocol)
+{
+   error_t error;
+
+   //Check protocol field
+   switch(protocol)
+   {
+      //LCP packet?
+      case PPP_PROTOCOL_LCP:
+         error = lcpDumpPacket(packet, length);
+         break;
+      //NCP packet?
+      case PPP_PROTOCOL_IPCP:
+      case PPP_PROTOCOL_IPV6CP:
+         error = ncpDumpPacket(packet, length, protocol);
+         break;
+      //PAP packet?
+      case PPP_PROTOCOL_PAP:
+         error = papDumpPacket(packet, length);
+         break;
+      //CHAP packet?
+      case PPP_PROTOCOL_CHAP:
+         error = chapDumpPacket(packet, length);
+         break;
+      //Unknown protocol?
+      default:
+         error = ERROR_FAILURE;
+         break;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Dump LCP packet for debugging purpose
+ * @param[in] packet Pointer to the LCP packet
+ * @param[in] length Length of the packet, in bytes
+ * @return Error code
+ **/
+
+error_t lcpDumpPacket(const PppPacket *packet, size_t length)
+{
+   error_t error;
+   const char_t *label;
+
+   //Make sure the LCP packet is valid
+   if(length < sizeof(PppPacket))
+      return ERROR_INVALID_LENGTH;
+
+   //Check the length field
+   if(ntohs(packet->length) > length)
+      return ERROR_INVALID_LENGTH;
+   if(ntohs(packet->length) < sizeof(PppPacket))
+      return ERROR_INVALID_LENGTH;
+
+   //Save the length of the LCP packet
+   length = ntohs(packet->length);
+
+   //Retrieve the name of the LCP packet
+   if(packet->code < arraysize(lcpCodeLabel))
+      label = lcpCodeLabel[packet->code];
+   else
+      label = "Unknown";
+
+   //Dump LCP packet header
+   TRACE_DEBUG("  Code = %" PRIu8 " (%s)\r\n", packet->code, label);
+   TRACE_DEBUG("  Identifier = %" PRIu8  "\r\n", packet->identifier);
+   TRACE_DEBUG("  Length = %" PRIu16 "\r\n", ntohs(packet->length));
+
+   //Configure-Request, Configure-Ack, Configure-Nak or Configure-Reject packet?
+   if(packet->code == PPP_CODE_CONFIGURE_REQ ||
+      packet->code == PPP_CODE_CONFIGURE_ACK ||
+      packet->code == PPP_CODE_CONFIGURE_NAK ||
+      packet->code == PPP_CODE_CONFIGURE_REJ)
+   {
+      //Cast LCP packet
+      PppConfigurePacket *p = (PppConfigurePacket *) packet;
+
+      //Valid packet length?
+      if(length < sizeof(PppConfigurePacket))
+         return ERROR_INVALID_LENGTH;
+
+      //Retrieve the length of the option list
+      length -= sizeof(PppConfigurePacket);
+
+      //Dump options
+      error = lcpDumpOptions((PppOption *) p->options, length);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+   //Terminate-Request or Terminate-Ack packet?
+   else if(packet->code == PPP_CODE_TERMINATE_REQ ||
+      packet->code == PPP_CODE_TERMINATE_ACK)
+   {
+      //Cast LCP packet
+      PppTerminatePacket *p = (PppTerminatePacket *) packet;
+
+      //Valid packet length?
+      if(length < sizeof(PppTerminatePacket))
+         return ERROR_INVALID_LENGTH;
+
+      //Retrieve the length of data
+      length -= sizeof(PppTerminatePacket);
+
+      //Any data?
+      if(length > 0)
+      {
+         //Dump data
+         TRACE_DEBUG("  Data (%" PRIuSIZE " bytes)\r\n", length);
+         TRACE_DEBUG_ARRAY("    ", p->data, length);
+      }
+   }
+   //Code-Reject packet?
+   else if(packet->code == PPP_CODE_CODE_REJ)
+   {
+      //Cast LCP packet
+      PppCodeRejPacket *p = (PppCodeRejPacket *) packet;
+
+      //Valid packet length?
+      if(length < sizeof(PppCodeRejPacket))
+         return ERROR_INVALID_LENGTH;
+
+      //Retrieve the length of Rejected-Packet field
+      length -= sizeof(PppCodeRejPacket);
+
+      //Rejected-Packet
+      TRACE_DEBUG("  Rejected-Packet (%" PRIuSIZE " bytes)\r\n", length);
+      TRACE_DEBUG_ARRAY("    ", p->rejectedPacket, length);
+   }
+   //Protocol-Reject packet?
+   else if(packet->code == PPP_CODE_PROTOCOL_REJ)
+   {
+      //Cast LCP packet
+      PppProtocolRejPacket *p = (PppProtocolRejPacket *) packet;
+
+      //Valid packet length?
+      if(length < sizeof(PppProtocolRejPacket))
+         return ERROR_INVALID_LENGTH;
+
+      //Retrieve the length of Rejected-Information field
+      length -= sizeof(PppProtocolRejPacket);
+
+      //Rejected-Protocol
+      TRACE_DEBUG("  Rejected-Protocol = %" PRIu16 "\r\n", htons(p->rejectedProtocol));
+      //Rejected-Information
+      TRACE_DEBUG("  Rejected-Information (%" PRIuSIZE " bytes)\r\n", length);
+      TRACE_DEBUG_ARRAY("    ", p->rejectedInfo, length);
+   }
+   //Echo-Request, Echo-Reply or Discard-Request packet?
+   else if(packet->code == PPP_CODE_ECHO_REQ ||
+      packet->code == PPP_CODE_ECHO_REP ||
+      packet->code == PPP_CODE_DISCARD_REQ)
+   {
+      //Cast LCP packet
+      PppEchoPacket *p = (PppEchoPacket *) packet;
+
+      //Valid packet length?
+      if(length < sizeof(PppEchoPacket))
+         return ERROR_INVALID_LENGTH;
+
+      //Retrieve the length of data
+      length -= sizeof(PppEchoPacket);
+
+      //Magic-Number
+      TRACE_DEBUG("  Magic-Number = %" PRIu32 "\r\n", htonl(p->magicNumber));
+      //Data
+      TRACE_DEBUG("  Data (%" PRIuSIZE " bytes)\r\n", length);
+      TRACE_DEBUG_ARRAY("    ", p->data, length);
+   }
+   //Unknown packet?
+   else
+   {
+      //Retrieve the length of data
+      length -= sizeof(PppPacket);
+
+      //Any data?
+      if(length > 0)
+      {
+         //Dump data
+         TRACE_DEBUG("  Data (%" PRIuSIZE " bytes)\r\n", length);
+         TRACE_DEBUG_ARRAY("    ", packet->data, length);
+      }
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump NCP packet for debugging purpose
+ * @param[in] packet Pointer to the NCP packet
+ * @param[in] length Length of the packet, in bytes
+ * @param[in] protocol Protocol field
+ * @return Error code
+ **/
+
+error_t ncpDumpPacket(const PppPacket *packet, size_t length, PppProtocol protocol)
+{
+   error_t error;
+   const char_t *label;
+
+   //Make sure the NDP packet is valid
+   if(length < sizeof(PppPacket))
+      return ERROR_INVALID_LENGTH;
+
+   //Check the length field
+   if(ntohs(packet->length) > length)
+      return ERROR_INVALID_LENGTH;
+   if(ntohs(packet->length) < sizeof(PppPacket))
+      return ERROR_INVALID_LENGTH;
+
+   //Save the length of the NDP packet
+   length = ntohs(packet->length);
+
+   //Retrieve the name of the NDP packet
+   if(packet->code < arraysize(ncpCodeLabel))
+      label = ncpCodeLabel[packet->code];
+   else
+      label = "Unknown";
+
+   //Dump NDP packet header
+   TRACE_DEBUG("  Code = %" PRIu8 " (%s)\r\n", packet->code, label);
+   TRACE_DEBUG("  Identifier = %" PRIu8  "\r\n", packet->identifier);
+   TRACE_DEBUG("  Length = %" PRIu16 "\r\n", ntohs(packet->length));
+
+   //Configure-Request, Configure-Ack, Configure-Nak or Configure-Reject packet?
+   if(packet->code == PPP_CODE_CONFIGURE_REQ ||
+      packet->code == PPP_CODE_CONFIGURE_ACK ||
+      packet->code == PPP_CODE_CONFIGURE_NAK ||
+      packet->code == PPP_CODE_CONFIGURE_REJ)
+   {
+      //Cast NDP packet
+      PppConfigurePacket *p = (PppConfigurePacket *) packet;
+
+      //Valid packet length?
+      if(length < sizeof(PppConfigurePacket))
+         return ERROR_INVALID_LENGTH;
+
+      //Retrieve the length of the option list
+      length -= sizeof(PppConfigurePacket);
+
+      //IPCP protocol?
+      if(protocol == PPP_PROTOCOL_IPCP)
+      {
+         //Dump options
+         error = ipcpDumpOptions((PppOption *) p->options, length);
+         //Any error to report?
+         if(error)
+            return error;
+      }
+      //IPV6CP protocol?
+      else if(protocol == PPP_PROTOCOL_IPV6CP)
+      {
+         //Dump options
+         error = ipv6cpDumpOptions((PppOption *) p->options, length);
+         //Any error to report?
+         if(error)
+            return error;
+      }
+   }
+   //Terminate-Request or Terminate-Ack packet?
+   else if(packet->code == PPP_CODE_TERMINATE_REQ ||
+      packet->code == PPP_CODE_TERMINATE_ACK)
+   {
+      //Cast NDP packet
+      PppTerminatePacket *p = (PppTerminatePacket *) packet;
+
+      //Valid packet length?
+      if(length < sizeof(PppTerminatePacket))
+         return ERROR_INVALID_LENGTH;
+
+      //Retrieve the length of data
+      length -= sizeof(PppTerminatePacket);
+
+      //Any data?
+      if(length > 0)
+      {
+         //Dump data
+         TRACE_DEBUG("  Data (%" PRIuSIZE " bytes)\r\n", length);
+         TRACE_DEBUG_ARRAY("    ", p->data, length);
+      }
+   }
+   //Code-Reject packet?
+   else if(packet->code == PPP_CODE_CODE_REJ)
+   {
+      //Cast NDP packet
+      PppCodeRejPacket *p = (PppCodeRejPacket *) packet;
+
+      //Valid packet length?
+      if(length < sizeof(PppCodeRejPacket))
+         return ERROR_INVALID_LENGTH;
+
+      //Retrieve the length of Rejected-Packet field
+      length -= sizeof(PppCodeRejPacket);
+
+      //Rejected-Packet
+      TRACE_DEBUG("  Rejected-Packet (%" PRIuSIZE " bytes)\r\n", length);
+      TRACE_DEBUG_ARRAY("    ", p->rejectedPacket, length);
+   }
+   //Unknown packet?
+   else
+   {
+      //Retrieve the length of data
+      length -= sizeof(PppPacket);
+
+      //Any data?
+      if(length > 0)
+      {
+         //Dump data
+         TRACE_DEBUG("  Data (%" PRIuSIZE " bytes)\r\n", length);
+         TRACE_DEBUG_ARRAY("    ", packet->data, length);
+      }
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump PAP packet for debugging purpose
+ * @param[in] packet Pointer to the PAP packet
+ * @param[in] length Length of the packet, in bytes
+ * @return Error code
+ **/
+
+error_t papDumpPacket(const PppPacket *packet, size_t length)
+{
+   const char_t *label;
+
+   //Make sure the PAP packet is valid
+   if(length < sizeof(PppPacket))
+      return ERROR_INVALID_LENGTH;
+
+   //Check the length field
+   if(ntohs(packet->length) > length)
+      return ERROR_INVALID_LENGTH;
+   if(ntohs(packet->length) < sizeof(PppPacket))
+      return ERROR_INVALID_LENGTH;
+
+   //Save the length of the PAP packet
+   length = ntohs(packet->length);
+
+   //Retrieve the name of the PAP packet
+   if(packet->code < arraysize(papCodeLabel))
+      label = papCodeLabel[packet->code];
+   else
+      label = "Unknown";
+
+   //Dump PAP packet header
+   TRACE_DEBUG("  Code = %" PRIu8 " (%s)\r\n", packet->code, label);
+   TRACE_DEBUG("  Identifier = %" PRIu8  "\r\n", packet->identifier);
+   TRACE_DEBUG("  Length = %" PRIu16 "\r\n", ntohs(packet->length));
+
+   //Authenticate-Request packet?
+   if(packet->code == PAP_CODE_AUTH_REQ)
+   {
+      uint8_t *q;
+      PapAuthReqPacket *p;
+
+      //Cast PAP packet
+      p = (PapAuthReqPacket *) packet;
+
+      //Valid packet length?
+      if(length < sizeof(PapAuthReqPacket))
+         return ERROR_INVALID_LENGTH;
+
+      //Peer-ID-Length field
+      TRACE_DEBUG("  Peer-ID-Length = %" PRIu8 "\r\n", p->peerIdLength);
+
+      //Check the length of the Peer-ID field
+      if(length < (sizeof(PapAuthAckPacket) + 1 + p->peerIdLength))
+         return ERROR_INVALID_LENGTH;
+
+      //Peer-ID field
+      TRACE_DEBUG("  Peer-ID\r\n");
+      TRACE_DEBUG_ARRAY("    ", p->peerId, p->peerIdLength);
+
+      //Point to the Passwd-Length field
+      q = p->peerId + p->peerIdLength;
+
+      //Passwd-Length field
+      TRACE_DEBUG("  Passwd-Length = %" PRIu8 "\r\n", q[0]);
+
+      //Check the length of the Password field
+      if(length < (sizeof(PapAuthAckPacket) + 1 + p->peerIdLength + q[0]))
+         return ERROR_INVALID_LENGTH;
+
+      //Password field
+      TRACE_DEBUG("  Password\r\n");
+      TRACE_DEBUG_ARRAY("    ", q + 1, q[0]);
+   }
+   //Authenticate-Ack or Authenticate-Nak packet?
+   else if(packet->code == PAP_CODE_AUTH_ACK ||
+      packet->code == PAP_CODE_AUTH_NAK)
+   {
+      PapAuthAckPacket *p;
+
+      //Cast PAP packet
+      p = (PapAuthAckPacket *) packet;
+
+      //Valid packet length?
+      if(length < sizeof(PapAuthAckPacket))
+         return ERROR_INVALID_LENGTH;
+
+      //Msg-Length field
+      TRACE_DEBUG("  Msg-Length = %" PRIu8 "\r\n", p->msgLength);
+
+      //Check the length of the Message field
+      if(length < (sizeof(PapAuthAckPacket) + p->msgLength))
+         return ERROR_INVALID_LENGTH;
+
+      if(length > 0)
+      {
+         //Message field
+         TRACE_DEBUG("  Message\r\n");
+         TRACE_DEBUG_ARRAY("    ", p->message, p->msgLength);
+      }
+   }
+   //Unknown packet?
+   else
+   {
+      //Retrieve the length of data
+      length -= sizeof(PppPacket);
+
+      //Any data?
+      if(length > 0)
+      {
+         //Dump data
+         TRACE_DEBUG("  Data (%" PRIuSIZE " bytes)\r\n", length);
+         TRACE_DEBUG_ARRAY("    ", packet->data, length);
+      }
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump CHAP packet for debugging purpose
+ * @param[in] packet Pointer to the PAP packet
+ * @param[in] length Length of the packet, in bytes
+ * @return Error code
+ **/
+
+error_t chapDumpPacket(const PppPacket *packet, size_t length)
+{
+   const char_t *label;
+
+   //Make sure the CHAP packet is valid
+   if(length < sizeof(PppPacket))
+      return ERROR_INVALID_LENGTH;
+
+   //Check the length field
+   if(ntohs(packet->length) > length)
+      return ERROR_INVALID_LENGTH;
+   if(ntohs(packet->length) < sizeof(PppPacket))
+      return ERROR_INVALID_LENGTH;
+
+   //Save the length of the CHAP packet
+   length = ntohs(packet->length);
+
+   //Retrieve the name of the CHAP packet
+   if(packet->code < arraysize(chapCodeLabel))
+      label = chapCodeLabel[packet->code];
+   else
+      label = "Unknown";
+
+   //Dump CHAP packet header
+   TRACE_DEBUG("  Code = %" PRIu8 " (%s)\r\n", packet->code, label);
+   TRACE_DEBUG("  Identifier = %" PRIu8  "\r\n", packet->identifier);
+   TRACE_DEBUG("  Length = %" PRIu16 "\r\n", ntohs(packet->length));
+
+   //Challenge or Response packet?
+   if(packet->code == CHAP_CODE_CHALLENGE ||
+      packet->code == CHAP_CODE_RESPONSE)
+   {
+      uint8_t *q;
+      ChapChallengePacket *p;
+
+      //Cast CHAP packet
+      p = (ChapChallengePacket *) packet;
+
+      //Valid packet length?
+      if(length < sizeof(ChapChallengePacket))
+         return ERROR_INVALID_LENGTH;
+
+      //Value-Size field
+      TRACE_DEBUG("  Value-Size = %" PRIu8 "\r\n", p->valueSize);
+
+      //Check the length of the Value field
+      if(length < (sizeof(ChapChallengePacket) + p->valueSize))
+         return ERROR_INVALID_LENGTH;
+
+      //Value field
+      TRACE_DEBUG("  Value\r\n");
+      TRACE_DEBUG_ARRAY("    ", p->value, p->valueSize);
+
+      //Point to the Name field
+      q = p->value + p->valueSize;
+      //Retrieve the length of the Name field
+      length -= sizeof(ChapChallengePacket) + p->valueSize;
+
+      //Name field
+      TRACE_DEBUG("  Name (%" PRIuSIZE " bytes)\r\n", length);
+      TRACE_DEBUG_ARRAY("    ", q, length);
+   }
+   //Success or Failure packet?
+   else if(packet->code == CHAP_CODE_SUCCESS ||
+      packet->code == CHAP_CODE_FAILURE)
+   {
+      ChapSuccessPacket *p;
+
+      //Cast CHAP packet
+      p = (ChapSuccessPacket *) packet;
+
+      //Valid packet length?
+      if(length < sizeof(ChapSuccessPacket))
+         return ERROR_INVALID_LENGTH;
+
+      //Retrieve the length of Message field
+      length -= sizeof(ChapSuccessPacket);
+
+      //Message field
+      TRACE_DEBUG("  Message (%" PRIuSIZE " bytes)\r\n", length);
+      TRACE_DEBUG_ARRAY("    ", p->message, length);
+   }
+   //Unknown packet?
+   else
+   {
+      //Retrieve the length of data
+      length -= sizeof(PppPacket);
+
+      //Any data?
+      if(length > 0)
+      {
+         //Dump data
+         TRACE_DEBUG("  Data (%" PRIuSIZE " bytes)\r\n", length);
+         TRACE_DEBUG_ARRAY("    ", packet->data, length);
+      }
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump LCP options for debugging purpose
+ * @param[in] option Pointer to the option list
+ * @param[in] length Length of the option list, in bytes
+ * @return Error code
+ **/
+
+error_t lcpDumpOptions(const PppOption *option, size_t length)
+{
+   uint32_t value;
+
+   //Parse options
+   while(length > 0)
+   {
+      //Malformed LCP packet?
+      if(length < sizeof(PppOption))
+         return ERROR_INVALID_LENGTH;
+
+      //Check option length
+      if(option->length < sizeof(PppOption))
+         return ERROR_INVALID_LENGTH;
+      if(option->length > length)
+         return ERROR_INVALID_LENGTH;
+
+      //Display the name of the current option
+      if(option->type < arraysize(lcpOptionLabel))
+         TRACE_DEBUG("  %s option (%" PRIu8 " bytes)\r\n", lcpOptionLabel[option->type], option->length);
+      else
+         TRACE_DEBUG("  Option %" PRIu8 " (%" PRIu8 " bytes)\r\n", option->type, option->length);
+
+      //Check option code
+      switch(option->type)
+      {
+      //16-bit unsigned value?
+      case LCP_OPTION_MRU:
+         //Check length field
+         if(option->length != (sizeof(PppOption) + sizeof(uint16_t)))
+            return ERROR_INVALID_OPTION;
+         //Retrieve 16-bit value
+         value = LOAD16BE(option->data);
+         //Dump option contents
+         TRACE_DEBUG("    %" PRIu32 "\r\n", value);
+         break;
+
+      //32-bit unsigned value?
+      case LCP_OPTION_ACCM:
+      case LCP_OPTION_MAGIC_NUMBER:
+         //Check length field
+         if(option->length != (sizeof(PppOption) + sizeof(uint32_t)))
+            return ERROR_INVALID_OPTION;
+         //Retrieve 32-bit value
+         value = LOAD32BE(option->data);
+         //Dump option contents
+         TRACE_DEBUG("    0x%08" PRIX32 "\r\n", value);
+         break;
+
+      //Raw data?
+      default:
+         //Dump option contents
+         TRACE_DEBUG_ARRAY("    ", option->data, option->length - sizeof(PppOption));
+         break;
+      }
+
+      //Remaining bytes to process
+      length -= option->length;
+      //Jump to the next option
+      option = (PppOption *) ((uint8_t *) option + option->length);
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+/**
+ * @brief Dump IPCP options for debugging purpose
+ * @param[in] option Pointer to the option list
+ * @param[in] length Length of the option list, in bytes
+ * @return Error code
+ **/
+
+error_t ipcpDumpOptions(const PppOption *option, size_t length)
+{
+#if (IPV4_SUPPORT == ENABLED)
+   Ipv4Addr ipAddr;
+#endif
+
+   //Parse options
+   while(length > 0)
+   {
+      //Malformed IPCP packet?
+      if(length < sizeof(PppOption))
+         return ERROR_INVALID_LENGTH;
+
+      //Check option length
+      if(option->length < sizeof(PppOption))
+         return ERROR_INVALID_LENGTH;
+      if(option->length > length)
+         return ERROR_INVALID_LENGTH;
+
+      //Display the name of the current option
+      if(option->type < arraysize(ipcpOptionLabel))
+         TRACE_DEBUG("  %s option (%" PRIu8 " bytes)\r\n", ipcpOptionLabel[option->type], option->length);
+      else if(option->type >= 128 && option->type < (128 + arraysize(ipcpOptionLabel2)))
+         TRACE_DEBUG("  %s option (%" PRIu8 " bytes)\r\n", ipcpOptionLabel2[option->type - 128], option->length);
+      else
+         TRACE_DEBUG("  Option %" PRIu8 " (%" PRIu8 " bytes)\r\n", option->type, option->length);
+
+      //Check option code
+      switch(option->type)
+      {
+#if (IPV4_SUPPORT == ENABLED)
+      //IP address?
+      case IPCP_OPTION_IP_ADDRESS:
+      case IPCP_OPTION_PRIMARY_DNS:
+      case IPCP_OPTION_PRIMARY_NBNS:
+      case IPCP_OPTION_SECONDARY_DNS:
+      case IPCP_OPTION_SECONDARY_NBNS:
+         //Check length field
+         if(option->length != (sizeof(PppOption) + sizeof(Ipv4Addr)))
+            return ERROR_INVALID_OPTION;
+         //Retrieve IPv4 address
+         ipv4CopyAddr(&ipAddr, option->data);
+         //Dump option contents
+         TRACE_DEBUG("    %s\r\n", ipv4AddrToString(ipAddr, NULL));
+         break;
+#endif
+      //Raw data?
+      default:
+         //Dump option contents
+         TRACE_DEBUG_ARRAY("    ", option->data, option->length - sizeof(PppOption));
+         break;
+      }
+
+      //Remaining bytes to process
+      length -= option->length;
+      //Jump to the next option
+      option = (PppOption *) ((uint8_t *) option + option->length);
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump IPV6CP options for debugging purpose
+ * @param[in] option Pointer to the option list
+ * @param[in] length Length of the option list, in bytes
+ * @return Error code
+ **/
+
+error_t ipv6cpDumpOptions(const PppOption *option, size_t length)
+{
+   //Parse options
+   while(length > 0)
+   {
+      //Malformed IPV6CP packet?
+      if(length < sizeof(PppOption))
+         return ERROR_INVALID_LENGTH;
+
+      //Check option length
+      if(option->length < sizeof(PppOption))
+         return ERROR_INVALID_LENGTH;
+      if(option->length > length)
+         return ERROR_INVALID_LENGTH;
+
+      //Display the name of the current option
+      if(option->type < arraysize(ipv6cpOptionLabel))
+         TRACE_DEBUG("  %s option (%" PRIu8 " bytes)\r\n", ipv6cpOptionLabel[option->type], option->length);
+      else
+         TRACE_DEBUG("  Option %" PRIu8 " (%" PRIu8 " bytes)\r\n", option->type, option->length);
+
+      //Check option code
+      switch(option->type)
+      {
+      //Raw data?
+      default:
+         //Dump option contents
+         TRACE_DEBUG_ARRAY("    ", option->data, option->length - sizeof(PppOption));
+         break;
+      }
+
+      //Remaining bytes to process
+      length -= option->length;
+      //Jump to the next option
+      option = (PppOption *) ((uint8_t *) option + option->length);
+   }
+
+   //No error to report
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/ppp_debug.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,58 @@
+/**
+ * @file ppp_debug.h
+ * @brief Data logging functions for debugging purpose (PPP)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DHCP_DEBUG_H
+#define _DHCP_DEBUG_H
+
+//Dependencies
+#include "core/net.h"
+#include "ppp/ppp.h"
+#include "ppp/lcp.h"
+#include "ppp/ipcp.h"
+#include "debug.h"
+
+//Check current trace level
+#if (PPP_TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+
+//PPP related functions
+error_t pppDumpPacket(const PppPacket *packet, size_t length, PppProtocol protocol);
+error_t lcpDumpPacket(const PppPacket *packet, size_t length);
+error_t ncpDumpPacket(const PppPacket *packet, size_t length, PppProtocol protocol);
+error_t papDumpPacket(const PppPacket *packet, size_t length);
+error_t chapDumpPacket(const PppPacket *packet, size_t length);
+
+error_t lcpDumpOptions(const PppOption *option, size_t length);
+error_t ipcpDumpOptions(const PppOption *option, size_t length);
+error_t ipv6cpDumpOptions(const PppOption *option, size_t length);
+
+#else
+   #define pppDumpPacket(packet, length, protocol)
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/ppp_fsm.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,954 @@
+/**
+ * @file ppp_fsm.c
+ * @brief PPP finite state machine
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL PPP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "ppp/ppp_fsm.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (PPP_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Process Up event
+ * @param[in] context PPP context
+ * @param[in,out] fsm Finite state machine
+ * @param[in] callbacks FSM actions
+ **/
+
+void pppUpEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks)
+{
+   //Check current state
+   switch(fsm->state)
+   {
+   case PPP_STATE_0_INITIAL:
+      //Switch to the Closed state
+      pppChangeState(fsm, PPP_STATE_2_CLOSED);
+      break;
+   case PPP_STATE_1_STARTING:
+       //Initialize restart counter
+      callbacks->initRestartCount(context, PPP_MAX_CONFIGURE);
+      //Send Configure-Request packet
+      callbacks->sendConfigureReq(context);
+      //Switch to the Req-Sent state
+      pppChangeState(fsm, PPP_STATE_6_REQ_SENT);
+      break;
+   default:
+      //This event cannot occur in a properly implemented automaton.
+      //No transition is taken, and the implementation should not
+      //reset or freeze
+      break;
+   }
+}
+
+
+/**
+ * @brief Process Down event
+ * @param[in] context PPP context
+ * @param[in,out] fsm Finite state machine
+ * @param[in] callbacks FSM actions
+ **/
+
+void pppDownEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks)
+{
+   //Check current state
+   switch(fsm->state)
+   {
+   case PPP_STATE_2_CLOSED:
+      //Switch to the Initial state
+      pppChangeState(fsm, PPP_STATE_0_INITIAL);
+      break;
+   case PPP_STATE_3_STOPPED:
+      //Switch to the Starting state
+      pppChangeState(fsm, PPP_STATE_1_STARTING);
+      //Indicate to the lower layers that the automaton is entering the
+      //Starting state. The lower layer is needed for the link
+      callbacks->thisLayerStarted(context);
+      break;
+   case PPP_STATE_4_CLOSING:
+      //Switch to the Initial state
+      pppChangeState(fsm, PPP_STATE_0_INITIAL);
+      break;
+   case PPP_STATE_5_STOPPING:
+   case PPP_STATE_6_REQ_SENT:
+   case PPP_STATE_7_ACK_RCVD:
+   case PPP_STATE_8_ACK_SENT:
+      //Switch to the Starting state
+      pppChangeState(fsm, PPP_STATE_1_STARTING);
+      break;
+   case PPP_STATE_9_OPENED:
+      //Switch to the Starting state
+      pppChangeState(fsm, PPP_STATE_1_STARTING);
+      //Indicate to the upper layers that the automaton is leaving the Opened
+      //state. The link is no longer available for network traffic
+      callbacks->thisLayerDown(context);
+      break;
+   default:
+      //This event cannot occur in a properly implemented automaton.
+      //No transition is taken, and the implementation should not
+      //reset or freeze
+      break;
+   }
+}
+
+
+/**
+ * @brief Process Open event
+ * @param[in] context PPP context
+ * @param[in,out] fsm Finite state machine
+ * @param[in] callbacks FSM actions
+ **/
+
+void pppOpenEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks)
+{
+   //Check current state
+   switch(fsm->state)
+   {
+   case PPP_STATE_0_INITIAL:
+      //Switch to the Starting state
+      pppChangeState(fsm, PPP_STATE_1_STARTING);
+      //Indicate to the lower layers that the automaton is entering the
+      //Starting state. The lower layer is needed for the link
+      callbacks->thisLayerStarted(context);
+      break;
+   case PPP_STATE_1_STARTING:
+      //Stay in current state
+      break;
+   case PPP_STATE_2_CLOSED:
+      //Initialize restart counter
+      callbacks->initRestartCount(context, PPP_MAX_CONFIGURE);
+      //Send Configure-Request packet
+      callbacks->sendConfigureReq(context);
+      //Switch to the Req-Sent state
+      pppChangeState(fsm, PPP_STATE_6_REQ_SENT);
+      break;
+   case PPP_STATE_3_STOPPED:
+      //Stay in current state
+      break;
+   case PPP_STATE_4_CLOSING:
+      //Switch to the Stopping state
+      pppChangeState(fsm, PPP_STATE_5_STOPPING);
+      break;
+   case PPP_STATE_5_STOPPING:
+   case PPP_STATE_6_REQ_SENT:
+   case PPP_STATE_7_ACK_RCVD:
+   case PPP_STATE_8_ACK_SENT:
+   case PPP_STATE_9_OPENED:
+      //Stay in current state
+      break;
+   default:
+      //This event cannot occur in a properly implemented automaton.
+      //No transition is taken, and the implementation should not
+      //reset or freeze
+      break;
+   }
+}
+
+
+/**
+ * @brief Process Close event
+ * @param[in] context PPP context
+ * @param[in,out] fsm Finite state machine
+ * @param[in] callbacks FSM actions
+ **/
+
+void pppCloseEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks)
+{
+   //Check current state
+   switch(fsm->state)
+   {
+   case PPP_STATE_0_INITIAL:
+      //Stay in current state
+      break;
+   case PPP_STATE_1_STARTING:
+      //Switch to the Initial state
+      pppChangeState(fsm, PPP_STATE_0_INITIAL);
+      //Indicate to the lower layers that the automaton is entering the
+      //Initial, Closed or Stopped states. The lower layer is no longer
+      //needed for the link
+      callbacks->thisLayerFinished(context);
+      break;
+   case PPP_STATE_2_CLOSED:
+      //Stay in current state
+      break;
+   case PPP_STATE_3_STOPPED:
+      //Switch to the Closed state
+      pppChangeState(fsm, PPP_STATE_2_CLOSED);
+      break;
+   case PPP_STATE_4_CLOSING:
+      //Stay in current state
+      break;
+   case PPP_STATE_5_STOPPING:
+      //Switch to the Closing state
+      pppChangeState(fsm, PPP_STATE_4_CLOSING);
+      break;
+   case PPP_STATE_6_REQ_SENT:
+   case PPP_STATE_7_ACK_RCVD:
+   case PPP_STATE_8_ACK_SENT:
+      //Initialize restart counter
+      callbacks->initRestartCount(context, PPP_MAX_TERMINATE);
+      //Send Terminate-Request packet
+      callbacks->sendTerminateReq(context);
+      //Switch to the Closing state
+      pppChangeState(fsm, PPP_STATE_4_CLOSING);
+      break;
+   case PPP_STATE_9_OPENED:
+      //Initialize restart counter
+      callbacks->initRestartCount(context, PPP_MAX_TERMINATE);
+      //Send Terminate-Request packet
+      callbacks->sendTerminateReq(context);
+      //Switch to the Closing state
+      pppChangeState(fsm, PPP_STATE_4_CLOSING);
+      //Indicate to the upper layers that the automaton is leaving the Opened
+      //state. The link is no longer available for network traffic
+      callbacks->thisLayerDown(context);
+      break;
+   default:
+      //This event cannot occur in a properly implemented automaton.
+      //No transition is taken, and the implementation should not
+      //reset or freeze
+      break;
+   }
+}
+
+
+/**
+ * @brief Process Timeout event
+ * @param[in] context PPP context
+ * @param[in,out] fsm Finite state machine
+ * @param[in] callbacks FSM actions
+ **/
+
+void pppTimeoutEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks)
+{
+   //The restart counter is greater than zero (TO+ event)
+   if(fsm->restartCounter > 0)
+   {
+      //Check current state
+      switch(fsm->state)
+      {
+      case PPP_STATE_4_CLOSING:
+      case PPP_STATE_5_STOPPING:
+         //Send Terminate-Request packet
+         callbacks->sendTerminateReq(context);
+         //Stay in current state
+         break;
+      case PPP_STATE_6_REQ_SENT:
+      case PPP_STATE_7_ACK_RCVD:
+         //Send Configuration-Request packet
+         callbacks->sendConfigureReq(context);
+         //Switch to the Req-Sent state
+         pppChangeState(fsm, PPP_STATE_6_REQ_SENT);
+         break;
+      case PPP_STATE_8_ACK_SENT:
+         //Send Configuration-Request packet
+          callbacks->sendConfigureReq(context);
+         //Stay in current state
+         break;
+      default:
+         //This event cannot occur in a properly implemented automaton.
+         //No transition is taken, and the implementation should not
+         //reset or freeze
+         break;
+      }
+   }
+   //The restart counter is not greater than zero (TO- event)
+   else
+   {
+      //Check current state
+      switch(fsm->state)
+      {
+      case PPP_STATE_4_CLOSING:
+         //Switch to the Closed state
+         pppChangeState(fsm, PPP_STATE_2_CLOSED);
+         //Indicate to the lower layers that the automaton is entering the
+         //Initial, Closed or Stopped states. The lower layer is no longer
+         //needed for the link
+         callbacks->thisLayerFinished(context);
+         break;
+      case PPP_STATE_5_STOPPING:
+      case PPP_STATE_6_REQ_SENT:
+      case PPP_STATE_7_ACK_RCVD:
+      case PPP_STATE_8_ACK_SENT:
+         //Switch to the Stopped state
+         pppChangeState(fsm, PPP_STATE_3_STOPPED);
+         //Indicate to the lower layers that the automaton is entering the
+         //Initial, Closed or Stopped states. The lower layer is no longer
+         //needed for the link
+         callbacks->thisLayerFinished(context);
+         break;
+      default:
+         //This event cannot occur in a properly implemented automaton.
+         //No transition is taken, and the implementation should not
+         //reset or freeze
+         break;
+      }
+   }
+}
+
+
+/**
+ * @brief Process Receive-Configure-Request event
+ * @param[in] context PPP context
+ * @param[in,out] fsm Finite state machine
+ * @param[in] callbacks FSM actions
+ * @param[in] configureReqPacket Configure-Request packet received from the peer
+ * @param[in] code Tells whether the configuration options are acceptable
+ **/
+
+void pppRcvConfigureReqEvent(PppContext *context, PppFsm *fsm, const PppCallbacks *callbacks,
+   const PppConfigurePacket *configureReqPacket, PppCode code)
+{
+   //Check whether the configuration options are acceptable
+   if(code == PPP_CODE_CONFIGURE_ACK)
+   {
+      //If every configuration option received in the Configure-Request is
+      //recognizable and all values are acceptable, then the implementation
+      //must transmit a Configure-Ack
+      switch(fsm->state)
+      {
+      case PPP_STATE_2_CLOSED:
+         //Send Terminate-Ack packet
+         callbacks->sendTerminateAck(context, NULL);
+         //Stay in current state
+         break;
+      case PPP_STATE_3_STOPPED:
+         //Initialize restart counter
+         callbacks->initRestartCount(context, PPP_MAX_CONFIGURE);
+         //Send Configure-Request packet
+         callbacks->sendConfigureReq(context);
+         //Send Configure-Ack packet
+         callbacks->sendConfigureAck(context, configureReqPacket);
+         //Switch to the Ack-Sent state
+         pppChangeState(fsm, PPP_STATE_8_ACK_SENT);
+         break;
+      case PPP_STATE_4_CLOSING:
+      case PPP_STATE_5_STOPPING:
+         //Stay in current state
+         break;
+      case PPP_STATE_6_REQ_SENT:
+         //Send Configure-Ack packet
+         callbacks->sendConfigureAck(context, configureReqPacket);
+         //Switch to the Ack-Sent state
+         pppChangeState(fsm, PPP_STATE_8_ACK_SENT);
+         break;
+      case PPP_STATE_7_ACK_RCVD:
+         //Send Configure-Ack packet
+         callbacks->sendConfigureAck(context, configureReqPacket);
+         //Switch to the Opened state
+         pppChangeState(fsm, PPP_STATE_9_OPENED);
+         //Indicate to the upper layers that the automaton is entering the
+         //Opened state. The link is available for network traffic
+         callbacks->thisLayerUp(context);
+         break;
+      case PPP_STATE_8_ACK_SENT:
+         //Send Configure-Ack packet
+         callbacks->sendConfigureAck(context, configureReqPacket);
+         //Stay in current state
+         break;
+      case PPP_STATE_9_OPENED:
+         //Send Configure-Request packet
+         callbacks->sendConfigureReq(context);
+         //Send Configure-Ack packet
+         callbacks->sendConfigureAck(context, configureReqPacket);
+         //Switch to the Ack-Sent state
+         pppChangeState(fsm, PPP_STATE_8_ACK_SENT);
+         //Indicate to the upper layers that the automaton is leaving the Opened
+         //state. The link is no longer available for network traffic
+         callbacks->thisLayerDown(context);
+         break;
+      default:
+         //This event cannot occur in a properly implemented automaton.
+         //No transition is taken, and the implementation should not
+         //reset or freeze
+         break;
+      }
+   }
+   else if(code == PPP_CODE_CONFIGURE_NAK)
+   {
+      //If all configuration options are recognizable, but some values are not
+      //acceptable, then the implementation must transmit a Configure-Nak
+      switch(fsm->state)
+      {
+      case PPP_STATE_2_CLOSED:
+         //Send Terminate-Ack packet
+         callbacks->sendTerminateAck(context, NULL);
+         //Stay in current state
+         break;
+      case PPP_STATE_3_STOPPED:
+         //Initialize restart counter
+         callbacks->initRestartCount(context, PPP_MAX_CONFIGURE);
+         //Send Configure-Request packet
+         callbacks->sendConfigureReq(context);
+         //Send Configure-Nak packet
+         callbacks->sendConfigureNak(context, configureReqPacket);
+         //Switch to the Req-Sent state
+         pppChangeState(fsm, PPP_STATE_6_REQ_SENT);
+         break;
+      case PPP_STATE_4_CLOSING:
+      case PPP_STATE_5_STOPPING:
+         //Stay in current state
+         break;
+      case PPP_STATE_6_REQ_SENT:
+      case PPP_STATE_7_ACK_RCVD:
+         //Send Configure-Nak packet
+         callbacks->sendConfigureNak(context, configureReqPacket);
+         //Stay in current state
+         break;
+      case PPP_STATE_8_ACK_SENT:
+         //Send Configure-Nak packet
+         callbacks->sendConfigureNak(context, configureReqPacket);
+         //Switch to the Req-Sent state
+         pppChangeState(fsm, PPP_STATE_6_REQ_SENT);
+         break;
+      case PPP_STATE_9_OPENED:
+         //Send Configure-Request packet
+         callbacks->sendConfigureReq(context);
+         //Send Configure-Nak packet
+         callbacks->sendConfigureNak(context, configureReqPacket);
+         //Switch to the Req-Sent state
+         pppChangeState(fsm, PPP_STATE_6_REQ_SENT);
+         //Indicate to the upper layers that the automaton is leaving the Opened
+         //state. The link is no longer available for network traffic
+         callbacks->thisLayerDown(context);
+         break;
+      default:
+         //This event cannot occur in a properly implemented automaton.
+         //No transition is taken, and the implementation should not
+         //reset or freeze
+         break;
+      }
+   }
+   else if(code == PPP_CODE_CONFIGURE_REJ)
+   {
+      //If some configuration options received in the Configure-Request are not
+      //recognizable or not acceptable for negotiation, then the implementation
+      //must transmit a Configure-Reject
+      switch(fsm->state)
+      {
+      case PPP_STATE_2_CLOSED:
+         //Send Terminate-Ack packet
+         callbacks->sendTerminateAck(context, NULL);
+         //Stay in current state
+         break;
+      case PPP_STATE_3_STOPPED:
+         //Initialize restart counter
+         callbacks->initRestartCount(context, PPP_MAX_CONFIGURE);
+         //Send Configure-Request packet
+         callbacks->sendConfigureReq(context);
+         //Send Configure-Reject packet
+         callbacks->sendConfigureRej(context, configureReqPacket);
+         //Switch to the Req-Sent state
+         pppChangeState(fsm, PPP_STATE_6_REQ_SENT);
+         break;
+      case PPP_STATE_4_CLOSING:
+      case PPP_STATE_5_STOPPING:
+         //Stay in current state
+         break;
+      case PPP_STATE_6_REQ_SENT:
+      case PPP_STATE_7_ACK_RCVD:
+         //Send Configure-Reject packet
+         callbacks->sendConfigureRej(context, configureReqPacket);
+         //Stay in current state
+         break;
+      case PPP_STATE_8_ACK_SENT:
+         //Send Configure-Reject packet
+         callbacks->sendConfigureRej(context, configureReqPacket);
+         //Switch to the Req-Sent state
+         pppChangeState(fsm, PPP_STATE_6_REQ_SENT);
+         break;
+      case PPP_STATE_9_OPENED:
+         //Send Configure-Request packet
+         callbacks->sendConfigureReq(context);
+         //Send Configure-Reject packet
+         callbacks->sendConfigureRej(context, configureReqPacket);
+         //Switch to the Req-Sent state
+         pppChangeState(fsm, PPP_STATE_6_REQ_SENT);
+         //Indicate to the upper layers that the automaton is leaving the Opened
+         //state. The link is no longer available for network traffic
+         callbacks->thisLayerDown(context);
+         break;
+      default:
+         //This event cannot occur in a properly implemented automaton.
+         //No transition is taken, and the implementation should not
+         //reset or freeze
+         break;
+      }
+   }
+}
+
+
+/**
+ * @brief Process Receive-Configure-Ack event
+ * @param[in] context PPP context
+ * @param[in,out] fsm Finite state machine
+ * @param[in] callbacks FSM actions
+ **/
+
+void pppRcvConfigureAckEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks)
+{
+   //Check current state
+   switch(fsm->state)
+   {
+   case PPP_STATE_2_CLOSED:
+   case PPP_STATE_3_STOPPED:
+      //Send Terminate-Ack packet
+      callbacks->sendTerminateAck(context, NULL);
+      //Stay in current state
+      break;
+   case PPP_STATE_4_CLOSING:
+   case PPP_STATE_5_STOPPING:
+      //Stay in current state
+      break;
+   case PPP_STATE_6_REQ_SENT:
+      //Initialize restart counter
+      callbacks->initRestartCount(context, PPP_MAX_CONFIGURE);
+      //Switch to the Ack-Rcvd state
+      fsm->state = PPP_STATE_7_ACK_RCVD;
+      break;
+   case PPP_STATE_7_ACK_RCVD:
+      //Send Configure-Request packet
+      callbacks->sendConfigureReq(context);
+      //Switch to the Req-Sent state
+      pppChangeState(fsm, PPP_STATE_6_REQ_SENT);
+      break;
+   case PPP_STATE_8_ACK_SENT:
+      //Initialize restart counter
+      callbacks->initRestartCount(context, PPP_MAX_CONFIGURE);
+      //Switch to the Opened state
+      pppChangeState(fsm, PPP_STATE_9_OPENED);
+      //Indicate to the upper layers that the automaton is entering the
+      //Opened state. The link is available for network traffic
+      callbacks->thisLayerUp(context);
+      break;
+   case PPP_STATE_9_OPENED:
+      //Send Configure-Request packet
+      callbacks->sendConfigureReq(context);
+      //Switch to the Req-Sent state
+      pppChangeState(fsm, PPP_STATE_6_REQ_SENT);
+      //Indicate to the upper layers that the automaton is leaving the Opened
+      //state. The link is no longer available for network traffic
+      callbacks->thisLayerDown(context);
+      break;
+   default:
+      //This event cannot occur in a properly implemented automaton.
+      //No transition is taken, and the implementation should not
+      //reset or freeze
+      break;
+   }
+}
+
+
+/**
+ * @brief Process Receive-Configure-Nak event
+ * @param[in] context PPP context
+ * @param[in,out] fsm Finite state machine
+ * @param[in] callbacks FSM actions
+ **/
+
+void pppRcvConfigureNakEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks)
+{
+   //Check current state
+   switch(fsm->state)
+   {
+   case PPP_STATE_2_CLOSED:
+   case PPP_STATE_3_STOPPED:
+      //Send Terminate-Ack packet
+      callbacks->sendTerminateAck(context, NULL);
+      //Stay in current state
+      break;
+   case PPP_STATE_4_CLOSING:
+   case PPP_STATE_5_STOPPING:
+      //Stay in current state
+      break;
+   case PPP_STATE_6_REQ_SENT:
+      //Initialize restart counter
+      callbacks->initRestartCount(context, PPP_MAX_CONFIGURE);
+      //Send Configure-Request packet
+      callbacks->sendConfigureReq(context);
+      //Stay in current state
+      break;
+   case PPP_STATE_7_ACK_RCVD:
+      //Send Configure-Request packet
+      callbacks->sendConfigureReq(context);
+      //Switch to the Req-Sent state
+      pppChangeState(fsm, PPP_STATE_6_REQ_SENT);
+      break;
+   case PPP_STATE_8_ACK_SENT:
+      //Initialize restart counter
+      callbacks->initRestartCount(context, PPP_MAX_CONFIGURE);
+      //Send Configure-Request packet
+      callbacks->sendConfigureReq(context);
+      //Stay in current state
+      break;
+   case PPP_STATE_9_OPENED:
+      //Send Configure-Request packet
+      callbacks->sendConfigureReq(context);
+      //Switch to the Req-Sent state
+      pppChangeState(fsm, PPP_STATE_6_REQ_SENT);
+      //Indicate to the upper layers that the automaton is leaving the Opened
+      //state. The link is no longer available for network traffic
+      callbacks->thisLayerDown(context);
+      break;
+   default:
+      //This event cannot occur in a properly implemented automaton.
+      //No transition is taken, and the implementation should not
+      //reset or freeze
+      break;
+   }
+}
+
+
+/**
+ * @brief Process Receive-Terminate-Req event
+ * @param[in] context PPP context
+ * @param[in,out] fsm Finite state machine
+ * @param[in] callbacks FSM actions
+ * @param[in] terminateReqPacket Terminate-Request packet received from the peer
+ **/
+
+void pppRcvTerminateReqEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks, const PppTerminatePacket *terminateReqPacket)
+{
+   //Check current state
+   switch(fsm->state)
+   {
+   case PPP_STATE_2_CLOSED:
+   case PPP_STATE_3_STOPPED:
+   case PPP_STATE_4_CLOSING:
+   case PPP_STATE_5_STOPPING:
+      //Send Terminate-Ack packet
+      callbacks->sendTerminateAck(context, terminateReqPacket);
+      //Stay in current state
+      break;
+   case PPP_STATE_6_REQ_SENT:
+   case PPP_STATE_7_ACK_RCVD:
+   case PPP_STATE_8_ACK_SENT:
+      //Send Terminate-Ack packet
+      callbacks->sendTerminateAck(context, terminateReqPacket);
+      //Switch to the Req-Sent state
+      pppChangeState(fsm, PPP_STATE_6_REQ_SENT);
+      break;
+   case PPP_STATE_9_OPENED:
+      //Zero restart counter
+      callbacks->zeroRestartCount(context);
+      //Send Terminate-Ack packet
+      callbacks->sendTerminateAck(context, terminateReqPacket);
+      //Switch to the Stopping state
+      pppChangeState(fsm, PPP_STATE_5_STOPPING);
+      //Indicate to the upper layers that the automaton is leaving the Opened
+      //state. The link is no longer available for network traffic
+      callbacks->thisLayerDown(context);
+      break;
+   default:
+      //This event cannot occur in a properly implemented automaton.
+      //No transition is taken, and the implementation should not
+      //reset or freeze
+      break;
+   }
+}
+
+
+/**
+ * @brief Process Receive-Terminate-Ack event
+ * @param[in] context PPP context
+ * @param[in,out] fsm Finite state machine
+ * @param[in] callbacks FSM actions
+ **/
+
+void pppRcvTerminateAckEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks)
+{
+   //Check current state
+   switch(fsm->state)
+   {
+   case PPP_STATE_2_CLOSED:
+   case PPP_STATE_3_STOPPED:
+      //Stay in current state
+      break;
+   case PPP_STATE_4_CLOSING:
+      //Switch to the Closed state
+      pppChangeState(fsm, PPP_STATE_2_CLOSED);
+      //Indicate to the lower layers that the automaton is entering the
+      //Initial, Closed or Stopped states. The lower layer is no longer
+      //needed for the link
+      callbacks->thisLayerFinished(context);
+      break;
+   case PPP_STATE_5_STOPPING:
+      //Switch to the Stopped state
+      pppChangeState(fsm, PPP_STATE_3_STOPPED);
+      //Indicate to the lower layers that the automaton is entering the
+      //Initial, Closed or Stopped states. The lower layer is no longer
+      //needed for the link
+      callbacks->thisLayerFinished(context);
+      break;
+   case PPP_STATE_6_REQ_SENT:
+   case PPP_STATE_7_ACK_RCVD:
+      //Switch to the Req-Sent state
+      pppChangeState(fsm, PPP_STATE_6_REQ_SENT);
+      break;
+   case PPP_STATE_8_ACK_SENT:
+      //Stay in current state
+      break;
+   case PPP_STATE_9_OPENED:
+      //Send Configure-Req packet
+      callbacks->sendConfigureReq(context);
+      //Switch to the Req-Sent state
+      pppChangeState(fsm, PPP_STATE_6_REQ_SENT);
+      //Indicate to the upper layers that the automaton is leaving the Opened
+      //state. The link is no longer available for network traffic
+      callbacks->thisLayerDown(context);
+      break;
+   default:
+      //This event cannot occur in a properly implemented automaton.
+      //No transition is taken, and the implementation should not
+      //reset or freeze
+      break;
+   }
+}
+
+
+/**
+ * @brief Process Receive-Unknown-Code event
+ * @param[in] context PPP context
+ * @param[in,out] fsm Finite state machine
+ * @param[in] callbacks FSM actions
+ * @param[in] packet Un-interpretable packet received from the peer
+ **/
+
+void pppRcvUnknownCodeEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks, const PppPacket *packet)
+{
+   //Check current state
+   switch(fsm->state)
+   {
+   case PPP_STATE_2_CLOSED:
+   case PPP_STATE_3_STOPPED:
+   case PPP_STATE_4_CLOSING:
+   case PPP_STATE_5_STOPPING:
+   case PPP_STATE_6_REQ_SENT:
+   case PPP_STATE_7_ACK_RCVD:
+   case PPP_STATE_8_ACK_SENT:
+   case PPP_STATE_9_OPENED:
+      //Send Reject-Code packet
+      callbacks->sendCodeRej(context, packet);
+      //Stay in current state
+      break;
+   default:
+      //This event cannot occur in a properly implemented automaton.
+      //No transition is taken, and the implementation should not
+      //reset or freeze
+      break;
+   }
+}
+
+/**
+ * @brief Process Receive-Code-Reject or Receive-Protocol-Reject event
+ * @param[in] context PPP context
+ * @param[in,out] fsm Finite state machine
+ * @param[in] callbacks FSM actions
+ * @param[in] acceptable This parameter tells whether the rejected value
+ *   is acceptable or catastrophic
+ **/
+
+void pppRcvCodeRejEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks, bool_t acceptable)
+{
+   //Check whether the rejected value is acceptable or catastrophic
+   if(acceptable)
+   {
+      //The RXJ+ event arises when the rejected value is acceptable, such
+      //as a Code-Reject of an extended code, or a Protocol-Reject of a
+      //NCP. These are within the scope of normal operation
+      switch(fsm->state)
+      {
+      case PPP_STATE_2_CLOSED:
+      case PPP_STATE_3_STOPPED:
+      case PPP_STATE_4_CLOSING:
+      case PPP_STATE_5_STOPPING:
+      case PPP_STATE_6_REQ_SENT:
+         //Stay in current state
+         break;
+      case PPP_STATE_7_ACK_RCVD:
+         //Switch to the Req-Sent state
+         pppChangeState(fsm, PPP_STATE_6_REQ_SENT);
+         break;
+      case PPP_STATE_8_ACK_SENT:
+      case PPP_STATE_9_OPENED:
+         //Stay in current state
+         break;
+      default:
+         //This event cannot occur in a properly implemented automaton.
+         //No transition is taken, and the implementation should not
+         //reset or freeze
+         break;
+      }
+   }
+   else
+   {
+      //The RXJ- event arises when the rejected value is catastrophic,
+      //such as a Code-Reject of Configure-Request, or a Protocol-Reject
+      //of LCP! This event communicates an unrecoverable error that
+      //terminates the connection
+      switch(fsm->state)
+      {
+      case PPP_STATE_2_CLOSED:
+      case PPP_STATE_3_STOPPED:
+         //Indicate to the lower layers that the automaton is entering the
+         //Initial, Closed or Stopped states. The lower layer is no longer
+         //needed for the link
+         callbacks->thisLayerFinished(context);
+         //Stay in current state
+         break;
+      case PPP_STATE_4_CLOSING:
+         //Switch to the Closed state
+         pppChangeState(fsm, PPP_STATE_2_CLOSED);
+         //Indicate to the lower layers that the automaton is entering the
+         //Initial, Closed or Stopped states. The lower layer is no longer
+         //needed for the link
+         callbacks->thisLayerFinished(context);
+         break;
+      case PPP_STATE_5_STOPPING:
+      case PPP_STATE_6_REQ_SENT:
+      case PPP_STATE_7_ACK_RCVD:
+      case PPP_STATE_8_ACK_SENT:
+         //Switch to the Stopped state
+         pppChangeState(fsm, PPP_STATE_3_STOPPED);
+         //Indicate to the lower layers that the automaton is entering the
+         //Initial, Closed or Stopped states. The lower layer is no longer
+         //needed for the link
+         callbacks->thisLayerFinished(context);
+         break;
+      case PPP_STATE_9_OPENED:
+         //Initialize restart counter
+         callbacks->initRestartCount(context, PPP_MAX_TERMINATE);
+         //Send Terminate-Req packet
+         callbacks->sendTerminateReq(context);
+         //Switch to the Stopping state
+         pppChangeState(fsm, PPP_STATE_5_STOPPING);
+         //Indicate to the upper layers that the automaton is leaving the Opened
+         //state. The link is no longer available for network traffic
+         callbacks->thisLayerDown(context);
+         break;
+      default:
+         //This event cannot occur in a properly implemented automaton.
+         //No transition is taken, and the implementation should not
+         //reset or freeze
+         break;
+      }
+   }
+}
+
+
+/**
+ * @brief Process Receive-Echo-Request event
+ * @param[in] context PPP context
+ * @param[in,out] fsm Finite state machine
+ * @param[in] callbacks FSM actions
+ * @param[in] echoReqPacket Echo-Request packet received from the peer
+ **/
+
+void pppRcvEchoReqEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks, const PppEchoPacket *echoReqPacket)
+{
+   //Check current state
+   switch(fsm->state)
+   {
+   case PPP_STATE_2_CLOSED:
+   case PPP_STATE_3_STOPPED:
+   case PPP_STATE_4_CLOSING:
+   case PPP_STATE_5_STOPPING:
+   case PPP_STATE_6_REQ_SENT:
+   case PPP_STATE_7_ACK_RCVD:
+   case PPP_STATE_8_ACK_SENT:
+      //Stay in current state
+      break;
+   case PPP_STATE_9_OPENED:
+      //Send Echo-Reply packet
+      callbacks->sendEchoRep(context, echoReqPacket);
+      //Stay in current state
+      break;
+   default:
+      //This event cannot occur in a properly implemented automaton.
+      //No transition is taken, and the implementation should not
+      //reset or freeze
+      break;
+   }
+}
+
+
+/**
+ * @brief Update PPP FSM state
+ * @param[in,out] fsm Finite state machine
+ * @param[in] newState New PPP state to switch to
+ **/
+
+void pppChangeState(PppFsm *fsm, PppState newState)
+{
+#if (PPP_TRACE_LEVEL >= TRACE_LEVEL_INFO)
+   //PPP FSM states
+   static const char_t *stateLabel[] =
+   {
+      "INITIAL",  //0
+      "STARTING", //1
+      "CLOSED",   //2
+      "STOPPED",  //3
+      "CLOSING",  //4
+      "STOPPING", //5
+      "REQ_SENT", //6
+      "ACK_RCVD", //7
+      "ACK_SENT", //8
+      "OPENED"    //9
+   };
+
+   //Sanity check
+   if(fsm->state < arraysize(stateLabel) && newState < arraysize(stateLabel))
+   {
+      //Debug message
+      TRACE_INFO("PPP FSM: %s (%u) -> %s (%u)\r\n", stateLabel[fsm->state],
+         fsm->state, stateLabel[newState], newState);
+   }
+#endif
+
+   //Switch to the new state
+   fsm->state = newState;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/ppp_fsm.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,207 @@
+/**
+ * @file ppp_fsm.h
+ * @brief PPP finite state machine
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _PPP_FSM_H
+#define _PPP_FSM_H
+
+//Dependencies
+#include "core/net.h"
+#include "ppp/ppp.h"
+
+
+/**
+ * @brief This-Layer-Up callback function
+ **/
+
+typedef void (*PppThisLayerUp)(PppContext *context);
+
+
+/**
+ * @brief This-Layer-Down callback function
+ **/
+
+typedef void (*PppThisLayerDown)(PppContext *context);
+
+
+/**
+ * @brief This-Layer-Started callback function
+ **/
+
+typedef void (*PppThisLayerStarted)(PppContext *context);
+
+
+/**
+ * @brief This-Layer-Finished callback function
+ **/
+
+typedef void (*PppThisLayerFinished)(PppContext *context);
+
+
+/**
+ * @brief Initialize-Restart-Count callback function
+ **/
+
+typedef void (*PppInitRestartCount)(PppContext *context, uint_t value);
+
+
+/**
+ * @brief Zero-Restart-Count callback function
+ **/
+
+typedef void (*PppZeroRestartCount)(PppContext *context);
+
+
+/**
+ * @brief Send-Configure-Request callback function
+ **/
+
+typedef error_t (*PppSendConfigureReq)(PppContext *context);
+
+
+/**
+ * @brief Send-Configure-Ack callback function
+ **/
+
+typedef error_t (*PppSendConfigureAck)(PppContext *context,
+   const PppConfigurePacket *configureReqPacket);
+
+
+/**
+ * @brief Send-Configure-Nak callback function
+ **/
+
+typedef error_t (*PppSendConfigureNak)(PppContext *context,
+   const PppConfigurePacket *configureReqPacket);
+
+
+/**
+ * @brief Send-Configure-Reject callback function
+ **/
+
+typedef error_t (*PppSendConfigureRej)(PppContext *context,
+   const PppConfigurePacket *configureReqPacket);
+
+
+/**
+ * @brief Send-Terminate-Request callback function
+ **/
+
+typedef error_t (*PppSendTerminateReq)(PppContext *context);
+
+
+/**
+ * @brief Send-Terminate-Ack callback function
+ **/
+
+typedef error_t (*PppSendTerminateAck)(PppContext *context,
+   const PppTerminatePacket *terminateReqPacket);
+
+
+/**
+ * @brief Send-Code-Reject callback function
+ **/
+
+typedef error_t (*PppSendCodeRej)(PppContext *context,
+   const PppPacket *packet);
+
+
+/**
+ * @brief Send-Echo-Reply callback function
+ **/
+
+typedef error_t (*PppSendEchoRep)(PppContext *context,
+   const PppEchoPacket *echoReqPacket);
+
+
+/**
+ *@brief PPP FSM actions
+ **/
+
+typedef struct
+{
+   PppThisLayerUp thisLayerUp;
+   PppThisLayerDown thisLayerDown;
+   PppThisLayerStarted thisLayerStarted;
+   PppThisLayerFinished thisLayerFinished;
+   PppInitRestartCount initRestartCount;
+   PppZeroRestartCount zeroRestartCount;
+   PppSendConfigureReq sendConfigureReq;
+   PppSendConfigureAck sendConfigureAck;
+   PppSendConfigureNak sendConfigureNak;
+   PppSendConfigureRej sendConfigureRej;
+   PppSendTerminateReq sendTerminateReq;
+   PppSendTerminateAck sendTerminateAck;
+   PppSendCodeRej sendCodeRej;
+   PppSendEchoRep sendEchoRep;
+} PppCallbacks;
+
+
+//PPP FSM events
+void pppUpEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks);
+
+void pppDownEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks);
+
+void pppOpenEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks);
+
+void pppCloseEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks);
+
+void pppTimeoutEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks);
+
+void pppRcvConfigureReqEvent(PppContext *context, PppFsm *fsm, const PppCallbacks *callbacks,
+   const PppConfigurePacket *configureReqPacket, PppCode code);
+
+void pppRcvConfigureAckEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks);
+
+void pppRcvConfigureNakEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks);
+
+void pppRcvTerminateReqEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks, const PppTerminatePacket *terminateReqPacket);
+
+void pppRcvTerminateAckEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks);
+
+void pppRcvUnknownCodeEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks, const PppPacket *packet);
+
+void pppRcvCodeRejEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks, bool_t acceptable);
+
+void pppRcvEchoReqEvent(PppContext *context, PppFsm *fsm,
+   const PppCallbacks *callbacks, const PppEchoPacket *echoReqPacket);
+
+void pppChangeState(PppFsm *fsm, PppState newState);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/ppp_hdlc.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,700 @@
+/**
+ * @file ppp_hdlc.c
+ * @brief PPP HDLC driver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL NIC_TRACE_LEVEL
+
+//Dependencies
+#include <stdio.h>
+#include "core/net.h"
+#include "ppp/ppp.h"
+#include "ppp/ppp_hdlc.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (PPP_SUPPORT == ENABLED)
+
+
+/**
+ * @brief PPP HDLC driver
+ **/
+
+const NicDriver pppHdlcDriver =
+{
+   NIC_TYPE_PPP,
+   PPP_DEFAULT_MRU,
+   pppHdlcDriverInit,
+   pppHdlcDriverTick,
+   pppHdlcDriverEnableIrq,
+   pppHdlcDriverDisableIrq,
+   pppHdlcDriverEventHandler,
+   pppHdlcDriverSendPacket,
+   pppHdlcDriverSetMulticastFilter,
+   NULL,
+   NULL,
+   NULL,
+   FALSE,
+   FALSE,
+   FALSE,
+   FALSE
+};
+
+
+/**
+ * @brief PPP HDLC driver initialization
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t pppHdlcDriverInit(NetInterface *interface)
+{
+   PppContext *context;
+
+   //Debug message
+   TRACE_INFO("Initializing PPP HDLC driver...\r\n");
+
+   //Point to the PPP context
+   context = interface->pppContext;
+
+   //Initialize variables
+   context->txBufferLen = 0;
+   context->txWriteIndex = 0;
+   context->txReadIndex = 0;
+   context->rxBufferLen = 0;
+   context->rxWriteIndex = 0;
+   context->rxReadIndex = 0;
+   context->rxFrameCount = 0;
+
+   //Initialize UART
+   interface->uartDriver->init();
+
+   //Accept any packets from the upper layer
+   osSetEvent(&interface->nicTxEvent);
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief PPP HDLC driver timer handler
+ *
+ * This routine is periodically called by the TCP/IP stack to
+ * handle periodic operations such as polling the link state
+ *
+ * @param[in] interface Underlying network interface
+ **/
+
+void pppHdlcDriverTick(NetInterface *interface)
+{
+}
+
+
+/**
+ * @brief Enable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void pppHdlcDriverEnableIrq(NetInterface *interface)
+{
+   //Enable UART interrupts
+   interface->uartDriver->enableIrq();
+}
+
+
+/**
+ * @brief Disable interrupts
+ * @param[in] interface Underlying network interface
+ **/
+
+void pppHdlcDriverDisableIrq(NetInterface *interface)
+{
+   //USART interrupts are always enabled
+}
+
+
+/**
+ * @brief PPP HDLC driver event handler
+ * @param[in] interface Underlying network interface
+ **/
+
+void pppHdlcDriverEventHandler(NetInterface *interface)
+{
+   PppContext *context;
+
+   //Point to the PPP context
+   context = interface->pppContext;
+
+   //Check PPP state
+   if(interface->pppContext->pppPhase != PPP_PHASE_DEAD)
+   {
+      //Process all pending packets
+      while(context->rxFrameCount > 0)
+      {
+         //Read incoming packet
+         pppHdlcDriverReceivePacket(interface);
+
+         //Enter critical section
+         __disable_irq();
+         //Decrement frame counter
+         context->rxFrameCount--;
+         //Exit critical section
+         __enable_irq();
+      }
+   }
+}
+
+
+/**
+ * @brief Send a packet
+ * @param[in] interface Underlying network interface
+ * @param[in] buffer Multi-part buffer containing the data to send
+ * @param[in] offset Offset to the first data byte
+ * @return Error code
+ **/
+
+error_t pppHdlcDriverSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset)
+{
+   uint_t i;
+   size_t j;
+   size_t n;
+   uint8_t *p;
+   uint16_t protocol;
+   uint32_t accm;
+   PppContext *context;
+
+   //Point to the PPP context
+   context = interface->pppContext;
+
+   //Point to the beginning of the frame
+   p = netBufferAt(buffer, offset);
+
+   //Parse the PPP frame header
+   pppParseFrameHeader(p, PPP_FRAME_HEADER_SIZE, &protocol);
+
+   //Check Protocol field
+   if(protocol == PPP_PROTOCOL_IP || protocol == PPP_PROTOCOL_IPV6)
+   {
+      //Use the ACCM value that has been negotiated
+      accm = context->peerConfig.accm;
+   }
+   else
+   {
+      //Use default ACCM mapping
+      accm = PPP_DEFAULT_ACCM;
+   }
+
+   //Send flag
+   pppHdlcDriverWriteTxQueue(context, PPP_FLAG_CHAR);
+
+   //Loop through data chunks
+   for(i = 0; i < buffer->chunkCount; i++)
+   {
+      //Is there any data to copy from the current chunk?
+      if(offset < buffer->chunk[i].length)
+      {
+         //Point to the first byte to be read
+         p = (uint8_t *) buffer->chunk[i].address + offset;
+         //Compute the number of bytes to copy at a time
+         n = buffer->chunk[i].length - offset;
+
+         //Copy data to TX queue
+         for(j = 0; j < n; j++)
+         {
+            if(p[j] < PPP_MASK_CHAR)
+            {
+                //Check whether the character is flagged
+               if(accm & (1 << p[j]))
+               {
+                  pppHdlcDriverWriteTxQueue(context, PPP_ESC_CHAR);
+                  pppHdlcDriverWriteTxQueue(context, p[j] ^ PPP_MASK_CHAR);
+               }
+               else
+               {
+                  //Enqueue current character
+                  pppHdlcDriverWriteTxQueue(context, p[j]);
+               }
+            }
+            else if(p[j] == PPP_ESC_CHAR || p[j] == PPP_FLAG_CHAR)
+            {
+               pppHdlcDriverWriteTxQueue(context, PPP_ESC_CHAR);
+               pppHdlcDriverWriteTxQueue(context, p[j] ^ PPP_MASK_CHAR);
+            }
+            else
+            {
+               //Enqueue current character
+               pppHdlcDriverWriteTxQueue(context, p[j]);
+            }
+         }
+
+         //Process the next block from the start
+         offset = 0;
+      }
+      else
+      {
+         //Skip the current chunk
+         offset -= buffer->chunk[i].length;
+      }
+   }
+
+   //Send flag
+   pppHdlcDriverWriteTxQueue(context, PPP_FLAG_CHAR);
+
+   //Start transferring data
+   interface->uartDriver->startTx();
+
+   //Check whether the TX queue is available for writing
+   if(context->txBufferLen <= (PPP_TX_BUFFER_SIZE - 3006))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Receive a packet
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t pppHdlcDriverReceivePacket(NetInterface *interface)
+{
+   size_t n;
+   uint8_t c;
+   bool_t escFlag;
+   uint32_t accm;
+   PppContext *context;
+
+   //Point to the PPP context
+   context = interface->pppContext;
+   //Retrieve ACCM
+   accm = context->localConfig.accm;
+
+   //Length of the original PPP frame
+   n = 0;
+   //This flag tells whether the next character is escaped
+   escFlag = FALSE;
+
+   //The receiver must reverse the octet stuffing procedure
+   while(n < PPP_MAX_FRAME_SIZE && context->rxBufferLen > 0)
+   {
+      //Read a single character
+      c = pppHdlcDriverReadRxQueue(context);
+
+      if(c < PPP_MASK_CHAR)
+      {
+         //Check whether the character is flagged
+         if(accm & (1 << c))
+         {
+            //The extra characters must be removed from the incoming data stream
+         }
+         else
+         {
+            //Copy current character
+            context->frame[n++] = c;
+         }
+      }
+      else if(c == PPP_ESC_CHAR)
+      {
+         //All occurrences of 0x7D indicate that the next character is escaped
+         escFlag = TRUE;
+      }
+      else if(c == PPP_FLAG_CHAR)
+      {
+         //0x7E flag found
+         break;
+      }
+      else if(escFlag)
+      {
+         //The character is XOR'ed with 0x20
+         context->frame[n++] = c ^ PPP_MASK_CHAR;
+         escFlag = FALSE;
+      }
+      else
+      {
+         //Copy current character
+         context->frame[n++] = c;
+      }
+   }
+
+   //Check whether a valid PPP frame has been received
+   if(n > 0)
+   {
+      //Debug message
+      TRACE_DEBUG("PPP frame received (%" PRIuSIZE " bytes)...\r\n", n);
+      TRACE_DEBUG_ARRAY("  ", context->frame, n);
+
+      //Pass the packet to the upper layer
+      nicProcessPacket(interface, context->frame, n);
+   }
+
+   //Successful read operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Configure multicast MAC address filtering
+ * @param[in] interface Underlying network interface
+ * @return Error code
+ **/
+
+error_t pppHdlcDriverSetMulticastFilter(NetInterface *interface)
+{
+   //Not implemented
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Send AT command
+ * @param[in] interface Underlying network interface
+ * @param[in] data NULL-terminated string that contains the AT command to be sent
+ * @return Error code
+ **/
+
+error_t pppHdlcDriverSendAtCommand(NetInterface *interface, const char_t *data)
+{
+   size_t i;
+   PppContext *context;
+
+   //Point to the PPP context
+   context = interface->pppContext;
+
+   //Send AT command
+   for(i = 0; data[i] != '\0' && i < 3006; i++)
+      pppHdlcDriverWriteTxQueue(context, data[i]);
+
+   //Start transferring data
+   interface->uartDriver->startTx();
+
+   //Check whether the TX queue is available for writing
+   if(context->txBufferLen <= (PPP_TX_BUFFER_SIZE - 3006))
+   {
+      //The transmitter can accept another packet
+      osSetEvent(&interface->nicTxEvent);
+   }
+
+   //Data successfully written
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Wait for an incoming AT command
+ * @param[in] interface Underlying network interface
+ * @param[out] data Buffer where to store the incoming AT command
+ * @param[in] size Size of the buffer, in bytes
+ * @return Error code
+ **/
+
+error_t pppHdlcDriverReceiveAtCommand(NetInterface *interface, char_t *data, size_t size)
+{
+   uint_t i;
+   uint_t k;
+   uint_t n;
+   bool_t valid;
+   PppContext *context;
+
+   //Point to the PPP context
+   context = interface->pppContext;
+
+   //Point to the first byte of the receive buffer
+   k = context->rxReadIndex;
+   //Number of characters pending in the receive buffer
+   n = context->rxBufferLen;
+
+   //Loop through received data
+   for(i = 0, valid = FALSE; i < n && !valid; i++)
+   {
+      //Read current character
+      data[i] = context->rxBuffer[k];
+
+      //Carriage return?
+      if(data[i] == '\r' || data[i] == '\n')
+      {
+         data[i] = '\0';
+         valid = TRUE;
+      }
+      //Special processing of null-modem connections
+      else if(i >= 5 && !memcmp(data + i - 5, "CLIENT", 6))
+      {
+         data[i + 1] = '\0';
+         valid = TRUE;
+      }
+      else if(i >= 11 && !memcmp(data + i - 11, "CLIENTSERVER", 12))
+      {
+         data[i + 1] = '\0';
+         valid = TRUE;
+      }
+      //Buffer full?
+      else if(i == (size - 2))
+      {
+         data[i + 1] = '\0';
+         valid = TRUE;
+      }
+
+      //Increment index and wrap around if necessary
+      if(++k >= PPP_RX_BUFFER_SIZE)
+         k = 0;
+   }
+
+   //Valid command received?
+   if(valid)
+   {
+      //Advance read index
+      context->rxReadIndex = (context->rxReadIndex + i) % PPP_RX_BUFFER_SIZE;
+
+      //Enter critical section
+      __disable_irq();
+      //Update the length of the RX buffer
+      context->rxBufferLen -= i;
+      //Exit critical section
+      __enable_irq();
+
+      //Successful processing
+      return NO_ERROR;
+   }
+   else
+   {
+      //data[i] = '\0';
+      //TRACE_INFO("PPP RX buffer residue (%d bytes)\r\n", i);
+      //TRACE_INFO_ARRAY("#  ", data, i);
+      return ERROR_BUFFER_EMPTY;
+   }
+}
+
+
+/**
+ * @brief Purge TX buffer
+ * @param[in] context Pointer to the PPP context
+ * @return Error code
+ **/
+
+error_t pppHdlcDriverPurgeTxBuffer(PppContext *context)
+{
+   //Enter critical section
+   __disable_irq();
+
+   //Purge TX buffer
+   context->txBufferLen = 0;
+   context->txWriteIndex = 0;
+   context->txReadIndex = 0;
+
+   //Exit critical section
+   __enable_irq();
+
+   //Successful operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Purge RX buffer
+ * @param[in] context Pointer to the PPP context
+ * @return Error code
+ **/
+
+error_t pppHdlcDriverPurgeRxBuffer(PppContext *context)
+{
+   //Enter critical section
+   __disable_irq();
+
+   //Purge RX buffer
+   context->rxBufferLen = 0;
+   context->rxWriteIndex = 0;
+   context->rxReadIndex = 0;
+   context->rxFrameCount = 0;
+
+   //Exit critical section
+   __enable_irq();
+
+   //Successful operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write TX queue
+ * @param[in] context Pointer to the PPP context
+ * @param[in] c Character to be written
+ **/
+
+void pppHdlcDriverWriteTxQueue(PppContext *context, uint8_t c)
+{
+   //Enqueue the character
+   context->txBuffer[context->txWriteIndex] = c;
+
+   //Increment index and wrap around if necessary
+   if(++context->txWriteIndex >= PPP_TX_BUFFER_SIZE)
+      context->txWriteIndex = 0;
+
+   //Enter critical section
+   __disable_irq();
+   //Update the length of the queue
+   context->txBufferLen++;
+   //Exit critical section
+   __enable_irq();
+}
+
+
+/**
+ * @brief Read RX queue
+ * @param[in] context Pointer to the PPP context
+ * @return Character read from the queue
+ **/
+
+uint8_t pppHdlcDriverReadRxQueue(PppContext *context)
+{
+   uint8_t c;
+
+   //Read a single character
+   c = context->rxBuffer[context->rxReadIndex];
+
+   //Increment index and wrap around if necessary
+   if(++context->rxReadIndex >= PPP_RX_BUFFER_SIZE)
+      context->rxReadIndex = 0;
+
+   //Enter critical section
+   __disable_irq();
+   //Update the length of the queue
+   context->rxBufferLen--;
+   //Exit critical section
+   __enable_irq();
+
+   //Return the character that has been read
+   return c;
+}
+
+
+/**
+ * @brief Read TX queue
+ * @param[in] interface Underlying network interface
+ * @param[out] c Character read from the queue
+ * @return TRUE if a context switch is required
+ **/
+
+bool_t pppHdlcDriverReadTxQueue(NetInterface *interface, int_t *c)
+{
+   bool_t flag;
+   PppContext *context;
+
+   //Point to the PPP context
+   context = interface->pppContext;
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Any data pending in the TX queue?
+   if(context->txBufferLen > 0)
+   {
+      //Read a single character
+      *c = context->txBuffer[context->txReadIndex];
+
+      //Increment index and wrap around if necessary
+      if(++context->txReadIndex >= PPP_TX_BUFFER_SIZE)
+         context->txReadIndex = 0;
+
+      //Update the length of the queue
+      context->txBufferLen--;
+
+      //Check whether the TX is available for writing
+      if(context->txBufferLen == (PPP_TX_BUFFER_SIZE - 3006))
+      {
+         flag = osSetEventFromIsr(&interface->nicTxEvent);
+      }
+   }
+   else
+   {
+      //The TX queue is empty
+      *c = EOF;
+   }
+
+   //The return value tells whether a context switch is required
+   return flag;
+}
+
+
+/**
+ * @brief Write RX queue
+ * @param[in] interface Underlying network interface
+ * @param[in] c Character to be written
+ * @return TRUE if a context switch is required
+ **/
+
+bool_t pppHdlcDriverWriteRxQueue(NetInterface *interface, uint8_t c)
+{
+   bool_t flag;
+   PppContext *context;
+
+   //Point to the PPP context
+   context = interface->pppContext;
+   //This flag will be set if a higher priority task must be woken
+   flag = FALSE;
+
+   //Make sure the RX queue is not full
+   if(context->rxBufferLen < PPP_RX_BUFFER_SIZE)
+   {
+      //Enqueue the character
+      context->rxBuffer[context->rxWriteIndex] = c;
+
+      //Increment index and wrap around if necessary
+      if(++context->rxWriteIndex >= PPP_RX_BUFFER_SIZE)
+         context->rxWriteIndex = 0;
+
+      //Update the length of the queue
+      context->rxBufferLen++;
+
+      //Check PPP connection state
+      if(interface->pppContext->pppPhase != PPP_PHASE_DEAD)
+      {
+         //0x7E flag found?
+         if(c == PPP_FLAG_CHAR)
+         {
+            //Increment frame counter
+            context->rxFrameCount++;
+
+            //A complete HDLC frame has been received
+            interface->nicEvent = TRUE;
+            //Notify the TCP/IP stack of the event
+            flag = osSetEventFromIsr(&netEvent);
+         }
+      }
+   }
+
+   //The return value tells whether a context switch is required
+   return flag;
+}
+
+#endif
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/ppp_hdlc.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,67 @@
+/**
+ * @file ppp_hdlc.h
+ * @brief PPP HDLC driver
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _PPP_HDLC_H
+#define _PPP_HDLC_H
+
+//Dependencies
+#include "core/nic.h"
+
+//PPP HDLC driver
+extern const NicDriver pppHdlcDriver;
+
+//PPP HDLC driver related functions
+error_t pppHdlcDriverInit(NetInterface *interface);
+
+void pppHdlcDriverTick(NetInterface *interface);
+
+void pppHdlcDriverEnableIrq(NetInterface *interface);
+void pppHdlcDriverDisableIrq(NetInterface *interface);
+void pppHdlcDriverEventHandler(NetInterface *interface);
+
+error_t pppHdlcDriverSendPacket(NetInterface *interface,
+   const NetBuffer *buffer, size_t offset);
+
+error_t pppHdlcDriverReceivePacket(NetInterface *interface);
+
+error_t pppHdlcDriverSetMulticastFilter(NetInterface *interface);
+
+error_t pppHdlcDriverSendAtCommand(NetInterface *interface, const char_t *data);
+error_t pppHdlcDriverReceiveAtCommand(NetInterface *interface, char_t *data, size_t size);
+
+error_t pppHdlcDriverPurgeTxBuffer(PppContext *context);
+error_t pppHdlcDriverPurgeRxBuffer(PppContext *context);
+
+void pppHdlcDriverWriteTxQueue(PppContext *context, uint8_t c);
+uint8_t pppHdlcDriverReadRxQueue(PppContext *context);
+
+bool_t pppHdlcDriverReadTxQueue(NetInterface *interface, int_t *c);
+bool_t pppHdlcDriverWriteRxQueue(NetInterface *interface, uint8_t c);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/ppp_misc.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,472 @@
+/**
+ * @file ppp_misc.c
+ * @brief PPP miscellaneous functions
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL PPP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "ppp/ppp_misc.h"
+#include "ppp/ppp_debug.h"
+#include "ppp/lcp.h"
+#include "ppp/ipcp.h"
+#include "ppp/ipv6cp.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (PPP_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Send Configure-Ack, Nak or Reject packet
+ * @param[in] context PPP context
+ * @param[in] configureReqPacket Pointer to the incoming Configure-Request
+ * @param[in] protocol Protocol field
+ * @param[in] code Code field
+ * @return Error code
+ **/
+
+error_t pppSendConfigureAckNak(PppContext *context,
+   const PppConfigurePacket *configureReqPacket, PppProtocol protocol, PppCode code)
+{
+   error_t error;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   PppConfigurePacket *configureAckNakPacket;
+   PppOption *option;
+
+   //Initialize status code
+   error = NO_ERROR;
+   //Retrieve the length of the Configure-Request packet
+   length = ntohs(configureReqPacket->length);
+
+   //Allocate a buffer memory to hold the Configure-Ack, Nak or Reject packet
+   buffer = pppAllocBuffer(length, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the beginning of the packet
+   configureAckNakPacket = netBufferAt(buffer, offset);
+
+   //Format packet header
+   configureAckNakPacket->code = code;
+   configureAckNakPacket->identifier = configureReqPacket->identifier;
+   configureAckNakPacket->length = sizeof(PppConfigurePacket);
+
+   //Retrieve the length of the option list
+   length -= sizeof(PppConfigurePacket);
+   //Point to the first option
+   option = (PppOption *) configureReqPacket->options;
+
+   //Parse configuration options
+   while(length > 0)
+   {
+      //LCP protocol?
+      if(protocol == PPP_PROTOCOL_LCP)
+      {
+         //Parse LCP option
+         lcpParseOption(context, option, length, configureAckNakPacket);
+      }
+#if (IPV4_SUPPORT == ENABLED)
+      //IPCP protocol?
+      else if(protocol == PPP_PROTOCOL_IPCP)
+      {
+         //Parse IPCP option
+         ipcpParseOption(context, option, length, configureAckNakPacket);
+      }
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+      //IPV6CP protocol?
+      else if(protocol == PPP_PROTOCOL_IPV6CP)
+      {
+         //Parse IPV6CP option
+         ipv6cpParseOption(context, option, length, configureAckNakPacket);
+      }
+#endif
+
+      //Remaining bytes to process
+      length -= option->length;
+      //Jump to the next option
+      option = (PppOption *) ((uint8_t *) option + option->length);
+   }
+
+   //Adjust the length of the multi-part buffer
+   netBufferSetLength(buffer, offset + configureAckNakPacket->length);
+   //Convert length field to network byte order
+   configureAckNakPacket->length = htons(configureAckNakPacket->length);
+
+   //Debug message
+   if(code == PPP_CODE_CONFIGURE_ACK)
+   {
+      TRACE_INFO("Sending Configure-Ack packet (%" PRIuSIZE " bytes)...\r\n",
+         ntohs(configureAckNakPacket->length));
+   }
+   else if(code == PPP_CODE_CONFIGURE_NAK)
+   {
+      TRACE_INFO("Sending Configure-Nak packet (%" PRIuSIZE " bytes)...\r\n",
+         ntohs(configureAckNakPacket->length));
+   }
+   else if(code == PPP_CODE_CONFIGURE_REJ)
+   {
+      TRACE_INFO("Sending Configure-Reject packet (%" PRIuSIZE " bytes)...\r\n",
+         ntohs(configureAckNakPacket->length));
+   }
+
+   //Dump packet contents for debugging purpose
+   pppDumpPacket((PppPacket *) configureAckNakPacket,
+      ntohs(configureAckNakPacket->length), protocol);
+
+   //Send PPP frame
+   error = pppSendFrame(context->interface, buffer, offset, protocol);
+
+   //Free previously allocated memory block
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send Terminate-Request packet
+ * @param[in] context PPP context
+ * @param[in] identifier Identifier field
+ * @param[in] protocol Protocol field
+ * @return Error code
+ **/
+
+error_t pppSendTerminateReq(PppContext *context,
+   uint8_t identifier, PppProtocol protocol)
+{
+   error_t error;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   PppTerminatePacket *terminateReqPacket;
+
+   //Length of the Terminate-Request packet
+   length = sizeof(PppTerminatePacket);
+
+   //Allocate a buffer memory to hold the Terminate-Request packet
+   buffer = pppAllocBuffer(length, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the Terminate-Request packet
+   terminateReqPacket = netBufferAt(buffer, offset);
+
+   //Format packet header
+   terminateReqPacket->code = PPP_CODE_TERMINATE_REQ;
+   terminateReqPacket->identifier = identifier;
+   terminateReqPacket->length = htons(length);
+
+   //Debug message
+   TRACE_INFO("Sending Terminate-Request packet (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump packet contents for debugging purpose
+   pppDumpPacket((PppPacket *) terminateReqPacket, length, protocol);
+
+   //Send PPP frame
+   error = pppSendFrame(context->interface, buffer, offset, protocol);
+
+   //Free previously allocated memory block
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send Terminate-Ack packet
+ * @param[in] context PPP context
+ * @param[in] identifier Identifier field
+ * @param[in] protocol Protocol field
+ * @return Error code
+ **/
+
+error_t pppSendTerminateAck(PppContext *context,
+   uint8_t identifier, PppProtocol protocol)
+{
+   error_t error;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   PppTerminatePacket *terminateAckPacket;
+
+   //Length of the Terminate-Ack packet
+   length = sizeof(PppTerminatePacket);
+
+   //Allocate a buffer memory to hold the Terminate-Ack packet
+   buffer = pppAllocBuffer(length, &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the Terminate-Ack packet
+   terminateAckPacket = netBufferAt(buffer, offset);
+
+   //Format packet header
+   terminateAckPacket->code = PPP_CODE_TERMINATE_ACK;
+   terminateAckPacket->identifier = identifier;
+   terminateAckPacket->length = htons(length);
+
+   //Debug message
+   TRACE_INFO("Sending Terminate-Ack packet (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump packet contents for debugging purpose
+   pppDumpPacket((PppPacket *) terminateAckPacket, length, protocol);
+
+   //Send PPP frame
+   error = pppSendFrame(context->interface, buffer, offset, protocol);
+
+   //Free previously allocated memory block
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send Code-Reject packet
+ * @param[in] context PPP context
+ * @param[in] packet Un-interpretable packet received from the peer
+ * @param[in] identifier Identifier field
+ * @param[in] protocol Protocol field
+ * @return Error code
+ **/
+
+error_t pppSendCodeRej(PppContext *context, const PppPacket *packet,
+   uint8_t identifier, PppProtocol protocol)
+{
+   error_t error;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   PppCodeRejPacket *codeRejPacket;
+
+   //Calculate the length of the Code-Reject packet
+   length = ntohs(packet->length) + sizeof(PppCodeRejPacket);
+
+   //The rejected packet must be truncated to comply with
+   //the peer's established MRU
+   length = MIN(length, context->peerConfig.mru);
+
+   //Allocate a buffer memory to hold the Code-Reject packet
+   buffer = pppAllocBuffer(sizeof(PppCodeRejPacket), &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the Code-Reject packet
+   codeRejPacket = netBufferAt(buffer, offset);
+
+   //Format packet header
+   codeRejPacket->code = PPP_CODE_CODE_REJ;
+   codeRejPacket->identifier = identifier;
+   codeRejPacket->length = htons(length);
+
+   //The Rejected-Packet field contains a copy of the packet which is being rejected
+   error = netBufferAppend(buffer, packet, length - sizeof(PppCodeRejPacket));
+
+   //Check status code
+   if(!error)
+   {
+      //Debug message
+      TRACE_INFO("Sending Code-Reject packet (%" PRIuSIZE " bytes)...\r\n", length);
+
+      //Send PPP frame
+      error = pppSendFrame(context->interface, buffer, offset, protocol);
+   }
+
+   //Free previously allocated memory block
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send Protocol-Reject packet
+ * @param[in] context PPP context
+ * @param[in] identifier Identifier field
+ * @param[in] protocol Rejected protocol
+ * @param[in] information Rejected information
+ * @param[in] length Length of the rejected information
+ * @return Error code
+ **/
+
+error_t pppSendProtocolRej(PppContext *context, uint8_t identifier,
+   uint16_t protocol, const uint8_t *information, size_t length)
+{
+   error_t error;
+   size_t offset;
+   NetBuffer *buffer;
+   PppProtocolRejPacket *protocolRejPacket;
+
+   //Calculate the length of the Protocol-Reject packet
+   length += sizeof(PppProtocolRejPacket);
+
+   //The Rejected-Information must be truncated to comply with
+   //the peer's established MRU
+   length = MIN(length, context->peerConfig.mru);
+
+   //Allocate a buffer memory to hold the Protocol-Reject packet
+   buffer = pppAllocBuffer(sizeof(PppProtocolRejPacket), &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the Protocol-Reject packet
+   protocolRejPacket = netBufferAt(buffer, offset);
+
+   //Format packet header
+   protocolRejPacket->code = PPP_CODE_PROTOCOL_REJ;
+   protocolRejPacket->identifier = identifier;
+   protocolRejPacket->length = htons(length);
+   protocolRejPacket->rejectedProtocol = htons(protocol);
+
+   //The Rejected-Information field contains a copy of the
+   //packet which is being rejected
+   error = netBufferAppend(buffer, information,
+      length - sizeof(PppProtocolRejPacket));
+
+   //Check status code
+   if(!error)
+   {
+      //Debug message
+      TRACE_INFO("Sending Protocol-Reject packet (%" PRIuSIZE " bytes)...\r\n", length);
+
+      //Send PPP frame
+      error = pppSendFrame(context->interface, buffer, offset, PPP_PROTOCOL_LCP);
+   }
+
+   //Free previously allocated memory block
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send Echo-Reply packet
+ * @param[in] context PPP context
+ * @param[in] echoReqPacket Echo-Request packet received from the peer
+ * @param[in] protocol Protocol field
+ * @return Error code
+ **/
+
+error_t pppSendEchoRep(PppContext *context,
+   const PppEchoPacket *echoReqPacket, PppProtocol protocol)
+{
+   error_t error;
+   size_t length;
+   size_t offset;
+   NetBuffer *buffer;
+   PppEchoPacket *echoRepPacket;
+
+   //Retrieve the length of the Echo-Request packet
+   length = ntohs(echoReqPacket->length);
+
+   //Make sure the length is valid
+   if(length < sizeof(PppEchoPacket))
+      return ERROR_INVALID_LENGTH;
+   if(length > context->peerConfig.mru)
+      return ERROR_INVALID_LENGTH;
+
+   //Allocate a buffer memory to hold the Echo-Reply packet
+   buffer = pppAllocBuffer(sizeof(PppEchoPacket), &offset);
+   //Failed to allocate memory?
+   if(buffer == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Point to the Echo-Reply packet
+   echoRepPacket = netBufferAt(buffer, offset);
+
+   //Format packet header
+   echoRepPacket->code = PPP_CODE_ECHO_REP;
+   echoRepPacket->identifier = echoReqPacket->identifier;
+   echoRepPacket->length = htons(length);
+   echoRepPacket->magicNumber = context->localConfig.magicNumber;
+
+   //The data field of the Echo-Request packet is copied into the data
+   //field of the Echo-Reply packet
+   error = netBufferAppend(buffer, echoReqPacket->data, length - sizeof(PppEchoPacket));
+
+   //Check status code
+   if(!error)
+   {
+      //Debug message
+      TRACE_INFO("Sending Echo-Reply packet (%" PRIuSIZE " bytes)...\r\n", length);
+
+      //Send PPP frame
+      error = pppSendFrame(context->interface, buffer, offset, protocol);
+   }
+
+   //Free previously allocated memory block
+   netBufferFree(buffer);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Add an option to a Configure packet
+ * @param[in,out] packet Pointer to the Configure packet
+ * @param[in] optionType Option type
+ * @param[in] optionValue Option value
+ * @param[in] optionLen Length of the option value
+ * @return Error code
+ **/
+
+error_t pppAddOption(PppConfigurePacket *packet, uint8_t optionType,
+   const void *optionValue, uint8_t optionLen)
+{
+   PppOption *option;
+
+   //Make sure the length is valid
+   if(optionLen > (UINT8_MAX - sizeof(PppOption)))
+      return ERROR_INVALID_LENGTH;
+
+   //Point to the end of the Configure packet
+   option = (PppOption *) ((uint8_t *) packet + packet->length);
+
+   //Write specified option at current location
+   option->type = optionType;
+   option->length = optionLen + sizeof(PppOption);
+   //Copy option data
+   memcpy(option->data, optionValue, optionLen);
+
+   //Update the length of the Configure packet
+   packet->length += optionLen + sizeof(PppOption);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/ppp/ppp_misc.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,59 @@
+/**
+ * @file ppp_misc.h
+ * @brief PPP miscellaneous functions
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _PPP_MISC_H
+#define _PPP_MISC_H
+
+//Dependencies
+#include "core/net.h"
+#include "ppp/ppp.h"
+
+//PPP related functions
+error_t pppSendConfigureAckNak(PppContext *context,
+   const PppConfigurePacket *configureReqPacket, PppProtocol protocol, PppCode code);
+
+error_t pppSendTerminateReq(PppContext *context,
+   uint8_t identifier, PppProtocol protocol);
+
+error_t pppSendTerminateAck(PppContext *context,
+   uint8_t identifier, PppProtocol protocol);
+
+error_t pppSendCodeRej(PppContext *context, const PppPacket *packet,
+   uint8_t identifier, PppProtocol protocol);
+
+error_t pppSendProtocolRej(PppContext *context, uint8_t identifier,
+   uint16_t protocol, const uint8_t *information, size_t length);
+
+error_t pppSendEchoRep(PppContext *context,
+   const PppEchoPacket *echoReqPacket, PppProtocol protocol);
+
+error_t pppAddOption(PppConfigurePacket *packet, uint8_t optionType,
+   const void *optionValue, uint8_t optionLen);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/smtp/smtp_client.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,961 @@
+/**
+ * @file smtp_client.c
+ * @brief SMTP client (Simple Mail Transfer Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * SMTP is designed as a mail transport and delivery protocol. Refer to
+ * the following RFCs for complete details:
+ * - RFC 5321: Simple Mail Transfer Protocol
+ * - RFC 4954: SMTP Service Extension for Authentication
+ * - RFC 3207: SMTP Service Extension for Secure SMTP over TLS
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL SMTP_TRACE_LEVEL
+
+//Dependencies
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include "core/net.h"
+#include "smtp/smtp_client.h"
+#include "core/socket.h"
+#include "str.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (SMTP_CLIENT_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Send a mail to the specified recipients
+ * @param[in] authInfo Authentication information
+ * @param[in] mail Mail contents
+ * @return Error code
+ **/
+
+error_t smtpSendMail(const SmtpAuthInfo *authInfo, const SmtpMail *mail)
+{
+   error_t error;
+   uint_t i;
+   uint_t replyCode;
+   IpAddr serverIpAddr;
+   SmtpClientContext *context;
+
+   //Check parameters
+   if(authInfo == NULL || mail == NULL)
+      return ERROR_INVALID_PARAMETER;
+   //Make sure the server name is valid
+   if(authInfo->serverName == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Debug message
+   TRACE_INFO("Sending a mail to %s port %" PRIu16 "...\r\n",
+      authInfo->serverName, authInfo->serverPort);
+
+   //The specified SMTP server can be either an IP or a host name
+   error = getHostByName(authInfo->interface,
+      authInfo->serverName, &serverIpAddr, 0);
+   //Unable to resolve server name?
+   if(error)
+      return ERROR_NAME_RESOLUTION_FAILED;
+
+   //Allocate a memory buffer to hold the SMTP client context
+   context = osAllocMem(sizeof(SmtpClientContext));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Open a TCP socket
+   context->socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
+   //Failed to open socket?
+   if(!context->socket)
+   {
+      //Free previously allocated resources
+      osFreeMem(context);
+      //Report an error
+      return ERROR_OPEN_FAILED;
+   }
+
+#if (SMTP_CLIENT_TLS_SUPPORT == ENABLED)
+   //Do not use SSL/TLS for the moment
+   context->tlsContext = NULL;
+#endif
+
+   //Start of exception handling block
+   do
+   {
+      //Bind the socket to a particular network interface?
+      if(authInfo->interface)
+      {
+         //Associate the socket with the relevant interface
+         error = socketBindToInterface(context->socket, authInfo->interface);
+         //Any error to report?
+         if(error)
+            break;
+      }
+
+      //Set timeout for blocking operations
+      error = socketSetTimeout(context->socket, SMTP_CLIENT_DEFAULT_TIMEOUT);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Connect to the SMTP server
+      error = socketConnect(context->socket, &serverIpAddr, authInfo->serverPort);
+      //Connection to server failed?
+      if(error)
+         break;
+
+#if (SMTP_CLIENT_TLS_SUPPORT == ENABLED)
+      //Open a secure SSL/TLS session?
+      if(authInfo->useTls)
+      {
+         //Initialize TLS context
+         context->tlsContext = tlsInit();
+         //Initialization failed?
+         if(context->tlsContext == NULL)
+         {
+            //Unable to allocate memory
+            error = ERROR_OUT_OF_MEMORY;
+            //Stop immediately
+            break;
+         }
+
+         //Bind TLS to the relevant socket
+         error = tlsSetSocket(context->tlsContext, context->socket);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Select client operation mode
+         error = tlsSetConnectionEnd(context->tlsContext, TLS_CONNECTION_END_CLIENT);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Set the PRNG algorithm to be used
+         error = tlsSetPrng(context->tlsContext, authInfo->prngAlgo, authInfo->prngContext);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Perform TLS handshake
+         error = tlsConnect(context->tlsContext);
+         //Failed to established a TLS session?
+         if(error)
+            break;
+      }
+#endif
+
+      //Wait for the connection greeting reply
+      error = smtpSendCommand(context, NULL, &replyCode, NULL);
+      //Any communication error to report?
+      if(error)
+         break;
+
+      //Check whether the greeting message was properly received
+      if(!SMTP_REPLY_CODE_2YZ(replyCode))
+      {
+         //An unexpected response was received...
+         error = ERROR_UNEXPECTED_RESPONSE;
+         //Stop immediately
+         break;
+      }
+
+      //Clear security features
+      context->authLoginSupported = FALSE;
+      context->authPlainSupported = FALSE;
+      context->authCramMd5Supported = FALSE;
+      context->startTlsSupported = FALSE;
+
+      //Send EHLO command and parse server response
+      error = smtpSendCommand(context, "EHLO [127.0.0.1]\r\n",
+         &replyCode, smtpEhloReplyCallback);
+      //Any communication error to report?
+      if(error)
+         break;
+
+      //Check SMTP response code
+      if(!SMTP_REPLY_CODE_2YZ(replyCode))
+      {
+         //An unexpected response was received...
+         error = ERROR_UNEXPECTED_RESPONSE;
+         //Stop immediately
+         break;
+      }
+
+#if (SMTP_CLIENT_TLS_SUPPORT == ENABLED)
+      //Check whether the STARTTLS command is supported
+      if(context->startTlsSupported && !context->tlsContext)
+      {
+         //Send STARTTLS command
+         error = smtpSendCommand(context, "STARTTLS\r\n", &replyCode, NULL);
+         //Any communication error to report?
+         if(error)
+            break;
+
+         //Check SMTP response code
+         if(!SMTP_REPLY_CODE_2YZ(replyCode))
+         {
+            //An unexpected response was received...
+            error = ERROR_UNEXPECTED_RESPONSE;
+            //Stop immediately
+            break;
+         }
+
+         //Initialize TLS context
+         context->tlsContext = tlsInit();
+         //Initialization failed?
+         if(context->tlsContext == NULL)
+         {
+            //Unable to allocate memory
+            error = ERROR_OUT_OF_MEMORY;
+            //Stop immediately
+            break;
+         }
+
+         //Bind TLS to the relevant socket
+         error = tlsSetSocket(context->tlsContext, context->socket);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Select client operation mode
+         error = tlsSetConnectionEnd(context->tlsContext, TLS_CONNECTION_END_CLIENT);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Set the PRNG algorithm to be used
+         error = tlsSetPrng(context->tlsContext, authInfo->prngAlgo, authInfo->prngContext);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Perform TLS handshake
+         error = tlsConnect(context->tlsContext);
+         //Failed to established a TLS session?
+         if(error)
+            break;
+
+         //Clear security features
+         context->authLoginSupported = FALSE;
+         context->authPlainSupported = FALSE;
+         context->authCramMd5Supported = FALSE;
+
+         //Send EHLO command and parse server response
+         error = smtpSendCommand(context, "EHLO [127.0.0.1]\r\n",
+            &replyCode, smtpEhloReplyCallback);
+         //Any communication error to report?
+         if(error)
+            break;
+
+         //Check SMTP response code
+         if(!SMTP_REPLY_CODE_2YZ(replyCode))
+         {
+            //An unexpected response was received...
+            error = ERROR_UNEXPECTED_RESPONSE;
+            //Stop immediately
+            break;
+         }
+      }
+#endif
+
+      //Authentication requires a valid user name and password
+      if(authInfo->userName && authInfo->password)
+      {
+#if (SMTP_CLIENT_LOGIN_AUTH_SUPPORT == ENABLED)
+         //LOGIN authentication mechanism supported?
+         if(context->authLoginSupported)
+         {
+            //Perform LOGIN authentication
+            error = smtpSendAuthLogin(context, authInfo);
+            //Authentication failed?
+            if(error)
+               break;
+         }
+         else
+#endif
+#if (SMTP_CLIENT_PLAIN_AUTH_SUPPORT == ENABLED)
+         //PLAIN authentication mechanism supported?
+         if(context->authPlainSupported)
+         {
+            //Perform PLAIN authentication
+            error = smtpSendAuthPlain(context, authInfo);
+            //Authentication failed?
+            if(error)
+               break;
+         }
+         else
+#endif
+#if (SMTP_CLIENT_CRAM_MD5_AUTH_SUPPORT == ENABLED)
+         //CRAM-MD5 authentication mechanism supported?
+         if(context->authCramMd5Supported)
+         {
+            //Perform CRAM-MD5 authentication
+            error = smtpSendAuthCramMd5(context, authInfo);
+            //Authentication failed?
+            if(error)
+               break;
+         }
+         else
+#endif
+         //No authentication mechanism supported?
+         {
+            //Skip authentication step
+         }
+      }
+
+      //Format the MAIL FROM command (a null return path must be accepted)
+      if(mail->from.addr)
+         sprintf(context->buffer, "MAIL FROM:<%s>\r\n", mail->from.addr);
+      else
+         strcpy(context->buffer, "MAIL FROM:<>\r\n");
+
+      //Send the command to the server
+      error = smtpSendCommand(context, context->buffer, &replyCode, NULL);
+      //Any communication error to report?
+      if(error)
+         break;
+
+      //Check SMTP response code
+      if(!SMTP_REPLY_CODE_2YZ(replyCode))
+      {
+         //An unexpected response was received...
+         error = ERROR_UNEXPECTED_RESPONSE;
+         //Stop immediately
+         break;
+      }
+
+      //Format the RCPT TO command
+      for(i = 0; i < mail->recipientCount; i++)
+      {
+         //Skip recipient addresses that are not valid
+         if(!mail->recipients[i].addr)
+            continue;
+
+         //Format the RCPT TO command
+         sprintf(context->buffer, "RCPT TO:<%s>\r\n", mail->recipients[i].addr);
+         //Send the command to the server
+         error = smtpSendCommand(context, context->buffer, &replyCode, NULL);
+         //Any communication error to report?
+         if(error)
+            break;
+
+         //Check SMTP response code
+         if(!SMTP_REPLY_CODE_2YZ(replyCode))
+         {
+            //An unexpected response was received...
+            error = ERROR_UNEXPECTED_RESPONSE;
+            //Stop immediately
+            break;
+         }
+      }
+
+      //Propagate exception if necessary
+      if(error)
+         break;
+
+      //Send message body
+      error = smtpSendData(context, mail);
+      //Any error to report?
+      if(error)
+         break;
+
+      //End of exception handling block
+   } while(0);
+
+   //Check status code
+   if(error == NO_ERROR ||
+      error == ERROR_UNEXPECTED_RESPONSE ||
+      error == ERROR_AUTHENTICATION_FAILED)
+   {
+      //Properly disconnect from the SMTP server
+      smtpSendCommand(context, "QUIT\r\n", &replyCode, NULL);
+   }
+
+#if (SMTP_CLIENT_TLS_SUPPORT == ENABLED)
+   if(context->tlsContext != NULL)
+   {
+      //Gracefully close SSL/TLS session
+      tlsShutdown(context->tlsContext);
+      //Release SSL/TLS context
+      tlsFree(context->tlsContext);
+   }
+#endif
+
+   //Close socket
+   socketClose(context->socket);
+   //Clean up previously allocated resources
+   osFreeMem(context);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Callback function to parse EHLO response
+ * @param[in] context SMTP client context
+ * @param[in] replyLine Response line
+ * @param[in] replyCode Response code
+ * @return Error code
+ **/
+
+error_t smtpEhloReplyCallback(SmtpClientContext *context,
+   char_t *replyLine, uint_t replyCode)
+{
+   char_t *p;
+   char_t *token;
+
+   //The line must be at least 4 characters long
+   if(strlen(replyLine) < 4)
+      return NO_ERROR;
+
+   //Skip the response code and the separator
+   replyLine += 4;
+
+   //Get the first keyword
+   token = strtok_r(replyLine, " ", &p);
+   //Check whether the response line is empty
+   if(token == NULL)
+      return ERROR_INVALID_SYNTAX;
+
+   //The AUTH keyword contains a space-separated list of
+   //names of available authentication mechanisms
+   if(!strcasecmp(token, "AUTH"))
+   {
+      //Process the rest of the line
+      while(1)
+      {
+         //Get the next keyword
+         token = strtok_r(NULL, " ", &p);
+         //Unable to find the next token?
+         if(token == NULL)
+            break;
+
+         //LOGIN authentication mechanism supported?
+         if(!strcasecmp(token, "LOGIN"))
+            context->authLoginSupported = TRUE;
+         //PLAIN authentication mechanism supported?
+         else if(!strcasecmp(token, "PLAIN"))
+            context->authPlainSupported = TRUE;
+         //CRAM-MD5 authentication mechanism supported?
+         else if(!strcasecmp(token, "CRAM-MD5"))
+            context->authCramMd5Supported = TRUE;
+      }
+   }
+   //The STARTTLS keyword is used to tell the SMTP client
+   //that the SMTP server allows use of TLS
+   else if(!strcasecmp(token, "STARTTLS"))
+   {
+      //STARTTLS use is allowed
+      context->startTlsSupported = TRUE;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Authentication using LOGIN mechanism
+ * @param[in] context SMTP client context
+ * @param[in] authInfo Authentication information
+ * @return Error code
+ **/
+
+error_t smtpSendAuthLogin(SmtpClientContext *context, const SmtpAuthInfo *authInfo)
+{
+#if (SMTP_CLIENT_LOGIN_AUTH_SUPPORT == ENABLED)
+   error_t error;
+   uint_t replyCode;
+
+   //Send AUTH LOGIN command
+   error = smtpSendCommand(context, "AUTH LOGIN\r\n", &replyCode, NULL);
+
+   //Any communication error to report?
+   if(error)
+      return error;
+   //Check SMTP reply code
+   if(!SMTP_REPLY_CODE_3YZ(replyCode))
+      return ERROR_AUTHENTICATION_FAILED;
+
+   //Encode the user name with Base64 algorithm
+   base64Encode(authInfo->userName, strlen(authInfo->userName), context->buffer, NULL);
+   //Add a line feed
+   strcat(context->buffer, "\r\n");
+
+   //Send the resulting string
+   error = smtpSendCommand(context, context->buffer, &replyCode, NULL);
+   //Any communication error to report?
+   if(error)
+      return error;
+
+   //Check SMTP reply code
+   if(!SMTP_REPLY_CODE_3YZ(replyCode))
+      return ERROR_AUTHENTICATION_FAILED;
+
+   //Encode the password with Base64 algorithm
+   base64Encode(authInfo->password, strlen(authInfo->password), context->buffer, NULL);
+   //Add a line feed
+   strcat(context->buffer, "\r\n");
+
+   //Send the resulting string
+   error = smtpSendCommand(context, context->buffer, &replyCode, NULL);
+   //Any communication error to report?
+   if(error)
+      return error;
+
+   //Check SMTP reply code
+   if(!SMTP_REPLY_CODE_2YZ(replyCode))
+      return ERROR_AUTHENTICATION_FAILED;
+
+   //Successful authentication
+   return NO_ERROR;
+#else
+   //LOGIN authentication is not supported
+   return ERROR_AUTHENTICATION_FAILED;
+#endif
+}
+
+
+/**
+ * @brief Authentication using PLAIN mechanism
+ * @param[in] context SMTP client context
+ * @param[in] authInfo Authentication information
+ * @return Error code
+ **/
+
+error_t smtpSendAuthPlain(SmtpClientContext *context, const SmtpAuthInfo *authInfo)
+{
+#if (SMTP_CLIENT_PLAIN_AUTH_SUPPORT == ENABLED)
+   error_t error;
+   uint_t n;
+   uint_t replyCode;
+
+   //Authorization identity
+   strcpy(context->buffer, authInfo->userName);
+   n = strlen(authInfo->userName) + 1;
+   //Authentication identity
+   strcpy(context->buffer + n, authInfo->userName);
+   n += strlen(authInfo->userName) + 1;
+   //Password
+   strcpy(context->buffer + n, authInfo->password);
+   n += strlen(authInfo->password);
+
+   //Base64 encoding
+   base64Encode(context->buffer, n, context->buffer2, NULL);
+   //Format the AUTH PLAIN command
+   sprintf(context->buffer, "AUTH PLAIN %s\r\n", context->buffer2);
+
+   //Send the command to the server
+   error = smtpSendCommand(context, context->buffer, &replyCode, NULL);
+   //Any communication error to report?
+   if(error)
+      return error;
+
+   //Check SMTP reply code
+   if(!SMTP_REPLY_CODE_2YZ(replyCode))
+      return ERROR_AUTHENTICATION_FAILED;
+
+   //Successful authentication
+   return NO_ERROR;
+#else
+   //PLAIN authentication is not supported
+   return ERROR_AUTHENTICATION_FAILED;
+#endif
+}
+
+
+/**
+ * @brief Authentication using CRAM-MD5 mechanism
+ * @param[in] context SMTP client context
+ * @param[in] authInfo Authentication information
+ * @return Error code
+ **/
+
+error_t smtpSendAuthCramMd5(SmtpClientContext *context, const SmtpAuthInfo *authInfo)
+{
+#if (SMTP_CLIENT_CRAM_MD5_AUTH_SUPPORT == ENABLED)
+   //Hex conversion table
+   static const char_t hexDigit[] =
+   {
+      '0', '1', '2', '3', '4', '5', '6', '7',
+      '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+   };
+
+   //Local variables
+   error_t error;
+   uint_t n;
+   uint_t replyCode;
+
+   //Alias pointers
+   uint8_t *challenge = (uint8_t *) context->buffer2;
+   uint8_t *digest = (uint8_t *) context->buffer;
+   char_t *textDigest = (char_t *) context->buffer2;
+
+   //Send AUTH CRAM-MD5 command
+   error = smtpSendCommand(context, "AUTH CRAM-MD5\r\n", &replyCode, NULL);
+   //Any communication error to report?
+   if(error)
+      return error;
+
+   //Check SMTP reply code
+   if(!SMTP_REPLY_CODE_3YZ(replyCode))
+      return ERROR_AUTHENTICATION_FAILED;
+
+   //Compute the length of the response
+   n = strlen(context->buffer);
+   //Unexpected response from the SMTP server?
+   if(n <= 4)
+      return ERROR_INVALID_SYNTAX;
+
+   //Decrypt the Base64 encoded challenge
+   error = base64Decode(context->buffer + 4, n - 4, challenge, &n);
+   //Decoding failed?
+   if(error)
+      return error;
+
+   //Compute HMAC using MD5
+   error = hmacCompute(MD5_HASH_ALGO, authInfo->password,
+      strlen(authInfo->password), challenge, n, digest);
+   //HMAC computation failed?
+   if(error)
+      return error;
+
+   //Convert the digest to text
+   for(n = 0; n < MD5_DIGEST_SIZE; n++)
+   {
+      //Convert upper nibble
+      textDigest[n * 2] = hexDigit[(digest[n] >> 4) & 0x0F];
+      //Then convert lower nibble
+      textDigest[n * 2 + 1] = hexDigit[digest[n] & 0x0F];
+   }
+
+   //Properly terminate the string
+   textDigest[MD5_DIGEST_SIZE * 2] = '\0';
+   //Concatenate the user name and the text representation of the digest
+   sprintf(context->buffer, "%s %s", authInfo->userName, textDigest);
+   //Encode the resulting string with Base64 algorithm
+   base64Encode(context->buffer, strlen(context->buffer), context->buffer2, NULL);
+   //Add a line feed
+   strcat(context->buffer2, "\r\n");
+
+   //Transmit the Base64 encoded string
+   error = smtpSendCommand(context, context->buffer2, &replyCode, NULL);
+   //Any communication error to report?
+   if(error)
+      return error;
+
+   //Check SMTP reply code
+   if(!SMTP_REPLY_CODE_2YZ(replyCode))
+      return ERROR_AUTHENTICATION_FAILED;
+
+   //Successful authentication
+   return NO_ERROR;
+#else
+   //CRAM-MD5 authentication is not supported
+   return ERROR_AUTHENTICATION_FAILED;
+#endif
+}
+
+
+/**
+ * @brief Send message body
+ * @param[in] context SMTP client context
+ * @param[in] mail Mail contents
+ * @return Error code
+ **/
+
+error_t smtpSendData(SmtpClientContext *context, const SmtpMail *mail)
+{
+   error_t error;
+   bool_t first;
+   uint_t i;
+   uint_t replyCode;
+   char_t *p;
+
+   //Send DATA command
+   error = smtpSendCommand(context, "DATA\r\n", &replyCode, NULL);
+   //Any communication error to report?
+   if(error)
+      return error;
+
+   //Check SMTP reply code
+   if(!SMTP_REPLY_CODE_3YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Point to the beginning of the buffer
+   p = context->buffer;
+
+   //Current date and time
+   if(mail->dateTime && mail->dateTime[0] != '\0')
+      p += sprintf(p, "Date: %s\r\n", mail->dateTime);
+
+   //Sender address
+   if(mail->from.addr)
+   {
+      //A friendly name may be associated with the sender address
+      if(mail->from.name && mail->from.name[0] != '\0')
+         p += sprintf(p, "From: \"%s\" <%s>\r\n", mail->from.name, mail->from.addr);
+      else
+         p += sprintf(p, "From: %s\r\n", mail->from.addr);
+   }
+
+   //Recipients
+   for(i = 0, first = TRUE; i < mail->recipientCount; i++)
+   {
+      //Skip recipient addresses that are not valid
+      if(!mail->recipients[i].addr)
+         continue;
+
+      //Check recipient type
+      if(mail->recipients[i].type & SMTP_RCPT_TYPE_TO)
+      {
+         //The first item of the list requires special processing
+         p += sprintf(p, first ? "To: " : ", ");
+
+         //A friendly name may be associated with the address
+         if(mail->recipients[i].name && mail->recipients[i].name[0] != '\0')
+            p += sprintf(p, "\"%s\" <%s>", mail->recipients[i].name, mail->recipients[i].addr);
+         else
+            p += sprintf(p, "%s", mail->recipients[i].addr);
+
+         //Prepare to add a new item to the list
+         first = FALSE;
+      }
+   }
+
+   //Properly terminate the line with CRLF
+   if(!first)
+      p += sprintf(p, "\r\n");
+
+   //Carbon copy
+   for(i = 0, first = TRUE; i < mail->recipientCount; i++)
+   {
+      //Skip recipient addresses that are not valid
+      if(!mail->recipients[i].addr)
+         continue;
+
+      //Check recipient type
+      if(mail->recipients[i].type & SMTP_RCPT_TYPE_CC)
+      {
+         //The first item of the list requires special processing
+         p += sprintf(p, first ? "Cc: " : ", ");
+
+         //A friendly name may be associated with the address
+         if(mail->recipients[i].name && mail->recipients[i].name[0] != '\0')
+            p += sprintf(p, "\"%s\" <%s>", mail->recipients[i].name, mail->recipients[i].addr);
+         else
+            p += sprintf(p, "%s", mail->recipients[i].addr);
+
+         //Prepare to add a new item to the list
+         first = FALSE;
+      }
+   }
+
+   //Properly terminate the line with CRLF
+   if(!first)
+      p += sprintf(p, "\r\n");
+
+   //Subject
+   if(mail->subject)
+      p += sprintf(p, "Subject: %s\r\n", mail->subject);
+
+   //The header and the body are separated by an empty line
+   sprintf(p, "\r\n");
+
+   //Debug message
+   TRACE_DEBUG(context->buffer);
+   TRACE_DEBUG(mail->body);
+   TRACE_DEBUG("\r\n.\r\n");
+
+   //Send message header
+   error = smtpWrite(context, context->buffer, strlen(context->buffer), 0);
+   //Any communication error to report?
+   if(error)
+      return error;
+
+   //Send message body
+   error = smtpWrite(context, mail->body, strlen(mail->body), 0);
+   //Any communication error to report?
+   if(error)
+      return error;
+
+   //Indicate the end of the mail data by sending a line containing only a "."
+   error = smtpSendCommand(context, "\r\n.\r\n", &replyCode, NULL);
+   //Any communication error to report?
+   if(error)
+      return error;
+
+   //Check SMTP reply code
+   if(!SMTP_REPLY_CODE_2YZ(replyCode))
+      return ERROR_UNEXPECTED_RESPONSE;
+
+   //Successful operation
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Send SMTP command and wait for a reply
+ * @param[in] context SMTP client context
+ * @param[in] command Command line
+ * @param[out] replyCode SMTP server reply code
+ * @param[in] callback Optional callback to parse each line of the reply
+ * @return Error code
+ **/
+
+error_t smtpSendCommand(SmtpClientContext *context, const char_t *command,
+   uint_t *replyCode, SmtpReplyCallback callback)
+{
+   error_t error;
+   size_t length;
+   char_t *line;
+
+   //Any command line to send?
+   if(command)
+   {
+      //Debug message
+      TRACE_DEBUG("SMTP client: %s", command);
+
+      //Send the command to the SMTP server
+      error = smtpWrite(context, command, strlen(command), SOCKET_FLAG_WAIT_ACK);
+      //Failed to send command?
+      if(error)
+         return error;
+   }
+
+   //Multiline replies are allowed for any command
+   while(1)
+   {
+      //Wait for a response from the server
+      error = smtpRead(context, context->buffer,
+         SMTP_CLIENT_MAX_LINE_LENGTH - 1, &length, SOCKET_FLAG_BREAK_CRLF);
+
+      //The remote server did not respond as expected?
+      if(error)
+         return error;
+
+      //Properly terminate the string with a NULL character
+      context->buffer[length] = '\0';
+      //Remove all leading and trailing whitespace from the response
+      line = strTrimWhitespace(context->buffer);
+
+      //Debug message
+      TRACE_DEBUG("SMTP server: %s\r\n", line);
+
+      //Check the length of the response
+      if(strlen(line) < 3)
+         return ERROR_INVALID_SYNTAX;
+      //All replies begin with a three digit numeric code
+      if(!isdigit((uint8_t) line[0]) || !isdigit((uint8_t) line[1]) || !isdigit((uint8_t) line[2]))
+         return ERROR_INVALID_SYNTAX;
+      //A hyphen or a space character must follow the response code
+      if(line[3] != '-' && line[3] != ' ' && line[3] != '\0')
+         return ERROR_INVALID_SYNTAX;
+
+      //Get the server response code
+      *replyCode = strtoul(line, NULL, 10);
+
+      //Any callback function to call?
+      if(callback)
+      {
+         //Invoke callback function to parse the response line
+         error = callback(context, line, *replyCode);
+         //Check status code
+         if(error)
+            return error;
+      }
+
+      //A hyphen follows the response code for all but the last line
+      if(line[3] != '-')
+         break;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Send data to the SMTP server
+ * @param[in] context SMTP client context
+ * @param[in] data Pointer to a buffer containing the data to be transmitted
+ * @param[in] length Number of bytes to be transmitted
+ * @param[in] flags Set of flags that influences the behavior of this function
+ **/
+
+error_t smtpWrite(SmtpClientContext *context, const void *data, size_t length, uint_t flags)
+{
+#if (SMTP_CLIENT_TLS_SUPPORT == ENABLED)
+   //Check whether a secure connection is being used
+   if(context->tlsContext != NULL)
+   {
+      //Use SSL/TLS to transmit data to the SMTP server
+      return tlsWrite(context->tlsContext, data, length, NULL, flags);
+   }
+   else
+#endif
+   {
+      //Transmit data to the SMTP server
+      return socketSend(context->socket, data, length, NULL, flags);
+   }
+}
+
+
+/**
+ * @brief Receive data from the SMTP server
+ * @param[in] context SMTP client context
+ * @param[out] data Buffer into which received data will be placed
+ * @param[in] size Maximum number of bytes that can be received
+ * @param[out] received Actual number of bytes that have been received
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t smtpRead(SmtpClientContext *context, void *data, size_t size, size_t *received, uint_t flags)
+{
+#if (SMTP_CLIENT_TLS_SUPPORT == ENABLED)
+   //Check whether a secure connection is being used
+   if(context->tlsContext != NULL)
+   {
+      //Use SSL/TLS to receive data from the SMTP server
+      return tlsRead(context->tlsContext, data, size, received, flags);
+   }
+   else
+#endif
+   {
+      //Receive data from the SMTP server
+      return socketReceive(context->socket, data, size, received, flags);
+   }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/smtp/smtp_client.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,217 @@
+/**
+ * @file smtp_client.h
+ * @brief SMTP client (Simple Mail Transfer Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SMTP_CLIENT_H
+#define _SMTP_CLIENT_H
+
+//Dependencies
+#include "core/socket.h"
+
+//SMTP client support
+#ifndef SMTP_CLIENT_SUPPORT
+   #define SMTP_CLIENT_SUPPORT ENABLED
+#elif (SMTP_CLIENT_SUPPORT != ENABLED && SMTP_CLIENT_SUPPORT != DISABLED)
+   #error SMTP_CLIENT_SUPPORT parameter is not valid
+#endif
+
+//Default timeout
+#ifndef SMTP_CLIENT_DEFAULT_TIMEOUT
+   #define SMTP_CLIENT_DEFAULT_TIMEOUT 10000
+#elif (SMTP_CLIENT_DEFAULT_TIMEOUT < 1000)
+   #error SMTP_CLIENT_DEFAULT_TIMEOUT parameter is not valid
+#endif
+
+//Maximum line length
+#ifndef SMTP_CLIENT_MAX_LINE_LENGTH
+   #define SMTP_CLIENT_MAX_LINE_LENGTH 512
+#elif (SMTP_CLIENT_MAX_LINE_LENGTH < 64)
+   #error SMTP_CLIENT_MAX_LINE_LENGTH parameter is not valid
+#endif
+
+//SMTP over SSL/TLS
+#ifndef SMTP_CLIENT_TLS_SUPPORT
+   #define SMTP_CLIENT_TLS_SUPPORT DISABLED
+#elif (SMTP_CLIENT_TLS_SUPPORT != ENABLED && SMTP_CLIENT_TLS_SUPPORT != DISABLED)
+   #error SMTP_CLIENT_TLS_SUPPORT parameter is not valid
+#endif
+
+//LOGIN authentication support
+#ifndef SMTP_CLIENT_LOGIN_AUTH_SUPPORT
+   #define SMTP_CLIENT_LOGIN_AUTH_SUPPORT ENABLED
+#elif (SMTP_CLIENT_LOGIN_AUTH_SUPPORT != ENABLED && SMTP_CLIENT_LOGIN_AUTH_SUPPORT != DISABLED)
+   #error SMTP_CLIENT_LOGIN_AUTH_SUPPORT parameter is not valid
+#endif
+
+//PLAIN authentication support
+#ifndef SMTP_CLIENT_PLAIN_AUTH_SUPPORT
+   #define SMTP_CLIENT_PLAIN_AUTH_SUPPORT ENABLED
+#elif (SMTP_CLIENT_PLAIN_AUTH_SUPPORT != ENABLED && SMTP_CLIENT_PLAIN_AUTH_SUPPORT != DISABLED)
+   #error SMTP_CLIENT_PLAIN_AUTH_SUPPORT parameter is not valid
+#endif
+
+//CRAM-MD5 authentication support
+#ifndef SMTP_CLIENT_CRAM_MD5_AUTH_SUPPORT
+   #define SMTP_CLIENT_CRAM_MD5_AUTH_SUPPORT DISABLED
+#elif (SMTP_CLIENT_CRAM_MD5_AUTH_SUPPORT != ENABLED && SMTP_CLIENT_CRAM_MD5_AUTH_SUPPORT != DISABLED)
+   #error SMTP_CLIENT_CRAM_MD5_AUTH_SUPPORT parameter is not valid
+#endif
+
+//SMTP over SSL/TLS supported?
+#if (SMTP_CLIENT_TLS_SUPPORT == ENABLED)
+   #include "crypto.h"
+   #include "tls.h"
+#endif
+
+//LOGIN or PLAIN authentication supported?
+#if (SMTP_CLIENT_LOGIN_AUTH_SUPPORT == ENABLED || SMTP_CLIENT_PLAIN_AUTH_SUPPORT == ENABLED)
+   #include "crypto.h"
+   #include "base64.h"
+#endif
+
+//CRAM-MD5 authentication supported?
+#if (SMTP_CLIENT_CRAM_MD5_AUTH_SUPPORT == ENABLED)
+   #include "crypto.h"
+   #include "base64.h"
+   #include "hmac.h"
+   #include "md5.h"
+#endif
+
+//SMTP port number
+#define SMTP_PORT 25
+//SMTPS port number (SMTP over SSL/TLS)
+#define SMTPS_PORT 465
+//SMTP message submission port number
+#define SMTP_SUBMISSION_PORT 587
+
+//Test macros for SMTP response codes
+#define SMTP_REPLY_CODE_2YZ(code) ((code) >= 200 && (code) < 300)
+#define SMTP_REPLY_CODE_3YZ(code) ((code) >= 300 && (code) < 400)
+#define SMTP_REPLY_CODE_4YZ(code) ((code) >= 400 && (code) < 500)
+#define SMTP_REPLY_CODE_5YZ(code) ((code) >= 500 && (code) < 600)
+
+
+/**
+ * @brief Recipient type
+ **/
+
+typedef enum
+{
+   SMTP_RCPT_TYPE_TO  = 1,
+   SMTP_RCPT_TYPE_CC  = 2,
+   SMTP_RCPT_TYPE_BCC = 4,
+} SmtpRecipientType;
+
+
+/**
+ * @brief Authentication information
+ **/
+
+typedef struct
+{
+   NetInterface *interface;
+   const char_t *serverName;
+   uint16_t serverPort;
+   const char_t *userName;
+   const char_t *password;
+#if (SMTP_CLIENT_TLS_SUPPORT == ENABLED)
+   bool_t useTls;
+   const PrngAlgo *prngAlgo;
+   void *prngContext;
+#endif
+} SmtpAuthInfo;
+
+
+/**
+ * @brief Mail address
+ **/
+
+typedef struct
+{
+   char_t *name;
+   char_t *addr;
+   uint_t type;
+} SmtpMailAddr;
+
+
+/**
+ * @brief Mail contents
+ **/
+
+typedef struct
+{
+   SmtpMailAddr from;
+   const SmtpMailAddr *recipients;
+   uint_t recipientCount;
+   char_t *dateTime;
+   const char_t *subject;
+   const char_t *body;
+} SmtpMail;
+
+
+/**
+ * @brief SMTP client context
+ **/
+
+typedef struct
+{
+   Socket *socket;                                  ///<Underlying socket
+   bool_t authLoginSupported;                       ///<LOGIN authentication mechanism supported
+   bool_t authPlainSupported;                       ///<PLAIN authentication mechanism supported
+   bool_t authCramMd5Supported;                     ///<CRAM-MD5 authentication mechanism supported
+   bool_t startTlsSupported;                        ///<STARTTLS command supported
+   char_t buffer[SMTP_CLIENT_MAX_LINE_LENGTH / 2];  ///<Memory buffer for input/output operations
+   char_t buffer2[SMTP_CLIENT_MAX_LINE_LENGTH / 2];
+#if (SMTP_CLIENT_TLS_SUPPORT == ENABLED)
+   TlsContext *tlsContext;                          ///<TLS context
+#endif
+} SmtpClientContext;
+
+
+//Callback function to parse a response line
+typedef error_t (*SmtpReplyCallback)(SmtpClientContext *context, char_t *replyLine, uint_t replyCode);
+
+//SMTP related functions
+error_t smtpSendMail(const SmtpAuthInfo *authInfo, const SmtpMail *mail);
+
+error_t smtpEhloReplyCallback(SmtpClientContext *context,
+   char_t *replyLine, uint_t replyCode);
+
+error_t smtpSendAuthLogin(SmtpClientContext *context, const SmtpAuthInfo *authInfo);
+error_t smtpSendAuthPlain(SmtpClientContext *context, const SmtpAuthInfo *authInfo);
+error_t smtpSendAuthCramMd5(SmtpClientContext *context, const SmtpAuthInfo *authInfo);
+
+error_t smtpSendData(SmtpClientContext *context, const SmtpMail *mail);
+
+error_t smtpSendCommand(SmtpClientContext *context, const char_t *command,
+   uint_t *replyCode, SmtpReplyCallback callback);
+
+error_t smtpWrite(SmtpClientContext *context, const void *data, size_t length, uint_t flags);
+error_t smtpRead(SmtpClientContext *context, void *data, size_t size, size_t *received, uint_t flags);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/snmp/snmp_agent.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1039 @@
+/**
+ * @file snmp_agent.c
+ * @brief SNMP agent (Simple Network Management Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * SNMP is a simple protocol by which management information for a network
+ * element may be inspected or altered by logically remote users. Refer
+ * to the following RFCs for complete details:
+ * - RFC 1157: A Simple Network Management Protocol (SNMP)
+ * - RFC 1905: Protocol Operations for Version 2 of the Simple Network
+ *     Management Protocol (SNMPv2)
+ * - RFC 3410: Introduction and Applicability Statements for Internet
+ *     Standard Management Framework
+ * - RFC 3411: An Architecture for Describing SNMP Management Frameworks
+ * - RFC 3412: Message Processing and Dispatching for the SNMP
+ * - RFC 3413: Simple Network Management Protocol (SNMP) Applications
+ * - RFC 3584: Coexistence between Version 1, Version 2, and Version 3 of
+ *     SNMP Framework
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL SNMP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "snmp/snmp_agent.h"
+#include "snmp/snmp_agent_dispatch.h"
+#include "snmp/snmp_agent_pdu.h"
+#include "snmp/snmp_agent_misc.h"
+#include "mibs/mib2_module.h"
+#include "crypto.h"
+#include "asn1.h"
+#include "oid.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (SNMP_AGENT_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Initialize settings with default values
+ * @param[out] settings Structure that contains SNMP agent settings
+ **/
+
+void snmpAgentGetDefaultSettings(SnmpAgentSettings *settings)
+{
+   //The SNMP agent is not bound to any interface
+   settings->interface = NULL;
+
+   //Minimum version accepted by the SNMP agent
+   settings->versionMin = SNMP_VERSION_1;
+   //Maximum version accepted by the SNMP agent
+   settings->versionMax = SNMP_VERSION_3;
+
+   //SNMP port number
+   settings->port = SNMP_PORT;
+   //SNMP trap port number
+   settings->trapPort = SNMP_TRAP_PORT;
+
+   //Random data generation callback function
+   settings->randCallback = NULL;
+}
+
+
+/**
+ * @brief SNMP agent initialization
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] settings SNMP agent specific settings
+ * @return Error code
+ **/
+
+error_t snmpAgentInit(SnmpAgentContext *context, const SnmpAgentSettings *settings)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing SNMP agent...\r\n");
+
+   //Ensure the parameters are valid
+   if(context == NULL || settings == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Check minimum and maximum SNMP versions
+   if(settings->versionMin > settings->versionMax)
+      return ERROR_INVALID_PARAMETER;
+
+   //Clear the SNMP agent context
+   memset(context, 0, sizeof(SnmpAgentContext));
+
+   //Save user settings
+   context->settings = *settings;
+
+#if (SNMP_V3_SUPPORT == ENABLED)
+   //Get current time
+   context->systemTime = osGetSystemTime();
+
+   //Each SNMP engine maintains two values, snmpEngineBoots and snmpEngineTime,
+   //which taken together provide an indication of time at that SNMP engine
+   context->engineBoots = 1;
+   context->engineTime = 0;
+
+   //Check whether SNMPv3 is supported
+   if(settings->versionMin <= SNMP_VERSION_3 &&
+      settings->versionMax >= SNMP_VERSION_3)
+   {
+      //Make sure a random number generator has been registered
+      if(settings->randCallback == NULL)
+         return ERROR_INVALID_PARAMETER;
+
+      //The salt integer is initialized to an arbitrary value at boot time
+      error = settings->randCallback((uint8_t *) &context->salt, sizeof(context->salt));
+      //Any error to report?
+      if(error)
+         return error;
+   }
+#endif
+
+   //Create a mutex to prevent simultaneous access to SNMP agent context
+   if(!osCreateMutex(&context->mutex))
+   {
+      //Failed to create mutex
+      return ERROR_OUT_OF_RESOURCES;
+   }
+
+   //Open a UDP socket
+   context->socket = socketOpen(SOCKET_TYPE_DGRAM, SOCKET_IP_PROTO_UDP);
+
+   //Failed to open socket?
+   if(!context->socket)
+   {
+      //Clean up side effects
+      osDeleteMutex(&context->mutex);
+      //Report an error
+      return ERROR_OPEN_FAILED;
+   }
+
+   //Start of exception handling block
+   do
+   {
+      //Explicitly associate the socket with the relevant interface
+      error = socketBindToInterface(context->socket, settings->interface);
+      //Unable to bind the socket to the desired interface?
+      if(error)
+         break;
+
+      //The SNMP agent listens for messages on port 161
+      error = socketBind(context->socket, &IP_ADDR_ANY, settings->port);
+      //Unable to bind the socket to the desired port?
+      if(error)
+         break;
+
+      //End of exception handling block
+   } while(0);
+
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects
+      osDeleteMutex(&context->mutex);
+      //Close underlying socket
+      socketClose(context->socket);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Start SNMP agent
+ * @param[in] context Pointer to the SNMP agent context
+ * @return Error code
+ **/
+
+error_t snmpAgentStart(SnmpAgentContext *context)
+{
+   OsTask *task;
+
+   //Debug message
+   TRACE_INFO("Starting SNMP agent...\r\n");
+
+   //Make sure the SNMP agent context is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Start the SNMP agent service
+   task = osCreateTask("SNMP Agent", (OsTaskCode) snmpAgentTask,
+      context, SNMP_AGENT_STACK_SIZE, SNMP_AGENT_PRIORITY);
+
+   //Unable to create the task?
+   if(task == OS_INVALID_HANDLE)
+      return ERROR_OUT_OF_RESOURCES;
+
+   //The SNMP agent has successfully started
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Load a MIB module
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] module Pointer the MIB module to be loaded
+ * @return Error code
+ **/
+
+error_t snmpAgentLoadMib(SnmpAgentContext *context, const MibModule *module)
+{
+   error_t error;
+   uint_t i;
+   uint_t j;
+
+   //Check parameters
+   if(context == NULL || module == NULL)
+      return ERROR_INVALID_PARAMETER;
+   if(module->numObjects < 1)
+      return ERROR_INVALID_PARAMETER;
+
+   //Acquire exclusive access to the SNMP agent context
+   osAcquireMutex(&context->mutex);
+
+   //Loop through existing MIBs
+   for(i = 0; i < context->mibModuleCount; i++)
+   {
+      //Check whether the specified MIB module is already loaded
+      if(context->mibModule[i] == module)
+         break;
+   }
+
+   //MIB module found?
+   if(i < context->mibModuleCount)
+   {
+      //Prevent the SNMP agent from loading the specified MIB multiple times
+      error = NO_ERROR;
+   }
+   else
+   {
+      //Make sure there is enough room to add the specified MIB
+      if(context->mibModuleCount < SNMP_AGENT_MAX_MIB_COUNT)
+      {
+         //Loop through existing MIBs
+         for(i = 0; i < context->mibModuleCount; i++)
+         {
+            //Compare object identifiers
+            if(oidComp(module->objects[0].oid, module->objects[0].oidLen,
+               context->mibModule[i]->objects[0].oid, context->mibModule[i]->objects[0].oidLen) < 0)
+            {
+               //Make room for the new MIB
+               for(j = context->mibModuleCount; j > i; j--)
+                  context->mibModule[j] = context->mibModule[j - 1];
+
+               //We are done
+               break;
+            }
+         }
+
+         //Insert the new MIB to the list
+         context->mibModule[i] = module;
+         //Update the number of MIBs
+         context->mibModuleCount++;
+
+         //Successful processing
+         error = NO_ERROR;
+      }
+      else
+      {
+         //Failed to load the specified MIB
+         error = ERROR_OUT_OF_RESOURCES;
+      }
+   }
+
+   //Release exclusive access to the SNMP agent context
+   osReleaseMutex(&context->mutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Unload a MIB module
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] module Pointer the MIB module to be unloaded
+ * @return Error code
+ **/
+
+error_t snmpAgentUnloadMib(SnmpAgentContext *context, const MibModule *module)
+{
+   error_t error;
+   uint_t i;
+   uint_t j;
+
+   //Check parameters
+   if(context == NULL || module == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Acquire exclusive access to the SNMP agent context
+   osAcquireMutex(&context->mutex);
+
+   //Loop through existing MIBs
+   for(i = 0; i < context->mibModuleCount; i++)
+   {
+      //Check whether the specified MIB module is already loaded
+      if(context->mibModule[i] == module)
+         break;
+   }
+
+   //MIB module found?
+   if(i < context->mibModuleCount)
+   {
+      //Update the number of MIBs
+      context->mibModuleCount--;
+
+      //Remove the specified MIB from the list
+      for(j = i; j < context->mibModuleCount; j++)
+         context->mibModule[j] = context->mibModule[j + 1];
+
+      //Successful processing
+      error = NO_ERROR;
+   }
+   else
+   {
+      //Failed to unload the specified MIB
+      error = ERROR_NOT_FOUND;
+   }
+
+   //Release exclusive access to the SNMP agent context
+   osReleaseMutex(&context->mutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Set the value of the snmpEngineBoots variable
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] engineBoots Number of times the SNMP engine has re-booted
+ * @return Error code
+ **/
+
+error_t snmpAgentSetEngineBoots(SnmpAgentContext *context, int32_t engineBoots)
+{
+#if (SNMP_V3_SUPPORT == ENABLED)
+   //Check parameters
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+   if(engineBoots < 0)
+      return ERROR_OUT_OF_RANGE;
+
+   //Acquire exclusive access to the SNMP agent context
+   osAcquireMutex(&context->mutex);
+
+   //Get current time
+   context->systemTime = osGetSystemTime();
+
+   //Set the value of the snmpEngineBoots
+   context->engineBoots = engineBoots;
+   //The snmpEngineTime is reset to zero
+   context->engineTime = 0;
+
+   //Release exclusive access to the SNMP agent context
+   osReleaseMutex(&context->mutex);
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Get the value of the snmpEngineBoots variable
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[out] engineBoots Number of times the SNMP engine has re-booted
+ * @return Error code
+ **/
+
+error_t snmpAgentGetEngineBoots(SnmpAgentContext *context, int32_t *engineBoots)
+{
+#if (SNMP_V3_SUPPORT == ENABLED)
+   //Check parameters
+   if(context == NULL || engineBoots == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Acquire exclusive access to the SNMP agent context
+   osAcquireMutex(&context->mutex);
+   //Get the current value of the snmpEngineBoots
+   *engineBoots = context->engineBoots;
+   //Release exclusive access to the SNMP agent context
+   osReleaseMutex(&context->mutex);
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Set enterprise OID
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] enterpriseOid Pointer to the enterprise OID
+ * @param[in] enterpriseOidLen Length of the enterprise OID
+ * @return Error code
+ **/
+
+error_t snmpAgentSetEnterpriseOid(SnmpAgentContext *context,
+   const uint8_t *enterpriseOid, size_t enterpriseOidLen)
+{
+   //Check parameters
+   if(context == NULL || enterpriseOid == NULL)
+      return ERROR_INVALID_PARAMETER;
+   if(enterpriseOidLen > SNMP_MAX_OID_SIZE)
+      return ERROR_INVALID_PARAMETER;
+
+   //Acquire exclusive access to the SNMP agent context
+   osAcquireMutex(&context->mutex);
+
+   //Set enterprise OID
+   memcpy(context->enterpriseOid, enterpriseOid, enterpriseOidLen);
+   //Save the length of the enterprise OID
+   context->enterpriseOidLen = enterpriseOidLen;
+
+   //Release exclusive access to the SNMP agent context
+   osReleaseMutex(&context->mutex);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set context engine identifier
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] contextEngine Pointer to the context engine identifier
+ * @param[in] contextEngineLen Length of the context engine identifier
+ * @return Error code
+ **/
+
+error_t snmpAgentSetContextEngine(SnmpAgentContext *context,
+   const void *contextEngine, size_t contextEngineLen)
+{
+#if (SNMP_V3_SUPPORT == ENABLED)
+   //Check parameters
+   if(context == NULL || contextEngine == NULL)
+      return ERROR_INVALID_PARAMETER;
+   if(contextEngineLen > SNMP_MAX_CONTEXT_ENGINE_SIZE)
+      return ERROR_INVALID_PARAMETER;
+
+   //Acquire exclusive access to the SNMP agent context
+   osAcquireMutex(&context->mutex);
+
+   //Set context engine identifier
+   memcpy(context->contextEngine, contextEngine, contextEngineLen);
+   //Save the length of the context engine identifier
+   context->contextEngineLen = contextEngineLen;
+
+   //Release exclusive access to the SNMP agent context
+   osReleaseMutex(&context->mutex);
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Set context name
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] contextName NULL-terminated string that contains the context name
+ * @return Error code
+ **/
+
+error_t snmpAgentSetContextName(SnmpAgentContext *context,
+   const char_t *contextName)
+{
+#if (SNMP_V3_SUPPORT == ENABLED)
+   size_t n;
+
+   //Check parameters
+   if(context == NULL || contextName == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Retrieve the length of the context name
+   n = strlen(contextName);
+
+   //Make sure the context name is valid
+   if(n > SNMP_MAX_CONTEXT_NAME_LEN)
+      return ERROR_INVALID_LENGTH;
+
+   //Acquire exclusive access to the SNMP agent context
+   osAcquireMutex(&context->mutex);
+   //Set context name
+   strcpy(context->contextName, contextName);
+   //Release exclusive access to the SNMP agent context
+   osReleaseMutex(&context->mutex);
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Create a new community string
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] community NULL-terminated string that contains the community name
+ * @param[in] mode Access rights
+ * @return Error code
+ **/
+
+error_t snmpAgentCreateCommunity(SnmpAgentContext *context,
+   const char_t *community, SnmpAccess mode)
+{
+#if (SNMP_V1_SUPPORT == ENABLED || SNMP_V2C_SUPPORT == ENABLED)
+   //Add the community string to the local configuration datastore
+   return snmpAgentCreateUser(context, community, mode, SNMP_KEY_FORMAT_NONE,
+      SNMP_AUTH_PROTOCOL_NONE, NULL, SNMP_PRIV_PROTOCOL_NONE, NULL);
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Remove a community string
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] community NULL-terminated string that contains the community name
+ * @return Error code
+ **/
+
+error_t snmpAgentDeleteCommunity(SnmpAgentContext *context, const char_t *community)
+{
+#if (SNMP_V1_SUPPORT == ENABLED || SNMP_V2C_SUPPORT == ENABLED)
+   //Remove the community string from the local configuration datastore
+   return snmpAgentDeleteUser(context, community);
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Create a new user
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] username NULL-terminated string that contains the user name
+ * @param[in] mode Access rights
+ * @param[in] keyFormat Key format (ASCII password or raw key)
+ * @param[in] authProtocol Authentication type
+ * @param[in] authKey Key to be used for data authentication
+ * @param[in] privProtocol Privacy type
+ * @param[in] privKey Key to be used for data encryption
+ * @return Error code
+ **/
+
+error_t snmpAgentCreateUser(SnmpAgentContext *context,
+   const char_t *username, SnmpAccess mode, SnmpKeyFormat keyFormat,
+   SnmpAuthProtocol authProtocol, const void *authKey,
+   SnmpPrivProtocol privProtocol, const void *privKey)
+{
+   error_t error;
+   uint_t i;
+   size_t n;
+   SnmpUserInfo *entry;
+   SnmpUserInfo *firstFreeEntry;
+
+   //Check parameters
+   if(context == NULL || username == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Data authentication?
+   if(authProtocol != SNMP_AUTH_PROTOCOL_NONE)
+   {
+      //Check key format
+      if(keyFormat != SNMP_KEY_FORMAT_TEXT && keyFormat != SNMP_KEY_FORMAT_RAW)
+         return ERROR_INVALID_PARAMETER;
+
+      //Data authentication requires a key
+      if(authKey == NULL)
+         return ERROR_INVALID_PARAMETER;
+   }
+
+   //Data confidentiality?
+   if(privProtocol != SNMP_PRIV_PROTOCOL_NONE)
+   {
+      //Check key format
+      if(keyFormat != SNMP_KEY_FORMAT_TEXT && keyFormat != SNMP_KEY_FORMAT_RAW)
+         return ERROR_INVALID_PARAMETER;
+
+      //Data confidentiality requires a key
+      if(privKey == NULL)
+         return ERROR_INVALID_PARAMETER;
+
+      //There is no provision for data confidentiality without data authentication
+      if(authProtocol == SNMP_AUTH_PROTOCOL_NONE)
+         return ERROR_INVALID_PARAMETER;
+   }
+
+   //Retrieve the length of the user name
+   n = strlen(username);
+
+   //Make sure the user name is valid
+   if(n == 0 || n > SNMP_MAX_USER_NAME_LEN)
+      return ERROR_INVALID_LENGTH;
+
+   //Acquire exclusive access to the SNMP agent context
+   osAcquireMutex(&context->mutex);
+
+   //Keep track of the first free entry
+   firstFreeEntry = NULL;
+
+   //Loop through the list of users
+   for(i = 0; i < SNMP_AGENT_MAX_USER_COUNT; i++)
+   {
+      //Point to the current entry
+      entry = &context->userTable[i];
+
+      //Check if the entry is currently in use
+      if(entry->name[0] != '\0')
+      {
+         //Check whether the user name already exists
+         if(!strcmp(entry->name, username))
+            break;
+      }
+      else
+      {
+         //Keep track of the first free entry
+         if(firstFreeEntry == NULL)
+            firstFreeEntry = entry;
+      }
+   }
+
+   //If the specified user name does not exist, then a new
+   //entry should be created
+   if(i >= SNMP_AGENT_MAX_USER_COUNT)
+      entry = firstFreeEntry;
+
+   //Check whether the service list runs out of space
+   if(entry != NULL)
+   {
+      //Save user name
+      strcpy(entry->name, username);
+      //Access rights
+      entry->mode = mode;
+
+      //Successful processing
+      error = NO_ERROR;
+
+#if (SNMP_V3_SUPPORT == ENABLED)
+      //Authentication protocol
+      entry->authProtocol = authProtocol;
+      //Privacy protocol
+      entry->privProtocol = privProtocol;
+
+      //Data authentication?
+      if(authProtocol != SNMP_AUTH_PROTOCOL_NONE)
+      {
+         //ASCII password or raw key?
+         if(keyFormat == SNMP_KEY_FORMAT_TEXT)
+         {
+            //Generate the authentication key from the provided password
+            error = snmpGenerateKey(authProtocol, authKey, context->contextEngine,
+               context->contextEngineLen, &entry->authKey);
+         }
+         else
+         {
+            //Save the authentication key
+            memcpy(&entry->authKey, authKey, sizeof(SnmpKey));
+         }
+      }
+
+      //Check status code
+      if(!error)
+      {
+         //Data confidentiality?
+         if(privProtocol != SNMP_PRIV_PROTOCOL_NONE)
+         {
+            //ASCII password or raw key?
+            if(keyFormat == SNMP_KEY_FORMAT_TEXT)
+            {
+               //Generate the privacy key from the provided password
+               error = snmpGenerateKey(authProtocol, privKey, context->contextEngine,
+                  context->contextEngineLen, &entry->privKey);
+            }
+            else
+            {
+               //Save the privacy key
+               memcpy(&entry->privKey, privKey, sizeof(SnmpKey));
+            }
+         }
+      }
+
+      //Check status code
+      if(error)
+      {
+         //Clean up side effects
+         memset(entry, 0, sizeof(SnmpUserInfo));
+      }
+#endif
+   }
+   else
+   {
+      //Unable to add new user
+      error = ERROR_OUT_OF_RESOURCES;
+   }
+
+   //Release exclusive access to the SNMP agent context
+   osReleaseMutex(&context->mutex);
+
+   //Return error code
+   return error;
+}
+
+
+/**
+ * @brief Remove existing user
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] username NULL-terminated string that contains the user name
+ * @return Error code
+ **/
+
+error_t snmpAgentDeleteUser(SnmpAgentContext *context, const char_t *username)
+{
+   error_t error;
+   uint_t i;
+   SnmpUserInfo *entry;
+
+   //Acquire exclusive access to the SNMP agent context
+   osAcquireMutex(&context->mutex);
+
+   //Loop through the list of users
+   for(i = 0; i < SNMP_AGENT_MAX_USER_COUNT; i++)
+   {
+      //Point to the current entry
+      entry = &context->userTable[i];
+
+      //Compare user names
+      if(!strcmp(entry->name, username))
+         break;
+   }
+
+   //User name found?
+   if(i < SNMP_AGENT_MAX_USER_COUNT)
+   {
+      //Clear the security profile of the user
+      memset(entry, 0, sizeof(SnmpUserInfo));
+      //Successful processing
+      error = NO_ERROR;
+   }
+   else
+   {
+      //The specified user name does not exist
+      error = ERROR_NOT_FOUND;
+   }
+
+   //Release exclusive access to the SNMP agent context
+   osReleaseMutex(&context->mutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send SNMP trap message
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] destIpAddr Destination IP address
+ * @param[in] version SNMP version identifier
+ * @param[in] username User name or community name
+ * @param[in] genericTrapType Generic trap type
+ * @param[in] specificTrapCode Specific code
+ * @param[in] objectList List of object names
+ * @param[in] objectListSize Number of entries in the list
+ * @return Error code
+ **/
+
+error_t snmpAgentSendTrap(SnmpAgentContext *context, const IpAddr *destIpAddr,
+   SnmpVersion version, const char_t *username, uint_t genericTrapType,
+   uint_t specificTrapCode, const SnmpTrapObject *objectList, uint_t objectListSize)
+{
+   error_t error;
+
+   //Check parameters
+   if(context == NULL || destIpAddr == NULL || username == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Make sure the list of objects is valid
+   if(objectListSize > 0 && objectList == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Acquire exclusive access to the SNMP agent context
+   osAcquireMutex(&context->mutex);
+
+   //Refresh SNMP engine time
+   snmpRefreshEngineTime(context);
+
+   //Start of exception handling block
+   do
+   {
+#if (SNMP_V1_SUPPORT == ENABLED)
+      //SNMPv1 version?
+      if(version == SNMP_VERSION_1)
+      {
+         //Format Trap-PDU
+         error = snmpFormatTrapPdu(context, version, username,
+            genericTrapType, specificTrapCode, objectList, objectListSize);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Format SMNP message header
+         error = snmpWriteMessageHeader(&context->response);
+         //Any error to report?
+         if(error)
+            break;
+      }
+      else
+#endif
+#if (SNMP_V2C_SUPPORT == ENABLED)
+      //SNMPv2c version?
+      if(version == SNMP_VERSION_2C)
+      {
+         //Format SNMPv2-Trap-PDU
+         error = snmpFormatTrapPdu(context, version, username,
+            genericTrapType, specificTrapCode, objectList, objectListSize);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Format SMNP message header
+         error = snmpWriteMessageHeader(&context->response);
+         //Any error to report?
+         if(error)
+            break;
+      }
+      else
+#endif
+#if (SNMP_V3_SUPPORT == ENABLED)
+      //SNMPv3 version?
+      if(version == SNMP_VERSION_3)
+      {
+         //Information about the user name is extracted from the local
+         //configuration datastore
+         context->user = snmpFindUser(context, username, strlen(username));
+
+         //Invalid user name?
+         if(context->user == NULL)
+         {
+            //Report an error
+            error = ERROR_UNKNOWN_USER_NAME;
+            //Exit immediately
+            break;
+         }
+
+         //Format SNMPv2-Trap-PDU
+         error = snmpFormatTrapPdu(context, version, username,
+            genericTrapType, specificTrapCode, objectList, objectListSize);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Format scopedPDU
+         error = snmpWriteScopedPdu(&context->response);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Check whether the privFlag is set
+         if(context->response.msgFlags & SNMP_MSG_FLAG_PRIV)
+         {
+            //Encrypt data
+            error = snmpEncryptData(context->user, &context->response, &context->salt);
+            //Any error to report?
+            if(error)
+               break;
+         }
+
+         //Format SMNP message header
+         error = snmpWriteMessageHeader(&context->response);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Check whether the authFlag is set
+         if(context->response.msgFlags & SNMP_MSG_FLAG_AUTH)
+         {
+            //Authenticate outgoing SNMP message
+            error = snmpAuthOutgoingMessage(context->user, &context->response);
+            //Any error to report?
+            if(error)
+               break;
+         }
+      }
+      else
+#endif
+      //Invalid SNMP version?
+      {
+         //Debug message
+         TRACE_WARNING("  Invalid SNMP version!\r\n");
+         //Report an error
+         error = ERROR_INVALID_VERSION;
+         //Exit immediately
+         break;
+      }
+
+      //Total number of messages which were passed from the SNMP protocol
+      //entity to the transport service
+      MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpOutPkts, 1);
+
+      //Debug message
+      TRACE_INFO("Sending SNMP message to %s port %" PRIu16
+         " (%" PRIuSIZE " bytes)...\r\n",
+         ipAddrToString(destIpAddr, NULL),
+         context->settings.trapPort, context->response.length);
+
+      //Display the contents of the SNMP message
+      TRACE_DEBUG_ARRAY("  ", context->response.pos, context->response.length);
+      //Display ASN.1 structure
+      asn1DumpObject(context->response.pos, context->response.length, 0);
+
+      //Send SNMP trap message
+      error = socketSendTo(context->socket, destIpAddr, context->settings.trapPort,
+         context->response.pos, context->response.length, NULL, 0);
+
+      //End of exception handling block
+   } while(0);
+
+   //Release exclusive access to the SNMP agent context
+   osReleaseMutex(&context->mutex);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief SNMP agent task
+ * @param[in] context Pointer to the SNMP agent context
+ **/
+
+void snmpAgentTask(SnmpAgentContext *context)
+{
+   error_t error;
+
+#if (NET_RTOS_SUPPORT == ENABLED)
+   //Main loop
+   while(1)
+   {
+#endif
+      //Wait for an incoming datagram
+      error = socketReceiveFrom(context->socket, &context->remoteIpAddr,
+         &context->remotePort, context->request.buffer,
+         SNMP_MAX_MSG_SIZE, &context->request.bufferLen, 0);
+
+      //Any datagram received?
+      if(!error)
+      {
+         //Acquire exclusive access to the SNMP agent context
+         osAcquireMutex(&context->mutex);
+
+         //Debug message
+         TRACE_INFO("\r\nSNMP message received from %s port %" PRIu16
+            " (%" PRIuSIZE " bytes)...\r\n",
+            ipAddrToString(&context->remoteIpAddr, NULL),
+            context->remotePort, context->request.bufferLen);
+
+         //Display the contents of the SNMP message
+         TRACE_DEBUG_ARRAY("  ", context->request.buffer, context->request.bufferLen);
+         //Dump ASN.1 structure
+         asn1DumpObject(context->request.buffer, context->request.bufferLen, 0);
+
+         //Process incoming SNMP message
+         error = snmpProcessMessage(context);
+
+         //Check status code
+         if(!error)
+         {
+            //Debug message
+            TRACE_INFO("Sending SNMP message to %s port %" PRIu16
+               " (%" PRIuSIZE " bytes)...\r\n",
+               ipAddrToString(&context->remoteIpAddr, NULL),
+               context->remotePort, context->response.length);
+
+            //Display the contents of the SNMP message
+            TRACE_DEBUG_ARRAY("  ", context->response.pos, context->response.length);
+            //Display ASN.1 structure
+            asn1DumpObject(context->response.pos, context->response.length, 0);
+
+            //Send SNMP response message
+            socketSendTo(context->socket, &context->remoteIpAddr, context->remotePort,
+               context->response.pos, context->response.length, NULL, 0);
+         }
+
+         //Release exclusive access to the SNMP agent context
+         osReleaseMutex(&context->mutex);
+      }
+#if (NET_RTOS_SUPPORT == ENABLED)
+   }
+#endif
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/snmp/snmp_agent.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,177 @@
+/**
+ * @file snmp_agent.h
+ * @brief SNMP agent (Simple Network Management Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SNMP_AGENT_H
+#define _SNMP_AGENT_H
+
+//Dependencies
+#include "core/net.h"
+#include "snmp/snmp_common.h"
+#include "snmp/snmp_usm.h"
+#include "mibs/mib_common.h"
+
+//SNMP agent support
+#ifndef SNMP_AGENT_SUPPORT
+   #define SNMP_AGENT_SUPPORT DISABLED
+#elif (SNMP_AGENT_SUPPORT != ENABLED && SNMP_AGENT_SUPPORT != DISABLED)
+   #error SNMP_AGENT_SUPPORT parameter is not valid
+#endif
+
+//Stack size required to run the SNMP agent
+#ifndef SNMP_AGENT_STACK_SIZE
+   #define SNMP_AGENT_STACK_SIZE 550
+#elif (SNMP_AGENT_STACK_SIZE < 1)
+   #error SNMP_AGENT_STACK_SIZE parameter is not valid
+#endif
+
+//Priority at which the SNMP agent should run
+#ifndef SNMP_AGENT_PRIORITY
+   #define SNMP_AGENT_PRIORITY OS_TASK_PRIORITY_NORMAL
+#endif
+
+//Maximum number of users
+#ifndef SNMP_AGENT_MAX_USER_COUNT
+   #define SNMP_AGENT_MAX_USER_COUNT 4
+#elif (SNMP_AGENT_MAX_USER_COUNT < 1)
+   #error SNMP_AGENT_MAX_USER_COUNT parameter is not valid
+#endif
+
+//Maximum number of MIBs
+#ifndef SNMP_AGENT_MAX_MIB_COUNT
+   #define SNMP_AGENT_MAX_MIB_COUNT 4
+#elif (SNMP_AGENT_MAX_MIB_COUNT < 1)
+   #error SNMP_AGENT_MAX_MIB_COUNT parameter is not valid
+#endif
+
+
+/**
+ * @brief Random data generation callback function
+ **/
+
+typedef error_t (*SnmpAgentRandCallback)(uint8_t *data, size_t length);
+
+
+/**
+ * @brief SNMP agent settings
+ **/
+
+typedef struct
+{
+   NetInterface *interface;                        ///<Network interface to configure
+   SnmpVersion versionMin;                         ///<Minimum version accepted by the SNMP agent
+   SnmpVersion versionMax;                         ///<Maximum version accepted by the SNMP agent
+   uint16_t port;                                  ///<SNMP port number
+   uint16_t trapPort;                              ///<SNMP trap port number
+   SnmpAgentRandCallback randCallback;             ///<Random data generation callback function
+} SnmpAgentSettings;
+
+
+/**
+ * @brief SNMP agent context
+ **/
+
+typedef struct
+{
+   SnmpAgentSettings settings;                           ///<SNMP agent settings
+   OsMutex mutex;                                        ///<Mutex preventing simultaneous access to SNMP agent context
+   uint8_t enterpriseOid[SNMP_MAX_OID_SIZE];             ///<Enterprise OID
+   size_t enterpriseOidLen;                              ///<Length of the enterprise OID
+   SnmpUserInfo userTable[SNMP_AGENT_MAX_USER_COUNT];    ///<List of users
+   const MibModule *mibModule[SNMP_AGENT_MAX_MIB_COUNT]; ///<MIB modules
+   uint_t mibModuleCount;                                ///<Number of MIB modules
+   Socket *socket;                                       ///<Underlying socket
+   IpAddr remoteIpAddr;                                  ///<IP address of the remote SNMP engine
+   uint16_t remotePort;                                  ///<Source port used by the remote SNMP engine
+   SnmpMessage request;                                  ///<SNMP request message
+   SnmpMessage response;                                 ///<SNMP response message
+   const SnmpUserInfo *user;                             ///<Security profile of current user
+#if (SNMP_V3_SUPPORT == ENABLED)
+   uint8_t contextEngine[SNMP_MAX_CONTEXT_ENGINE_SIZE];  ///<Context engine identifier
+   size_t contextEngineLen;                              ///<Length of the context engine identifier
+   char_t contextName[SNMP_MAX_CONTEXT_NAME_LEN + 1];    ///<Context name
+   systime_t systemTime;                                 ///<System time
+   int32_t engineBoots;                                  ///<Number of times that the SNMP engine has rebooted
+   int32_t engineTime;                                   ///<SNMP engine time
+   uint64_t salt;                                        ///<Integer initialized to a random value at boot time
+   uint8_t privParameters[8];                            ///<Privacy parameters
+#endif
+} SnmpAgentContext;
+
+
+/**
+ * @brief Object descriptor
+ **/
+
+typedef struct
+{
+   uint8_t oid[SNMP_MAX_OID_SIZE];
+   size_t oidLen;
+} SnmpTrapObject;
+
+
+//SNMP agent related functions
+void snmpAgentGetDefaultSettings(SnmpAgentSettings *settings);
+error_t snmpAgentInit(SnmpAgentContext *context, const SnmpAgentSettings *settings);
+error_t snmpAgentStart(SnmpAgentContext *context);
+
+error_t snmpAgentLoadMib(SnmpAgentContext *context, const MibModule *module);
+error_t snmpAgentUnloadMib(SnmpAgentContext *context, const MibModule *module);
+
+error_t snmpAgentSetEngineBoots(SnmpAgentContext *context, int32_t engineBoots);
+error_t snmpAgentGetEngineBoots(SnmpAgentContext *context, int32_t *engineBoots);
+
+error_t snmpAgentSetEnterpriseOid(SnmpAgentContext *context,
+   const uint8_t *enterpriseOid, size_t enterpriseOidLen);
+
+error_t snmpAgentSetContextEngine(SnmpAgentContext *context,
+   const void *contextEngine, size_t contextEngineLen);
+
+error_t snmpAgentSetContextName(SnmpAgentContext *context,
+   const char_t *contextName);
+
+error_t snmpAgentCreateCommunity(SnmpAgentContext *context,
+   const char_t *community, SnmpAccess mode);
+
+error_t snmpAgentDeleteCommunity(SnmpAgentContext *context,
+   const char_t *community);
+
+error_t snmpAgentCreateUser(SnmpAgentContext *context,
+   const char_t *username, SnmpAccess mode, SnmpKeyFormat keyFormat,
+   SnmpAuthProtocol authProtocol, const void *authKey,
+   SnmpPrivProtocol privProtocol, const void *privKey);
+
+error_t snmpAgentDeleteUser(SnmpAgentContext *context, const char_t *username);
+
+error_t snmpAgentSendTrap(SnmpAgentContext *context, const IpAddr *destIpAddr,
+   SnmpVersion version, const char_t *username, uint_t genericTrapType,
+   uint_t specificTrapCode, const SnmpTrapObject *objectList, uint_t objectListSize);
+
+void snmpAgentTask(SnmpAgentContext *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/snmp/snmp_agent_dispatch.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,394 @@
+/**
+ * @file snmp_agent_dispatch.c
+ * @brief SNMP message dispatching
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL SNMP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "snmp/snmp_agent.h"
+#include "snmp/snmp_agent_dispatch.h"
+#include "snmp/snmp_agent_pdu.h"
+#include "snmp/snmp_agent_misc.h"
+#include "mibs/mib2_module.h"
+#include "crypto.h"
+#include "asn1.h"
+#include "oid.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (SNMP_AGENT_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Process incoming SNMP message
+ * @param[in] context Pointer to the SNMP agent context
+ * @return Error code
+ **/
+
+error_t snmpProcessMessage(SnmpAgentContext *context)
+{
+   error_t error;
+
+   //Total number of messages delivered to the SNMP entity from the
+   //transport service
+   MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpInPkts, 1);
+
+   //Refresh SNMP engine time
+   snmpRefreshEngineTime(context);
+
+   //Message parsing initialization
+   snmpInitMessage(&context->request);
+
+   //Parse SNMP message header
+   error = snmpParseMessageHeader(&context->request);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //The SNMP agent verifies the version number. If there is a mismatch,
+   //it discards the datagram and performs no further actions
+   if(context->request.version < context->settings.versionMin ||
+      context->request.version > context->settings.versionMax)
+   {
+      //Debug message
+      TRACE_WARNING("  Invalid SNMP version!\r\n");
+      //Discard incoming SNMP message
+      return ERROR_INVALID_VERSION;
+   }
+
+#if (SNMP_V1_SUPPORT == ENABLED)
+   //SNMPv1 version?
+   if(context->request.version == SNMP_VERSION_1)
+   {
+      //Process incoming SNMPv1 message
+      error = snmpv1ProcessMessage(context);
+   }
+   else
+#endif
+#if (SNMP_V2C_SUPPORT == ENABLED)
+   //SNMPv2c version?
+   if(context->request.version == SNMP_VERSION_2C)
+   {
+      //Process incoming SNMPv2c message
+      error = snmpv2cProcessMessage(context);
+   }
+   else
+#endif
+#if (SNMP_V3_SUPPORT == ENABLED)
+   //SNMPv3 version?
+   if(context->request.version == SNMP_VERSION_3)
+   {
+      //Process incoming SNMPv3 message
+      error = snmpv3ProcessMessage(context);
+   }
+   else
+#endif
+   //Invalid SNMP version?
+   {
+      //Debug message
+      TRACE_WARNING("  Invalid SNMP version!\r\n");
+
+      //Total number of SNMP messages which were delivered to the SNMP
+      //protocol entity and were for an unsupported SNMP version
+      MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpInBadVersions, 1);
+
+      //Discard incoming SNMP message
+      error = ERROR_INVALID_VERSION;
+   }
+
+   //Check status code
+   if(error == NO_ERROR)
+   {
+      //Total number of messages which were passed from the SNMP protocol
+      //entity to the transport service
+      MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpOutPkts, 1);
+   }
+   else if(error == ERROR_INVALID_TAG)
+   {
+      //Total number of ASN.1 or BER errors encountered by the SNMP protocol
+      //entity when decoding received SNMP messages
+      MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpInASNParseErrs, 1);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process incoming SNMPv1 message
+ * @param[in] context Pointer to the SNMP agent context
+ * @return Error code
+ **/
+
+error_t snmpv1ProcessMessage(SnmpAgentContext *context)
+{
+   error_t error;
+
+#if (SNMP_V1_SUPPORT == ENABLED)
+   //Parse community name
+   error = snmpParseCommunity(&context->request);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Information about the community name is extracted from the local
+   //configuration datastore
+   context->user = snmpFindUser(context, context->request.community,
+      context->request.communityLen);
+
+   //Invalid community name?
+   if(context->user == NULL)
+   {
+      //Debug message
+      TRACE_WARNING("  Invalid community name!\r\n");
+
+      //Total number of SNMP messages delivered to the SNMP protocol entity
+      //which used a SNMP community name not known to said entity
+      MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpInBadCommunityNames, 1);
+
+      //Report an error
+      return ERROR_UNKNOWN_USER_NAME;
+   }
+
+   //Process PDU
+   error = snmpProcessPdu(context);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Format SNMP message header
+   error = snmpWriteMessageHeader(&context->response);
+#else
+   //Report an error
+   error = ERROR_INVALID_VERSION;
+#endif
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process incoming SNMPv2c message
+ * @param[in] context Pointer to the SNMP agent context
+ * @return Error code
+ **/
+
+error_t snmpv2cProcessMessage(SnmpAgentContext *context)
+{
+   error_t error;
+
+#if (SNMP_V2C_SUPPORT == ENABLED)
+   //Parse community name
+   error = snmpParseCommunity(&context->request);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Information about the community name is extracted from the local
+   //configuration datastore
+   context->user = snmpFindUser(context, context->request.community,
+      context->request.communityLen);
+
+   //Invalid community name?
+   if(context->user == NULL)
+   {
+      //Debug message
+      TRACE_WARNING("  Invalid community name!\r\n");
+
+      //Total number of SNMP messages delivered to the SNMP protocol entity
+      //which used a SNMP community name not known to said entity
+      MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpInBadCommunityNames, 1);
+
+      //Report an error
+      return ERROR_UNKNOWN_USER_NAME;
+   }
+
+   //Process PDU
+   error = snmpProcessPdu(context);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Format SNMP message header
+   error = snmpWriteMessageHeader(&context->response);
+#else
+   //Report an error
+   error = ERROR_INVALID_VERSION;
+#endif
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process incoming SNMPv3 message
+ * @param[in] context Pointer to the SNMP agent context
+ * @return Error code
+ **/
+
+error_t snmpv3ProcessMessage(SnmpAgentContext *context)
+{
+   error_t error;
+
+#if (SNMP_V3_SUPPORT == ENABLED)
+   //Parse msgGlobalData field
+   error = snmpParseGlobalData(&context->request);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Parse msgSecurityParameters field
+   error = snmpParseSecurityParameters(&context->request);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Start of exception handling block
+   do
+   {
+      //Information about the value of the msgUserName field is extracted
+      //from the local configuration datastore
+      context->user = snmpFindUser(context, context->request.msgUserName,
+         context->request.msgUserNameLen);
+
+      //Check security parameters
+      error = snmpCheckSecurityParameters(context->user, &context->request,
+         context->contextEngine, context->contextEngineLen);
+      //Invalid security parameters?
+      if(error)
+         break;
+
+      //Check whether the authFlag is set
+      if(context->request.msgFlags & SNMP_MSG_FLAG_AUTH)
+      {
+         //Authenticate incoming SNMP message
+         error = snmpAuthIncomingMessage(context->user, &context->request);
+         //Data authentication failed?
+         if(error)
+            break;
+
+         //Replay protection
+         error = snmpCheckEngineTime(context, &context->request);
+         //Message outside of the time window?
+         if(error)
+            break;
+      }
+
+      //Check whether the privFlag is set
+      if(context->request.msgFlags & SNMP_MSG_FLAG_PRIV)
+      {
+         //Decrypt data
+         error = snmpDecryptData(context->user, &context->request);
+         //Data decryption failed?
+         if(error)
+            break;
+      }
+
+      //End of exception handling block
+   } while(0);
+
+   //Check error indication
+   if(error == ERROR_UNSUPPORTED_SECURITY_LEVEL ||
+      error == ERROR_NOT_IN_TIME_WINDOW ||
+      error == ERROR_UNKNOWN_USER_NAME ||
+      error == ERROR_UNKNOWN_ENGINE_ID ||
+      error == ERROR_AUTHENTICATION_FAILED ||
+      error == ERROR_DECRYPTION_FAILED)
+   {
+      //Report the error indication to the remote SNMP engine using a Report-PDU
+      error = snmpFormatReportPdu(context, error);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+   else if(error == NO_ERROR)
+   {
+      //Parse scopedPDU
+      error = snmpParseScopedPdu(&context->request);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Process PDU
+      error = snmpProcessPdu(context);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+   else
+   {
+      //Stop processing
+      return error;
+   }
+
+   //Format scopedPDU
+   error = snmpWriteScopedPdu(&context->response);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check whether the privFlag is set
+   if(context->response.msgFlags & SNMP_MSG_FLAG_PRIV)
+   {
+      //Encrypt data
+      error = snmpEncryptData(context->user, &context->response, &context->salt);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+
+   //Format SNMP message header
+   error = snmpWriteMessageHeader(&context->response);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check whether the authFlag is set
+   if(context->response.msgFlags & SNMP_MSG_FLAG_AUTH)
+   {
+      //Authenticate outgoing SNMP message
+      error = snmpAuthOutgoingMessage(context->user, &context->response);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+#else
+   //Report an error
+   error = ERROR_INVALID_VERSION;
+#endif
+
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/snmp/snmp_agent_dispatch.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,44 @@
+/**
+ * @file snmp_agent_dispatch.h
+ * @brief SNMP message dispatching
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SNMP_AGENT_DISPATCH_H
+#define _SNMP_AGENT_DISPATCH_H
+
+//Dependencies
+#include "core/net.h"
+#include "snmp/snmp_agent.h"
+
+//SNMP agent related functions
+error_t snmpProcessMessage(SnmpAgentContext *context);
+
+error_t snmpv1ProcessMessage(SnmpAgentContext *context);
+error_t snmpv2cProcessMessage(SnmpAgentContext *context);
+error_t snmpv3ProcessMessage(SnmpAgentContext *context);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/snmp/snmp_agent_misc.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1157 @@
+/**
+ * @file snmp_agent_misc.c
+ * @brief SNMP agent (miscellaneous functions)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL SNMP_TRACE_LEVEL
+
+//Dependencies
+#include <limits.h>
+#include "core/net.h"
+#include "snmp/snmp_agent.h"
+#include "snmp/snmp_agent_misc.h"
+#include "mibs/mib2_module.h"
+#include "crypto.h"
+#include "asn1.h"
+#include "oid.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (SNMP_AGENT_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Lock MIB bases
+ **/
+
+void snmpLockMib(SnmpAgentContext *context)
+{
+   uint_t i;
+
+   //Loop through MIBs
+   for(i = 0; i < context->mibModuleCount; i++)
+   {
+      //Lock access to MIB base
+      if(context->mibModule[i]->lock != NULL)
+      {
+         context->mibModule[i]->lock();
+      }
+   }
+}
+
+
+/**
+ * @brief Unlock MIB bases
+ **/
+
+void snmpUnlockMib(SnmpAgentContext *context)
+{
+   uint_t i;
+
+   //Loop through MIBs
+   for(i = 0; i < context->mibModuleCount; i++)
+   {
+      //Unlock access to MIB base
+      if(context->mibModule[i]->unlock != NULL)
+      {
+         context->mibModule[i]->unlock();
+      }
+   }
+}
+
+
+/**
+ * @brief Initialize a GetResponse-PDU
+ * @param[in] context Pointer to the SNMP agent context
+ * @return Error code
+ **/
+
+error_t snmpInitResponse(SnmpAgentContext *context)
+{
+   error_t error;
+
+   //Initialize SNMP message
+   snmpInitMessage(&context->response);
+
+   //SNMP version identifier
+   context->response.version = context->request.version;
+
+#if (SNMP_V1_SUPPORT == ENABLED || SNMP_V2C_SUPPORT == ENABLED)
+   //Community name
+   context->response.community = context->request.community;
+   context->response.communityLen = context->request.communityLen;
+#endif
+
+#if (SNMP_V3_SUPPORT == ENABLED)
+   //Message identifier
+   context->response.msgId = context->request.msgId;
+   //Maximum message size supported by the sender
+   context->response.msgMaxSize = SNMP_MAX_MSG_SIZE;
+
+   //Bit fields which control processing of the message
+   context->response.msgFlags = context->request.msgFlags &
+      (SNMP_MSG_FLAG_AUTH | SNMP_MSG_FLAG_PRIV);
+
+   //Security model used by the sender
+   context->response.msgSecurityModel = context->request.msgSecurityModel;
+
+   //Authoritative engine identifier
+   context->response.msgAuthEngineId = context->contextEngine;
+   context->response.msgAuthEngineIdLen = context->contextEngineLen;
+
+   //Number of times the SNMP engine has rebooted
+   context->response.msgAuthEngineBoots = context->engineBoots;
+   //Number of seconds since last reboot
+   context->response.msgAuthEngineTime = context->engineTime;
+
+   //User name
+   context->response.msgUserName = context->request.msgUserName;
+   context->response.msgUserNameLen = context->request.msgUserNameLen;
+
+   //Authentication parameters
+   context->response.msgAuthParameters = NULL;
+   context->response.msgAuthParametersLen = context->request.msgAuthParametersLen;
+
+   //Privacy parameters
+   context->response.msgPrivParameters = context->privParameters;
+   context->response.msgPrivParametersLen = context->request.msgPrivParametersLen;
+
+   //Context engine identifier
+   context->response.contextEngineId = context->contextEngine;
+   context->response.contextEngineIdLen = context->contextEngineLen;
+
+   //Context name
+   context->response.contextName = context->request.contextName;
+   context->response.contextNameLen = context->request.contextNameLen;
+#endif
+
+   //PDU type
+   context->response.pduType = SNMP_PDU_GET_RESPONSE;
+   //Request identifier
+   context->response.requestId = context->request.requestId;
+
+   //Make room for the message header at the beginning of the buffer
+   error = snmpComputeMessageOverhead(&context->response);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Refresh SNMP engine time
+ * @param[in] context Pointer to the SNMP agent context
+ **/
+
+void snmpRefreshEngineTime(SnmpAgentContext *context)
+{
+#if (SNMP_V3_SUPPORT == ENABLED)
+   systime_t delta;
+   int32_t newEngineTime;
+
+   //Number of seconds elapsed since the last call
+   delta = (osGetSystemTime() - context->systemTime) / 1000;
+   //Increment SNMP engine time
+   newEngineTime = context->engineTime + delta;
+
+   //Check whether the SNMP engine time has rolled over
+   if(newEngineTime < context->engineTime)
+   {
+      //If snmpEngineTime ever reaches its maximum value (2147483647), then
+      //snmpEngineBoots is incremented as if the SNMP engine has re-booted
+      //and snmpEngineTime is reset to zero and starts incrementing again
+      context->engineBoots++;
+      context->engineTime = 0;
+   }
+   else
+   {
+      //Update SNMP engine time
+      context->engineTime = newEngineTime;
+   }
+
+   //Save timestamp
+   context->systemTime += delta * 1000;
+#endif
+}
+
+
+/**
+ * @brief Replay protection
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in,out] message Pointer to the incoming SNMP message
+ * @return Error code
+ **/
+
+error_t snmpCheckEngineTime(SnmpAgentContext *context, SnmpMessage *message)
+{
+   error_t error = NO_ERROR;
+
+#if (SNMP_V3_SUPPORT == ENABLED)
+   //If any of the following conditions is true, then the message is
+   //considered to be outside of the time window
+   if(context->engineBoots == INT32_MAX)
+   {
+      //The local value of snmpEngineBoots is 2147483647
+      error = ERROR_NOT_IN_TIME_WINDOW;
+   }
+   else if(context->engineBoots != message->msgAuthEngineBoots)
+   {
+      //The value of the msgAuthoritativeEngineBoots field differs from
+      //the local value of snmpEngineBoots
+      error = ERROR_NOT_IN_TIME_WINDOW;
+   }
+   else if((context->engineTime - message->msgAuthEngineTime) > SNMP_TIME_WINDOW ||
+      (message->msgAuthEngineTime - context->engineTime) > SNMP_TIME_WINDOW)
+   {
+      //The value of the msgAuthoritativeEngineTime field differs from the
+      //local notion of snmpEngineTime by more than +/- 150 seconds
+      error = ERROR_NOT_IN_TIME_WINDOW;
+   }
+#endif
+
+   //If the message is considered to be outside of the time window then an
+   //error indication (notInTimeWindow) is returned to the calling module
+   return error;
+}
+
+
+/**
+ * @brief Find user in the local configuration datastore
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] name Pointer to the user name
+ * @param[in] length Length of the user name
+ * @return Security profile corresponding to the specified user name
+ **/
+
+SnmpUserInfo *snmpFindUser(SnmpAgentContext *context,
+   const char_t *name, size_t length)
+{
+   uint_t i;
+   SnmpUserInfo *entry;
+
+   //Initialize pointer
+   entry = NULL;
+
+   //Sanity check
+   if(name != NULL)
+   {
+      //Loop through the local configuration datastore
+      for(i = 0; i < SNMP_AGENT_MAX_USER_COUNT; i++)
+      {
+         //Compare user names
+         if(strlen(context->userTable[i].name) == length)
+         {
+            if(!strncmp(context->userTable[i].name, name, length))
+            {
+               //A matching entry has been found
+               entry = &context->userTable[i];
+               //We are done
+               break;
+            }
+         }
+      }
+   }
+
+   //Return the security profile that corresponds to the specified user name
+   return entry;
+}
+
+
+/**
+ * @brief Parse variable binding
+ * @param[in] p Input stream where to read the variable binding
+ * @param[in] length Number of bytes available in the input stream
+ * @param[out] var Variable binding
+ * @param[out] consumed Total number of bytes that have been consumed
+ * @return Error code
+ **/
+
+error_t snmpParseVarBinding(const uint8_t *p,
+   size_t length, SnmpVarBind *var, size_t *consumed)
+{
+   error_t error;
+   Asn1Tag tag;
+
+   //The variable binding is encapsulated within a sequence
+   error = asn1ReadTag(p, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Total number of bytes that have been consumed
+   *consumed = tag.totalLength;
+
+   //Point to the first item of the sequence
+   p = tag.value;
+   length = tag.length;
+
+   //Read object name
+   error = asn1ReadTag(p, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OBJECT_IDENTIFIER);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Save object identifier
+   var->oid = tag.value;
+   var->oidLen = tag.length;
+
+   //Point to the next item
+   p += tag.totalLength;
+   length -= tag.totalLength;
+
+   //Read object value
+   error = asn1ReadTag(p, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Make sure that the tag is valid
+   if(tag.constructed)
+      return ERROR_INVALID_TAG;
+
+   //Save object class
+   var->objClass = tag.objClass;
+   //Save object type
+   var->objType = tag.objType;
+   //Save object value
+   var->value = tag.value;
+   var->valueLen = tag.length;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Write variable binding
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] var Variable binding
+ * @return Error code
+ **/
+
+error_t snmpWriteVarBinding(SnmpAgentContext *context, const SnmpVarBind *var)
+{
+   error_t error;
+   size_t m;
+   size_t n;
+   uint8_t *p;
+   Asn1Tag tag;
+
+   //The object's name is encoded in ASN.1 format
+   tag.constructed = FALSE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_OBJECT_IDENTIFIER;
+   tag.length = var->oidLen;
+   tag.value = var->oid;
+
+   //Calculate the total length of the ASN.1 tag
+   error = asn1WriteTag(&tag, FALSE, NULL, &m);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //The object's value is encoded in ASN.1 format
+   tag.constructed = FALSE;
+   tag.objClass = var->objClass;
+   tag.objType = var->objType;
+   tag.length = var->valueLen;
+   tag.value = var->value;
+
+   //Calculate the total length of the ASN.1 tag
+   error = asn1WriteTag(&tag, FALSE, NULL, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //The variable binding is encapsulated within a sequence
+   tag.constructed = TRUE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_SEQUENCE;
+   tag.length = m + n;
+   tag.value = NULL;
+
+   //The first pass computes the total length of the sequence
+   error = asn1WriteTag(&tag, FALSE, NULL, NULL);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Make sure the buffer is large enough to hold the whole sequence
+   if((context->response.varBindListLen + tag.totalLength) >
+      context->response.varBindListMaxLen)
+   {
+      //Report an error
+      return ERROR_BUFFER_OVERFLOW;
+   }
+
+   //The second pass encodes the sequence in reverse order
+   p = context->response.varBindList + context->response.varBindListLen +
+      tag.totalLength;
+
+   //Encode the object's value using ASN.1
+   tag.constructed = FALSE;
+   tag.objClass = var->objClass;
+   tag.objType = var->objType;
+   tag.length = var->valueLen;
+   tag.value = var->value;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, TRUE, p, &m);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Move backward
+   p -= m;
+
+   //Encode the object's name using ASN.1
+   tag.constructed = FALSE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_OBJECT_IDENTIFIER;
+   tag.length = var->oidLen;
+   tag.value = var->oid;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, TRUE, p, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Move backward
+   p -= n;
+
+   //The variable binding is encapsulated within a sequence
+   tag.constructed = TRUE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_SEQUENCE;
+   tag.length = m + n;
+   tag.value = NULL;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, TRUE, p, NULL);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Update the length of the list
+   context->response.varBindListLen += tag.totalLength;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Copy the list of variable bindings
+ * @param[in] context Pointer to the SNMP agent context
+ * @return Error code
+ **/
+
+error_t snmpCopyVarBindingList(SnmpAgentContext *context)
+{
+   //Sanity check
+   if(context->request.varBindListLen > context->response.varBindListMaxLen)
+      return ERROR_BUFFER_OVERFLOW;
+
+   //Copy the list of variable bindings to the response buffer
+   memcpy(context->response.varBindList, context->request.varBindList,
+      context->request.varBindListLen);
+
+   //Save the length of the list
+   context->response.varBindListLen = context->request.varBindListLen;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Assign object value
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] var Variable binding
+ * @param[in] commit This flag tells whether the changes should be
+ *   committed to the MIB base
+ * @return Error code
+ **/
+
+error_t snmpSetObjectValue(SnmpAgentContext *context, SnmpVarBind *var, bool_t commit)
+{
+   error_t error;
+   size_t n;
+   MibVariant *value;
+   const MibObject *object;
+
+   //Search the MIB for the specified object
+   error = snmpFindMibObject(context, var->oid, var->oidLen, &object);
+   //Cannot found the specified object?
+   if(error)
+      return error;
+
+   //Debug message
+   TRACE_INFO("  %s\r\n", object->name);
+
+   //Make sure the specified object is available for set operations
+   if(object->access != MIB_ACCESS_WRITE_ONLY &&
+      object->access != MIB_ACCESS_READ_WRITE)
+   {
+      //Report an error
+      return ERROR_NOT_WRITABLE;
+   }
+
+   //Check class
+   if(var->objClass != object->objClass)
+      return ERROR_WRONG_TYPE;
+   //Check type
+   if(var->objType != object->objType)
+      return ERROR_WRONG_TYPE;
+
+   //Point to the object value
+   value = (MibVariant *) var->value;
+   //Get the length of the object value
+   n = var->valueLen;
+
+   //Check object class
+   if(object->objClass == ASN1_CLASS_UNIVERSAL)
+   {
+      //Check object type
+      if(object->objType == ASN1_TYPE_INTEGER)
+      {
+         int32_t val;
+
+         //Integer objects use ASN.1 encoding rules
+         error = snmpDecodeInt32(var->value, n, &val);
+         //Conversion failed?
+         if(error)
+            return ERROR_WRONG_ENCODING;
+
+         //Point to the scratch buffer
+         value = (MibVariant *) context->response.buffer;
+         //Save resulting value
+         value->integer = val;
+         //Integer size
+         n = sizeof(int32_t);
+      }
+   }
+   else if(object->objClass == ASN1_CLASS_APPLICATION)
+   {
+      //Check object type
+      if(object->objType == MIB_TYPE_IP_ADDRESS)
+      {
+         //IpAddress objects have fixed size
+         if(n != object->valueSize)
+            return ERROR_WRONG_LENGTH;
+      }
+      else if(object->objType == MIB_TYPE_COUNTER32 ||
+         object->objType == MIB_TYPE_GAUGE32 ||
+         object->objType == MIB_TYPE_TIME_TICKS)
+      {
+         uint32_t val;
+
+         //Counter32, Gauge32 and TimeTicks objects use ASN.1 encoding rules
+         error = snmpDecodeUnsignedInt32(var->value, n, &val);
+         //Conversion failed?
+         if(error)
+            return ERROR_WRONG_ENCODING;
+
+         //Point to the scratch buffer
+         value = (MibVariant *) context->response.buffer;
+         //Save resulting value
+         value->counter32 = val;
+         //Integer size
+         n = sizeof(uint32_t);
+      }
+      else if(object->objType == MIB_TYPE_COUNTER64)
+      {
+         uint64_t val;
+
+         //Counter64 objects use ASN.1 encoding rules
+         error = snmpDecodeUnsignedInt64(var->value, n, &val);
+         //Conversion failed?
+         if(error)
+            return ERROR_WRONG_ENCODING;
+
+         //Point to the scratch buffer
+         value = (MibVariant *) context->response.buffer;
+         //Save resulting value
+         value->counter64 = val;
+         //Integer size
+         n = sizeof(uint64_t);
+      }
+   }
+
+   //Objects can be assigned a value using a callback function
+   if(object->setValue != NULL)
+   {
+      //Check whether the changes shall be committed to the MIB base
+      if(commit)
+      {
+         //Invoke callback function to assign object value
+         error = object->setValue(object, var->oid, var->oidLen, value, n);
+      }
+      else
+      {
+         //Successful write operation
+         error = NO_ERROR;
+      }
+   }
+   //Simple scalar objects can also be attached to a variable
+   else if(object->value != NULL)
+   {
+      //Check the length of the object
+      if(n <= object->valueSize)
+      {
+         //Check whether the changes shall be committed to the MIB base
+         if(commit)
+         {
+            //Record the length of the object value
+            if(object->valueLen != NULL)
+               *object->valueLen = n;
+
+            //Set object value
+            memcpy(object->value, value, n);
+         }
+
+         //Successful write operation
+         error = NO_ERROR;
+      }
+      else
+      {
+         //Invalid length
+         error = ERROR_WRONG_LENGTH;
+      }
+   }
+   else
+   {
+      //Report an error
+      error = ERROR_WRITE_FAILED;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Retrieve object value
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[out] var Variable binding
+ * @return Error code
+ **/
+
+error_t snmpGetObjectValue(SnmpAgentContext *context, SnmpVarBind *var)
+{
+   error_t error;
+   size_t n;
+   MibVariant *value;
+   const MibObject *object;
+
+   //Search the MIB for the specified object
+   error = snmpFindMibObject(context, var->oid, var->oidLen, &object);
+   //Cannot found the specified object?
+   if(error)
+      return error;
+
+   //Debug message
+   TRACE_INFO("  %s\r\n", object->name);
+
+   //Make sure the specified object is available for get operations
+   if(object->access != MIB_ACCESS_READ_ONLY &&
+      object->access != MIB_ACCESS_READ_WRITE)
+   {
+      //Report an error
+      return ERROR_ACCESS_DENIED;
+   }
+
+   //Buffer where to store the object value
+   value = (MibVariant *) (context->response.varBindList +
+      context->response.varBindListLen + context->response.oidLen);
+
+   //Number of bytes available in the buffer
+   n = context->response.varBindListMaxLen -
+      (context->response.varBindListLen + context->response.oidLen);
+
+   //Check object class
+   if(object->objClass == ASN1_CLASS_UNIVERSAL)
+   {
+      //Check object type
+      if(object->objType == ASN1_TYPE_INTEGER)
+      {
+         //Make sure the buffer is large enough
+         if(n < object->valueSize)
+            return ERROR_BUFFER_OVERFLOW;
+
+         //Integer objects have fixed size
+         n = object->valueSize;
+      }
+   }
+   else if(object->objClass == ASN1_CLASS_APPLICATION)
+   {
+      //Check object type
+      if(object->objType == MIB_TYPE_IP_ADDRESS ||
+         object->objType == MIB_TYPE_COUNTER32 ||
+         object->objType == MIB_TYPE_GAUGE32 ||
+         object->objType == MIB_TYPE_TIME_TICKS ||
+         object->objType == MIB_TYPE_COUNTER64)
+      {
+         //Make sure the buffer is large enough
+         if(n < object->valueSize)
+            return ERROR_BUFFER_OVERFLOW;
+
+         //IpAddress, Counter32, Gauge32, TimeTicks and
+         //Counter64 objects have fixed size
+         n = object->valueSize;
+      }
+   }
+
+   //Object value can be retrieved using a callback function
+   if(object->getValue != NULL)
+   {
+      //Invoke callback function to retrieve object value
+      error = object->getValue(object, var->oid, var->oidLen, value, &n);
+   }
+   //Simple scalar objects can also be attached to a variable
+   else if(object->value != NULL)
+   {
+      //Get the length of the object value
+      if(object->valueLen != NULL)
+         n = *object->valueLen;
+
+      //Retrieve object value
+      memcpy(value, object->value, n);
+      //Successful read operation
+      error = NO_ERROR;
+   }
+   else
+   {
+      //Report an error
+      error = ERROR_READ_FAILED;
+   }
+
+   //Unable to retrieve object value?
+   if(error)
+      return error;
+
+   //Check object class
+   if(object->objClass == ASN1_CLASS_UNIVERSAL)
+   {
+      //Check object type
+      if(object->objType == ASN1_TYPE_INTEGER)
+      {
+         //Encode Integer objects using ASN.1 rules
+         error = snmpEncodeInt32(value->integer, (uint8_t *) value, &n);
+      }
+      else
+      {
+         //No conversion required for OctetString and ObjectIdentifier objects
+         error = NO_ERROR;
+      }
+   }
+   else if(object->objClass == ASN1_CLASS_APPLICATION)
+   {
+      //Check object type
+      if(object->objType == MIB_TYPE_COUNTER32 ||
+         object->objType == MIB_TYPE_GAUGE32 ||
+         object->objType == MIB_TYPE_TIME_TICKS)
+      {
+         //Encode Counter32, Gauge32 and TimeTicks objects using ASN.1 rules
+         error = snmpEncodeUnsignedInt32(value->counter32, (uint8_t *) value, &n);
+      }
+      else if(object->objType == MIB_TYPE_COUNTER64)
+      {
+         //Encode Counter64 objects using ASN.1 rules
+         error = snmpEncodeUnsignedInt64(value->counter64, (uint8_t *) value, &n);
+      }
+      else
+      {
+         //No conversion required for Opaque objects
+         error = NO_ERROR;
+      }
+   }
+
+   //Save object class and type
+   var->objClass = object->objClass;
+   var->objType = object->objType;
+
+   //Save object value
+   var->value = (uint8_t *) value;
+   var->valueLen = n;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Search MIBs for the next object
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] var Variable binding
+ * @return Error pointer
+ **/
+
+error_t snmpGetNextObject(SnmpAgentContext *context, SnmpVarBind *var)
+{
+   error_t error;
+   uint_t i;
+   uint_t j;
+   size_t nextOidLen;
+   uint8_t *nextOid;
+   const MibObject *object;
+
+   //Buffer where to store the next object identifier
+   nextOid = context->response.varBindList + context->response.varBindListLen;
+
+   //Loop through MIBs
+   for(i = 0; i < context->mibModuleCount; i++)
+   {
+      //Point the first object of the MIB
+      object = context->mibModule[i]->objects;
+
+      //Loop through objects
+      for(j = 0; j < context->mibModule[i]->numObjects; j++)
+      {
+         //Scalar or tabular object?
+         if(object->getNext == NULL)
+         {
+            //Take in account the instance sub-identifier to determine
+            //the length of the OID
+            nextOidLen = object->oidLen + 1;
+
+            //Make sure the buffer is large enough to hold the entire OID
+            if((context->response.varBindListLen + nextOidLen) >
+               context->response.varBindListMaxLen)
+            {
+               //Report an error
+               return ERROR_BUFFER_OVERFLOW;
+            }
+
+            //Copy object identifier
+            memcpy(nextOid, object->oid, object->oidLen);
+            //Append instance sub-identifier
+            nextOid[nextOidLen - 1] = 0;
+
+            //Perform lexicographical comparison
+            if(oidComp(var->oid, var->oidLen, nextOid, nextOidLen) < 0)
+            {
+               //Replace the original OID with the name of the next object
+               var->oid = nextOid;
+               var->oidLen = nextOidLen;
+
+               //Save the length of the OID
+               context->response.oidLen = nextOidLen;
+
+               //The specified OID lexicographically precedes the name
+               //of the current object
+               return NO_ERROR;
+            }
+         }
+         else
+         {
+            //Maximum acceptable size of the OID
+            nextOidLen = context->response.varBindListMaxLen -
+               context->response.varBindListLen;
+
+            //Search the MIB for the next object
+            error = object->getNext(object, var->oid, var->oidLen, nextOid, &nextOidLen);
+
+            //Check status code
+            if(error == NO_ERROR)
+            {
+               //Replace the original OID with the name of the next object
+               var->oid = nextOid;
+               var->oidLen = nextOidLen;
+
+               //Save the length of the OID
+               context->response.oidLen = nextOidLen;
+
+               //The specified OID lexicographically precedes the name
+               //of the current object
+               return NO_ERROR;
+            }
+            if(error != ERROR_OBJECT_NOT_FOUND)
+            {
+               //Exit immediately
+               return error;
+            }
+         }
+
+         //Point to the next object in the MIB
+         object++;
+      }
+   }
+
+   //The specified OID does not lexicographically precede the
+   //name of some object
+   return ERROR_OBJECT_NOT_FOUND;
+}
+
+
+/**
+ * @brief Search MIBs for the given object
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] oid Object identifier
+ * @param[in] oidLen Length of the OID
+ * @param[out] object Pointer the MIB object descriptor
+ * @return Error code
+ **/
+
+error_t snmpFindMibObject(SnmpAgentContext *context,
+   const uint8_t *oid, size_t oidLen, const MibObject **object)
+{
+   error_t error;
+   uint_t i;
+   uint_t j;
+   const MibObject *p;
+
+   //Initialize status code
+   error = ERROR_OBJECT_NOT_FOUND;
+
+   //Loop through MIBs
+   for(i = 0; i < context->mibModuleCount; i++)
+   {
+      //Point the first object of the MIB
+      p = context->mibModule[i]->objects;
+
+      //Loop through objects
+      for(j = 0; j < context->mibModule[i]->numObjects; j++)
+      {
+         //Check the length of the OID
+         if(oidLen > p->oidLen)
+         {
+            //Compare object names
+            if(!memcmp(oid, p->oid, p->oidLen))
+            {
+               //Scalar object?
+               if(p->getNext == NULL)
+               {
+                  //The instance sub-identifier shall be 0 for scalar objects
+                  if(oidLen == (p->oidLen + 1) && oid[oidLen - 1] == 0)
+                  {
+                     //Return a pointer to the matching object
+                     *object = p;
+                     //No error to report
+                     error = NO_ERROR;
+                  }
+                  else
+                  {
+                     //No such instance...
+                     error = ERROR_INSTANCE_NOT_FOUND;
+                  }
+               }
+               //Tabular object?
+               else
+               {
+                  //Return a pointer to the matching object
+                  *object = p;
+                  //No error to report
+                  error = NO_ERROR;
+               }
+
+               //Exit immediately
+               break;
+            }
+         }
+
+         //Point to the next object in the MIB
+         p++;
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Translate status code
+ * @param[in,out] message Pointer to the outgoing SNMP message
+ * @param[in] status Status code
+ * @param[in] index Index of the variable binding in the list that caused an exception
+ * @return error code
+ **/
+
+error_t snmpTranslateStatusCode(SnmpMessage *message, error_t status, uint_t index)
+{
+   //SNMPv1 version?
+   if(message->version == SNMP_VERSION_1)
+   {
+      //Set error-status and error-index fields
+      switch(status)
+      {
+      case NO_ERROR:
+         //Return noError status code
+         message->errorStatus = SNMP_ERROR_NONE;
+         message->errorIndex = 0;
+         break;
+
+      case ERROR_OBJECT_NOT_FOUND:
+      case ERROR_INSTANCE_NOT_FOUND:
+      case ERROR_ACCESS_DENIED:
+         //Return noSuchName status code
+         message->errorStatus = SNMP_ERROR_NO_SUCH_NAME;
+         message->errorIndex = index;
+
+         //Total number of SNMP PDUs which were generated by the SNMP protocol
+         //entity and for which the value of the error-status field is noSuchName
+         MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpOutNoSuchNames, 1);
+         break;
+
+      case ERROR_WRONG_TYPE:
+      case ERROR_WRONG_LENGTH:
+      case ERROR_WRONG_ENCODING:
+      case ERROR_WRONG_VALUE:
+         //Return badValue status code
+         message->errorStatus = SNMP_ERROR_BAD_VALUE;
+         message->errorIndex = index;
+
+         //Total number of SNMP PDUs which were generated by the SNMP protocol
+         //entity and for which the value of the error-status field is badValue
+         MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpOutBadValues, 1);
+         break;
+
+      case ERROR_READ_FAILED:
+      case ERROR_WRITE_FAILED:
+      case ERROR_NOT_WRITABLE:
+         //Return genError status code
+         message->errorStatus = SNMP_ERROR_GENERIC;
+         message->errorIndex = index;
+
+         //Total number of SNMP PDUs which were generated by the SNMP protocol
+         //entity and for which the value of the error-status field is genError
+         MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpOutGenErrs, 1);
+         break;
+
+      case ERROR_BUFFER_OVERFLOW:
+         //Return tooBig status code
+         message->errorStatus = SNMP_ERROR_TOO_BIG;
+         message->errorIndex = 0;
+
+         //Total number of SNMP PDUs which were generated by the SNMP protocol
+         //entity and for which the value of the error-status field is tooBig
+         MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpOutTooBigs, 1);
+         break;
+
+      default:
+         //If the parsing of the request fails, the SNMP agent discards
+         //the message and performs no further actions
+         return status;
+      }
+   }
+   //SNMPv2c or SNMPv3 version?
+   else
+   {
+      //Set error-status and error-index fields
+      switch(status)
+      {
+      case NO_ERROR:
+         //Return noError status code
+         message->errorStatus = SNMP_ERROR_NONE;
+         message->errorIndex = 0;
+         break;
+
+      case ERROR_OBJECT_NOT_FOUND:
+      case ERROR_INSTANCE_NOT_FOUND:
+      case ERROR_ACCESS_DENIED:
+         //Return noAccess status code
+         message->errorStatus = SNMP_ERROR_NO_ACCESS;
+         message->errorIndex = index;
+         break;
+
+      case ERROR_WRONG_TYPE:
+         //Return wrongType status code
+         message->errorStatus = SNMP_ERROR_WRONG_TYPE;
+         message->errorIndex = index;
+         break;
+
+      case ERROR_WRONG_LENGTH:
+         //Return wrongLength status code
+         message->errorStatus = SNMP_ERROR_WRONG_LENGTH;
+         message->errorIndex = index;
+         break;
+
+      case ERROR_WRONG_ENCODING:
+         //Return wrongEncoding status code
+         message->errorStatus = SNMP_ERROR_WRONG_ENCODING;
+         message->errorIndex = index;
+         break;
+
+      case ERROR_WRONG_VALUE:
+         //Return wrongValue status code
+         message->errorStatus = SNMP_ERROR_WRONG_VALUE;
+         message->errorIndex = index;
+         break;
+
+      case ERROR_READ_FAILED:
+      case ERROR_WRITE_FAILED:
+         //Return genError status code
+         message->errorStatus = SNMP_ERROR_GENERIC;
+         message->errorIndex = index;
+
+         //Total number of SNMP PDUs which were generated by the SNMP protocol
+         //entity and for which the value of the error-status field is genError
+         MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpOutGenErrs, 1);
+         break;
+
+      case ERROR_NOT_WRITABLE:
+         //Return notWritable status code
+         message->errorStatus = SNMP_ERROR_NOT_WRITABLE;
+         message->errorIndex = index;
+         break;
+
+      case ERROR_BUFFER_OVERFLOW:
+         //Return tooBig status code
+         message->errorStatus = SNMP_ERROR_TOO_BIG;
+         message->errorIndex = 0;
+
+         //Total number of SNMP PDUs which were generated by the SNMP protocol
+         //entity and for which the value of the error-status field is tooBig
+         MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpOutTooBigs, 1);
+         break;
+
+      default:
+         //If the parsing of the request fails, the SNMP agent discards
+         //the message and performs no further actions
+         return status;
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/snmp/snmp_agent_misc.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,64 @@
+/**
+ * @file snmp_agent_misc.h
+ * @brief SNMP agent (miscellaneous functions)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SNMP_AGENT_MISC_H
+#define _SNMP_AGENT_MISC_H
+
+//Dependencies
+#include "core/net.h"
+#include "snmp/snmp_agent.h"
+
+//SNMP agent related functions
+void snmpLockMib(SnmpAgentContext *context);
+void snmpUnlockMib(SnmpAgentContext *context);
+
+error_t snmpInitResponse(SnmpAgentContext *context);
+
+void snmpRefreshEngineTime(SnmpAgentContext *context);
+error_t snmpCheckEngineTime(SnmpAgentContext *context, SnmpMessage *message);
+
+SnmpUserInfo *snmpFindUser(SnmpAgentContext *context,
+   const char_t *name, size_t length);
+
+error_t snmpParseVarBinding(const uint8_t *p,
+   size_t length, SnmpVarBind *var, size_t *consumed);
+
+error_t snmpWriteVarBinding(SnmpAgentContext *context, const SnmpVarBind *var);
+error_t snmpCopyVarBindingList(SnmpAgentContext *context);
+
+error_t snmpSetObjectValue(SnmpAgentContext *context, SnmpVarBind *var, bool_t commit);
+error_t snmpGetObjectValue(SnmpAgentContext *context, SnmpVarBind *var);
+error_t snmpGetNextObject(SnmpAgentContext *context, SnmpVarBind *var);
+
+error_t snmpFindMibObject(SnmpAgentContext *context,
+   const uint8_t *oid, size_t oidLen, const MibObject **object);
+
+error_t snmpTranslateStatusCode(SnmpMessage *message, error_t status, uint_t index);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/snmp/snmp_agent_pdu.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1075 @@
+/**
+ * @file snmp_agent_pdu.c
+ * @brief SNMP agent (PDU processing)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL SNMP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "snmp/snmp_agent.h"
+#include "snmp/snmp_agent_pdu.h"
+#include "snmp/snmp_agent_misc.h"
+#include "mibs/mib2_module.h"
+#include "crypto.h"
+#include "asn1.h"
+#include "oid.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (SNMP_AGENT_SUPPORT == ENABLED)
+
+//sysUpTime.0 object (1.3.6.1.2.1.1.3.0)
+static const uint8_t sysUpTimeObject[] = {43, 6, 1, 2, 1, 1, 3, 0};
+//snmpTrapOID.0 object (1.3.6.1.6.3.1.1.4.1.0)
+static const uint8_t snmpTrapOidObject[] = {43, 6, 1, 6, 3, 1, 1, 4, 1, 0};
+//snmpTraps object (1.3.6.1.6.3.1.1.5)
+static const uint8_t snmpTrapsObject[] = {43, 6, 1, 6, 3, 1, 1, 5};
+
+
+/**
+ * @brief Process PDU
+ * @param[in] context Pointer to the SNMP agent context
+ * @return Error code
+ **/
+
+error_t snmpProcessPdu(SnmpAgentContext *context)
+{
+   error_t error;
+
+   //Parse PDU header
+   error = snmpParsePduHeader(&context->request);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check PDU type
+   switch(context->request.pduType)
+   {
+   case SNMP_PDU_GET_REQUEST:
+   case SNMP_PDU_GET_NEXT_REQUEST:
+      //Process GetRequest-PDU or GetNextRequest-PDU
+      error = snmpProcessGetRequestPdu(context);
+      break;
+   case SNMP_PDU_GET_BULK_REQUEST:
+      //Process GetBulkRequest-PDU
+      error = snmpProcessGetBulkRequestPdu(context);
+      break;
+   case SNMP_PDU_SET_REQUEST:
+      //Process SetRequest-PDU
+      error = snmpProcessSetRequestPdu(context);
+      break;
+   default:
+      //Invalid PDU type
+      error = ERROR_INVALID_TYPE;
+      break;
+   }
+
+   //Check status code
+   if(!error)
+   {
+      //Total number of SNMP Get-Response PDUs which have been generated
+      //by the SNMP protocol entity
+      MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpOutGetResponses, 1);
+
+      //Format PDU header
+      error = snmpWritePduHeader(&context->response);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Process GetRequest-PDU or GetNextRequest-PDU
+ * @param[in] context Pointer to the SNMP agent context
+ * @return Error code
+ **/
+
+error_t snmpProcessGetRequestPdu(SnmpAgentContext *context)
+{
+   error_t error;
+   int_t index;
+   size_t n;
+   size_t length;
+   const uint8_t *p;
+   SnmpVarBind var;
+
+   //Check PDU type
+   if(context->request.pduType == SNMP_PDU_GET_REQUEST)
+   {
+      //Debug message
+      TRACE_INFO("Parsing GetRequest-PDU...\r\n");
+
+      //Total number of SNMP Get-Request PDUs which have been accepted and
+      //processed by the SNMP protocol entity
+      MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpInGetRequests, 1);
+   }
+   else if(context->request.pduType == SNMP_PDU_GET_NEXT_REQUEST)
+   {
+      //Debug message
+      TRACE_INFO("Parsing GetNextRequest-PDU...\r\n");
+
+      //Total number of SNMP Get-NextRequest PDUs which have been accepted
+      //and processed by the SNMP protocol entity
+      MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpInGetNexts, 1);
+   }
+
+   //Enforce access policy
+   if(context->user->mode != SNMP_ACCESS_READ_ONLY &&
+      context->user->mode != SNMP_ACCESS_READ_WRITE)
+   {
+      //Total number of SNMP messages delivered to the SNMP protocol entity
+      //which represented an SNMP operation which was not allowed by the SNMP
+      MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpInBadCommunityUses, 1);
+
+      //Report an error
+      return ERROR_ACCESS_DENIED;
+   }
+
+   //Initialize response message
+   error = snmpInitResponse(context);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Point to the first variable binding of the request
+   p = context->request.varBindList;
+   length = context->request.varBindListLen;
+
+   //Lock access to MIB bases
+   snmpLockMib(context);
+
+   //Loop through the list
+   for(index = 1; length > 0; index++)
+   {
+      //Parse variable binding
+      error = snmpParseVarBinding(p, length, &var, &n);
+      //Failed to parse variable binding?
+      if(error)
+         break;
+
+      //Make sure that the object identifier is valid
+      error = oidCheck(var.oid, var.oidLen);
+      //Invalid object identifier?
+      if(error)
+         break;
+
+      //GetRequest-PDU?
+      if(context->request.pduType == SNMP_PDU_GET_REQUEST)
+      {
+         //Retrieve object value
+         error = snmpGetObjectValue(context, &var);
+      }
+      //GetNextRequest-PDU?
+      else
+      {
+         //Search the MIB for the next object
+         error = snmpGetNextObject(context, &var);
+
+         //SNMPv1 version?
+         if(context->request.version == SNMP_VERSION_1)
+         {
+            //Check status code
+            if(error == NO_ERROR)
+            {
+               //Retrieve object value
+               error = snmpGetObjectValue(context, &var);
+            }
+            else
+            {
+               //Stop immediately
+               break;
+            }
+         }
+         //SNMPv2c or SNMPv3 version?
+         else
+         {
+            //Check status code
+            if(error == NO_ERROR)
+            {
+               //Retrieve object value
+               error = snmpGetObjectValue(context, &var);
+            }
+            else if(error == ERROR_OBJECT_NOT_FOUND)
+            {
+               //The variable binding's value field is set to endOfMibView
+               var.objClass = ASN1_CLASS_CONTEXT_SPECIFIC;
+               var.objType = SNMP_EXCEPTION_END_OF_MIB_VIEW;
+               var.valueLen = 0;
+
+               //Catch exception
+               error = NO_ERROR;
+            }
+            else
+            {
+               //Stop immediately
+               break;
+            }
+         }
+      }
+
+      //Failed to retrieve object value?
+      if(error)
+      {
+         //SNMPv1 version?
+         if(context->request.version == SNMP_VERSION_1)
+         {
+            //Stop immediately
+            break;
+         }
+         //SNMPv2c or SNMPv3 version?
+         else
+         {
+            //Catch exception
+            if(error == ERROR_ACCESS_DENIED ||
+               error == ERROR_OBJECT_NOT_FOUND)
+            {
+               //The variable binding's value field is set to noSuchObject
+               var.objClass = ASN1_CLASS_CONTEXT_SPECIFIC;
+               var.objType = SNMP_EXCEPTION_NO_SUCH_OBJECT;
+               var.valueLen = 0;
+            }
+            else if(error == ERROR_INSTANCE_NOT_FOUND)
+            {
+               //The variable binding's value field is set to noSuchInstance
+               var.objClass = ASN1_CLASS_CONTEXT_SPECIFIC;
+               var.objType = SNMP_EXCEPTION_NO_SUCH_INSTANCE;
+               var.valueLen = 0;
+            }
+            else
+            {
+               //Stop immediately
+               break;
+            }
+         }
+      }
+      else
+      {
+         //Total number of MIB objects which have been retrieved successfully
+         //by the SNMP protocol entity as the result of receiving valid SNMP
+         //Get-Request and Get-NextRequest PDUs
+         MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpInTotalReqVars, 1);
+      }
+
+      //Append variable binding to the list
+      error = snmpWriteVarBinding(context, &var);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Advance data pointer
+      p += n;
+      length -= n;
+   }
+
+   //Unlock access to MIB bases
+   snmpUnlockMib(context);
+
+   //Check status code
+   if(error)
+   {
+      //Set error-status and error-index fields
+      error = snmpTranslateStatusCode(&context->response, error, index);
+      //If the parsing of the request fails, the SNMP agent discards the message
+      if(error)
+         return error;
+
+      //Check whether an alternate Response-PDU should be sent
+      if(context->response.version != SNMP_VERSION_1 &&
+         context->response.errorStatus == SNMP_ERROR_TOO_BIG)
+      {
+         //The alternate Response-PDU is formatted with the same value in its
+         //request-id field as the received GetRequest-PDU and an empty
+         //variable-bindings field
+         context->response.varBindListLen = 0;
+      }
+      else
+      {
+         //The Response-PDU is re-formatted with the same values in its request-id
+         //and variable-bindings fields as the received GetRequest-PDU
+         error = snmpCopyVarBindingList(context);
+         //Any error to report?
+         if(error)
+            return error;
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Process GetBulkRequest-PDU
+ * @param[in] context Pointer to the SNMP agent context
+ * @return Error code
+ **/
+
+error_t snmpProcessGetBulkRequestPdu(SnmpAgentContext *context)
+{
+#if (SNMP_V2C_SUPPORT == ENABLED || SNMP_V3_SUPPORT == ENABLED)
+   error_t error;
+   int_t index;
+   size_t n;
+   size_t m;
+   size_t length;
+   bool_t endOfMibView;
+   const uint8_t *p;
+   const uint8_t *next;
+   SnmpVarBind var;
+
+   //Debug message
+   TRACE_INFO("Parsing GetBulkRequest-PDU...\r\n");
+
+   //Make sure the SNMP version identifier is valid
+   if(context->request.version == SNMP_VERSION_1)
+   {
+      //The SNMP version is not acceptable
+      return ERROR_INVALID_TYPE;
+   }
+
+   //Enforce access policy
+   if(context->user->mode != SNMP_ACCESS_READ_ONLY &&
+      context->user->mode != SNMP_ACCESS_READ_WRITE)
+   {
+      //Total number of SNMP messages delivered to the SNMP protocol entity
+      //which represented an SNMP operation which was not allowed by the SNMP
+      MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpInBadCommunityUses, 1);
+
+      //Report an error
+      return ERROR_ACCESS_DENIED;
+   }
+
+   //Initialize response message
+   error = snmpInitResponse(context);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Point to the first variable binding of the request
+   p = context->request.varBindList;
+   length = context->request.varBindListLen;
+
+   //Lock access to MIB bases
+   snmpLockMib(context);
+
+   //Loop through the list
+   for(index = 1; length > 0; index++)
+   {
+      //The non-repeaters field specifies the number of non-repeating objects
+      //at the start of the variable binding list
+      if((index - 1) == context->request.nonRepeaters)
+      {
+         //Pointer to the first variable binding that will be processed during
+         //the next iteration
+         next = context->response.varBindList + context->response.varBindListLen;
+
+         //Actual size of the variable binding list
+         m = context->response.varBindListLen;
+
+         //This flag tells whether all variable bindings have the value field
+         //set to endOfMibView for a given iteration
+         endOfMibView = TRUE;
+
+         //If the max-repetitions field is zero, the list is trimmed to the
+         //first non-repeating variable bindings
+         if(context->request.maxRepetitions == 0)
+            break;
+      }
+
+      //Parse variable binding
+      error = snmpParseVarBinding(p, length, &var, &n);
+      //Failed to parse variable binding?
+      if(error)
+         break;
+
+      //Make sure that the object identifier is valid
+      error = oidCheck(var.oid, var.oidLen);
+      //Invalid object identifier?
+      if(error)
+         break;
+
+      //Search the MIB for the next object
+      error = snmpGetNextObject(context, &var);
+
+      //Check status code
+      if(error == NO_ERROR)
+      {
+         //Next object found
+         endOfMibView = FALSE;
+         //Retrieve object value
+         error = snmpGetObjectValue(context, &var);
+      }
+      else if(error == ERROR_OBJECT_NOT_FOUND)
+      {
+         //The variable binding's value field is set to endOfMibView
+         var.objClass = ASN1_CLASS_CONTEXT_SPECIFIC;
+         var.objType = SNMP_EXCEPTION_END_OF_MIB_VIEW;
+         var.valueLen = 0;
+
+         //Catch exception
+         error = NO_ERROR;
+      }
+      else
+      {
+         //Stop immediately
+         break;
+      }
+
+      //Failed to retrieve object value?
+      if(error)
+      {
+         //Catch exception
+         if(error == ERROR_ACCESS_DENIED ||
+            error == ERROR_OBJECT_NOT_FOUND)
+         {
+            //The variable binding's value field is set to noSuchObject
+            var.objClass = ASN1_CLASS_CONTEXT_SPECIFIC;
+            var.objType = SNMP_EXCEPTION_NO_SUCH_OBJECT;
+            var.valueLen = 0;
+         }
+         else if(error == ERROR_INSTANCE_NOT_FOUND)
+         {
+            //The variable binding's value field is set to noSuchInstance
+            var.objClass = ASN1_CLASS_CONTEXT_SPECIFIC;
+            var.objType = SNMP_EXCEPTION_NO_SUCH_INSTANCE;
+            var.valueLen = 0;
+         }
+         else
+         {
+            //Stop immediately
+            break;
+         }
+      }
+      else
+      {
+         //Total number of MIB objects which have been retrieved successfully
+         //by the SNMP protocol entity as the result of receiving valid SNMP
+         //Get-Request and Get-NextRequest PDUs
+         MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpInTotalReqVars, 1);
+      }
+
+      //Append variable binding to the list
+      error = snmpWriteVarBinding(context, &var);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Advance data pointer
+      p += n;
+      length -= n;
+
+      //Next iteration?
+      if(length == 0 && index > context->request.nonRepeaters)
+      {
+         //Decrement repeat counter
+         context->request.maxRepetitions--;
+
+         //Last iteration?
+         if(!context->request.maxRepetitions)
+            break;
+         //All variable bindings have the value field set to endOfMibView?
+         if(endOfMibView)
+            break;
+
+         //Point to the first variable binding to be processed
+         p = next;
+         //Number of bytes to be processed
+         length = context->response.varBindListLen - m;
+         //Rewind index
+         index = context->request.nonRepeaters;
+      }
+   }
+
+   //Unlock access to MIB bases
+   snmpUnlockMib(context);
+
+   //Check status code
+   if(error == ERROR_BUFFER_OVERFLOW)
+   {
+      //If the size of the message containing the requested number of variable
+      //bindings would be greater than the maximum message size, then the
+      //response is generated with a lesser number of variable bindings
+   }
+   else if(error)
+   {
+      //Set error-status and error-index fields
+      error = snmpTranslateStatusCode(&context->response, error, index);
+      //If the parsing of the request fails, the SNMP agent discards the message
+      if(error)
+         return error;
+
+      //The Response-PDU is re-formatted with the same values in its request-id
+      //and variable-bindings fields as the received GetRequest-PDU
+      error = snmpCopyVarBindingList(context);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Process SetRequest-PDU
+ * @param[in] context Pointer to the SNMP agent context
+ * @return Error code
+ **/
+
+error_t snmpProcessSetRequestPdu(SnmpAgentContext *context)
+{
+   error_t error;
+   int_t index;
+   size_t n;
+   size_t length;
+   const uint8_t *p;
+   SnmpVarBind var;
+
+   //Debug message
+   TRACE_INFO("Parsing SetRequest-PDU...\r\n");
+
+   //Total number of SNMP Set-Request PDUs which have been accepted and
+   //processed by the SNMP protocol entity
+   MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpInSetRequests, 1);
+
+   //Enforce access policy
+   if(context->user->mode != SNMP_ACCESS_WRITE_ONLY &&
+      context->user->mode != SNMP_ACCESS_READ_WRITE)
+   {
+      //Total number of SNMP messages delivered to the SNMP protocol entity
+      //which represented an SNMP operation which was not allowed by the SNMP
+      MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpInBadCommunityUses, 1);
+
+      //Report an error
+      return ERROR_ACCESS_DENIED;
+   }
+
+   //Initialize response message
+   error = snmpInitResponse(context);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //The variable bindings are processed as a two phase operation. In the
+   //first phase, each variable binding is validated
+   p = context->request.varBindList;
+   length = context->request.varBindListLen;
+
+   //Loop through the list
+   for(index = 1; length > 0; index++)
+   {
+      //Parse variable binding
+      error = snmpParseVarBinding(p, length, &var, &n);
+      //Failed to parse variable binding?
+      if(error)
+         break;
+
+      //Assign object value
+      error = snmpSetObjectValue(context, &var, FALSE);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Advance data pointer
+      p += n;
+      length -= n;
+   }
+
+   //If all validations are successful, then each variable is altered in
+   //the second phase
+   if(!error)
+   {
+      //The changes are committed to the MIB base during the second phase
+      p = context->request.varBindList;
+      length = context->request.varBindListLen;
+
+      //Lock access to MIB bases
+      snmpLockMib(context);
+
+      //Loop through the list
+      for(index = 1; length > 0; index++)
+      {
+         //Parse variable binding
+         error = snmpParseVarBinding(p, length, &var, &n);
+         //Failed to parse variable binding?
+         if(error)
+            break;
+
+         //Assign object value
+         error = snmpSetObjectValue(context, &var, TRUE);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Total number of MIB objects which have been altered successfully
+         //by the SNMP protocol entity as the result of receiving valid
+         //SNMP Set-Request PDUs
+         MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpInTotalSetVars, 1);
+
+         //Advance data pointer
+         p += n;
+         length -= n;
+      }
+
+      //Unlock access to MIB bases
+      snmpUnlockMib(context);
+   }
+
+   //Any error to report?
+   if(error)
+   {
+      //Set error-status and error-index fields
+      error = snmpTranslateStatusCode(&context->response, error, index);
+      //If the parsing of the request fails, the SNMP agent discards the message
+      if(error)
+         return error;
+   }
+
+   //The SNMP agent sends back a GetResponse-PDU of identical form
+   error = snmpCopyVarBindingList(context);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Format Trap-PDU or SNMPv2-Trap-PDU
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] version SNMP version identifier
+ * @param[in] username User name or community name
+ * @param[in] genericTrapType Generic trap type
+ * @param[in] specificTrapCode Specific code
+ * @param[in] objectList List of object names
+ * @param[in] objectListSize Number of entries in the list
+ * @return Error code
+ **/
+
+error_t snmpFormatTrapPdu(SnmpAgentContext *context, SnmpVersion version,
+   const char_t *username, uint_t genericTrapType, uint_t specificTrapCode,
+   const SnmpTrapObject *objectList, uint_t objectListSize)
+{
+   error_t error;
+   uint_t i;
+   size_t n;
+   systime_t time;
+   SnmpMessage *message;
+   SnmpVarBind var;
+
+   //Point to the SNMP message
+   message = &context->response;
+   //Initialize SNMP message
+   snmpInitMessage(message);
+
+   //SNMP version identifier
+   message->version = version;
+
+#if (SNMP_V1_SUPPORT == ENABLED)
+   //SNMPv1 version?
+   if(version == SNMP_VERSION_1)
+   {
+#if (IPV4_SUPPORT == ENABLED)
+      NetInterface *interface;
+
+      //Point to the underlying network interface
+      interface = context->settings.interface;
+#endif
+
+      //Community name
+      message->community = username;
+      message->communityLen = strlen(username);
+
+      //Prepare to send a Trap-PDU
+      message->pduType = SNMP_PDU_TRAP;
+      //Type of object generating trap
+      message->enterpriseOid = context->enterpriseOid;
+      message->enterpriseOidLen = context->enterpriseOidLen;
+
+#if (IPV4_SUPPORT == ENABLED)
+      //Address of object generating trap
+      if(interface != NULL)
+         message->agentAddr = interface->ipv4Context.addr;
+#endif
+
+      //Generic trap type
+      message->genericTrapType = genericTrapType;
+      //Specific trap code
+      message->specificTrapCode = specificTrapCode;
+      //Timestamp
+      message->timestamp = osGetSystemTime() / 10;
+   }
+   else
+#endif
+#if (SNMP_V2C_SUPPORT == ENABLED)
+   //SNMPv2c version?
+   if(version == SNMP_VERSION_2C)
+   {
+      //Community name
+      message->community = username;
+      message->communityLen = strlen(username);
+
+      //Prepare to send a SNMPv2-Trap-PDU
+      message->pduType = SNMP_PDU_TRAP_V2;
+   }
+   else
+#endif
+#if (SNMP_V3_SUPPORT == ENABLED)
+   //SNMPv3 version?
+   if(version == SNMP_VERSION_3)
+   {
+      //Maximum message size supported by the sender
+      message->msgMaxSize = SNMP_MAX_MSG_SIZE;
+
+      //Bit fields which control processing of the message
+      if(context->user->authProtocol != SNMP_AUTH_PROTOCOL_NONE)
+         message->msgFlags |= SNMP_MSG_FLAG_AUTH;
+      if(context->user->privProtocol != SNMP_PRIV_PROTOCOL_NONE)
+         message->msgFlags |= SNMP_MSG_FLAG_PRIV;
+
+      //Security model used by the sender
+      message->msgSecurityModel = SNMP_SECURITY_MODEL_USM;
+
+      //Authoritative engine identifier
+      message->msgAuthEngineId = context->contextEngine;
+      message->msgAuthEngineIdLen = context->contextEngineLen;
+      //Number of times the SNMP engine has rebooted
+      message->msgAuthEngineBoots = context->engineBoots;
+      //Number of seconds since last reboot
+      message->msgAuthEngineTime = context->engineTime;
+      //User name
+      message->msgUserName = username;
+      message->msgUserNameLen = strlen(username);
+      //Authentication parameters
+      message->msgAuthParameters = NULL;
+
+      //Length of the authentication parameters
+      if(context->user->authProtocol == SNMP_AUTH_PROTOCOL_MD5)
+         message->msgAuthParametersLen = 12;
+      else if(context->user->authProtocol == SNMP_AUTH_PROTOCOL_SHA1)
+         message->msgAuthParametersLen = 12;
+      else if(context->user->authProtocol == SNMP_AUTH_PROTOCOL_SHA224)
+         message->msgAuthParametersLen = 16;
+      else if(context->user->authProtocol == SNMP_AUTH_PROTOCOL_SHA256)
+         message->msgAuthParametersLen = 24;
+      else if(context->user->authProtocol == SNMP_AUTH_PROTOCOL_SHA384)
+         message->msgAuthParametersLen = 32;
+      else if(context->user->authProtocol == SNMP_AUTH_PROTOCOL_SHA512)
+         message->msgAuthParametersLen = 48;
+      else
+         message->msgAuthParametersLen = 0;
+
+      //Privacy parameters
+      message->msgPrivParameters = context->privParameters;
+
+      //Length of the privacy parameters
+      if(context->user->privProtocol == SNMP_PRIV_PROTOCOL_DES)
+         message->msgPrivParametersLen = 8;
+      else if(context->user->privProtocol == SNMP_PRIV_PROTOCOL_AES)
+         message->msgPrivParametersLen = 8;
+      else
+         message->msgPrivParametersLen = 0;
+
+      //Context engine identifier
+      message->contextEngineId = context->contextEngine;
+      message->contextEngineIdLen = context->contextEngineLen;
+      //Context name
+      message->contextName = (uint8_t *) context->contextName;
+      message->contextNameLen = strlen(context->contextName);
+
+      //Prepare to send a SNMPv2-Trap-PDU
+      message->pduType = SNMP_PDU_TRAP_V2;
+   }
+   else
+#endif
+   //Invalid SNMP version?
+   {
+      //Report an error
+      return ERROR_INVALID_VERSION;
+   }
+
+   //Make room for the message header at the beginning of the buffer
+   error = snmpComputeMessageOverhead(&context->response);
+   //Any error to report?
+   if(error)
+      return error;
+
+#if (SNMP_V2C_SUPPORT == ENABLED || SNMP_V3_SUPPORT == ENABLED)
+   //SNMPv2c or SNMPv3 version?
+   if(version == SNMP_VERSION_2C || version == SNMP_VERSION_3)
+   {
+      //Get current time
+      time = osGetSystemTime() / 10;
+
+      //Encode the object value using ASN.1 rules
+      error = snmpEncodeUnsignedInt32(time, message->buffer, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //The first two variable bindings in the variable binding list of an
+      //SNMPv2-Trap-PDU are sysUpTime.0 and snmpTrapOID.0 respectively
+      var.oid = sysUpTimeObject;
+      var.oidLen = sizeof(sysUpTimeObject);
+      var.objClass = ASN1_CLASS_APPLICATION;
+      var.objType = MIB_TYPE_TIME_TICKS;
+      var.value = message->buffer;
+      var.valueLen = n;
+
+      //Append sysUpTime.0 to the variable binding list
+      error = snmpWriteVarBinding(context, &var);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Generic or enterprise-specific trap?
+      if(genericTrapType < SNMP_TRAP_ENTERPRISE_SPECIFIC)
+      {
+         //Retrieve the length of the snmpTraps OID
+         n = sizeof(snmpTrapsObject);
+         //Copy the OID
+         memcpy(message->buffer, snmpTrapsObject, n);
+
+         //For generic traps, the SNMPv2 snmpTrapOID parameter shall be
+         //the corresponding trap as defined in section 2 of 3418
+         message->buffer[n] = genericTrapType + 1;
+
+         //Update the length of the snmpTrapOID parameter
+         n++;
+      }
+      else
+      {
+         //Retrieve the length of the enterprise OID
+         n = context->enterpriseOidLen;
+
+         //For enterprise specific traps, the SNMPv2 snmpTrapOID parameter shall
+         //be the concatenation of the SNMPv1 enterprise OID and two additional
+         //sub-identifiers: '0' and the SNMPv1 specific trap parameter
+         memcpy(message->buffer, context->enterpriseOid, n);
+
+         //Concatenate the '0' sub-identifier
+         message->buffer[n++] = 0;
+
+         //Concatenate the specific trap parameter
+         message->buffer[n] = specificTrapCode % 128;
+
+         //Loop as long as necessary
+         for(i = 1; specificTrapCode > 128; i++)
+         {
+            //Split the binary representation into 7 bit chunks
+            specificTrapCode /= 128;
+            //Make room for the new chunk
+            memmove(message->buffer + n + 1, message->buffer + n, i);
+            //Set the most significant bit in the current chunk
+            message->buffer[n] = OID_MORE_FLAG | (specificTrapCode % 128);
+         }
+
+         //Update the length of the snmpTrapOID parameter
+         n += i;
+      }
+
+      //The snmpTrapOID.0 variable occurs as the second variable
+      //binding in every SNMPv2-Trap-PDU
+      var.oid = snmpTrapOidObject;
+      var.oidLen = sizeof(snmpTrapOidObject);
+      var.objClass = ASN1_CLASS_UNIVERSAL;
+      var.objType = ASN1_TYPE_OBJECT_IDENTIFIER;
+      var.value = message->buffer;
+      var.valueLen = n;
+
+      //Append snmpTrapOID.0 to the variable binding list
+      error = snmpWriteVarBinding(context, &var);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+#endif
+
+   //Loop through the list of objects
+   for(i = 0; i < objectListSize; i++)
+   {
+      //Get object identifier
+      var.oid = objectList[i].oid;
+      var.oidLen = objectList[i].oidLen;
+
+      //Retrieve object value
+      error = snmpGetObjectValue(context, &var);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Append variable binding to the list
+      error = snmpWriteVarBinding(context, &var);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+
+   //Total number of SNMP Trap PDUs which have been generated by
+   //the SNMP protocol entity
+   MIB2_INC_COUNTER32(mib2Base.snmpGroup.snmpOutTraps, 1);
+
+   //Format PDU header
+   error = snmpWritePduHeader(&context->response);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Format Report-PDU
+ * @param[in] context Pointer to the SNMP agent context
+ * @param[in] errorIndication Error indication
+ * @return Error code
+ **/
+
+error_t snmpFormatReportPdu(SnmpAgentContext *context, error_t errorIndication)
+{
+   error_t error;
+
+#if (SNMP_V3_SUPPORT == ENABLED)
+   size_t n;
+   SnmpVarBind var;
+
+   //Initialize SNMP message
+   snmpInitMessage(&context->response);
+
+   //SNMP version identifier
+   context->response.version = context->request.version;
+
+   //Message identifier
+   context->response.msgId = context->request.msgId;
+   //Maximum message size supported by the sender
+   context->response.msgMaxSize = SNMP_MAX_MSG_SIZE;
+   //Bit fields which control processing of the message
+   context->response.msgFlags = 0;
+   //Security model used by the sender
+   context->response.msgSecurityModel = SNMP_SECURITY_MODEL_USM;
+
+   //Authoritative engine identifier
+   context->response.msgAuthEngineId = context->contextEngine;
+   context->response.msgAuthEngineIdLen = context->contextEngineLen;
+   //Number of times the SNMP engine has rebooted
+   context->response.msgAuthEngineBoots = context->engineBoots;
+   //Number of seconds since last reboot
+   context->response.msgAuthEngineTime = context->engineTime;
+
+   //Context engine identifier
+   context->response.contextEngineId = context->contextEngine;
+   context->response.contextEngineIdLen = context->contextEngineLen;
+   //Context name
+   context->response.contextName = (uint8_t *) context->contextName;
+   context->response.contextNameLen = strlen(context->contextName);
+
+   //PDU type
+   context->response.pduType = SNMP_PDU_REPORT;
+   //Request identifier
+   context->response.requestId = context->request.requestId;
+
+   //Make room for the message header at the beginning of the buffer
+   error = snmpComputeMessageOverhead(&context->response);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Encode the object value using ASN.1 rules
+   error = snmpEncodeUnsignedInt32(1, context->response.buffer, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Check error indication
+   switch(errorIndication)
+   {
+   case ERROR_UNSUPPORTED_SECURITY_LEVEL:
+      //Add the usmStatsUnsupportedSecLevels counter in the varBindList
+      var.oid = usmStatsUnsupportedSecLevelsObject;
+      var.oidLen = sizeof(usmStatsUnsupportedSecLevelsObject);
+      break;
+   case ERROR_NOT_IN_TIME_WINDOW:
+      //Add the usmStatsNotInTimeWindows counter in the varBindList
+      var.oid = usmStatsNotInTimeWindowsObject;
+      var.oidLen = sizeof(usmStatsNotInTimeWindowsObject);
+      break;
+   case ERROR_UNKNOWN_USER_NAME:
+      //Add the usmStatsUnknownUserNames counter in the varBindList
+      var.oid = usmStatsUnknownUserNamesObject;
+      var.oidLen = sizeof(usmStatsUnknownUserNamesObject);
+      break;
+   case ERROR_UNKNOWN_ENGINE_ID:
+      //Add the usmStatsUnknownEngineIDs counter in the varBindList
+      var.oid = usmStatsUnknownEngineIdsObject;
+      var.oidLen = sizeof(usmStatsUnknownEngineIdsObject);
+      break;
+   case ERROR_AUTHENTICATION_FAILED:
+      //Add the usmStatsWrongDigests counter in the varBindList
+      var.oid = usmStatsWrongDigestsObject;
+      var.oidLen = sizeof(usmStatsWrongDigestsObject);
+      break;
+   case ERROR_DECRYPTION_FAILED:
+      //Add the usmStatsDecryptionErrors counter in the varBindList
+      var.oid = usmStatsDecryptionErrorsObject;
+      var.oidLen = sizeof(usmStatsDecryptionErrorsObject);
+      break;
+   default:
+      //Just for sanity's sake...
+      var.oid = NULL;
+      var.oidLen = 0;
+      break;
+   }
+
+   //The counter is encoded in ASN.1 format
+   var.objClass = ASN1_CLASS_APPLICATION;
+   var.objType = MIB_TYPE_COUNTER32;
+   var.value = context->response.buffer;
+   var.valueLen = n;
+
+   //Append the variable binding list to the varBindList
+   error = snmpWriteVarBinding(context, &var);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Format PDU header
+   error = snmpWritePduHeader(&context->response);
+#else
+   //SNMPv3 is not supported
+   error = ERROR_NOT_IMPLEMENTED;
+#endif
+
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/snmp/snmp_agent_pdu.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,50 @@
+/**
+ * @file snmp_agent_pdu.h
+ * @brief SNMP agent (PDU processing)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SNMP_AGENT_PDU_H
+#define _SNMP_AGENT_PDU_H
+
+//Dependencies
+#include "core/net.h"
+#include "snmp/snmp_agent.h"
+
+//SNMP agent related functions
+error_t snmpProcessPdu(SnmpAgentContext *context);
+
+error_t snmpProcessGetRequestPdu(SnmpAgentContext *context);
+error_t snmpProcessGetBulkRequestPdu(SnmpAgentContext *context);
+error_t snmpProcessSetRequestPdu(SnmpAgentContext *context);
+
+error_t snmpFormatTrapPdu(SnmpAgentContext *context, SnmpVersion version,
+   const char_t *username, uint_t genericTrapType, uint_t specificTrapCode,
+   const SnmpTrapObject *objectList, uint_t objectListSize);
+
+error_t snmpFormatReportPdu(SnmpAgentContext *context, error_t errorIndication);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/snmp/snmp_common.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1701 @@
+/**
+ * @file snmp_common.c
+ * @brief Functions common to SNMP agent and SNMP manager
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL SNMP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "snmp/snmp_agent.h"
+#include "snmp/snmp_common.h"
+#include "crypto.h"
+#include "asn1.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (SNMP_AGENT_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Initialize a SNMP message
+ * @param[in] message Pointer to the SNMP message
+ **/
+
+void snmpInitMessage(SnmpMessage *message)
+{
+   //Current position in the message
+   message->pos = NULL;
+   //Length of the message
+   message->length = 0;
+
+   //SNMP version identifier
+   message->version = SNMP_VERSION_1;
+
+#if (SNMP_V1_SUPPORT == ENABLED || SNMP_V2C_SUPPORT == ENABLED)
+   //Initialize community name
+   message->community = NULL;
+   message->communityLen = 0;
+#endif
+
+#if (SNMP_V3_SUPPORT == ENABLED)
+   //Initialize msgGlobalData fields
+   message->msgId = 0;
+   message->msgMaxSize = 0;
+   message->msgFlags = 0;
+   message->msgSecurityModel = 0;
+
+   //Initialize msgSecurityParameters fields
+   message->msgAuthEngineId = NULL;
+   message->msgAuthEngineIdLen = 0;
+   message->msgAuthEngineBoots = 0;
+   message->msgAuthEngineTime = 0;
+   message->msgUserName = NULL;
+   message->msgUserNameLen = 0;
+   message->msgAuthParameters = NULL;
+   message->msgAuthParametersLen = 0;
+   message->msgPrivParameters = NULL;
+   message->msgPrivParametersLen = 0;
+
+   //Initialize scopedPDU fields
+   message->contextEngineId = NULL;
+   message->contextEngineIdLen = 0;
+   message->contextName = NULL;
+   message->contextNameLen = 0;
+#endif
+
+   //Initialize PDU fields
+   message->pduType = SNMP_PDU_GET_REQUEST;
+   message->requestId = 0;
+   message->errorStatus = SNMP_ERROR_NONE;
+   message->errorIndex = 0;
+
+#if (SNMP_V1_SUPPORT == ENABLED)
+   message->enterpriseOid = NULL;
+   message->enterpriseOidLen = 0;
+   message->agentAddr = IPV4_UNSPECIFIED_ADDR;
+   message->genericTrapType = 0;
+   message->specificTrapCode = 0;
+   message->timestamp = 0;
+#endif
+
+#if (SNMP_V2C_SUPPORT == ENABLED || SNMP_V3_SUPPORT == ENABLED)
+   message->nonRepeaters = 0;
+   message->maxRepetitions = 0;
+#endif
+
+   //Initialize the list of variable bindings
+   message->varBindList = NULL;
+   message->varBindListLen = 0;
+   message->varBindListMaxLen = 0;
+   message->oidLen = 0;
+}
+
+
+/**
+ * @brief Compute SNMP message overhead
+ * @param[in] message Pointer to the SNMP message
+ **/
+
+error_t snmpComputeMessageOverhead(SnmpMessage *message)
+{
+   size_t n;
+
+#if (SNMP_V1_SUPPORT == ENABLED)
+   //SNMPv1 version?
+   if(message->version == SNMP_VERSION_1)
+   {
+      //SNMPv1 message header overhead
+      n = SNMP_V1_MSG_HEADER_OVERHEAD;
+      //Take into consideration variable-length fields
+      n += message->communityLen + message->enterpriseOidLen;
+   }
+   else
+#endif
+#if (SNMP_V2C_SUPPORT == ENABLED)
+   //SNMPv2c version?
+   if(message->version == SNMP_VERSION_2C)
+   {
+      //SNMPv2c message header overhead
+      n = SNMP_V2C_MSG_HEADER_OVERHEAD;
+      //Take into consideration variable-length fields
+      n += message->communityLen;
+   }
+   else
+#endif
+#if (SNMP_V3_SUPPORT == ENABLED)
+   //SNMPv3 version?
+   if(message->version == SNMP_VERSION_3)
+   {
+      //SNMPv3 message header overhead
+      n = SNMP_V3_MSG_HEADER_OVERHEAD;
+
+      //Take into consideration variable-length fields
+      n += message->msgAuthEngineIdLen + message->msgUserNameLen +
+         message->msgAuthParametersLen + message->msgPrivParametersLen +
+         message->contextEngineIdLen + message->contextNameLen;
+   }
+   else
+#endif
+   //Invalid SNMP version?
+   {
+      //Report an error
+      return ERROR_INVALID_VERSION;
+   }
+
+   //Sanity check
+   if(n > (SNMP_MAX_MSG_SIZE - SNMP_MSG_ENCRYPTION_OVERHEAD))
+      return ERROR_FAILURE;
+
+   //Make room for the message header at the beginning of the buffer
+   message->varBindList = message->buffer + n;
+   //Maximum length of the variable binding list
+   message->varBindListMaxLen = (SNMP_MAX_MSG_SIZE - SNMP_MSG_ENCRYPTION_OVERHEAD) - n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse SNMP message header
+ * @param[in,out] message Pointer to the incoming SNMP message
+ * @return Error code
+ **/
+
+error_t snmpParseMessageHeader(SnmpMessage *message)
+{
+   error_t error;
+   size_t length;
+   const uint8_t *p;
+   Asn1Tag tag;
+
+   //Point to the first byte of the SNMP message
+   p = message->buffer;
+   //Retrieve the length of the SNMP message
+   length = message->bufferLen;
+
+   //The SNMP message is encapsulated within a sequence
+   error = asn1ReadTag(p, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Point to the first field of the sequence
+   p = tag.value;
+   length = tag.length;
+
+   //Read version identifier
+   error = asn1ReadInt32(p, length, &tag, &message->version);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Make sure the SNMP version identifier is valid
+   if(message->version != SNMP_VERSION_1 &&
+      message->version != SNMP_VERSION_2C &&
+      message->version != SNMP_VERSION_3)
+   {
+      //The SNMP version is not acceptable
+      return ERROR_INVALID_VERSION;
+   }
+
+   //Advance data pointer
+   message->pos = (uint8_t *) p + tag.totalLength;
+   //Remaining bytes to process
+   message->length = length - tag.totalLength;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format SNMP message header
+ * @param[in,out] message Pointer to the outgoing SNMP message
+ * @return Error code
+ **/
+
+error_t snmpWriteMessageHeader(SnmpMessage *message)
+{
+   error_t error;
+   size_t n;
+   Asn1Tag tag;
+
+   //SNMPv1 or SNMPv2c version?
+   if(message->version == SNMP_VERSION_1 ||
+      message->version == SNMP_VERSION_2C)
+   {
+      //Write the community name
+      error = snmpWriteCommunity(message);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+   //SNMPv3 version?
+   else if(message->version == SNMP_VERSION_3)
+   {
+      //Write msgSecurityParameters field
+      error = snmpWriteSecurityParameters(message);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Write msgGlobalData field
+      error = snmpWriteGlobalData(message);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+   //Invalid version?
+   else
+   {
+      //Report an error
+      return ERROR_INVALID_VERSION;
+   }
+
+   //Write version identifier
+   error = asn1WriteInt32(message->version, TRUE, message->pos, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Move backward
+   message->pos -= n;
+   //Update the length of the message
+   message->length += n;
+
+   //The SNMP message is encapsulated within a sequence
+   tag.constructed = TRUE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_SEQUENCE;
+   tag.length = message->length;
+   tag.value = NULL;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, TRUE, message->pos, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Move backward
+   message->pos -= n;
+   //Total length of the SNMP message
+   message->length += n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse community name
+ * @param[in,out] message Pointer to the incoming SNMP message
+ * @return Error code
+ **/
+
+error_t snmpParseCommunity(SnmpMessage *message)
+{
+#if (SNMP_V1_SUPPORT == ENABLED || SNMP_V2C_SUPPORT == ENABLED)
+   error_t error;
+   Asn1Tag tag;
+
+   //Read community name
+   error = asn1ReadTag(message->pos, message->length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OCTET_STRING);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Save community name
+   message->community = (char_t *) tag.value;
+   message->communityLen = tag.length;
+
+   //Advance data pointer
+   message->pos += tag.totalLength;
+   //Remaining bytes to process
+   message->length -= tag.totalLength;
+
+   //No error to report
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Format community name
+ * @param[in,out] message Pointer to the outgoing SNMP message
+ * @return Error code
+ **/
+
+error_t snmpWriteCommunity(SnmpMessage *message)
+{
+#if (SNMP_V1_SUPPORT == ENABLED || SNMP_V2C_SUPPORT == ENABLED)
+   error_t error;
+   size_t n;
+   Asn1Tag tag;
+
+   //The community name is an octet string
+   tag.constructed = FALSE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_OCTET_STRING;
+   tag.length = message->communityLen;
+   tag.value = (uint8_t *) message->community;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, TRUE, message->pos, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Point to the first byte of the community name
+   message->pos -= n;
+   //Total length of the message
+   message->length += n;
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Parse msgGlobalData field
+ * @param[in,out] message Pointer to the incoming SNMP message
+ * @return Error code
+ **/
+
+error_t snmpParseGlobalData(SnmpMessage *message)
+{
+#if (SNMP_V3_SUPPORT == ENABLED)
+   error_t error;
+   size_t length;
+   const uint8_t *p;
+   Asn1Tag tag;
+
+   //Read the msgGlobalData field
+   error = asn1ReadTag(message->pos, message->length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Advance pointer over the msgGlobalData field
+   message->pos += tag.totalLength;
+   //Remaining bytes to process
+   message->length -= tag.totalLength;
+
+   //Point to the first field of the sequence
+   p = tag.value;
+   length = tag.length;
+
+   //The msgID is used between two SNMP entities to coordinate request
+   //messages and responses
+   error = asn1ReadInt32(p, length, &tag, &message->msgId);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Make sure the value is in the range 0..2147483647
+   if(message->msgId < 0)
+      return ERROR_WRONG_ENCODING;
+
+   //Point to the next field
+   p += tag.totalLength;
+   length -= tag.totalLength;
+
+   //The msgMaxSize field of the message conveys the maximum message size
+   //supported by the sender of the message
+   error = asn1ReadInt32(p, length, &tag, &message->msgMaxSize);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Make sure the value is in the range 484..2147483647
+   if(message->msgMaxSize < 484)
+      return ERROR_WRONG_ENCODING;
+
+   //Point to the next field
+   p += tag.totalLength;
+   length -= tag.totalLength;
+
+   //The msgFlags field of the message contains several bit fields which
+   //control processing of the message
+   error = asn1ReadTag(p, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OCTET_STRING);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //The msgFlags field consists of a single byte
+   if(tag.length != sizeof(uint8_t))
+      return ERROR_WRONG_ENCODING;
+
+   //Save the bit field
+   message->msgFlags = tag.value[0];
+
+   //Point to the next field
+   p += tag.totalLength;
+   length -= tag.totalLength;
+
+   //The msgSecurityModel field identifies which security model was used
+   //by the sender to generate the message
+   error = asn1ReadInt32(p, length, &tag, &message->msgSecurityModel);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Make sure the value is in the range 1..2147483647
+   if(message->msgSecurityModel < 1)
+      return ERROR_WRONG_ENCODING;
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Format msgGlobalData field
+ * @param[in,out] message Pointer to the outgoing SNMP message
+ * @return Error code
+ **/
+
+error_t snmpWriteGlobalData(SnmpMessage *message)
+{
+#if (SNMP_V3_SUPPORT == ENABLED)
+   error_t error;
+   size_t n;
+   size_t length;
+   uint8_t *p;
+   Asn1Tag tag;
+
+   //The msgGlobalData field is encoded in reverse order
+   p = message->pos;
+   //Length of the msgGlobalData field
+   length = 0;
+
+   //Write msgSecurityModel field
+   error = asn1WriteInt32(message->msgSecurityModel, TRUE, p, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Move backward
+   p -= n;
+   //Update the length of the msgGlobalData field
+   length += n;
+
+   //The msgFlags field consists of a single byte
+   tag.constructed = FALSE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_OCTET_STRING;
+   tag.length = sizeof(uint8_t);
+   tag.value = &message->msgFlags;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, TRUE, p, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Move backward
+   p -= n;
+   //Update the length of the msgGlobalData field
+   length += n;
+
+   //Write msgMaxSize field
+   error = asn1WriteInt32(message->msgMaxSize, TRUE, p, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Move backward
+   p -= n;
+   //Update the length of the msgGlobalData field
+   length += n;
+
+   //Write msgID field
+   error = asn1WriteInt32(message->msgId, TRUE, p, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Move backward
+   p -= n;
+   //Update the length of the msgGlobalData field
+   length += n;
+
+   //The parameters are encapsulated within a sequence
+   tag.constructed = TRUE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_SEQUENCE;
+   tag.length = length;
+   tag.value = NULL;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, TRUE, p, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Point to the first byte of the msgGlobalData field
+   message->pos = p - n;
+   //Total length of the message
+   message->length += length + n;
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Parse msgSecurityParameters field
+ * @param[in,out] message Pointer to the incoming SNMP message
+ * @return Error code
+ **/
+
+error_t snmpParseSecurityParameters(SnmpMessage *message)
+{
+#if (SNMP_V3_SUPPORT == ENABLED)
+   error_t error;
+   size_t length;
+   const uint8_t *p;
+   Asn1Tag tag;
+
+   //Read the msgSecurityParameters field
+   error = asn1ReadTag(message->pos, message->length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OCTET_STRING);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Advance pointer over the msgSecurityParameters field
+   message->pos += tag.totalLength;
+   //Remaining bytes to process
+   message->length -= tag.totalLength;
+
+   //Point to the very first field of the sequence
+   p = tag.value;
+   length = tag.length;
+
+   //User-based security model?
+   if(message->msgSecurityModel == SNMP_SECURITY_MODEL_USM)
+   {
+      //The USM security parameters are encapsulated within a sequence
+      error = asn1ReadTag(p, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+      //The tag does not match the criteria?
+      if(error)
+         return error;
+
+      //Point to the first field of the sequence
+      p = tag.value;
+      length = tag.length;
+
+      //Read the msgAuthoritativeEngineID field
+      error = asn1ReadTag(p, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OCTET_STRING);
+      //The tag does not match the criteria?
+      if(error)
+         return error;
+
+      //Save authoritative engine identifier
+      message->msgAuthEngineId = tag.value;
+      message->msgAuthEngineIdLen = tag.length;
+
+      //Point to the next field
+      p += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the msgAuthoritativeEngineBoots field
+      error = asn1ReadInt32(p, length, &tag,
+         &message->msgAuthEngineBoots);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //Point to the next field
+      p += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the msgAuthoritativeEngineTime field
+      error = asn1ReadInt32(p, length, &tag,
+         &message->msgAuthEngineTime);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //Point to the next field
+      p += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the msgUserName field
+      error = asn1ReadTag(p, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OCTET_STRING);
+      //The tag does not match the criteria?
+      if(error)
+         return error;
+
+      //Check the length of the user name
+      if(tag.length > 32)
+         return ERROR_WRONG_ENCODING;
+
+      //Save user name
+      message->msgUserName = (char_t *) tag.value;
+      message->msgUserNameLen = tag.length;
+
+      //Point to the next field
+      p += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the msgAuthenticationParameters field
+      error = asn1ReadTag(p, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OCTET_STRING);
+      //The tag does not match the criteria?
+      if(error)
+         return error;
+
+      //Save authentication parameters
+      message->msgAuthParameters = (uint8_t *) tag.value;
+      message->msgAuthParametersLen = tag.length;
+
+      //Point to the next field
+      p += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read the msgPrivacyParameters field
+      error = asn1ReadTag(p, length, &tag);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //Enforce encoding, class and type
+      error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OCTET_STRING);
+      //The tag does not match the criteria?
+      if(error)
+         return error;
+
+      //Save privacy parameters
+      message->msgPrivParameters = tag.value;
+      message->msgPrivParametersLen = tag.length;
+   }
+   else
+   {
+      //The security model is not supported
+      return ERROR_FAILURE;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Format msgSecurityParameters field
+ * @param[in,out] message Pointer to the outgoing SNMP message
+ * @return Error code
+ **/
+
+error_t snmpWriteSecurityParameters(SnmpMessage *message)
+{
+#if (SNMP_V3_SUPPORT == ENABLED)
+   error_t error;
+   size_t n;
+   size_t length;
+   uint8_t *p;
+   Asn1Tag tag;
+
+   //The msgSecurityParameters field is encoded in reverse order
+   p = message->pos;
+   //Length of the msgSecurityParameters field
+   length = 0;
+
+   //User-based security model?
+   if(message->msgSecurityModel == SNMP_SECURITY_MODEL_USM)
+   {
+      //Encode the msgPrivacyParameters field as an octet string
+      tag.constructed = FALSE;
+      tag.objClass = ASN1_CLASS_UNIVERSAL;
+      tag.objType = ASN1_TYPE_OCTET_STRING;
+      tag.length = message->msgPrivParametersLen;
+      tag.value = message->msgPrivParameters;
+
+      //Write the corresponding ASN.1 tag
+      error = asn1WriteTag(&tag, TRUE, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Move backward
+      p -= n;
+      //Update the length of the msgSecurityParameters field
+      length += n;
+
+      //Authentication required?
+      if(message->msgAuthParametersLen > 0)
+      {
+         //Make room for the message digest
+         p -= message->msgAuthParametersLen;
+         //Update the length of the msgSecurityParameters field
+         length += message->msgAuthParametersLen;
+
+         //Clear the message digest
+         memset(p, 0, message->msgAuthParametersLen);
+      }
+
+      //Save the location of the msgAuthenticationParameters field
+      message->msgAuthParameters = p;
+
+      //Encoded the msgAuthenticationParameters field as an octet string
+      tag.constructed = FALSE;
+      tag.objClass = ASN1_CLASS_UNIVERSAL;
+      tag.objType = ASN1_TYPE_OCTET_STRING;
+      tag.length = message->msgAuthParametersLen;
+      tag.value = NULL;
+
+      //Write the corresponding ASN.1 tag
+      error = asn1WriteTag(&tag, TRUE, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Move backward
+      p -= n;
+      //Update the length of the msgSecurityParameters field
+      length += n;
+
+      //Encode the msgUserName field as an octet string
+      tag.constructed = FALSE;
+      tag.objClass = ASN1_CLASS_UNIVERSAL;
+      tag.objType = ASN1_TYPE_OCTET_STRING;
+      tag.length = message->msgUserNameLen;
+      tag.value = (uint8_t *) message->msgUserName;
+
+      //Write the corresponding ASN.1 tag
+      error = asn1WriteTag(&tag, TRUE, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Move backward
+      p -= n;
+      //Update the length of the msgSecurityParameters field
+      length += n;
+
+      //Write the msgAuthoritativeEngineTime field
+      error = asn1WriteInt32(message->msgAuthEngineTime, TRUE, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Move backward
+      p -= n;
+      //Update the length of the msgSecurityParameters field
+      length += n;
+
+      //Write the msgAuthoritativeEngineBoots field
+      error = asn1WriteInt32(message->msgAuthEngineBoots, TRUE, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Move backward
+      p -= n;
+      //Update the length of the msgSecurityParameters field
+      length += n;
+
+      //The msgAuthoritativeEngineID field is an octet string
+      tag.constructed = FALSE;
+      tag.objClass = ASN1_CLASS_UNIVERSAL;
+      tag.objType = ASN1_TYPE_OCTET_STRING;
+      tag.length = message->msgAuthEngineIdLen;
+      tag.value = message->msgAuthEngineId;
+
+      //Write the corresponding ASN.1 tag
+      error = asn1WriteTag(&tag, TRUE, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Move backward
+      p -= n;
+      //Update the length of the msgSecurityParameters field
+      length += n;
+
+      //The USM security parameters are encapsulated within a sequence
+      tag.constructed = TRUE;
+      tag.objClass = ASN1_CLASS_UNIVERSAL;
+      tag.objType = ASN1_TYPE_SEQUENCE;
+      tag.length = length;
+      tag.value = NULL;
+
+      //Write the corresponding ASN.1 tag
+      error = asn1WriteTag(&tag, TRUE, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Move backward
+      p -= n;
+      //Update the length of the msgSecurityParameters field
+      length += n;
+   }
+   else
+   {
+      //The security model is not supported
+      return ERROR_FAILURE;
+   }
+
+   //The security parameters are encapsulated within an octet string
+   tag.constructed = FALSE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_OCTET_STRING;
+   tag.length = length;
+   tag.value = NULL;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, TRUE, p, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Point to the first byte of the msgSecurityParameters field
+   message->pos = p - n;
+   //Total length of the message
+   message->length += length + n;
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Parse scopedPDU field
+ * @param[in,out] message Pointer to the incoming SNMP message
+ * @return Error code
+ **/
+
+error_t snmpParseScopedPdu(SnmpMessage *message)
+{
+#if (SNMP_V3_SUPPORT == ENABLED)
+   error_t error;
+   size_t length;
+   const uint8_t *p;
+   Asn1Tag tag;
+
+   //Read the scopedPDU field
+   error = asn1ReadTag(message->pos, message->length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Point to the first field of the sequence
+   p = tag.value;
+   length = tag.length;
+
+   //Read contextEngineID field
+   error = asn1ReadTag(p, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OCTET_STRING);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Save context engine identifier
+   message->contextEngineId = tag.value;
+   message->contextEngineIdLen = tag.length;
+
+   //Point to the next field
+   p += tag.totalLength;
+   length -= tag.totalLength;
+
+   //Read contextName field
+   error = asn1ReadTag(p, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OCTET_STRING);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Save context name
+   message->contextName = tag.value;
+   message->contextNameLen = tag.length;
+
+   //Point to the first byte of the PDU
+   message->pos = (uint8_t *) p + tag.totalLength;
+   //Length of the PDU
+   message->length = length - tag.totalLength;
+
+   //Return status code
+   return error;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Format scopedPDU
+ * @param[in,out] message Pointer to the outgoing SNMP message
+ * @return Error code
+ **/
+
+error_t snmpWriteScopedPdu(SnmpMessage *message)
+{
+#if (SNMP_V3_SUPPORT == ENABLED)
+   error_t error;
+   size_t n;
+   size_t length;
+   uint8_t *p;
+   Asn1Tag tag;
+
+   //Point to the first byte of the PDU
+   p = message->pos;
+   //Retrieve the length of the PDU
+   length = message->length;
+
+   //The contextName is an octet string
+   tag.constructed = FALSE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_OCTET_STRING;
+   tag.length = message->contextNameLen;
+   tag.value = message->contextName;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, TRUE, p, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Move backward
+   p -= n;
+   //Update the length of the scopedPduData
+   length += n;
+
+   //The contextEngineID is an octet string
+   tag.constructed = FALSE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_OCTET_STRING;
+   tag.length = message->contextEngineIdLen;
+   tag.value = message->contextEngineId;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, TRUE, p, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Move backward
+   p -= n;
+   //Update the length of the scopedPduData
+   length += n;
+
+   //The scopedPduData is encapsulated within a sequence
+   tag.constructed = TRUE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_SEQUENCE;
+   tag.length = length;
+   tag.value = NULL;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, TRUE, p, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Point to the first byte of the scopedPDU
+   message->pos = p - n;
+   //Length of the scopedPDU
+   message->length = length + n;
+
+   //Successful processing
+   return NO_ERROR;
+#else
+   //Not implemented
+   return ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @brief Parse PDU header
+ * @param[in,out] message Pointer to the incoming SNMP message
+ * @return Error code
+ **/
+
+error_t snmpParsePduHeader(SnmpMessage *message)
+{
+   error_t error;
+   size_t length;
+   const uint8_t *p;
+   Asn1Tag tag;
+
+   //The PDU is encapsulated within a sequence
+   error = asn1ReadTag(message->pos, message->length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Check encoding
+   if(tag.constructed != TRUE)
+      return ERROR_WRONG_ENCODING;
+   //Enforce class
+   if(tag.objClass != ASN1_CLASS_CONTEXT_SPECIFIC)
+      return ERROR_INVALID_CLASS;
+
+   //Save PDU type
+   message->pduType = (SnmpPduType) tag.objType;
+
+   //Point to the first field
+   p = tag.value;
+   //Remaining bytes to process
+   length = tag.length;
+
+   //Read request-id field
+   error = asn1ReadInt32(p, length, &tag, &message->requestId);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Point to the next field
+   p += tag.totalLength;
+   length -= tag.totalLength;
+
+#if (SNMP_V2C_SUPPORT == ENABLED || SNMP_V3_SUPPORT == ENABLED)
+   //GetBulkRequest-PDU?
+   if(message->pduType == SNMP_PDU_GET_BULK_REQUEST)
+   {
+      //Read non-repeaters field
+      error = asn1ReadInt32(p, length, &tag, &message->nonRepeaters);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //If the value in the non-repeaters field is less than zero, then the
+      //value of the field is set to zero
+      if(message->nonRepeaters < 0)
+         message->nonRepeaters = 0;
+
+      //Point to the next field
+      p += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read max-repetitions field
+      error = asn1ReadInt32(p, length, &tag, &message->maxRepetitions);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //If the value in the max-repetitions field is less than zero, then the
+      //value of the field is set to zero
+      if(message->maxRepetitions < 0)
+         message->maxRepetitions = 0;
+   }
+   else
+#endif
+   {
+      //Read error-status field
+      error = asn1ReadInt32(p, length, &tag, &message->errorStatus);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+
+      //Point to the next field
+      p += tag.totalLength;
+      length -= tag.totalLength;
+
+      //Read error-index field
+      error = asn1ReadInt32(p, length, &tag, &message->errorIndex);
+      //Failed to decode ASN.1 tag?
+      if(error)
+         return error;
+   }
+
+   //Point to the next field
+   p += tag.totalLength;
+   length -= tag.totalLength;
+
+   //The variable bindings are encapsulated within a sequence
+   error = asn1ReadTag(p, length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, TRUE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_SEQUENCE);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Save the location of the variable binding list
+   message->varBindList = (uint8_t *) tag.value;
+   message->varBindListLen = tag.length;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format PDU header
+ * @param[in,out] message Pointer to the outgoing SNMP message
+ * @return Error code
+ **/
+
+error_t snmpWritePduHeader(SnmpMessage *message)
+{
+   error_t error;
+   size_t n;
+   size_t length;
+   uint8_t *p;
+   Asn1Tag tag;
+
+   //The PDU header will be encoded in reverse order...
+   p = message->varBindList;
+   //Length of the PDU
+   length = message->varBindListLen;
+
+   //The variable bindings are encapsulated within a sequence
+   tag.constructed = TRUE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_SEQUENCE;
+   tag.length = length;
+   tag.value = NULL;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, TRUE, p, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Move backward
+   p -= n;
+   //Update the length of the PDU
+   length += n;
+
+   //GetResponse-PDU, SNMPv2-Trap-PDU or Report-PDU?
+   if(message->pduType == SNMP_PDU_GET_RESPONSE ||
+      message->pduType == SNMP_PDU_TRAP_V2 ||
+      message->pduType == SNMP_PDU_REPORT)
+   {
+      //Write error index
+      error = asn1WriteInt32(message->errorIndex, TRUE, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Move backward
+      p -= n;
+      //Update the length of the PDU
+      length += n;
+
+      //Write error status
+      error = asn1WriteInt32(message->errorStatus, TRUE, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Move backward
+      p -= n;
+      //Update the length of the PDU
+      length += n;
+
+      //Write request identifier
+      error = asn1WriteInt32(message->requestId, TRUE, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Move backward
+      p -= n;
+      //Update the length of the PDU
+      length += n;
+   }
+#if (SNMP_V1_SUPPORT == ENABLED)
+   //Trap-PDU?
+   else if(message->pduType == SNMP_PDU_TRAP)
+   {
+      //Encode the object value using ASN.1 rules
+      error = snmpEncodeUnsignedInt32(message->timestamp, message->buffer, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //The time stamp is encoded in ASN.1 format
+      tag.constructed = FALSE;
+      tag.objClass = ASN1_CLASS_APPLICATION;
+      tag.objType = MIB_TYPE_TIME_TICKS;
+      tag.length = n;
+      tag.value = message->buffer;
+
+      //Write the corresponding ASN.1 tag
+      error = asn1WriteTag(&tag, TRUE, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Move backward
+      p -= n;
+      //Update the length of the PDU
+      length += n;
+
+      //Write specific trap code
+      error = asn1WriteInt32(message->specificTrapCode, TRUE, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Move backward
+      p -= n;
+      //Update the length of the PDU
+      length += n;
+
+      //Write generic trap type
+      error = asn1WriteInt32(message->genericTrapType, TRUE, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Move backward
+      p -= n;
+      //Update the length of the PDU
+      length += n;
+
+      //The agent address is encoded in ASN.1 format
+      tag.constructed = FALSE;
+      tag.objClass = ASN1_CLASS_APPLICATION;
+      tag.objType = MIB_TYPE_IP_ADDRESS;
+      tag.length = sizeof(Ipv4Addr);
+      tag.value = (uint8_t *) &message->agentAddr;
+
+      //Write the corresponding ASN.1 tag
+      error = asn1WriteTag(&tag, TRUE, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Move backward
+      p -= n;
+      //Update the length of the PDU
+      length += n;
+
+      //The enterprise OID is encoded in ASN.1 format
+      tag.constructed = FALSE;
+      tag.objClass = ASN1_CLASS_UNIVERSAL;
+      tag.objType = ASN1_TYPE_OBJECT_IDENTIFIER;
+      tag.length = message->enterpriseOidLen;
+      tag.value = message->enterpriseOid;
+
+      //Write the corresponding ASN.1 tag
+      error = asn1WriteTag(&tag, TRUE, p, &n);
+      //Any error to report?
+      if(error)
+         return error;
+
+      //Move backward
+      p -= n;
+      //Update the length of the PDU
+      length += n;
+   }
+#endif
+   //Unknown PDU type?
+   else
+   {
+      //Report an error
+      return ERROR_FAILURE;
+   }
+
+   //The PDU is encapsulated within a sequence
+   tag.constructed = TRUE;
+   tag.objClass = ASN1_CLASS_CONTEXT_SPECIFIC;
+   tag.objType = message->pduType;
+   tag.length = length;
+   tag.value = NULL;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, TRUE, p, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Point to the first byte of the PDU
+   message->pos = p - n;
+   //Total length of the PDU
+   message->length = length + n;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Encode a 32-bit signed integer
+ * @param[in] value Integer value
+ * @param[out] dest Buffer where to encode the integer
+ * @param[out] length Total number of bytes that have been written
+ * @return Error code
+ **/
+
+error_t snmpEncodeInt32(int32_t value, uint8_t *dest, size_t *length)
+{
+   size_t i;
+   size_t j;
+   uint8_t *src;
+
+   //Check parameters
+   if(dest == NULL || length == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //The integer is encoded MSB first
+   value = htobe32(value);
+   //Cast the integer to byte array
+   src = (uint8_t *) &value;
+
+   //An integer value is always encoded in the smallest possible number of octets
+   for(i = 0; i < 3; i++)
+   {
+      //The upper 9 bits shall not have the same value (all 0 or all 1)
+      if((src[i] != 0x00 || (src[i + 1] & 0x80) != 0x00) &&
+         (src[i] != 0xFF || (src[i + 1] & 0x80) != 0x80))
+      {
+         break;
+      }
+   }
+
+   //Point to the beginning of the output buffer
+   j = 0;
+
+   //Copy integer value
+   while(i < 4)
+      dest[j++] = src[i++];
+
+   //Total number of bytes that have been written
+   *length = j;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Encode a 32-bit unsigned integer
+ * @param[in] value Integer value
+ * @param[out] dest Buffer where to encode the integer
+ * @param[out] length Total number of bytes that have been written
+ * @return Error code
+ **/
+
+error_t snmpEncodeUnsignedInt32(uint32_t value, uint8_t *dest, size_t *length)
+{
+   size_t i;
+   size_t j;
+   uint8_t *src;
+
+   //Check parameters
+   if(dest == NULL || length == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //The integer is encoded MSB first
+   value = htobe32(value);
+   //Cast the integer to byte array
+   src = (uint8_t *) &value;
+
+   //An integer value is always encoded in the smallest possible number of octets
+   for(i = 0; i < 3; i++)
+   {
+      //Check the upper 8 bits
+      if(src[i] != 0x00)
+         break;
+   }
+
+   //Point to the beginning of the output buffer
+   j = 0;
+
+   //Check the most significant bit
+   if(src[i] & 0x80)
+      dest[j++] = 0;
+
+   //Copy integer value
+   while(i < 4)
+      dest[j++] = src[i++];
+
+   //Total number of bytes that have been written
+   *length = j;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Encode a 64-bit unsigned integer
+ * @param[in] value Integer value
+ * @param[out] dest Buffer where to encode the integer
+ * @param[out] length Total number of bytes that have been written
+ * @return Error code
+ **/
+
+error_t snmpEncodeUnsignedInt64(uint64_t value, uint8_t *dest, size_t *length)
+{
+   size_t i;
+   size_t j;
+   uint8_t *src;
+
+   //Check parameters
+   if(dest == NULL || length == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //The integer is encoded MSB first
+   value = htobe64(value);
+   //Cast the integer to byte array
+   src = (uint8_t *) &value;
+
+   //An integer value is always encoded in the smallest possible number of octets
+   for(i = 0; i < 7; i++)
+   {
+      //Check the upper 8 bits
+      if(src[i] != 0x00)
+         break;
+   }
+
+   //Point to the beginning of the output buffer
+   j = 0;
+
+   //Check the most significant bit
+   if(src[i] & 0x80)
+      dest[j++] = 0;
+
+   //Copy integer value
+   while(i < 8)
+      dest[j++] = src[i++];
+
+   //Total number of bytes that have been written
+   *length = j;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Decode a 32-bit signed integer
+ * @param[in] src Buffer that contains the encoded value
+ * @param[in] length Number of bytes to be processed
+ * @param[out] value Resulting integer value
+ * @return Error code
+ **/
+
+error_t snmpDecodeInt32(const uint8_t *src, size_t length, int32_t *value)
+{
+   size_t i;
+
+   //Check parameters
+   if(src == NULL || value == NULL)
+      return ERROR_INVALID_PARAMETER;
+   if(length < 1)
+      return ERROR_INVALID_PARAMETER;
+
+   //The contents octets shall be a two's complement binary
+   //number equal to the integer value
+   *value = (src[0] & 0x80) ? -1 : 0;
+
+   //Process contents octets
+   for(i = 0; i < length; i++)
+   {
+      //Rotate left operation
+      *value <<= 8;
+      //Reconstruct integer value
+      *value |= src[i];
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Decode a 32-bit unsigned integer
+ * @param[in] src Buffer that contains the encoded value
+ * @param[in] length Number of bytes to be processed
+ * @param[out] value Resulting integer value
+ * @return Error code
+ **/
+
+error_t snmpDecodeUnsignedInt32(const uint8_t *src, size_t length, uint32_t *value)
+{
+   size_t i;
+
+   //Check parameters
+   if(src == NULL || value == NULL)
+      return ERROR_INVALID_PARAMETER;
+   if(length < 1)
+      return ERROR_INVALID_PARAMETER;
+
+   //Only accept non-negative numbers
+   if(src[0] & 0x80)
+      return ERROR_FAILURE;
+
+   //Initialize integer value
+   *value = 0;
+
+   //Process contents octets
+   for(i = 0; i < length; i++)
+   {
+      //Rotate left operation
+      *value <<= 8;
+      //Reconstruct integer value
+      *value |= src[i];
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Decode a 64-bit unsigned integer
+ * @param[in] src Buffer that contains the encoded value
+ * @param[in] length Number of bytes to be processed
+ * @param[out] value Resulting integer value
+ * @return Error code
+ **/
+
+error_t snmpDecodeUnsignedInt64(const uint8_t *src, size_t length, uint64_t *value)
+{
+   size_t i;
+
+   //Check parameters
+   if(src == NULL || value == NULL)
+      return ERROR_INVALID_PARAMETER;
+   if(length < 1)
+      return ERROR_INVALID_PARAMETER;
+
+   //Only accept non-negative numbers
+   if(src[0] & 0x80)
+      return ERROR_FAILURE;
+
+   //Initialize integer value
+   *value = 0;
+
+   //Process contents octets
+   for(i = 0; i < length; i++)
+   {
+      //Rotate left operation
+      *value <<= 8;
+      //Reconstruct integer value
+      *value |= src[i];
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/snmp/snmp_common.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,308 @@
+/**
+ * @file snmp_common.h
+ * @brief Functions common to SNMP agent and SNMP manager
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SNMP_COMMON_H
+#define _SNMP_COMMON_H
+
+//Dependencies
+#include "core/net.h"
+
+//SNMPv1 support
+#ifndef SNMP_V1_SUPPORT
+   #define SNMP_V1_SUPPORT ENABLED
+#elif (SNMP_V1_SUPPORT != ENABLED && SNMP_V1_SUPPORT != DISABLED)
+   #error SNMP_V1_SUPPORT parameter is not valid
+#endif
+
+//SNMPv2c support
+#ifndef SNMP_V2C_SUPPORT
+   #define SNMP_V2C_SUPPORT ENABLED
+#elif (SNMP_V2C_SUPPORT != ENABLED && SNMP_V2C_SUPPORT != DISABLED)
+   #error SNMP_V2C_SUPPORT parameter is not valid
+#endif
+
+//SNMPv3 support
+#ifndef SNMP_V3_SUPPORT
+   #define SNMP_V3_SUPPORT DISABLED
+#elif (SNMP_V3_SUPPORT != ENABLED && SNMP_V3_SUPPORT != DISABLED)
+   #error SNMP_V3_SUPPORT parameter is not valid
+#endif
+
+//Maximum size of SNMP messages
+#ifndef SNMP_MAX_MSG_SIZE
+   #define SNMP_MAX_MSG_SIZE 484
+#elif (SNMP_MAX_MSG_SIZE < 484 || SNMP_MAX_MSG_SIZE > 65535)
+   #error SNMP_MAX_MSG_SIZE parameter is not valid
+#endif
+
+//Maximum size for context engine identifier
+#ifndef SNMP_MAX_CONTEXT_ENGINE_SIZE
+   #define SNMP_MAX_CONTEXT_ENGINE_SIZE 32
+#elif (SNMP_MAX_CONTEXT_ENGINE_SIZE < 8)
+   #error SNMP_MAX_CONTEXT_ENGINE_SIZE parameter is not valid
+#endif
+
+//Maximum length for context name
+#ifndef SNMP_MAX_CONTEXT_NAME_LEN
+   #define SNMP_MAX_CONTEXT_NAME_LEN 16
+#elif (SNMP_MAX_CONTEXT_NAME_LEN < 8)
+   #error SNMP_MAX_CONTEXT_NAME_LEN parameter is not valid
+#endif
+
+//Maximum length for user names and community names
+#ifndef SNMP_MAX_USER_NAME_LEN
+   #define SNMP_MAX_USER_NAME_LEN 16
+#elif (SNMP_MAX_USER_NAME_LEN < 8)
+   #error SNMP_MAX_USER_NAME_LEN parameter is not valid
+#endif
+
+//Maximum size for object identifiers
+#ifndef SNMP_MAX_OID_SIZE
+   #define SNMP_MAX_OID_SIZE 16
+#elif (SNMP_MAX_OID_SIZE < 1)
+   #error SNMP_MAX_OID_SIZE parameter is not valid
+#endif
+
+//SNMP port number
+#define SNMP_PORT 161
+//SNMP trap port number
+#define SNMP_TRAP_PORT 162
+
+//SNMPv1 message header overhead
+#define SNMP_V1_MSG_HEADER_OVERHEAD 48
+//SNMPv2c message header overhead
+#define SNMP_V2C_MSG_HEADER_OVERHEAD 37
+//SNMPv3 message header overhead
+#define SNMP_V3_MSG_HEADER_OVERHEAD 105
+
+
+/**
+ * @brief SNMP version identifiers
+ **/
+
+typedef enum
+{
+   SNMP_VERSION_1  = 0,
+   SNMP_VERSION_2C = 1,
+   SNMP_VERSION_3  = 3
+} SnmpVersion;
+
+
+/**
+ * @brief SNMP PDU types
+ **/
+
+typedef enum
+{
+   SNMP_PDU_GET_REQUEST      = 0,
+   SNMP_PDU_GET_NEXT_REQUEST = 1,
+   SNMP_PDU_GET_RESPONSE     = 2,
+   SNMP_PDU_SET_REQUEST      = 3,
+   SNMP_PDU_TRAP             = 4,
+   SNMP_PDU_GET_BULK_REQUEST = 5,
+   SNMP_PDU_INFORM_REQUEST   = 6,
+   SNMP_PDU_TRAP_V2          = 7,
+   SNMP_PDU_REPORT           = 8
+} SnmpPduType;
+
+
+/**
+ * @brief SNMP generic trap types
+ **/
+
+typedef enum
+{
+   SNMP_TRAP_COLD_START          = 0,
+   SNMP_TRAP_WARM_START          = 1,
+   SNMP_TRAP_LINK_DOWN           = 2,
+   SNMP_TRAP_LINK_UP             = 3,
+   SNMP_TRAP_AUTH_FAILURE        = 4,
+   SNMP_TRAP_EGP_NEIGHBOR_LOSS   = 5,
+   SNMP_TRAP_ENTERPRISE_SPECIFIC = 6
+} SnmpGenericTrapType;
+
+
+/**
+ * @brief SNMP error status
+ **/
+
+typedef enum
+{
+   SNMP_ERROR_NONE                 = 0,
+   SNMP_ERROR_TOO_BIG              = 1,
+   SNMP_ERROR_NO_SUCH_NAME         = 2,
+   SNMP_ERROR_BAD_VALUE            = 3,
+   SNMP_ERROR_READ_ONLY            = 4,
+   SNMP_ERROR_GENERIC              = 5,
+   SNMP_ERROR_NO_ACCESS            = 6,
+   SNMP_ERROR_WRONG_TYPE           = 7,
+   SNMP_ERROR_WRONG_LENGTH         = 8,
+   SNMP_ERROR_WRONG_ENCODING       = 9,
+   SNMP_ERROR_WRONG_VALUE          = 10,
+   SNMP_ERROR_NO_CREATION          = 11,
+   SNMP_ERROR_INCONSISTENT_VALUE   = 12,
+   SNMP_ERROR_RESOURCE_UNAVAILABLE = 13,
+   SNMP_ERROR_COMMIT_FAILED        = 14,
+   SNMP_ERROR_UNDO_FAILED          = 15,
+   SNMP_ERROR_AUTHORIZATION        = 16,
+   SNMP_ERROR_NOT_WRITABLE         = 17,
+   SNMP_ERROR_INCONSISTENT_NAME    = 18
+} SnmpErrorStatus;
+
+
+/**
+ * @brief SNMP exceptions
+ **/
+
+typedef enum
+{
+   SNMP_EXCEPTION_NO_SUCH_OBJECT   = 0,
+   SNMP_EXCEPTION_NO_SUCH_INSTANCE = 1,
+   SNMP_EXCEPTION_END_OF_MIB_VIEW  = 2
+} SnmpException;
+
+
+/**
+ * @brief SNMP engine ID format
+ **/
+
+typedef enum
+{
+   SNMP_ENGINE_ID_FORMAT_IPV4   = 1,
+   SNMP_ENGINE_ID_FORMAT_IPV6   = 2,
+   SNMP_ENGINE_ID_FORMAT_MAC    = 3,
+   SNMP_ENGINE_ID_FORMAT_TEXT   = 4,
+   SNMP_ENGINE_ID_FORMAT_OCTETS = 5,
+} SnmpEngineIdFormat;
+
+
+/**
+ * @brief SNMP message
+ **/
+
+typedef struct
+{
+   uint8_t buffer[SNMP_MAX_MSG_SIZE]; ///<Buffer that holds the message
+   size_t bufferLen;                  ///<Original length of the message
+   uint8_t *pos;                      ///<Current position
+   size_t length;                     ///<Length of the message
+   int32_t version;                   ///<SNMP version identifier
+#if (SNMP_V1_SUPPORT == ENABLED || SNMP_V2C_SUPPORT == ENABLED)
+   const char_t *community;           ///<Community name
+   size_t communityLen;               ///<Length of the community name
+#endif
+#if (SNMP_V3_SUPPORT == ENABLED)
+   int32_t msgId;                     ///<Message identifier
+   int32_t msgMaxSize;                ///<Maximum message size supported by the sender
+   uint8_t msgFlags;                  ///<Bit fields which control processing of the message
+   int32_t msgSecurityModel;          ///<Security model used by the sender
+   const uint8_t *msgAuthEngineId;    ///<Authoritative engine identifier
+   size_t msgAuthEngineIdLen;         ///<Length of the authoritative engine identifier
+   int32_t msgAuthEngineBoots;        ///<Number of times the SNMP engine has rebooted
+   int32_t msgAuthEngineTime;         ///<Number of seconds since last reboot
+   const char_t *msgUserName;         ///<User name
+   size_t msgUserNameLen;             ///<Length of the user name
+   uint8_t *msgAuthParameters;        ///<Authentication parameters
+   size_t msgAuthParametersLen;       ///<Length of the authentication parameters
+   const uint8_t *msgPrivParameters;  ///<Privacy parameters
+   size_t msgPrivParametersLen;       ///<Length of the privacy parameters
+   const uint8_t *contextEngineId;    ///<Context engine identifier
+   size_t contextEngineIdLen;         ///<Length of the context engine identifier
+   const uint8_t *contextName;        ///<Context name
+   size_t contextNameLen;             ///<Length of the context name
+#endif
+   SnmpPduType pduType;               ///<PDU type
+   int32_t requestId;                 ///<Request identifier
+   int32_t errorStatus;               ///<Error status
+   int32_t errorIndex;                ///<Error index
+#if (SNMP_V1_SUPPORT == ENABLED)
+   const uint8_t *enterpriseOid;      ///<Type of object generating trap
+   size_t enterpriseOidLen;           ///<Length of the enterprise OID
+   Ipv4Addr agentAddr;                ///<Address of object generating trap
+   int32_t genericTrapType;           ///<Generic trap type
+   int32_t specificTrapCode;          ///<Specific trap code
+   uint32_t timestamp;                ///<Timestamp
+#endif
+#if (SNMP_V2C_SUPPORT == ENABLED || SNMP_V3_SUPPORT == ENABLED)
+   int32_t nonRepeaters;              ///<GetBulkRequest-PDU specific parameter
+   int32_t maxRepetitions;            ///<GetBulkRequest-PDU specific parameter
+#endif
+   uint8_t *varBindList;              ///<List of variable bindings
+   size_t varBindListLen;             ///<Length of the list in bytes
+   size_t varBindListMaxLen;          ///<Maximum length of the list in bytes
+   size_t oidLen;                     ///<Length of the object identifier
+} SnmpMessage;
+
+
+/**
+ * @brief Variable binding
+ **/
+
+typedef struct
+{
+   const uint8_t *oid;
+   size_t oidLen;
+   uint_t objClass;
+   uint_t objType;
+   const uint8_t *value;
+   size_t valueLen;
+} SnmpVarBind;
+
+
+//SNMP related functions
+void snmpInitMessage(SnmpMessage *message);
+error_t snmpComputeMessageOverhead(SnmpMessage *message);
+
+error_t snmpParseMessageHeader(SnmpMessage *message);
+error_t snmpWriteMessageHeader(SnmpMessage *message);
+
+error_t snmpParseCommunity(SnmpMessage *message);
+error_t snmpWriteCommunity(SnmpMessage *message);
+
+error_t snmpParseGlobalData(SnmpMessage *message);
+error_t snmpWriteGlobalData(SnmpMessage *message);
+
+error_t snmpParseSecurityParameters(SnmpMessage *message);
+error_t snmpWriteSecurityParameters(SnmpMessage *message);
+
+error_t snmpParseScopedPdu(SnmpMessage *message);
+error_t snmpWriteScopedPdu(SnmpMessage *message);
+
+error_t snmpParsePduHeader(SnmpMessage *message);
+error_t snmpWritePduHeader(SnmpMessage *message);
+
+error_t snmpEncodeInt32(int32_t value, uint8_t *dest, size_t *length);
+error_t snmpEncodeUnsignedInt32(uint32_t value, uint8_t *dest, size_t *length);
+error_t snmpEncodeUnsignedInt64(uint64_t value, uint8_t *dest, size_t *length);
+
+error_t snmpDecodeInt32(const uint8_t *src, size_t length, int32_t *value);
+error_t snmpDecodeUnsignedInt32(const uint8_t *src, size_t length, uint32_t *value);
+error_t snmpDecodeUnsignedInt64(const uint8_t *src, size_t length, uint64_t *value);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/snmp/snmp_usm.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,707 @@
+/**
+ * @file snmp_usm.c
+ * @brief User-based Security Model (USM) for SNMPv3
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * This module implements the User-based Security Model (USM) for Simple
+ * Network Management Protocol (SNMP) version 3. Refer to the following
+ * RFCs for complete details:
+ * - RFC 3414: User-based Security Model (USM) for SNMPv3
+ * - RFC 3826: AES Cipher Algorithm in the SNMP User-based Security Model
+ * - RFC 7860: HMAC-SHA-2 Authentication Protocols in the User-based Security Model
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL SNMP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "snmp/snmp_common.h"
+#include "snmp/snmp_usm.h"
+#include "crypto.h"
+#include "asn1.h"
+#include "hmac.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (SNMP_V3_SUPPORT == ENABLED)
+
+//usmStatsUnsupportedSecLevels.0 object (1.3.6.1.6.3.15.1.1.1.0)
+const uint8_t usmStatsUnsupportedSecLevelsObject[10] = {43, 6, 1, 6, 3, 15, 1, 1, 1, 0};
+//usmStatsNotInTimeWindows.0 object (1.3.6.1.6.3.15.1.1.2.0)
+const uint8_t usmStatsNotInTimeWindowsObject[10] = {43, 6, 1, 6, 3, 15, 1, 1, 2, 0};
+//usmStatsUnknownUserNames.0 object (1.3.6.1.6.3.15.1.1.3.0)
+const uint8_t usmStatsUnknownUserNamesObject[10] = {43, 6, 1, 6, 3, 15, 1, 1, 3, 0};
+//usmStatsUnknownEngineIDs.0 object (1.3.6.1.6.3.15.1.1.4.0)
+const uint8_t usmStatsUnknownEngineIdsObject[10] = {43, 6, 1, 6, 3, 15, 1, 1, 4, 0};
+//usmStatsWrongDigests.0 object (1.3.6.1.6.3.15.1.1.5.0)
+const uint8_t usmStatsWrongDigestsObject[10] = {43, 6, 1, 6, 3, 15, 1, 1, 5, 0};
+//usmStatsDecryptionErrors.0 object (1.3.6.1.6.3.15.1.1.6.0)
+const uint8_t usmStatsDecryptionErrorsObject[10] = {43, 6, 1, 6, 3, 15, 1, 1, 6, 0};
+
+
+/**
+ * @brief Password to key algorithm
+ * @param[in] authProtocol Authentication protocol (MD5 or SHA-1)
+ * @param[in] password NULL-terminated string that contains the password
+ * @param[in] engineId Pointer to the engine ID
+ * @param[in] engineIdLen Length of the engine ID
+ * @param[out] key Pointer to the resulting key
+ * @return Error code
+ **/
+
+error_t snmpGenerateKey(SnmpAuthProtocol authProtocol, const char_t *password,
+   const uint8_t *engineId, size_t engineIdLen, SnmpKey *key)
+{
+   size_t i;
+   size_t n;
+   size_t passwordLen;
+   const HashAlgo *hash;
+   uint8_t context[MAX_HASH_CONTEXT_SIZE];
+
+   //Clear SNMP key
+   memset(key, 0, sizeof(SnmpKey));
+
+#if (SNMP_MD5_SUPPORT == ENABLED)
+   //HMAC-MD5-96 authentication protocol?
+   if(authProtocol == SNMP_AUTH_PROTOCOL_MD5)
+   {
+      //Use MD5 to generate the key
+      hash = MD5_HASH_ALGO;
+   }
+   else
+#endif
+#if (SNMP_SHA1_SUPPORT == ENABLED)
+   //HMAC-SHA-1-96 authentication protocol?
+   if(authProtocol == SNMP_AUTH_PROTOCOL_SHA1)
+   {
+      //Use SHA-1 to generate the key
+      hash = SHA1_HASH_ALGO;
+   }
+   else
+#endif
+#if (SNMP_SHA224_SUPPORT == ENABLED)
+   //HMAC-SHA-224-128 authentication protocol?
+   if(authProtocol == SNMP_AUTH_PROTOCOL_SHA224)
+   {
+      //Use SHA-224 to generate the key
+      hash = SHA224_HASH_ALGO;
+   }
+   else
+#endif
+#if (SNMP_SHA256_SUPPORT == ENABLED)
+   //HMAC-SHA-256-192 authentication protocol?
+   if(authProtocol == SNMP_AUTH_PROTOCOL_SHA256)
+   {
+      //Use SHA-256 to generate the key
+      hash = SHA256_HASH_ALGO;
+   }
+   else
+#endif
+#if (SNMP_SHA384_SUPPORT == ENABLED)
+   //HMAC-SHA-384-256 authentication protocol?
+   if(authProtocol == SNMP_AUTH_PROTOCOL_SHA384)
+   {
+      //Use SHA-384 to generate the key
+      hash = SHA384_HASH_ALGO;
+   }
+   else
+#endif
+#if (SNMP_SHA512_SUPPORT == ENABLED)
+   //HMAC-SHA-512-384 authentication protocol?
+   if(authProtocol == SNMP_AUTH_PROTOCOL_SHA512)
+   {
+      //Use SHA-512 to generate the key
+      hash = SHA512_HASH_ALGO;
+   }
+   else
+#endif
+   {
+      //Invalid authentication protocol
+      return ERROR_INVALID_PARAMETER;
+   }
+
+   //Retrieve the length of the password
+   passwordLen = strlen(password);
+
+   //SNMP implementations must ensure that passwords are at
+   //least 8 characters in length (see RFC 3414 11.2)
+   if(passwordLen < 8)
+      return ERROR_INVALID_LENGTH;
+
+   //Initialize hash context
+   hash->init(context);
+
+   //Loop until we have done 1 megabyte
+   for(i = 0; i < 1048576; i += n)
+   {
+      n = MIN(passwordLen, 1048576 - i);
+      hash->update(context, password, n);
+   }
+
+   //Finalize hash computation
+   hash->final(context, key->b);
+
+   //Key localization
+   hash->init(context);
+   hash->update(context, key, hash->digestSize);
+   hash->update(context, engineId, engineIdLen);
+   hash->update(context, key, hash->digestSize);
+   hash->final(context, key->b);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Check security parameters
+ * @param[in] user Security profile of the user
+ * @param[in,out] message Pointer to the incoming SNMP message
+ * @param[in] engineId Pointer to the authoritative engine ID
+ * @param[in] engineIdLen Length of the authoritative engine ID
+ * @return Error code
+ **/
+
+error_t snmpCheckSecurityParameters(const SnmpUserInfo *user,
+   SnmpMessage *message, const uint8_t *engineId, size_t engineIdLen)
+{
+   //Check the length of the authoritative engine ID
+   if(message->msgAuthEngineIdLen != engineIdLen)
+      return ERROR_UNKNOWN_ENGINE_ID;
+
+   //If the value of the msgAuthoritativeEngineID field is unknown, then an
+   //error indication (unknownEngineID) is returned to the calling module
+   if(memcmp(message->msgAuthEngineId, engineId, engineIdLen))
+      return ERROR_UNKNOWN_ENGINE_ID;
+
+   //If no information is available for the user, then an error indication
+   //(unknownSecurityName) is returned to the calling module
+   if(user == NULL)
+      return ERROR_UNKNOWN_USER_NAME;
+
+   //Check whether the securityLevel specifies that the message should
+   //be authenticated
+   if(user->authProtocol != SNMP_AUTH_PROTOCOL_NONE)
+   {
+      //Make sure the authFlag is set
+      if(!(message->msgFlags & SNMP_MSG_FLAG_AUTH))
+         return ERROR_UNSUPPORTED_SECURITY_LEVEL;
+   }
+
+   //Check whether the securityLevel specifies that the message should
+   //be encrypted
+   if(user->privProtocol != SNMP_PRIV_PROTOCOL_NONE)
+   {
+      //Make sure the privFlag is set
+      if(!(message->msgFlags & SNMP_MSG_FLAG_PRIV))
+         return ERROR_UNSUPPORTED_SECURITY_LEVEL;
+   }
+
+   //Security parameters are valid
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Authenticate outgoing SNMP message
+ * @param[in] user Security profile of the user
+ * @param[in,out] message Pointer to the outgoing SNMP message
+ * @return Error code
+ **/
+
+error_t snmpAuthOutgoingMessage(const SnmpUserInfo *user, SnmpMessage *message)
+{
+   const HashAlgo *hash;
+   size_t hmacDigestSize;
+   HmacContext hmacContext;
+
+#if (SNMP_MD5_SUPPORT == ENABLED)
+   //HMAC-MD5-96 authentication protocol?
+   if(user->authProtocol == SNMP_AUTH_PROTOCOL_MD5)
+   {
+      //Use MD5 hash algorithm
+      hash = MD5_HASH_ALGO;
+      //Length of the message digest
+      hmacDigestSize = 12;
+   }
+   else
+#endif
+#if (SNMP_SHA1_SUPPORT == ENABLED)
+   //HMAC-SHA-1-96 authentication protocol?
+   if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA1)
+   {
+      //Use SHA-1 hash algorithm
+      hash = SHA1_HASH_ALGO;
+      //Length of the message digest
+      hmacDigestSize = 12;
+   }
+   else
+#endif
+#if (SNMP_SHA224_SUPPORT == ENABLED)
+   //HMAC-SHA-224-128 authentication protocol?
+   if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA224)
+   {
+      //Use SHA-224 hash algorithm
+      hash = SHA224_HASH_ALGO;
+      //Length of the message digest
+      hmacDigestSize = 16;
+   }
+   else
+#endif
+#if (SNMP_SHA256_SUPPORT == ENABLED)
+   //HMAC-SHA-256-192 authentication protocol?
+   if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA256)
+   {
+      //Use SHA-256 hash algorithm
+      hash = SHA256_HASH_ALGO;
+      //Length of the message digest
+      hmacDigestSize = 24;
+   }
+   else
+#endif
+#if (SNMP_SHA384_SUPPORT == ENABLED)
+   //HMAC-SHA-384-256 authentication protocol?
+   if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA384)
+   {
+      //Use SHA-384 hash algorithm
+      hash = SHA384_HASH_ALGO;
+      //Length of the message digest
+      hmacDigestSize = 32;
+   }
+   else
+#endif
+#if (SNMP_SHA512_SUPPORT == ENABLED)
+   //HMAC-SHA-512-384 authentication protocol?
+   if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA512)
+   {
+      //Use SHA-512 hash algorithm
+      hash = SHA512_HASH_ALGO;
+      //Length of the message digest
+      hmacDigestSize = 48;
+   }
+   else
+#endif
+   //Invalid authentication protocol?
+   {
+      //Report en error
+      return ERROR_FAILURE;
+   }
+
+   //Check the length of the msgAuthenticationParameters field
+   if(message->msgAuthParametersLen != hmacDigestSize)
+      return ERROR_FAILURE;
+
+   //The MAC is calculated over the whole message
+   hmacInit(&hmacContext, hash, user->authKey.b, hash->digestSize);
+   hmacUpdate(&hmacContext, message->pos, message->length);
+   hmacFinal(&hmacContext, NULL);
+
+   //Replace the msgAuthenticationParameters field with the calculated MAC
+   memcpy(message->msgAuthParameters, hmacContext.digest, hmacDigestSize);
+
+   //Successful message authentication
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Authenticate incoming SNMP message
+ * @param[in] user Security profile of the user
+ * @param[in] message Pointer to the incoming SNMP message
+ * @return Error code
+ **/
+
+error_t snmpAuthIncomingMessage(const SnmpUserInfo *user, SnmpMessage *message)
+{
+   const HashAlgo *hash;
+   size_t hmacDigestSize;
+   uint8_t hmacDigest[SNMP_MAX_HMAC_DIGEST_SIZE];
+   HmacContext hmacContext;
+
+#if (SNMP_MD5_SUPPORT == ENABLED)
+   //HMAC-MD5-96 authentication protocol?
+   if(user->authProtocol == SNMP_AUTH_PROTOCOL_MD5)
+   {
+      //Use MD5 hash algorithm
+      hash = MD5_HASH_ALGO;
+      //Length of the message digest
+      hmacDigestSize = 12;
+   }
+   else
+#endif
+#if (SNMP_SHA1_SUPPORT == ENABLED)
+   //HMAC-SHA-1-96 authentication protocol?
+   if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA1)
+   {
+      //Use SHA-1 hash algorithm
+      hash = SHA1_HASH_ALGO;
+      //Length of the message digest
+      hmacDigestSize = 12;
+   }
+   else
+#endif
+#if (SNMP_SHA224_SUPPORT == ENABLED)
+   //HMAC-SHA-224-128 authentication protocol?
+   if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA224)
+   {
+      //Use SHA-224 hash algorithm
+      hash = SHA224_HASH_ALGO;
+      //Length of the message digest
+      hmacDigestSize = 16;
+   }
+   else
+#endif
+#if (SNMP_SHA256_SUPPORT == ENABLED)
+   //HMAC-SHA-256-192 authentication protocol?
+   if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA256)
+   {
+      //Use SHA-256 hash algorithm
+      hash = SHA256_HASH_ALGO;
+      //Length of the message digest
+      hmacDigestSize = 24;
+   }
+   else
+#endif
+#if (SNMP_SHA384_SUPPORT == ENABLED)
+   //HMAC-SHA-384-256 authentication protocol?
+   if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA384)
+   {
+      //Use SHA-384 hash algorithm
+      hash = SHA384_HASH_ALGO;
+      //Length of the message digest
+      hmacDigestSize = 32;
+   }
+   else
+#endif
+#if (SNMP_SHA512_SUPPORT == ENABLED)
+   //HMAC-SHA-512-384 authentication protocol?
+   if(user->authProtocol == SNMP_AUTH_PROTOCOL_SHA512)
+   {
+      //Use SHA-512 hash algorithm
+      hash = SHA512_HASH_ALGO;
+      //Length of the message digest
+      hmacDigestSize = 48;
+   }
+   else
+#endif
+   //Invalid authentication protocol?
+   {
+      //Report an error
+      return ERROR_AUTHENTICATION_FAILED;
+   }
+
+   //Check the length of the msgAuthenticationParameters field
+   if(message->msgAuthParametersLen != hmacDigestSize)
+      return ERROR_AUTHENTICATION_FAILED;
+
+   //The MAC received in the msgAuthenticationParameters field is saved
+   memcpy(hmacDigest, message->msgAuthParameters, hmacDigestSize);
+
+   //The digest in the msgAuthenticationParameters field is replaced by
+   //a null octet string
+   memset(message->msgAuthParameters, 0, hmacDigestSize);
+
+   //The MAC is calculated over the whole message
+   hmacInit(&hmacContext, hash, user->authKey.b, hash->digestSize);
+   hmacUpdate(&hmacContext, message->buffer, message->bufferLen);
+   hmacFinal(&hmacContext, NULL);
+
+   //Restore the value of the msgAuthenticationParameters field
+   memcpy(message->msgAuthParameters, hmacDigest, hmacDigestSize);
+
+   //The newly calculated MAC is compared with the MAC value that was
+   //saved in the first step
+   if(memcmp(hmacContext.digest, hmacDigest, hmacDigestSize))
+      return ERROR_AUTHENTICATION_FAILED;
+
+   //Successful message authentication
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Data encryption
+ * @param[in] user Security profile of the user
+ * @param[in,out] message Pointer to the outgoing SNMP message
+ * @param[in,out] salt Pointer to the salt integer
+ * @return Error code
+ **/
+
+error_t snmpEncryptData(const SnmpUserInfo *user, SnmpMessage *message, uint64_t *salt)
+{
+   error_t error;
+   uint_t i;
+   size_t n;
+   Asn1Tag tag;
+
+   //Debug message
+   TRACE_DEBUG("Scoped PDU (%" PRIuSIZE " bytes):\r\n", message->length);
+   //Display the contents of the scopedPDU
+   TRACE_DEBUG_ARRAY("  ", message->pos, message->length);
+   //Display ASN.1 structure
+   asn1DumpObject(message->pos, message->length, 0);
+
+#if (SNMP_DES_SUPPORT == ENABLED)
+   //DES-CBC privacy protocol?
+   if(user->privProtocol == SNMP_PRIV_PROTOCOL_DES)
+   {
+      DesContext desContext;
+      uint8_t iv[DES_BLOCK_SIZE];
+
+      //The data to be encrypted is treated as sequence of octets. Its length
+      //should be an integral multiple of 8
+      if(message->length % 8)
+      {
+         //If it is not, the data is padded at the end as necessary
+         n = 8 - (message->length % 8);
+         //The actual pad value is irrelevant
+         memset(message->pos + message->length, n, n);
+         //Update the length of the data
+         message->length += n;
+      }
+
+      //The 32-bit snmpEngineBoots is converted to the first 4 octets of our salt
+      STORE32BE(message->msgAuthEngineBoots, message->msgPrivParameters);
+      //The 32-bit integer is then converted to the last 4 octet of our salt
+      STORE32BE(*salt, message->msgPrivParameters + 4);
+
+      //The resulting salt is then put into the msgPrivacyParameters field
+      message->msgPrivParametersLen = 8;
+
+      //Initialize DES context
+      error = desInit(&desContext, user->privKey.b, 8);
+      //Initialization failed?
+      if(error)
+         return error;
+
+      //The last 8 octets of the 16-octet secret (private privacy key) are
+      //used as pre-IV
+      memcpy(iv, user->privKey.b + DES_BLOCK_SIZE, DES_BLOCK_SIZE);
+
+      //The msgPrivacyParameters field is XOR-ed with the pre-IV to obtain the IV
+      for(i = 0; i < DES_BLOCK_SIZE; i++)
+         iv[i] ^= message->msgPrivParameters[i];
+
+      //Perform CBC encryption
+      error = cbcEncrypt(DES_CIPHER_ALGO, &desContext, iv,
+         message->pos, message->pos, message->length);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+   else
+#endif
+#if (SNMP_AES_SUPPORT == ENABLED)
+   //AES-128-CFB privacy protocol?
+   if(user->privProtocol == SNMP_PRIV_PROTOCOL_AES)
+   {
+      AesContext aesContext;
+      uint8_t iv[AES_BLOCK_SIZE];
+
+      //The 32-bit snmpEngineBoots is converted to the first 4 octets of the IV
+      STORE32BE(message->msgAuthEngineBoots, iv);
+      //The 32-bit snmpEngineTime is converted to the subsequent 4 octets
+      STORE32BE(message->msgAuthEngineTime, iv + 4);
+      //The 64-bit integer is then converted to the last 8 octets
+      STORE64BE(*salt, iv + 8);
+
+      //The 64-bit integer must be placed in the msgPrivacyParameters field to
+      //enable the receiving entity to compute the correct IV and to decrypt
+      //the message
+      STORE64BE(*salt, message->msgPrivParameters);
+      message->msgPrivParametersLen = 8;
+
+      //Initialize AES context
+      error = aesInit(&aesContext, user->privKey.b, 16);
+      //Initialization failed?
+      if(error)
+         return error;
+
+      //Perform CFB-128 encryption
+      error = cfbEncrypt(AES_CIPHER_ALGO, &aesContext, 128, iv,
+         message->pos, message->pos, message->length);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+   else
+#endif
+   //Invalid privacy protocol?
+   {
+      //Report an error
+      return ERROR_FAILURE;
+   }
+
+   //The encryptedPDU is encapsulated within an octet string
+   tag.constructed = FALSE;
+   tag.objClass = ASN1_CLASS_UNIVERSAL;
+   tag.objType = ASN1_TYPE_OCTET_STRING;
+   tag.length = message->length;
+   tag.value = NULL;
+
+   //Write the corresponding ASN.1 tag
+   error = asn1WriteTag(&tag, TRUE, message->pos, &n);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Move backward
+   message->pos -= n;
+   //Total length of the encryptedPDU
+   message->length += n;
+
+   //The salt integer is then modified. It is incremented by one and wrap
+   //when it reaches its maximum value
+   *salt += 1;
+
+   //Successful encryption
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Data decryption
+ * @param[in] user Security profile of the user
+ * @param[in,out] message Pointer to the incoming SNMP message
+ * @return Error code
+ **/
+
+error_t snmpDecryptData(const SnmpUserInfo *user, SnmpMessage *message)
+{
+   error_t error;
+   uint_t i;
+   Asn1Tag tag;
+
+   //The encryptedPDU is encapsulated within an octet string
+   error = asn1ReadTag(message->pos, message->length, &tag);
+   //Failed to decode ASN.1 tag?
+   if(error)
+      return error;
+
+   //Enforce encoding, class and type
+   error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL, ASN1_TYPE_OCTET_STRING);
+   //The tag does not match the criteria?
+   if(error)
+      return error;
+
+   //Point to the encryptedPDU
+   message->pos = (uint8_t *) tag.value;
+   //Length of the encryptedPDU
+   message->length = tag.length;
+
+#if (SNMP_DES_SUPPORT == ENABLED)
+   //DES-CBC privacy protocol?
+   if(user->privProtocol == SNMP_PRIV_PROTOCOL_DES)
+   {
+      DesContext desContext;
+      uint8_t iv[DES_BLOCK_SIZE];
+
+      //Before decryption, the encrypted data length is verified. The length
+      //of the encrypted data must be a multiple of 8 octets
+      if(message->length % 8)
+         return ERROR_DECRYPTION_FAILED;
+
+      //Check the length of the msgPrivacyParameters field
+      if(message->msgPrivParametersLen != 8)
+         return ERROR_DECRYPTION_FAILED;
+
+      //Initialize DES context
+      error = desInit(&desContext, user->privKey.b, 8);
+      //Initialization failed?
+      if(error)
+         return error;
+
+      //The last 8 octets of the 16-octet secret (private privacy key) are
+      //used as pre-IV
+      memcpy(iv, user->privKey.b + DES_BLOCK_SIZE, DES_BLOCK_SIZE);
+
+      //The msgPrivacyParameters field is XOR-ed with the pre-IV to obtain the IV
+      for(i = 0; i < DES_BLOCK_SIZE; i++)
+         iv[i] ^= message->msgPrivParameters[i];
+
+      //Perform CBC decryption
+      error = cbcDecrypt(DES_CIPHER_ALGO, &desContext, iv,
+         message->pos, message->pos, message->length);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+   else
+#endif
+#if (SNMP_AES_SUPPORT == ENABLED)
+   //AES-128-CFB privacy protocol?
+   if(user->privProtocol == SNMP_PRIV_PROTOCOL_AES)
+   {
+      AesContext aesContext;
+      uint8_t iv[AES_BLOCK_SIZE];
+
+      //Check the length of the msgPrivacyParameters field
+      if(message->msgPrivParametersLen != 8)
+         return ERROR_DECRYPTION_FAILED;
+
+      //The 32-bit snmpEngineBoots is converted to the first 4 octets of the IV
+      STORE32BE(message->msgAuthEngineBoots, iv);
+      //The 32-bit snmpEngineTime is converted to the subsequent 4 octets
+      STORE32BE(message->msgAuthEngineTime, iv + 4);
+      //The 64-bit integer is then converted to the last 8 octets
+      memcpy(iv + 8, message->msgPrivParameters, 8);
+
+      //Initialize AES context
+      error = aesInit(&aesContext, user->privKey.b, 16);
+      //Initialization failed?
+      if(error)
+         return error;
+
+      //Perform CFB-128 encryption
+      error = cfbDecrypt(AES_CIPHER_ALGO, &aesContext, 128, iv,
+         message->pos, message->pos, message->length);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+   else
+#endif
+   //Invalid privacy protocol?
+   {
+      //Report an error
+      return ERROR_DECRYPTION_FAILED;
+   }
+
+   //Debug message
+   TRACE_DEBUG("Scoped PDU (%" PRIuSIZE " bytes):\r\n", message->length);
+   //Display the contents of the scopedPDU
+   TRACE_DEBUG_ARRAY("  ", message->pos, message->length);
+   //Display ASN.1 structure
+   asn1DumpObject(message->pos, message->length, 0);
+
+   //Successful decryption
+   return NO_ERROR;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/snmp/snmp_usm.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,305 @@
+/**
+ * @file snmp_usm.h
+ * @brief User-based Security Model (USM) for SNMPv3
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _SNMP_USM_H
+#define _SNMP_USM_H
+
+//Dependencies
+#include "core/net.h"
+#include "snmp/snmp_common.h"
+#include "crypto.h"
+
+//Time window for replay protection
+#ifndef SNMP_TIME_WINDOW
+   #define SNMP_TIME_WINDOW 150
+#elif (SNMP_TIME_WINDOW < 1)
+   #error SNMP_TIME_WINDOW parameter is not valid
+#endif
+
+//MD5 authentication support
+#ifndef SNMP_MD5_SUPPORT
+   #define SNMP_MD5_SUPPORT ENABLED
+#elif (SNMP_MD5_SUPPORT != ENABLED && SNMP_MD5_SUPPORT != DISABLED)
+   #error SNMP_MD5_SUPPORT parameter is not valid
+#endif
+
+//SHA-1 authentication support
+#ifndef SNMP_SHA1_SUPPORT
+   #define SNMP_SHA1_SUPPORT ENABLED
+#elif (SNMP_SHA1_SUPPORT != ENABLED && SNMP_SHA1_SUPPORT != DISABLED)
+   #error SNMP_SHA1_SUPPORT parameter is not valid
+#endif
+
+//SHA-224 authentication support
+#ifndef SNMP_SHA224_SUPPORT
+   #define SNMP_SHA224_SUPPORT DISABLED
+#elif (SNMP_SHA224_SUPPORT != ENABLED && SNMP_SHA224_SUPPORT != DISABLED)
+   #error SNMP_SHA224_SUPPORT parameter is not valid
+#endif
+
+//SHA-256 authentication support
+#ifndef SNMP_SHA256_SUPPORT
+   #define SNMP_SHA256_SUPPORT DISABLED
+#elif (SNMP_SHA256_SUPPORT != ENABLED && SNMP_SHA256_SUPPORT != DISABLED)
+   #error SNMP_SHA256_SUPPORT parameter is not valid
+#endif
+
+//SHA-384 authentication support
+#ifndef SNMP_SHA384_SUPPORT
+   #define SNMP_SHA384_SUPPORT DISABLED
+#elif (SNMP_SHA384_SUPPORT != ENABLED && SNMP_SHA384_SUPPORT != DISABLED)
+   #error SNMP_SHA384_SUPPORT parameter is not valid
+#endif
+
+//SHA-512 authentication support
+#ifndef SNMP_SHA512_SUPPORT
+   #define SNMP_SHA512_SUPPORT DISABLED
+#elif (SNMP_SHA512_SUPPORT != ENABLED && SNMP_SHA512_SUPPORT != DISABLED)
+   #error SNMP_SHA512_SUPPORT parameter is not valid
+#endif
+
+//DES encryption support
+#ifndef SNMP_DES_SUPPORT
+   #define SNMP_DES_SUPPORT ENABLED
+#elif (SNMP_DES_SUPPORT != ENABLED && SNMP_DES_SUPPORT != DISABLED)
+   #error SNMP_DES_SUPPORT parameter is not valid
+#endif
+
+//AES encryption support
+#ifndef SNMP_AES_SUPPORT
+   #define SNMP_AES_SUPPORT ENABLED
+#elif (SNMP_AES_SUPPORT != ENABLED && SNMP_AES_SUPPORT != DISABLED)
+   #error SNMP_AES_SUPPORT parameter is not valid
+#endif
+
+//Support for MD5 authentication?
+#if (SNMP_MD5_SUPPORT == ENABLED)
+   #include "md5.h"
+#endif
+
+//Support for SHA-1 authentication?
+#if (SNMP_SHA1_SUPPORT == ENABLED)
+   #include "sha1.h"
+#endif
+
+//Support for SHA-224 authentication?
+#if (SNMP_SHA224_SUPPORT == ENABLED)
+   #include "sha224.h"
+#endif
+
+//Support for SHA-256 authentication?
+#if (SNMP_SHA256_SUPPORT == ENABLED)
+   #include "sha256.h"
+#endif
+
+//Support for SHA-384 authentication?
+#if (SNMP_SHA384_SUPPORT == ENABLED)
+   #include "sha384.h"
+#endif
+
+//Support for SHA-512 authentication?
+#if (SNMP_SHA512_SUPPORT == ENABLED)
+   #include "sha512.h"
+#endif
+
+//Support for DES encryption?
+#if (SNMP_DES_SUPPORT == ENABLED)
+   #include "des.h"
+   #include "cipher_mode_cbc.h"
+#endif
+
+//Support for AES encryption ?
+#if (SNMP_AES_SUPPORT == ENABLED)
+   #include "aes.h"
+   #include "cipher_mode_cfb.h"
+#endif
+
+//Maximum size for authentication and privacy keys
+#if (SNMP_SHA512_SUPPORT == ENABLED)
+   #define SNMP_MAX_KEY_SIZE 64
+#elif (SNMP_SHA384_SUPPORT == ENABLED)
+   #define SNMP_MAX_KEY_SIZE 48
+#elif (SNMP_SHA256_SUPPORT == ENABLED)
+   #define SNMP_MAX_KEY_SIZE 32
+#elif (SNMP_SHA224_SUPPORT == ENABLED)
+   #define SNMP_MAX_KEY_SIZE 28
+#elif (SNMP_SHA1_SUPPORT == ENABLED)
+   #define SNMP_MAX_KEY_SIZE 20
+#else
+   #define SNMP_MAX_KEY_SIZE 16
+#endif
+
+//Maximum size for HMAC digests
+#if (SNMP_SHA512_SUPPORT == ENABLED)
+   #define SNMP_MAX_HMAC_DIGEST_SIZE 48
+#elif (SNMP_SHA384_SUPPORT == ENABLED)
+   #define SNMP_MAX_HMAC_DIGEST_SIZE 32
+#elif (SNMP_SHA256_SUPPORT == ENABLED)
+   #define SNMP_MAX_HMAC_DIGEST_SIZE 24
+#elif (SNMP_SHA224_SUPPORT == ENABLED)
+   #define SNMP_MAX_HMAC_DIGEST_SIZE 16
+#elif (SNMP_SHA1_SUPPORT == ENABLED)
+   #define SNMP_MAX_HMAC_DIGEST_SIZE 12
+#else
+   #define SNMP_MAX_HMAC_DIGEST_SIZE 12
+#endif
+
+//SNMP message encryption overhead
+#if (SNMP_DES_SUPPORT == ENABLED)
+   #define SNMP_MSG_ENCRYPTION_OVERHEAD 8
+#else
+   #define SNMP_MSG_ENCRYPTION_OVERHEAD 0
+#endif
+
+
+/**
+ * SNMP message flags
+ **/
+
+typedef enum
+{
+   SNMP_MSG_FLAG_AUTH   = 1,
+   SNMP_MSG_FLAG_PRIV   = 2,
+   SNMP_MSG_FLAG_REPORT = 4
+} SnmpMessageFlags;
+
+
+/**
+ * SNMP security models
+ **/
+
+typedef enum
+{
+   SNMP_SECURITY_MODEL_USM = 3, ///<User-based security model
+   SNMP_SECURITY_MODEL_TSM = 4  ///<Transport security model
+} SnmpSecurityModel;
+
+
+/**
+ * @brief Access modes
+ **/
+
+typedef enum
+{
+   SNMP_ACCESS_NONE       = 0,
+   SNMP_ACCESS_READ_ONLY  = 1,
+   SNMP_ACCESS_WRITE_ONLY = 2,
+   SNMP_ACCESS_READ_WRITE = 3
+} SnmpAccess;
+
+
+/**
+ * SNMP authentication protocols
+ **/
+
+typedef enum
+{
+   SNMP_AUTH_PROTOCOL_NONE   = 0, ///<No authentication
+   SNMP_AUTH_PROTOCOL_MD5    = 1, ///<HMAC-MD5-96
+   SNMP_AUTH_PROTOCOL_SHA1   = 2, ///<HMAC-SHA-1-96
+   SNMP_AUTH_PROTOCOL_SHA224 = 3, ///<HMAC-SHA-224-128
+   SNMP_AUTH_PROTOCOL_SHA256 = 4, ///<HMAC-SHA-256-192
+   SNMP_AUTH_PROTOCOL_SHA384 = 5, ///<HMAC-SHA-384-256
+   SNMP_AUTH_PROTOCOL_SHA512 = 6  ///<HMAC-SHA-512-384
+} SnmpAuthProtocol;
+
+
+/**
+ * SNMP privacy protocols
+ **/
+
+typedef enum
+{
+   SNMP_PRIV_PROTOCOL_NONE = 0, ///<No privacy
+   SNMP_PRIV_PROTOCOL_DES  = 1, ///<DES-CBC
+   SNMP_PRIV_PROTOCOL_AES  = 2  ///<AES-128-CFB
+} SnmpPrivProtocol;
+
+
+/**
+ * @brief SNMP key format
+ **/
+
+typedef enum
+{
+   SNMP_KEY_FORMAT_NONE = 0, ///<Unspecified key format
+   SNMP_KEY_FORMAT_TEXT = 1, ///<ASCII password
+   SNMP_KEY_FORMAT_RAW  = 2  ///<Raw key
+} SnmpKeyFormat;
+
+
+/**
+ * @brief SNMP secret key
+ **/
+
+typedef struct
+{
+   uint8_t b[SNMP_MAX_KEY_SIZE];
+} SnmpKey;
+
+
+/**
+ * @brief SNMP user information
+ **/
+
+typedef struct
+{
+   char_t name[SNMP_MAX_USER_NAME_LEN + 1]; ///<User name
+   SnmpAccess mode;                         ///<Access mode
+#if (SNMP_V3_SUPPORT == ENABLED)
+   SnmpAuthProtocol authProtocol;           ///<Authentication protocol
+   SnmpKey authKey;                         ///<Authentication key
+   SnmpPrivProtocol privProtocol;           ///<Privacy protocol
+   SnmpKey privKey;                         ///<Privacy key
+#endif
+} SnmpUserInfo;
+
+
+//USM related constants
+extern const uint8_t usmStatsUnsupportedSecLevelsObject[10];
+extern const uint8_t usmStatsNotInTimeWindowsObject[10];
+extern const uint8_t usmStatsUnknownUserNamesObject[10];
+extern const uint8_t usmStatsUnknownEngineIdsObject[10];
+extern const uint8_t usmStatsWrongDigestsObject[10];
+extern const uint8_t usmStatsDecryptionErrorsObject[10];
+
+//USM related functions
+error_t snmpGenerateKey(SnmpAuthProtocol authProtocol, const char_t *password,
+   const uint8_t *engineId, size_t engineIdLen, SnmpKey *key);
+
+error_t snmpCheckSecurityParameters(const SnmpUserInfo *user,
+   SnmpMessage *message, const uint8_t *engineId, size_t engineIdLen);
+
+error_t snmpAuthOutgoingMessage(const SnmpUserInfo *user, SnmpMessage *message);
+error_t snmpAuthIncomingMessage(const SnmpUserInfo *user, SnmpMessage *message);
+
+error_t snmpEncryptData(const SnmpUserInfo *user, SnmpMessage *message, uint64_t *salt);
+error_t snmpDecryptData(const SnmpUserInfo *user, SnmpMessage *message);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/sntp/sntp_client.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,354 @@
+/**
+ * @file sntp_client.c
+ * @brief SNTP client (Simple Network Time Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The Simple Network Time Protocol is used to synchronize computer clocks
+ * in the Internet. Refer to RFC 4330 for more details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL SNTP_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "sntp/sntp_client.h"
+#include "error.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (SNTP_CLIENT_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Retrieve current time from NTP server using SNTP protocol
+ * @param[in] interface Underlying network interface (optional parameter)
+ * @param[in] serverIpAddr IP address of the NTP server
+ * @param[out] timestamp Current time
+ * @return Error code
+ **/
+
+ error_t sntpClientGetTimestamp(NetInterface *interface,
+   const IpAddr *serverIpAddr, NtpTimestamp *timestamp)
+{
+   error_t error;
+   uint_t i;
+   systime_t timeout;
+   SntpClientContext context;
+
+   //Check parameters
+   if(serverIpAddr == NULL || timestamp == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Use default network interface?
+   if(interface == NULL)
+      interface = netGetDefaultInterface();
+
+   //Open a UDP socket
+   context.socket = socketOpen(SOCKET_TYPE_DGRAM, SOCKET_IP_PROTO_UDP);
+   //Failed to open socket?
+   if(!context.socket)
+      return ERROR_OPEN_FAILED;
+
+   //Associate the socket with the relevant interface
+   error = socketBindToInterface(context.socket, interface);
+   //Any error to report?
+   if(error)
+   {
+      //Close socket
+      socketClose(context.socket);
+      //Return status code
+      return error;
+   }
+
+   //Only accept datagrams from the specified NTP server
+   error = socketConnect(context.socket, serverIpAddr, NTP_PORT);
+   //Any error to report?
+   if(error)
+   {
+      //Close socket
+      socketClose(context.socket);
+      //Return status code
+      return error;
+   }
+
+   //Initial timeout value
+   timeout = SNTP_CLIENT_INIT_TIMEOUT;
+
+   //Retransmission loop
+   for(i = 0; i < SNTP_CLIENT_MAX_RETRIES; i++)
+   {
+      //Send NTP request message
+      error = sntpSendRequest(&context);
+      //Failed to send message ?
+      if(error)
+         break;
+
+      //Wait for a valid NTP response message
+      error = sntpWaitForResponse(&context, timeout);
+      //Valid NTP response received?
+      if(!error)
+         break;
+
+      //The timeout value is doubled for each subsequent retransmission
+      timeout = MIN(timeout * 2, SNTP_CLIENT_MAX_TIMEOUT);
+   }
+
+   //Successful processing?
+   if(!error)
+   {
+      //Save server timestamp
+      timestamp->seconds = ntohl(context.message.transmitTimestamp.seconds);
+      timestamp->fraction = ntohl(context.message.transmitTimestamp.fraction);
+   }
+
+   //Close socket
+   socketClose(context.socket);
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send NTP request using SNTP protocol
+ * @param[in] context SNTP client context
+ * @return Error code
+ **/
+
+error_t sntpSendRequest(SntpClientContext *context)
+{
+   size_t length;
+   NtpHeader *message;
+
+   //Point to the buffer where to format the NTP message
+   message = &context->message;
+
+   //Clear NTP message
+   memset(message, 0, sizeof(NtpHeader));
+
+   //Format NTP request
+   message->vn = NTP_VERSION_3;
+   message->mode = NTP_MODE_CLIENT;
+
+   //Time at which the NTP request was sent
+   context->t1 = osGetSystemTime();
+
+   //The Transmit Timestamp allows a simple calculation to determine the
+   //propagation delay between the server and client and to align the system
+   //clock generally within a few tens of milliseconds relative to the server
+   message->transmitTimestamp.seconds = 0;
+   message->transmitTimestamp.fraction = htonl(context->t1);
+
+   //Length of the NTP request
+   length = sizeof(NtpHeader);
+
+   //Debug message
+   TRACE_INFO("Sending NTP request message (%" PRIuSIZE " bytes)...\r\n", sizeof(NtpHeader));
+   //Dump NTP message
+   sntpDumpMessage(message, length);
+
+   //Send NTP request
+   return socketSend(context->socket, message, length, NULL, 0);
+}
+
+
+/**
+ * @brief Wait for a valid response from the NTP server
+ * @param[in] context Pointer to the SNTP client context
+ * @param[in] timeout Maximum time period to wait
+ * @return Error code
+ **/
+
+error_t sntpWaitForResponse(SntpClientContext *context, systime_t timeout)
+{
+   error_t error;
+   size_t length;
+   systime_t elapsedTime;
+
+   //Time elapsed since the NTP request was sent
+   elapsedTime = 0;
+
+   //Keep listening as long as the retransmission timeout has not been reached
+   while(elapsedTime < timeout)
+   {
+      //Adjust receive timeout
+      error = socketSetTimeout(context->socket, timeout - elapsedTime);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Wait for a response from the NTP server
+      error = socketReceive(context->socket, &context->message,
+         sizeof(NtpHeader), &length, 0);
+
+      //Any datagram received?
+      if(!error)
+      {
+         //Time at which the response was received
+         context->t4 = osGetSystemTime();
+
+         //Parse incoming datagram
+         error = sntpParseResponse(context, &context->message, length);
+         //Valid NTP response message?
+         if(!error)
+            return NO_ERROR;
+      }
+
+      //Compute the time elapsed since the NTP request was sent
+      elapsedTime = osGetSystemTime() - context->t1;
+   }
+
+   //The timeout period elapsed
+   return ERROR_TIMEOUT;
+}
+
+
+/**
+ * @brief Parse NTP server response
+ * @param[in] context Pointer to the SNTP client context
+ * @param[in] message NTP response message to parse
+ * @param[in] length Length of the incoming NTP message
+ * @return Error code
+ **/
+
+error_t sntpParseResponse(SntpClientContext *context,
+   const NtpHeader *message, size_t length)
+{
+   //Ensure the NTP message is valid
+   if(length < sizeof(NtpHeader))
+      return ERROR_INVALID_MESSAGE;
+
+   //Debug message
+   TRACE_INFO("NTP response message received (%" PRIuSIZE " bytes)...\r\n", length);
+   //Dump NTP message
+   sntpDumpMessage(message, length);
+
+   //The server reply should be discarded if any of the VN, Stratum,
+   //or Transmit Timestamp fields is 0
+   if(!message->vn || !message->stratum)
+      return ERROR_INVALID_MESSAGE;
+   if(!message->transmitTimestamp.seconds || !message->transmitTimestamp.fraction)
+      return ERROR_INVALID_MESSAGE;
+
+   //The server reply should be discarded if the Mode field is
+   //not 4 (unicast) or 5 (broadcast)
+   if(message->mode != NTP_MODE_SERVER && message->mode != NTP_MODE_BROADCAST)
+      return ERROR_INVALID_MESSAGE;
+
+   //The Originate Timestamp in the server reply should match the
+   //Transmit Timestamp used in the client request
+   if(message->originateTimestamp.seconds != 0)
+      return ERROR_INVALID_TIMESTAMP;
+   if(message->originateTimestamp.fraction != htonl(context->t1))
+      return ERROR_INVALID_TIMESTAMP;
+
+   //The NTP response message is acceptable
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Dump NTP message for debugging purpose
+ * @param[in] message Pointer to the NTP message
+ * @param[in] length Length of the NTP message
+ **/
+
+void sntpDumpMessage(const NtpHeader *message, size_t length)
+{
+#if (SNTP_TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
+   uint8_t *p;
+   NtpAuthData* authData;
+
+   //Point to the beginning of the message
+   p = (uint8_t *) message;
+
+   //Check message length
+   if(length >= sizeof(NtpHeader))
+   {
+      //Dump NTP message
+      TRACE_DEBUG("  Mode = %" PRIu8 "\r\n", message->mode);
+      TRACE_DEBUG("  Version = %" PRIu8 "\r\n", message->vn);
+      TRACE_DEBUG("  Leap indicator = %" PRIu8 "\r\n", message->li);
+      TRACE_DEBUG("  Stratum = %" PRIu8 "\r\n", message->stratum);
+      TRACE_DEBUG("  Poll = %" PRIu8 "\r\n", message->poll);
+      TRACE_DEBUG("  Precision = %" PRId8 "\r\n", message->precision);
+      TRACE_DEBUG("  Root Delay = %" PRIu32 "\r\n", ntohl(message->rootDelay));
+      TRACE_DEBUG("  Root Dispersion = %" PRIu32 "\r\n", ntohl(message->rootDispersion));
+      TRACE_DEBUG("  Reference Identifier = %" PRIu32 "\r\n", ntohl(message->referenceIdentifier));
+
+      //Dump reference timestamp
+      TRACE_DEBUG("  ReferenceTimestamp\r\n");
+      sntpDumpTimestamp(&message->referenceTimestamp);
+
+      //Dump originate timestamp
+      TRACE_DEBUG("  Originate Timestamp\r\n");
+      sntpDumpTimestamp(&message->originateTimestamp);
+
+      //Dump receive timestamp
+      TRACE_DEBUG("  Receive Timestamp\r\n");
+      sntpDumpTimestamp(&message->receiveTimestamp);
+
+      //Dump transmit timestamp
+      TRACE_DEBUG("  Transmit Timestamp\r\n");
+      sntpDumpTimestamp(&message->transmitTimestamp);
+
+      //Advance data pointer
+      p += sizeof(NtpHeader);
+      length -= sizeof(NtpHeader);
+
+      //Any authentication data?
+      if(length >= sizeof(NtpAuthData))
+      {
+         //Point to the beginning of the authentication data
+         authData = (NtpAuthData *) p;
+
+         //Dump transmit timestamp
+         TRACE_DEBUG("  Key Identifier = %" PRIu32 "\r\n", ntohl(authData->keyIdentifier));
+         //Dump message digest
+         TRACE_DEBUG("  Message Digest\r\n");
+         TRACE_DEBUG_ARRAY("    ", authData->messageDigest, 16);
+      }
+   }
+#endif
+}
+
+
+/**
+ * @brief Dump NTP timestamp
+ * @param[in] timestamp Pointer to the NTP timestamp
+ **/
+
+void sntpDumpTimestamp(const NtpTimestamp *timestamp)
+{
+   //Dump seconds
+   TRACE_DEBUG("    Seconds = %" PRIu32 "\r\n", ntohl(timestamp->seconds));
+   //Dump fraction field
+   TRACE_DEBUG("    Fraction = %" PRIu32 "\r\n", ntohl(timestamp->fraction));
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/sntp/sntp_client.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,193 @@
+/**
+ * @file sntp_client.h
+ * @brief SNTP client (Simple Network Time Protocol)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Dependencies
+#include "core/net.h"
+#include "core/socket.h"
+
+//SNTP client support
+#ifndef SNTP_CLIENT_SUPPORT
+   #define SNTP_CLIENT_SUPPORT ENABLED
+#elif (SNTP_CLIENT_SUPPORT != ENABLED && SNTP_CLIENT_SUPPORT != DISABLED)
+   #error SNTP_CLIENT_SUPPORT parameter is not valid
+#endif
+
+//Maximum number of retransmissions of SNTP requests
+#ifndef SNTP_CLIENT_MAX_RETRIES
+   #define SNTP_CLIENT_MAX_RETRIES 3
+#elif (SNTP_CLIENT_MAX_RETRIES < 1)
+   #error SNTP_CLIENT_MAX_RETRIES parameter is not valid
+#endif
+
+//Initial retransmission timeout
+#ifndef SNTP_CLIENT_INIT_TIMEOUT
+   #define SNTP_CLIENT_INIT_TIMEOUT 1000
+#elif (SNTP_CLIENT_INIT_TIMEOUT < 1000)
+   #error SNTP_CLIENT_INIT_TIMEOUT parameter is not valid
+#endif
+
+//Maximum retransmission timeout
+#ifndef SNTP_CLIENT_MAX_TIMEOUT
+   #define SNTP_CLIENT_MAX_TIMEOUT 5000
+#elif (SNTP_CLIENT_MAX_TIMEOUT < 1000)
+   #error SNTP_CLIENT_MAX_TIMEOUT parameter is not valid
+#endif
+
+//NTP port number
+#define NTP_PORT 123
+//Maximum size of NTP packets
+#define NTP_MESSAGE_MAX_SIZE 68
+
+/**
+ * @brief Leap indicator
+ **/
+
+typedef enum
+{
+   NTP_LI_NO_WARNING           = 0,
+   NTP_LI_LAST_MIN_HAS_61_SECS = 1,
+   NTP_LI_LAST_MIN_HAS_59_SECS = 2,
+   NTP_LI_ALARM_CONDITION      = 3
+} NtpLeapIndicator;
+
+
+/**
+ * @brief NTP version number
+ **/
+
+typedef enum
+{
+   NTP_VERSION_1 = 1,
+   NTP_VERSION_2 = 2,
+   NTP_VERSION_3 = 3,
+   NTP_VERSION_4 = 4
+} NtpVersion;
+
+
+/**
+ * @brief Protocol mode
+ **/
+
+typedef enum
+{
+   NTP_MODE_SYMMETRIC_ACTIVE  = 1,
+   NTP_MODE_SYMMETRIC_PASSIVE = 2,
+   NTP_MODE_CLIENT            = 3,
+   NTP_MODE_SERVER            = 4,
+   NTP_MODE_BROADCAST         = 5
+} NtpMode;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief Time representation
+ **/
+
+typedef __start_packed struct
+{
+   uint32_t seconds;
+   uint32_t fraction;
+} __end_packed NtpTimestamp;
+
+
+/**
+ * @brief NTP packet header
+ **/
+
+typedef __start_packed struct
+{
+#ifdef _CPU_BIG_ENDIAN
+   uint8_t li : 2;                  //0
+   uint8_t vn : 3;
+   uint8_t mode : 3;
+#else
+   uint8_t mode : 3;                //0
+   uint8_t vn : 3;
+   uint8_t li : 2;
+#endif
+   uint8_t stratum;                 //1
+   uint8_t poll;                    //2
+   int8_t precision;                //3
+   uint32_t rootDelay;              //4-7
+   uint32_t rootDispersion;         //8-11
+   uint32_t referenceIdentifier;    //12-15
+   NtpTimestamp referenceTimestamp; //16-23
+   NtpTimestamp originateTimestamp; //24-31
+   NtpTimestamp receiveTimestamp;   //32-39
+   NtpTimestamp transmitTimestamp;  //40-47
+} __end_packed NtpHeader;
+
+
+/**
+ * @brief Authentication data
+ **/
+
+typedef __start_packed struct
+{
+   uint32_t keyIdentifier;
+   uint8_t messageDigest[16];
+} __end_packed NtpAuthData;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+/**
+ * @brief SNTP client context
+ **/
+
+typedef struct
+{
+   Socket *socket;    ///<Underlying socket
+   NtpHeader message; ///<Buffer where to format NTP messages
+   systime_t t1;      ///<Time at which the NTP request was sent by the client
+   systime_t t4;      ///<Time at which the NTP reply was received by the client
+} SntpClientContext;
+
+
+//SNTP client related functions
+error_t sntpClientGetTimestamp(NetInterface *interface,
+   const IpAddr *serverIpAddr, NtpTimestamp *timestamp);
+
+error_t sntpSendRequest(SntpClientContext *context);
+error_t sntpWaitForResponse(SntpClientContext *context, systime_t timeout);
+
+error_t sntpParseResponse(SntpClientContext *context,
+   const NtpHeader *message, size_t length);
+
+void sntpDumpMessage(const NtpHeader *message, size_t length);
+void sntpDumpTimestamp(const NtpTimestamp *timestamp);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/std_services/chargen.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,404 @@
+/**
+ * @file chargen.c
+ * @brief Character generator protocol
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The character generator service is a useful debugging and measurement
+ * tool. The service simply sends data until the calling user terminates
+ * the connection. Refer to RFC 864 for complete details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL STD_SERVICES_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "std_services/chargen.h"
+#include "debug.h"
+
+//Character pattern (from RFC 864)
+const char_t pattern[190] =
+{
+   '!', '"', '#',  '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3',
+   '4', '5', '6',  '7', '8', '9', ':',  ';', '<', '=', '>', '?', '@', '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', ':',  ';', '<', '=', '>', '?', '@', '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', '{', '|', '}', '~', ' '
+};
+
+
+/**
+ * @brief Start TCP chargen service
+ * @return Error code
+ **/
+
+error_t tcpChargenStart(void)
+{
+   error_t error;
+   Socket *socket;
+   OsTask *task;
+
+   //Debug message
+   TRACE_INFO("Starting TCP chargen service...\r\n");
+
+   //Open a TCP socket
+   socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
+   //Failed to open socket?
+   if(socket == NULL)
+      return ERROR_OPEN_FAILED;
+
+   //Start of exception handling block
+   do
+   {
+      //Bind the newly created socket to port 19
+      error = socketBind(socket, &IP_ADDR_ANY, CHARGEN_PORT);
+      //Failed to bind the socket to the desired port?
+      if(error)
+         break;
+
+      //Place the socket into listening mode
+      error = socketListen(socket, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Create a task to handle incoming connection requests
+      task = osCreateTask("TCP Chargen Listener", tcpChargenListenerTask,
+         socket, CHARGEN_SERVICE_STACK_SIZE, CHARGEN_SERVICE_PRIORITY);
+
+      //Unable to create the task?
+      if(task == OS_INVALID_HANDLE)
+      {
+         //Report an error to the calling function
+         error = ERROR_OUT_OF_RESOURCES;
+         break;
+      }
+
+      //End of exception handling block
+   } while(0);
+
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects...
+      socketClose(socket);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Task handling connection requests
+ * @param[in] param Pointer to the chargen service context
+ **/
+
+void tcpChargenListenerTask(void *param)
+{
+   error_t error;
+   uint16_t clientPort;
+   IpAddr clientIpAddr;
+   Socket *serverSocket;
+   Socket *clientSocket;
+   ChargenServiceContext *context;
+   OsTask *task;
+
+   //Point to the listening socket
+   serverSocket = (Socket *) param;
+
+   //Main loop
+   while(1)
+   {
+      //Accept an incoming connection
+      clientSocket = socketAccept(serverSocket, &clientIpAddr, &clientPort);
+      //Check whether a valid connection request has been received
+      if(!clientSocket) continue;
+
+      //Debug message
+      TRACE_INFO("Chargen service: connection established with client %s port %" PRIu16 "\r\n",
+         ipAddrToString(&clientIpAddr, NULL), clientPort);
+
+      //Adjust timeout
+      error = socketSetTimeout(clientSocket, CHARGEN_TIMEOUT);
+
+      //Any error to report?
+      if(error)
+      {
+         //Close socket
+         socketClose(clientSocket);
+         //Wait for an incoming connection attempt
+         continue;
+      }
+
+      //Allocate resources for the new connection
+      context = osAllocMem(sizeof(ChargenServiceContext));
+
+      //Failed to allocate memory?
+      if(context == NULL)
+      {
+         //Close socket
+         socketClose(clientSocket);
+         //Wait for an incoming connection attempt
+         continue;
+      }
+
+      //Record the handle of the newly created socket
+      context->socket = clientSocket;
+
+      //Create a task to service the current connection
+      task = osCreateTask("TCP Chargen Connection", tcpChargenConnectionTask,
+         context, CHARGEN_SERVICE_STACK_SIZE, CHARGEN_SERVICE_PRIORITY);
+
+      //Did we encounter an error?
+      if(task == OS_INVALID_HANDLE)
+      {
+         //Close socket
+         socketClose(clientSocket);
+         //Release resources
+         osFreeMem(context);
+      }
+   }
+}
+
+
+/**
+ * @brief TCP chargen service implementation
+ * @param[in] param Pointer to the chargen service context
+ **/
+
+void tcpChargenConnectionTask(void *param)
+{
+   error_t error;
+   //size_t i;
+   size_t n;
+   //size_t offset;
+   size_t byteCount;
+   systime_t startTime;
+   systime_t duration;
+   ChargenServiceContext *context;
+
+   //Get a pointer to the context
+   context = (ChargenServiceContext *) param;
+   //Get current time
+   startTime = osGetSystemTime();
+
+   //Initialize counters
+   byteCount = 0;
+   //offset = 0;
+
+   //Once a connection is established a stream of data is sent out
+   //the connection (and any data received is thrown away). This
+   //continues until the calling user terminates the connection
+   while(1)
+   {
+      //Format output data
+      /*for(i = 0; i < CHARGEN_BUFFER_SIZE; i += 95)
+      {
+         //Calculate the length of the current line
+         n = MIN(CHARGEN_BUFFER_SIZE - i, 95);
+         //Copy character pattern
+         memcpy(context->buffer + i, pattern + offset, n);
+      }
+
+      //Update offset
+      offset += CHARGEN_BUFFER_SIZE + 95 - i;
+      //Wrap around if necessary
+      if(offset >= 95) offset = 0;*/
+
+      //Send data
+      error = socketSend(context->socket, context->buffer, CHARGEN_BUFFER_SIZE, &n, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Total number of bytes sent
+      byteCount += n;
+   }
+
+   //Graceful shutdown
+   socketShutdown(context->socket, SOCKET_SD_BOTH);
+   //Compute total duration
+   duration = osGetSystemTime() - startTime;
+   //Avoid division by zero...
+   if(!duration) duration = 1;
+
+   //Debug message
+   TRACE_INFO("Chargen service: %" PRIuSIZE " bytes "
+      "sent in %" PRIu32 " ms (%" PRIu32 " kBps, %" PRIu32 " kbps)\r\n",
+      byteCount, duration, byteCount / duration, (byteCount * 8) / duration);
+
+   //Close socket
+   socketClose(context->socket);
+   //Release previously allocated memory
+   osFreeMem(context);
+
+   //Kill ourselves
+   osDeleteTask(NULL);
+}
+
+
+/**
+ * @brief Start UDP chargen service
+ * @return Error code
+ **/
+
+error_t udpChargenStart(void)
+{
+   error_t error;
+   ChargenServiceContext *context;
+   OsTask *task;
+
+   //Debug message
+   TRACE_INFO("Starting UDP chargen service...\r\n");
+
+   //Allocate a memory block to hold the context
+   context = osAllocMem(sizeof(ChargenServiceContext));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Start of exception handling block
+   do
+   {
+      //Open a UDP socket
+      context->socket = socketOpen(SOCKET_TYPE_DGRAM, SOCKET_IP_PROTO_UDP);
+
+      //Failed to open socket?
+      if(!context->socket)
+      {
+         //Report an error
+         error = ERROR_OPEN_FAILED;
+         //Exit immediately
+         break;
+      }
+
+      //The server listens for incoming datagrams on port 19
+      error = socketBind(context->socket, &IP_ADDR_ANY, CHARGEN_PORT);
+      //Unable to bind the socket to the desired port?
+      if(error)
+         break;
+
+      //Create a task to handle incoming datagrams
+      task = osCreateTask("UDP Chargen", udpChargenTask,
+         context, CHARGEN_SERVICE_STACK_SIZE, CHARGEN_SERVICE_PRIORITY);
+
+      //Unable to create the task?
+      if(task == OS_INVALID_HANDLE)
+      {
+         //Report an error to the calling function
+         error = ERROR_OUT_OF_RESOURCES;
+         break;
+      }
+
+      //End of exception handling block
+   } while(0);
+
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects...
+      socketClose(context->socket);
+      osFreeMem(context);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief UDP chargen service implementation
+ * @param[in] param Pointer to the chargen service context
+ **/
+
+void udpChargenTask(void *param)
+{
+   error_t error;
+   size_t i;
+   size_t k;
+   size_t n;
+   size_t length;
+   uint16_t port;
+   IpAddr ipAddr;
+   ChargenServiceContext *context;
+
+   //Get a pointer to the context
+   context = (ChargenServiceContext *) param;
+
+   //Main loop
+   while(1)
+   {
+      //Wait for an incoming datagram
+      error = socketReceiveFrom(context->socket, &ipAddr, &port,
+         context->buffer, CHARGEN_BUFFER_SIZE, &n, 0);
+
+      //Any datagram received?
+      if(!error)
+      {
+         //When a datagram is received, an answering datagram is sent
+         //containing a random number (between 0 and 512) of characters
+         length = netGetRand() % 513;
+
+         //Reset line counter
+         n = 0;
+
+         //Format output data
+         for(i = 0; i < length; i += 74)
+         {
+            //Calculate the length of the current line
+            k = MIN(length - i, 74);
+            //Copy character pattern
+            memcpy(context->buffer + i, pattern + n, k);
+
+            //End each line with carriage return and line feed
+            if(k == 74)
+            {
+               context->buffer[i + 72] = '\r';
+               context->buffer[i + 73] = '\n';
+            }
+
+            //Increment line counter
+            if(++n >= 95) n = 0;
+         }
+
+         //Send data to the remote host
+         error = socketSendTo(context->socket, &ipAddr, port,
+            context->buffer, length, &n, 0);
+
+         //Debug message
+         TRACE_INFO("Chargen service: %" PRIuSIZE " bytes sent to %s port %" PRIu16 "\r\n",
+            n, ipAddrToString(&ipAddr, NULL), port);
+      }
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/std_services/chargen.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,87 @@
+/**
+ * @file chargen.h
+ * @brief Character generator protocol
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _CHARGEN_H
+#define _CHARGEN_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/socket.h"
+
+//Stack size required to run the chargen service
+#ifndef CHARGEN_SERVICE_STACK_SIZE
+   #define CHARGEN_SERVICE_STACK_SIZE 600
+#elif (CHARGEN_SERVICE_STACK_SIZE < 1)
+   #error CHARGEN_SERVICE_STACK_SIZE parameter is not valid
+#endif
+
+//Priority at which the chargen service should run
+#ifndef CHARGEN_SERVICE_PRIORITY
+   #define CHARGEN_SERVICE_PRIORITY OS_TASK_PRIORITY_NORMAL
+#endif
+
+//Size of the buffer for input/output operations
+#ifndef CHARGEN_BUFFER_SIZE
+   #define CHARGEN_BUFFER_SIZE 1500
+#elif (CHARGEN_BUFFER_SIZE < 1)
+   #error CHARGEN_BUFFER_SIZE parameter is not valid
+#endif
+
+//Maximum time the TCP chargen server will wait before closing the connection
+#ifndef CHARGEN_TIMEOUT
+   #define CHARGEN_TIMEOUT 20000
+#elif (CHARGEN_TIMEOUT < 1)
+   #error CHARGEN_TIMEOUT parameter is not valid
+#endif
+
+//Chargen service port
+#define CHARGEN_PORT 19
+
+
+/**
+ * @brief Chargen service context
+ **/
+
+typedef struct
+{
+   Socket *socket;
+   char_t buffer[CHARGEN_BUFFER_SIZE];
+} ChargenServiceContext;
+
+
+//TCP chargen service related functions
+error_t tcpChargenStart(void);
+void tcpChargenListenerTask(void *param);
+void tcpChargenConnectionTask(void *param);
+
+//UDP chargen service related functions
+error_t udpChargenStart(void);
+void udpChargenTask(void *param);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/std_services/daytime.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,43 @@
+/**
+ * @file daytime.c
+ * @brief Daytime protocol
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The daytime service is a useful debugging and measurement tool. The service
+ * simply sends the current date and time as a character string without regard
+ * to the input. Refer to RFC 867 for complete details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL STD_SERVICES_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include "core/net.h"
+#include "std_services/daytime.h"
+#include "debug.h"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/std_services/daytime.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,40 @@
+/**
+ * @file daytime.h
+ * @brief Daytime protocol
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DAYTIME_H
+#define _DAYTIME_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/socket.h"
+
+//Daytime service port
+#define DAYTIME_PORT 13
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/std_services/discard.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,338 @@
+/**
+ * @file discard.c
+ * @brief Discard protocol
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The discard service is a useful debugging and measurement tool. The service
+ * simply throws away any data it receives. Refer to RFC 863 for complete details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL STD_SERVICES_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "std_services/discard.h"
+#include "debug.h"
+
+
+/**
+ * @brief Start TCP discard service
+ * @return Error code
+ **/
+
+error_t tcpDiscardStart(void)
+{
+   error_t error;
+   Socket *socket;
+   OsTask *task;
+
+   //Debug message
+   TRACE_INFO("Starting TCP discard service...\r\n");
+
+   //Open a TCP socket
+   socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
+   //Failed to open socket?
+   if(socket == NULL)
+      return ERROR_OPEN_FAILED;
+
+   //Start of exception handling block
+   do
+   {
+      //Bind the newly created socket to port 9
+      error = socketBind(socket, &IP_ADDR_ANY, DISCARD_PORT);
+      //Failed to bind the socket to the desired port?
+      if(error)
+         break;
+
+      //Place the socket into listening mode
+      error = socketListen(socket, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Create a task to handle incoming connection requests
+      task = osCreateTask("TCP Discard Listener", tcpDiscardListenerTask,
+         socket, DISCARD_SERVICE_STACK_SIZE, DISCARD_SERVICE_PRIORITY);
+
+      //Unable to create the task?
+      if(task == OS_INVALID_HANDLE)
+      {
+         //Report an error to the calling function
+         error = ERROR_OUT_OF_RESOURCES;
+         break;
+      }
+
+      //End of exception handling block
+   } while(0);
+
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects...
+      socketClose(socket);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Task handling connection requests
+ * @param[in] param Pointer to the discard service context
+ **/
+
+void tcpDiscardListenerTask(void *param)
+{
+   error_t error;
+   uint16_t clientPort;
+   IpAddr clientIpAddr;
+   Socket *serverSocket;
+   Socket *clientSocket;
+   DiscardServiceContext *context;
+   OsTask *task;
+
+   //Point to the listening socket
+   serverSocket = (Socket *) param;
+
+   //Main loop
+   while(1)
+   {
+      //Accept an incoming connection
+      clientSocket = socketAccept(serverSocket, &clientIpAddr, &clientPort);
+      //Check whether a valid connection request has been received
+      if(!clientSocket) continue;
+
+      //Debug message
+      TRACE_INFO("Discard service: connection established with client %s port %" PRIu16 "\r\n",
+         ipAddrToString(&clientIpAddr, NULL), clientPort);
+
+      //Adjust timeout
+      error = socketSetTimeout(clientSocket, DISCARD_TIMEOUT);
+
+      //Any error to report?
+      if(error)
+      {
+         //Close socket
+         socketClose(clientSocket);
+         //Wait for an incoming connection attempt
+         continue;
+      }
+
+      //Allocate resources for the new connection
+      context = osAllocMem(sizeof(DiscardServiceContext));
+
+      //Failed to allocate memory?
+      if(context == NULL)
+      {
+         //Close socket
+         socketClose(clientSocket);
+         //Wait for an incoming connection attempt
+         continue;
+      }
+
+      //Record the handle of the newly created socket
+      context->socket = clientSocket;
+
+      //Create a task to service the current connection
+      task = osCreateTask("TCP Discard Connection", tcpDiscardConnectionTask,
+         context, DISCARD_SERVICE_STACK_SIZE, DISCARD_SERVICE_PRIORITY);
+
+      //Did we encounter an error?
+      if(task == OS_INVALID_HANDLE)
+      {
+         //Close socket
+         socketClose(clientSocket);
+         //Release resources
+         osFreeMem(context);
+      }
+   }
+}
+
+
+/**
+ * @brief TCP discard service implementation
+ * @param[in] param Pointer to the discard service context
+ **/
+
+void tcpDiscardConnectionTask(void *param)
+{
+   error_t error;
+   size_t n;
+   size_t byteCount;
+   systime_t startTime;
+   systime_t duration;
+   DiscardServiceContext *context;
+
+   //Get a pointer to the context
+   context = (DiscardServiceContext *) param;
+   //Get current time
+   startTime = osGetSystemTime();
+
+   //Total number of bytes received
+   byteCount = 0;
+
+   //Main loop
+   while(1)
+   {
+      //Throw away any received datagram...
+      error = socketReceive(context->socket, context->buffer, DISCARD_BUFFER_SIZE, &n, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Total number of bytes received
+      byteCount += n;
+   }
+
+   //Graceful shutdown
+   socketShutdown(context->socket, SOCKET_SD_BOTH);
+   //Compute total duration
+   duration = osGetSystemTime() - startTime;
+   //Avoid division by zero...
+   if(!duration) duration = 1;
+
+   //Debug message
+   TRACE_INFO("Discard service: %" PRIuSIZE " bytes "
+      "received in %" PRIu32 " ms (%" PRIu32 " kBps, %" PRIu32 " kbps)\r\n",
+      byteCount, duration, byteCount / duration, (byteCount * 8) / duration);
+
+   //Close socket
+   socketClose(context->socket);
+   //Release previously allocated memory
+   osFreeMem(context);
+
+   //Kill ourselves
+   osDeleteTask(NULL);
+}
+
+
+/**
+ * @brief Start UDP discard service
+ * @return Error code
+ **/
+
+error_t udpDiscardStart(void)
+{
+   error_t error;
+   DiscardServiceContext *context;
+   OsTask *task;
+
+   //Debug message
+   TRACE_INFO("Starting UDP discard service...\r\n");
+
+   //Allocate a memory block to hold the context
+   context = osAllocMem(sizeof(DiscardServiceContext));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Start of exception handling block
+   do
+   {
+      //Open a UDP socket
+      context->socket = socketOpen(SOCKET_TYPE_DGRAM, SOCKET_IP_PROTO_UDP);
+
+      //Failed to open socket?
+      if(!context->socket)
+      {
+         //Report an error
+         error = ERROR_OPEN_FAILED;
+         //Exit immediately
+         break;
+      }
+
+      //The server listens for incoming datagrams on port 9
+      error = socketBind(context->socket, &IP_ADDR_ANY, DISCARD_PORT);
+      //Unable to bind the socket to the desired port?
+      if(error)
+         break;
+
+      //Create a task to handle incoming datagrams
+      task = osCreateTask("UDP Discard", udpDiscardTask,
+         context, DISCARD_SERVICE_STACK_SIZE, DISCARD_SERVICE_PRIORITY);
+
+      //Unable to create the task?
+      if(task == OS_INVALID_HANDLE)
+      {
+         //Report an error to the calling function
+         error = ERROR_OUT_OF_RESOURCES;
+         break;
+      }
+
+      //End of exception handling block
+   } while(0);
+
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects...
+      socketClose(context->socket);
+      osFreeMem(context);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief UDP discard service implementation
+ * @param[in] param Pointer to the discard service context
+ **/
+
+void udpDiscardTask(void *param)
+{
+   error_t error;
+   size_t length;
+   uint16_t port;
+   IpAddr ipAddr;
+   DiscardServiceContext *context;
+
+   //Get a pointer to the context
+   context = (DiscardServiceContext *) param;
+
+   //Main loop
+   while(1)
+   {
+      //Wait for an incoming datagram
+      error = socketReceiveFrom(context->socket, &ipAddr, &port,
+         context->buffer, DISCARD_BUFFER_SIZE, &length, 0);
+
+      //Any datagram received?
+      if(!error)
+      {
+         //Debug message
+         TRACE_INFO("Discard service: %" PRIuSIZE " bytes received from %s port %" PRIu16 "\r\n",
+            length, ipAddrToString(&ipAddr, NULL), port);
+
+         //Throw away any received datagram...
+      }
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/std_services/discard.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,87 @@
+/**
+ * @file discard.h
+ * @brief Discard protocol
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _DISCARD_H
+#define _DISCARD_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/socket.h"
+
+//Stack size required to run the discard service
+#ifndef DISCARD_SERVICE_STACK_SIZE
+   #define DISCARD_SERVICE_STACK_SIZE 600
+#elif (DISCARD_SERVICE_STACK_SIZE < 1)
+   #error DISCARD_SERVICE_STACK_SIZE parameter is not valid
+#endif
+
+//Priority at which the discard service should run
+#ifndef DISCARD_SERVICE_PRIORITY
+   #define DISCARD_SERVICE_PRIORITY OS_TASK_PRIORITY_NORMAL
+#endif
+
+//Size of the buffer for input/output operations
+#ifndef DISCARD_BUFFER_SIZE
+   #define DISCARD_BUFFER_SIZE 1500
+#elif (DISCARD_BUFFER_SIZE < 1)
+   #error DISCARD_BUFFER_SIZE parameter is not valid
+#endif
+
+//Maximum time the TCP discard server will wait before closing the connection
+#ifndef DISCARD_TIMEOUT
+   #define DISCARD_TIMEOUT 20000
+#elif (DISCARD_TIMEOUT < 1)
+   #error DISCARD_TIMEOUT parameter is not valid
+#endif
+
+//Discard service port
+#define DISCARD_PORT 9
+
+
+/**
+ * @brief Discard service context
+ **/
+
+typedef struct
+{
+   Socket *socket;
+   char_t buffer[DISCARD_BUFFER_SIZE];
+} DiscardServiceContext;
+
+
+//TCP discard service related functions
+error_t tcpDiscardStart(void);
+void tcpDiscardListenerTask(void *param);
+void tcpDiscardConnectionTask(void *param);
+
+//UDP discard service related functions
+error_t udpDiscardStart(void);
+void udpDiscardTask(void *param);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/std_services/echo.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,433 @@
+/**
+ * @file echo.c
+ * @brief Echo protocol
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * The echo service simply sends back to the originating source
+ * any data it receives. Refer to RFC 862 for complete details
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL STD_SERVICES_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "std_services/echo.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (NET_STATIC_OS_RESOURCES == ENABLED)
+
+//UDP Echo service
+static OsTask udpEchoTaskStruct;
+static uint_t udpEchoTaskStack[ECHO_SERVICE_STACK_SIZE];
+
+#endif
+
+
+/**
+ * @brief Start TCP echo service
+ * @return Error code
+ **/
+
+error_t tcpEchoStart(void)
+{
+   error_t error;
+   Socket *socket;
+   OsTask *task;
+
+   //Debug message
+   TRACE_INFO("Starting TCP echo service...\r\n");
+
+   //Open a TCP socket
+   socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
+   //Failed to open socket?
+   if(socket == NULL)
+      return ERROR_OPEN_FAILED;
+
+   //Start of exception handling block
+   do
+   {
+      //Bind the newly created socket to port 7
+      error = socketBind(socket, &IP_ADDR_ANY, ECHO_PORT);
+      //Failed to bind the socket to the desired port?
+      if(error)
+         break;
+
+      //Place the socket into listening mode
+      error = socketListen(socket, 0);
+      //Any error to report?
+      if(error)
+         break;
+
+      //Create a task to handle incoming connection requests
+      task = osCreateTask("TCP Echo Listener", tcpEchoListenerTask,
+         socket, ECHO_SERVICE_STACK_SIZE, ECHO_SERVICE_PRIORITY);
+
+      //Unable to create the task?
+      if(task == OS_INVALID_HANDLE)
+      {
+         //Report an error to the calling function
+         error = ERROR_OUT_OF_RESOURCES;
+         break;
+      }
+
+      //End of exception handling block
+   } while(0);
+
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects...
+      socketClose(socket);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Task handling connection requests
+ * @param[in] param Pointer to the echo service context
+ **/
+
+void tcpEchoListenerTask(void *param)
+{
+   error_t error;
+   uint16_t clientPort;
+   IpAddr clientIpAddr;
+   Socket *serverSocket;
+   Socket *clientSocket;
+   EchoServiceContext *context;
+   OsTask *task;
+
+   //Point to the listening socket
+   serverSocket = (Socket *) param;
+
+   //Main loop
+   while(1)
+   {
+      //Accept an incoming connection
+      clientSocket = socketAccept(serverSocket, &clientIpAddr, &clientPort);
+      //Check whether a valid connection request has been received
+      if(!clientSocket) continue;
+
+      //Debug message
+      TRACE_INFO("Echo service: connection established with client %s port %" PRIu16 "\r\n",
+         ipAddrToString(&clientIpAddr, NULL), clientPort);
+
+      //The socket operates in non-blocking mode
+      error = socketSetTimeout(clientSocket, 0);
+
+      //Any error to report?
+      if(error)
+      {
+         //Close socket
+         socketClose(clientSocket);
+         //Wait for an incoming connection attempt
+         continue;
+      }
+
+      //Allocate resources for the new connection
+      context = osAllocMem(sizeof(EchoServiceContext));
+
+      //Failed to allocate memory?
+      if(context == NULL)
+      {
+         //Close socket
+         socketClose(clientSocket);
+         //Wait for an incoming connection attempt
+         continue;
+      }
+
+      //Record the handle of the newly created socket
+      context->socket = clientSocket;
+
+      //Create a task to service the current connection
+      task = osCreateTask("TCP Echo Connection", tcpEchoConnectionTask,
+         context, ECHO_SERVICE_STACK_SIZE, ECHO_SERVICE_PRIORITY);
+
+      //Did we encounter an error?
+      if(task == OS_INVALID_HANDLE)
+      {
+         //Close socket
+         socketClose(clientSocket);
+         //Release resources
+         osFreeMem(context);
+      }
+   }
+}
+
+
+/**
+ * @brief TCP echo service implementation
+ * @param[in] param Pointer to the echo service context
+ **/
+
+void tcpEchoConnectionTask(void *param)
+{
+   error_t error;
+   size_t n;
+   size_t writeIndex;
+   size_t readIndex;
+   size_t bufferLength;
+   size_t rxByteCount;
+   size_t txByteCount;
+   systime_t startTime;
+   systime_t duration;
+   SocketEventDesc eventDesc;
+   EchoServiceContext *context;
+
+   //Get a pointer to the context
+   context = (EchoServiceContext *) param;
+   //Get current time
+   startTime = osGetSystemTime();
+
+   //Initialize variables
+   writeIndex = 0;
+   readIndex = 0;
+   bufferLength = 0;
+   rxByteCount = 0;
+   txByteCount = 0;
+
+   //Main loop
+   while(1)
+   {
+      //Buffer is empty?
+      if(!bufferLength)
+      {
+         //Get notified when the socket is readable
+         eventDesc.socket = context->socket;
+         eventDesc.eventMask = SOCKET_EVENT_RX_READY;
+      }
+      //Buffer is not empty of full?
+      else if(bufferLength < ECHO_BUFFER_SIZE)
+      {
+         //Get notified when the socket is readable or writable
+         eventDesc.socket = context->socket;
+         eventDesc.eventMask = SOCKET_EVENT_RX_READY | SOCKET_EVENT_TX_READY;
+      }
+      //Buffer is full?
+      else
+      {
+         //Get notified when the socket is writable
+         eventDesc.socket = context->socket;
+         eventDesc.eventMask = SOCKET_EVENT_TX_READY;
+      }
+
+      //Wait for an event to be fired
+      error = socketPoll(&eventDesc, 1, NULL, ECHO_TIMEOUT);
+      //Timeout error or any other exception to report?
+      if(error)
+         break;
+
+      //The socket is available for reading
+      if(eventDesc.eventFlags & SOCKET_EVENT_RX_READY)
+      {
+         //Read as much data as possible
+         n = MIN(ECHO_BUFFER_SIZE - writeIndex, ECHO_BUFFER_SIZE - bufferLength);
+
+         //Read incoming data
+         error = socketReceive(context->socket, context->buffer + writeIndex, n, &n, 0);
+         //Any error to report?
+         if(error)
+            break;
+
+         //Increment write index
+         writeIndex += n;
+         //Wrap around if necessary
+         if(writeIndex >= ECHO_BUFFER_SIZE)
+            writeIndex = 0;
+
+         //Increment buffer length
+         bufferLength += n;
+         //Total number of bytes received
+         rxByteCount += n;
+      }
+
+      //The socket is available for writing?
+      if(eventDesc.eventFlags & SOCKET_EVENT_TX_READY)
+      {
+         //Write as much data as possible
+         n = MIN(ECHO_BUFFER_SIZE - readIndex, bufferLength);
+
+         //Send data back to the client
+         error = socketSend(context->socket, context->buffer + readIndex, n, &n, 0);
+         //Any error to report?
+         if(error && error != ERROR_TIMEOUT)
+            break;
+
+         //Increment read index
+         readIndex += n;
+         //Wrap around if necessary
+         if(readIndex >= ECHO_BUFFER_SIZE)
+            readIndex = 0;
+
+         //Update buffer length
+         bufferLength -= n;
+         //Total number of bytes sent
+         txByteCount += n;
+      }
+   }
+
+   //Adjust timeout value
+   socketSetTimeout(context->socket, ECHO_TIMEOUT);
+   //Graceful shutdown
+   socketShutdown(context->socket, SOCKET_SD_BOTH);
+   //Compute total duration
+   duration = osGetSystemTime() - startTime;
+
+   //Debug message
+   TRACE_INFO("Echo service: %" PRIuSIZE " bytes received, %" PRIuSIZE " bytes sent in %" PRIu32 " ms\r\n",
+      rxByteCount, txByteCount, duration);
+
+   //Close socket
+   socketClose(context->socket);
+   //Release previously allocated memory
+   osFreeMem(context);
+
+   //Kill ourselves
+   osDeleteTask(NULL);
+}
+
+
+/**
+ * @brief Start UDP echo service
+ * @return Error code
+ **/
+
+error_t udpEchoStart(void)
+{
+   error_t error;
+   EchoServiceContext *context;
+
+#if (NET_STATIC_OS_RESOURCES == DISABLED)
+   OsTask *task;
+#endif
+
+   //Debug message
+   TRACE_INFO("Starting UDP echo service...\r\n");
+
+   //Allocate a memory block to hold the context
+   context = osAllocMem(sizeof(EchoServiceContext));
+   //Failed to allocate memory?
+   if(context == NULL)
+      return ERROR_OUT_OF_MEMORY;
+
+   //Start of exception handling block
+   do
+   {
+      //Open a UDP socket
+      context->socket = socketOpen(SOCKET_TYPE_DGRAM, SOCKET_IP_PROTO_UDP);
+
+      //Failed to open socket?
+      if(!context->socket)
+      {
+         //Report an error
+         error = ERROR_OPEN_FAILED;
+         //Exit immediately
+         break;
+      }
+
+      //The server listens for incoming datagrams on port 7
+      error = socketBind(context->socket, &IP_ADDR_ANY, ECHO_PORT);
+      //Unable to bind the socket to the desired port?
+      if(error)
+         break;
+
+#if (NET_STATIC_OS_RESOURCES == ENABLED)
+      //Create a task to handle incoming datagrams
+      osCreateStaticTask(&udpEchoTaskStruct, "UDP Echo", udpEchoTask, context,
+         udpEchoTaskStack, ECHO_SERVICE_STACK_SIZE, ECHO_SERVICE_PRIORITY);
+#else
+      //Create a task to handle incoming datagrams
+      task = osCreateTask("UDP Echo", udpEchoTask,
+         context, ECHO_SERVICE_STACK_SIZE, ECHO_SERVICE_PRIORITY);
+
+      //Unable to create the task?
+      if(task == OS_INVALID_HANDLE)
+      {
+         //Report an error to the calling function
+         error = ERROR_OUT_OF_RESOURCES;
+         break;
+      }
+#endif
+
+      //End of exception handling block
+   } while(0);
+
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects...
+      socketClose(context->socket);
+      osFreeMem(context);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief UDP echo service implementation
+ * @param[in] param Pointer to the echo service context
+ **/
+
+void udpEchoTask(void *param)
+{
+   error_t error;
+   size_t length;
+   uint16_t port;
+   IpAddr ipAddr;
+   EchoServiceContext *context;
+
+   //Get a pointer to the context
+   context = (EchoServiceContext *) param;
+
+   //Main loop
+   while(1)
+   {
+      //Wait for an incoming datagram
+      error = socketReceiveFrom(context->socket, &ipAddr, &port,
+         context->buffer, ECHO_BUFFER_SIZE, &length, 0);
+
+      //Any datagram received?
+      if(!error)
+      {
+         //Debug message
+         TRACE_INFO("Echo service: %" PRIuSIZE " bytes received from %s port %" PRIu16 "\r\n",
+            length, ipAddrToString(&ipAddr, NULL), port);
+
+         //Send the data back to the source
+         error = socketSendTo(context->socket, &ipAddr, port,
+            context->buffer, length, NULL, 0);
+      }
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/std_services/echo.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,87 @@
+/**
+ * @file echo.h
+ * @brief Echo protocol
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _ECHO_H
+#define _ECHO_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/socket.h"
+
+//Stack size required to run the echo service
+#ifndef ECHO_SERVICE_STACK_SIZE
+   #define ECHO_SERVICE_STACK_SIZE 600
+#elif (ECHO_SERVICE_STACK_SIZE < 1)
+   #error ECHO_SERVICE_STACK_SIZE parameter is not valid
+#endif
+
+//Priority at which the echo service should run
+#ifndef ECHO_SERVICE_PRIORITY
+   #define ECHO_SERVICE_PRIORITY OS_TASK_PRIORITY_NORMAL
+#endif
+
+//Size of the buffer for input/output operations
+#ifndef ECHO_BUFFER_SIZE
+   #define ECHO_BUFFER_SIZE 1500
+#elif (ECHO_BUFFER_SIZE < 1)
+   #error ECHO_BUFFER_SIZE parameter is not valid
+#endif
+
+//Maximum time the TCP echo server will wait before closing the connection
+#ifndef ECHO_TIMEOUT
+   #define ECHO_TIMEOUT 20000
+#elif (ECHO_TIMEOUT < 1)
+   #error ECHO_TIMEOUT parameter is not valid
+#endif
+
+//Echo service port
+#define ECHO_PORT 7
+
+
+/**
+ * @brief Echo service context
+ **/
+
+typedef struct
+{
+   Socket *socket;
+   char_t buffer[ECHO_BUFFER_SIZE];
+} EchoServiceContext;
+
+
+//TCP echo service related functions
+error_t tcpEchoStart(void);
+void tcpEchoListenerTask(void *param);
+void tcpEchoConnectionTask(void *param);
+
+//UDP echo service related functions
+error_t udpEchoStart(void);
+void udpEchoTask(void *param);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/tftp/tftp_common.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,140 @@
+/**
+ * @file tftp_common.h
+ * @brief Definitions common to TFTP client and server
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TFTP_COMMON_H
+#define _TFTP_COMMON_H
+
+//Dependencies
+#include "core/net.h"
+
+//TFTP port number
+#define TFTP_PORT 69
+
+
+/**
+ * @brief TFTP opcodes
+ **/
+
+typedef enum
+{
+   TFTP_OPCODE_RRQ   = 1, ///<Read request
+   TFTP_OPCODE_WRQ   = 2, ///<Write request
+   TFTP_OPCODE_DATA  = 3, ///<Data
+   TFTP_OPCODE_ACK   = 4, ///<Acknowledgment
+   TFTP_OPCODE_ERROR = 5, ///<Error
+   TFTP_OPCODE_OACK  = 6  ///<Option acknowledgment
+} TftpOpcode;
+
+
+/**
+ * @brief TFTP error codes
+ **/
+
+typedef enum
+{
+   TFTP_ERROR_NOT_DEFINED         = 0,
+   TFTP_ERROR_FILE_NOT_FOUND      = 1,
+   TFTP_ERROR_ACCESS_VIOLATION    = 2,
+   TFTP_ERROR_DISK_FULL           = 3,
+   TFTP_ERROR_ILLEGAL_OPERATION   = 4,
+   TFTP_ERROR_UNKNOWN_TID         = 5,
+   TFTP_ERROR_FILE_ALREADY_EXISTS = 6,
+   TFTP_ERROR_NO_SUCH_USER        = 7
+} TftpErrorCode;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief Read request packet (RRQ)
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t opcode;   //0-1
+   char_t filename[]; //2
+} __end_packed TftpRrqPacket;
+
+
+/**
+ * @brief Write request packet (WRQ)
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t opcode;   //0-1
+   char_t filename[]; //2
+} __end_packed TftpWrqPacket;
+
+
+/**
+ * @brief Data packet (DATA)
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t opcode; //0-1
+   uint16_t block;  //2-3
+   uint8_t data[];  //4
+} __end_packed TftpDataPacket;
+
+
+/**
+ * @brief Acknowledgment packet (ACK)
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t opcode; //0-1
+   uint16_t block;  //2-3
+} __end_packed TftpAckPacket;
+
+
+/**
+ * @brief Error packet (ERROR)
+ **/
+
+typedef __start_packed struct
+{
+   uint16_t opcode;    //0-1
+   uint16_t errorCode; //2-3
+   char_t errorMsg[];  //4
+} __end_packed TftpErrorPacket;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/tftp/tftp_server.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1167 @@
+/**
+ * @file tftp_server.c
+ * @brief TFTP server
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @section Description
+ *
+ * TFTP is a very simple protocol used to transfer files. Refer to the
+ * following RFCs for complete details:
+ * - RFC 1123:  Requirements for Internet Hosts
+ * - RFC 1350: The TFTP Protocol (Revision 2)
+ * - RFC 1782: TFTP Option Extension
+ * - RFC 1783: TFTP Blocksize Option
+ * - RFC 1784: TFTP Timeout Interval and Transfer Size Options
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL TFTP_TRACE_LEVEL
+
+//Dependencies
+#include "tftp/tftp_server.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (TFTP_SERVER_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Initialize settings with default values
+ * @param[out] settings Structure that contains TFTP server settings
+ **/
+
+void tftpServerGetDefaultSettings(TftpServerSettings *settings)
+{
+   //The TFTP server is not bound to any interface
+   settings->interface = NULL;
+
+   //TFTP port number
+   settings->port = TFTP_PORT;
+
+   //Open file callback function
+   settings->openFileCallback = NULL;
+   //Write file callback function
+   settings->writeFileCallback = NULL;
+   //Read file callback function
+   settings->readFileCallback = NULL;
+   //Close file callback function
+   settings->closeFileCallback = NULL;
+}
+
+
+/**
+ * @brief TFTP server initialization
+ * @param[in] context Pointer to the TFTP server context
+ * @param[in] settings TFTP server specific settings
+ * @return Error code
+ **/
+
+error_t tftpServerInit(TftpServerContext *context, const TftpServerSettings *settings)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("Initializing TFTP server...\r\n");
+
+   //Ensure the parameters are valid
+   if(context == NULL || settings == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Clear the TFTP server context
+   memset(context, 0, sizeof(TftpServerContext));
+
+   //Save user settings
+   context->settings = *settings;
+
+   //Create an event object to poll the state of sockets
+   if(!osCreateEvent(&context->event))
+   {
+      //Failed to create event
+      return ERROR_OUT_OF_RESOURCES;
+   }
+
+   //Start of exception handling block
+   do
+   {
+      //Open a UDP socket
+      context->socket = socketOpen(SOCKET_TYPE_DGRAM, SOCKET_IP_PROTO_UDP);
+
+      //Failed to open socket?
+      if(context->socket == NULL)
+      {
+         //Report an error
+         error = ERROR_OPEN_FAILED;
+         //Exit immediately
+         break;
+      }
+
+      //Associate the socket with the relevant interface
+      error = socketBindToInterface(context->socket, settings->interface);
+      //Unable to bind the socket to the desired interface?
+      if(error)
+         break;
+
+      //The TFTP server listens for connection requests on port 69
+      error = socketBind(context->socket, &IP_ADDR_ANY, settings->port);
+      //Unable to bind the socket to the desired port?
+      if(error)
+         break;
+
+      //End of exception handling block
+   } while(0);
+
+   //Did we encounter an error?
+   if(error)
+   {
+      //Free previously allocated resources
+      osDeleteEvent(&context->event);
+      //Close socket
+      socketClose(context->socket);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Start TFTP server
+ * @param[in] context Pointer to the TFTP server context
+ * @return Error code
+ **/
+
+error_t tftpServerStart(TftpServerContext *context)
+{
+   OsTask *task;
+
+   //Debug message
+   TRACE_INFO("Starting TFTP server...\r\n");
+
+   //Make sure the TFTP server context is valid
+   if(context == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Create the TFTP server task
+   task = osCreateTask("TFTP Server", (OsTaskCode) tftpServerTask,
+      context, TFTP_SERVER_STACK_SIZE, TFTP_SERVER_PRIORITY);
+
+   //Unable to create the task?
+   if(task == OS_INVALID_HANDLE)
+      return ERROR_OUT_OF_RESOURCES;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief TFTP server task
+ * @param[in] context Pointer to the TFTP server context
+ **/
+
+void tftpServerTask(TftpServerContext *context)
+{
+   error_t error;
+   uint_t i;
+   TftpClientConnection *connection;
+
+#if (NET_RTOS_SUPPORT == ENABLED)
+   //Process events
+   while(1)
+   {
+#endif
+      //Clear event descriptor set
+      memset(context->eventDesc, 0, sizeof(context->eventDesc));
+
+      //Specify the events the application is interested in
+      for(i = 0; i < TFTP_SERVER_MAX_CONNECTIONS; i++)
+      {
+         //Point to the structure describing the current connection
+         connection = &context->connection[i];
+
+         //Loop through active connections only
+         if(connection->state != TFTP_STATE_CLOSED)
+         {
+            //Wait for a packet to be received
+            context->eventDesc[i].socket = connection->socket;
+            context->eventDesc[i].eventMask = SOCKET_EVENT_RX_READY;
+         }
+      }
+
+      //The TFTP server listens for connection requests on port 69
+      context->eventDesc[i].socket = context->socket;
+      context->eventDesc[i].eventMask = SOCKET_EVENT_RX_READY;
+
+      //Wait for one of the set of sockets to become ready to perform I/O
+      error = socketPoll(context->eventDesc, TFTP_SERVER_MAX_CONNECTIONS + 1,
+         &context->event, TFTP_SERVER_TICK_INTERVAL);
+
+      //Verify status code
+      if(!error)
+      {
+         //Event-driven processing
+         for(i = 0; i < TFTP_SERVER_MAX_CONNECTIONS; i++)
+         {
+            //Point to the structure describing the current connection
+            connection = &context->connection[i];
+
+            //Loop through active connections only
+            if(connection->state != TFTP_STATE_CLOSED)
+            {
+               //Check whether a packet has been received
+               if(context->eventDesc[i].eventFlags & SOCKET_EVENT_RX_READY)
+               {
+                  //Process incoming packet
+                  tftpServerProcessPacket(context, connection);
+               }
+            }
+         }
+
+         //Any connection request received on port 69?
+         if(context->eventDesc[i].eventFlags & SOCKET_EVENT_RX_READY)
+         {
+            //Accept connection request
+            tftpServerAcceptRequest(context);
+         }
+      }
+
+      //Handle periodic operations
+      tftpServerTick(context);
+
+#if (NET_RTOS_SUPPORT == ENABLED)
+   }
+#endif
+}
+
+
+/**
+ * @brief Handle periodic operations
+ * @param[in] context Pointer to the TFTP server context
+ **/
+
+void tftpServerTick(TftpServerContext *context)
+{
+   uint_t i;
+   systime_t time;
+   TftpClientConnection *connection;
+
+   //Get current time
+   time = osGetSystemTime();
+
+   //Handle periodic operations
+   for(i = 0; i < TFTP_SERVER_MAX_CONNECTIONS; i++)
+   {
+      //Point to the structure describing the current connection
+      connection = &context->connection[i];
+
+      //Check current state
+      if(connection->state == TFTP_STATE_READING ||
+         connection->state == TFTP_STATE_WRITING ||
+         connection->state == TFTP_STATE_READ_COMPLETE)
+      {
+         //Check current time
+         if(timeCompare(time, connection->timestamp + TFTP_SERVER_TIMEOUT) >= 0)
+         {
+            //Handle retransmissions
+            if(connection->retransmitCount < TFTP_SERVER_MAX_RETRIES)
+            {
+               //Retransmit last packet
+               tftpServerRetransmitPacket(connection);
+
+               //Save the time at which the packet was sent
+               connection->timestamp = osGetSystemTime();
+               //Increment retransmission counter
+               connection->retransmitCount++;
+            }
+            else
+            {
+               //Close connection
+               tftpServerCloseConnection(connection);
+            }
+         }
+      }
+      else if(connection->state == TFTP_STATE_WRITE_COMPLETE)
+      {
+         //The host sending the final ACK will wait for a while before terminating
+         //in order to retransmit the final ACK if it has been lost
+         if(timeCompare(time, connection->timestamp + TFTP_SERVER_FINAL_DELAY) >= 0)
+         {
+            //Close connection
+            tftpServerCloseConnection(connection);
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief Accept connection request
+ * @param[in] context Pointer to the TFTP server context
+ **/
+
+void tftpServerAcceptRequest(TftpServerContext *context)
+{
+   error_t error;
+   size_t length;
+   uint16_t opcode;
+   IpAddr clientIpAddr;
+   uint16_t clientPort;
+
+   //Read incoming TFTP packet
+   error = socketReceiveFrom(context->socket, &clientIpAddr, &clientPort,
+      context->packet, TFTP_SERVER_MAX_PACKET_SIZE, &length, 0);
+
+   //Failed to read packet?
+   if(error)
+      return;
+
+   //Debug message
+   TRACE_INFO("TFTP Server: Accepting connection from %s port %" PRIu16 "...\r\n",
+      ipAddrToString(&clientIpAddr, NULL), clientPort);
+
+   //Sanity check
+   if(length < sizeof(uint16_t))
+      return;
+
+   //Retrieve TFTP packet type
+   opcode = LOAD16BE(context->packet);
+
+   //Read request received?
+   if(opcode == TFTP_OPCODE_RRQ)
+   {
+      //Process RRQ packet
+      tftpServerProcessRrqPacket(context, &clientIpAddr,
+         clientPort, (TftpRrqPacket *) context->packet, length);
+   }
+   //Write request received?
+   else if(opcode == TFTP_OPCODE_WRQ)
+   {
+      //Process WRQ packet
+      tftpServerProcessWrqPacket(context, &clientIpAddr,
+         clientPort, (TftpWrqPacket *) context->packet, length);
+   }
+   //Invalid request received?
+   else
+   {
+      //Discard incoming packet
+   }
+}
+
+
+/**
+ * @brief Process incoming packet
+ * @param[in] context Pointer to the TFTP server context
+ * @param[in] connection Pointer to the client connection
+ **/
+
+void tftpServerProcessPacket(TftpServerContext *context,
+   TftpClientConnection *connection)
+{
+   error_t error;
+   size_t length;
+   uint16_t opcode;
+   IpAddr clientIpAddr;
+   uint16_t clientPort;
+
+   //Read incoming TFTP packet
+   error = socketReceiveFrom(connection->socket, &clientIpAddr, &clientPort,
+      context->packet, TFTP_SERVER_MAX_PACKET_SIZE, &length, 0);
+
+   //Failed to read packet?
+   if(error)
+      return;
+
+   //Sanity check
+   if(length < sizeof(uint16_t))
+      return;
+
+   //Retrieve TFTP packet type
+   opcode = LOAD16BE(context->packet);
+
+   //Data packet received?
+   if(opcode == TFTP_OPCODE_DATA)
+   {
+      //Process DATA packet
+      tftpServerProcessDataPacket(connection,
+         (TftpDataPacket *) context->packet, length);
+   }
+   //Acknowledgment packet received?
+   else if(opcode == TFTP_OPCODE_ACK)
+   {
+      //Process ACK packet
+      tftpServerProcessAckPacket(connection,
+         (TftpAckPacket *) context->packet, length);
+   }
+   //Error packet received?
+   else if(opcode == TFTP_OPCODE_ERROR)
+   {
+      //Process ERROR packet
+      tftpServerProcessErrorPacket(connection,
+         (TftpErrorPacket *) context->packet, length);
+   }
+   //Invalid packet received?
+   else
+   {
+      //Discard incoming packet
+   }
+}
+
+
+/**
+ * @brief Process incoming RRQ packet
+ * @param[in] context Pointer to the TFTP server context
+ * @param[in] clientIpAddr IP address of the client
+ * @param[in] clientPort Port number used by the client
+ * @param[in] rrqPacket Pointer to the RRQ packet
+ * @param[in] length Length of the packet, in bytes
+ **/
+
+void tftpServerProcessRrqPacket(TftpServerContext *context, const IpAddr *clientIpAddr,
+   uint16_t clientPort, const TftpRrqPacket *rrqPacket, size_t length)
+{
+   const char_t *mode;
+   TftpClientConnection *connection;
+
+   //Debug message
+   TRACE_DEBUG("TFTP Server: RRQ packet received (%" PRIuSIZE " bytes)...\r\n",
+      length);
+
+   //Make sure the length of the RRQ packet is acceptable
+   if(length <= sizeof(TftpRrqPacket))
+      return;
+
+   //Compute the number of bytes that follows the 2-byte opcode
+   length -= sizeof(TftpRrqPacket);
+
+   //Point to the incoming RRQ packet
+   rrqPacket = (TftpRrqPacket *) context->packet;
+
+   //Malformed RRQ packet?
+   if(rrqPacket->filename[length - 1] != '\0')
+      return;
+
+   //Compute the length of the mode string
+   length -= strlen(rrqPacket->filename) + 1;
+
+   //Malformed RRQ packet?
+   if(length == 0)
+      return;
+
+   //Point to the mode string
+   mode = rrqPacket->filename + strlen(rrqPacket->filename) + 1;
+
+   //Debug message
+   TRACE_DEBUG("  Opcode = %u\r\n", ntohs(rrqPacket->opcode));
+   TRACE_DEBUG("  Filename = %s\r\n", rrqPacket->filename);
+   TRACE_DEBUG("  Mode = %s\r\n", mode);
+
+   //Create a new connection
+   connection = tftpServerOpenConnection(context, clientIpAddr, clientPort);
+
+   //Any error to report?
+   if(connection == NULL)
+      return;
+
+   //Open the specified file for reading
+   if(context->settings.openFileCallback != NULL)
+   {
+      //Invoke user callback function
+      connection->file = context->settings.openFileCallback(rrqPacket->filename,
+         mode, FALSE);
+   }
+   else
+   {
+      //No callback function defined
+      connection->file = NULL;
+   }
+
+   //Check if the file was successfully opened
+   if(connection->file != NULL)
+   {
+      //The read operation is in progress...
+      connection->state = TFTP_STATE_READING;
+      //Initialize block number
+      connection->block = 1;
+
+      //Send the first DATA packet
+      tftpServerSendDataPacket(connection);
+   }
+   else
+   {
+      //If the reply is an error packet, then the request has been denied
+      tftpServerSendErrorPacket(connection, TFTP_ERROR_NOT_DEFINED,
+         "Failed to open file");
+
+      //Close the connection
+      tftpServerCloseConnection(connection);
+   }
+}
+
+
+/**
+ * @brief Process incoming WRQ packet
+ * @param[in] context Pointer to the TFTP server context
+ * @param[in] clientIpAddr IP address of the client
+ * @param[in] clientPort Port number used by the client
+ * @param[in] wrqPacket Pointer to the WRQ packet
+ * @param[in] length Length of the packet, in bytes
+ **/
+
+void tftpServerProcessWrqPacket(TftpServerContext *context, const IpAddr *clientIpAddr,
+   uint16_t clientPort, const TftpWrqPacket *wrqPacket, size_t length)
+{
+   const char_t *mode;
+   TftpClientConnection *connection;
+
+   //Debug message
+   TRACE_DEBUG("TFTP Server: WRQ packet received (%" PRIuSIZE " bytes)...\r\n",
+      length);
+
+   //Make sure the length of the WRQ packet is acceptable
+   if(length <= sizeof(TftpWrqPacket))
+      return;
+
+   //Compute the number of bytes that follows the 2-byte opcode
+   length -= sizeof(TftpWrqPacket);
+
+   //Point to the incoming WRQ packet
+   wrqPacket = (TftpWrqPacket *) context->packet;
+
+   //Malformed WRQ packet?
+   if(wrqPacket->filename[length - 1] != '\0')
+      return;
+
+   //Compute the length of the mode string
+   length -= strlen(wrqPacket->filename) + 1;
+
+   //Malformed WRQ packet?
+   if(length == 0)
+      return;
+
+   //Point to the mode string
+   mode = wrqPacket->filename + strlen(wrqPacket->filename) + 1;
+
+   //Debug message
+   TRACE_DEBUG("  Opcode = %u\r\n", ntohs(wrqPacket->opcode));
+   TRACE_DEBUG("  Filename = %s\r\n", wrqPacket->filename);
+   TRACE_DEBUG("  Mode = %s\r\n", mode);
+
+   //Create a new connection
+   connection = tftpServerOpenConnection(context, clientIpAddr, clientPort);
+
+   //Any error to report?
+   if(connection == NULL)
+      return;
+
+   //Open the specified file for writing
+   if(context->settings.openFileCallback != NULL)
+   {
+      //Invoke user callback function
+      connection->file = context->settings.openFileCallback(wrqPacket->filename,
+         mode, TRUE);
+   }
+   else
+   {
+      //No callback function defined
+      connection->file = NULL;
+   }
+
+   //Check if the file was successfully opened
+   if(connection->file != NULL)
+   {
+      //The write operation is in progress...
+      connection->state = TFTP_STATE_WRITING;
+      //Initialize block number
+      connection->block = 0;
+
+      //The positive response to a write request is an acknowledgment
+      //packet with block number zero
+      tftpServerSendAckPacket(connection);
+
+      //Increment block number
+      connection->block++;
+   }
+   else
+   {
+      //If the reply is an error packet, then the request has been denied
+      tftpServerSendErrorPacket(connection, TFTP_ERROR_NOT_DEFINED,
+         "Failed to open file");
+
+      //Close the connection
+      tftpServerCloseConnection(connection);
+   }
+}
+
+
+/**
+ * @brief Process incoming DATA packet
+ * @param[in] connection Pointer to the client connection
+ * @param[in] dataPacket Pointer to the DATA packet
+ * @param[in] length Length of the packet, in bytes
+ **/
+
+void tftpServerProcessDataPacket(TftpClientConnection *connection,
+   const TftpDataPacket *dataPacket, size_t length)
+{
+   error_t error;
+   size_t offset;
+
+   //Debug message
+   TRACE_DEBUG("TFTP Server: DATA packet received (%" PRIuSIZE " bytes)...\r\n",
+      length);
+
+   //Make sure the length of the DATA packet is acceptable
+   if(length < sizeof(TftpDataPacket))
+      return;
+
+   //Calculate the length of the data
+   length -= sizeof(TftpDataPacket);
+
+   //Debug message
+   TRACE_DEBUG("  Opcode = %" PRIu16 "\r\n", ntohs(dataPacket->opcode));
+   TRACE_DEBUG("  Block = %" PRIu16 "\r\n", ntohs(dataPacket->block));
+
+   //Check current state
+   if(connection->state == TFTP_STATE_WRITING)
+   {
+      //Check block number
+      if(ntohs(dataPacket->block) == connection->block)
+      {
+         //Write data to the output file
+         if(connection->settings->writeFileCallback != NULL)
+         {
+            //Calculate the offset relative to the beginning of the file
+            offset = (connection->block - 1) * TFTP_SERVER_BLOCK_SIZE;
+
+            //Invoke user callback function
+            error = connection->settings->writeFileCallback(connection->file,
+               offset, dataPacket->data, length);
+         }
+         else
+         {
+            //No callback function defined
+            error = ERROR_WRITE_FAILED;
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Acknowledge the DATA packet
+            tftpServerSendAckPacket(connection);
+
+            //Increment block number
+            connection->block++;
+
+            //A data packet of less than 512 bytes signals termination of the transfer
+            if(length < TFTP_SERVER_BLOCK_SIZE)
+            {
+               //Properly close the file
+               if(connection->settings->closeFileCallback != NULL)
+               {
+                  //Invoke user callback function
+                  connection->settings->closeFileCallback(connection->file);
+               }
+
+               //Mark the file as closed
+               connection->file = NULL;
+
+               //The host sending the final ACK will wait for a while before terminating
+               //in order to retransmit the final ACK if it has been lost
+               connection->state = TFTP_STATE_WRITE_COMPLETE;
+
+               //Save current time
+               connection->timestamp = osGetSystemTime();
+            }
+         }
+         else
+         {
+            //An error occurs during the transfer
+            tftpServerSendErrorPacket(connection, TFTP_ERROR_NOT_DEFINED,
+               "Failed to write file");
+
+            //A TFTP server may terminate after sending an error message
+            tftpServerCloseConnection(connection);
+         }
+      }
+      else
+      {
+         //Retransmit ACK packet
+         tftpServerRetransmitPacket(connection);
+      }
+   }
+   else if(connection->state == TFTP_STATE_WRITE_COMPLETE)
+   {
+      //The acknowledger will know that the ACK has been lost if it
+      //receives the final DATA packet again
+      tftpServerRetransmitPacket(connection);
+   }
+}
+
+
+/**
+ * @brief Process incoming ACK packet
+ * @param[in] connection Pointer to the client connection
+ * @param[in] ackPacket Pointer to the ACK packet
+ * @param[in] length Length of the packet, in bytes
+ **/
+
+void tftpServerProcessAckPacket(TftpClientConnection *connection,
+   const TftpAckPacket *ackPacket, size_t length)
+{
+   //Debug message
+   TRACE_DEBUG("TFTP Server: ACK packet received (%" PRIuSIZE " bytes)...\r\n",
+      length);
+
+   //Make sure the length of the ACK packet is acceptable
+   if(length < sizeof(TftpAckPacket))
+      return;
+
+   //Debug message
+   TRACE_DEBUG("  Opcode = %" PRIu16 "\r\n", ntohs(ackPacket->opcode));
+   TRACE_DEBUG("  Block = %" PRIu16 "\r\n", ntohs(ackPacket->block));
+
+   //Check current state
+   if(connection->state == TFTP_STATE_READING)
+   {
+      //Make sure the ACK is not a duplicate
+      if(ntohs(ackPacket->block) == connection->block)
+      {
+         //The block number increases by one for each new block of data
+         connection->block++;
+
+         //Send DATA packet
+         tftpServerSendDataPacket(connection);
+      }
+   }
+   else if(connection->state == TFTP_STATE_READ_COMPLETE)
+   {
+      //Make sure the ACK is not a duplicate
+      if(ntohs(ackPacket->block) == connection->block)
+      {
+         //The host sending the last DATA must retransmit it until the packet is
+         //acknowledged or the sending host times out. If the response is an ACK,
+         //the transmission was completed successfully
+         tftpServerCloseConnection(connection);
+      }
+   }
+}
+
+
+/**
+ * @brief Process incoming ERROR packet
+ * @param[in] connection Pointer to the client connection
+ * @param[in] errorPacket Pointer to the ERROR packet
+ * @param[in] length Length of the packet, in bytes
+ **/
+
+void tftpServerProcessErrorPacket(TftpClientConnection *connection,
+   const TftpErrorPacket *errorPacket, size_t length)
+{
+   //Debug message
+   TRACE_DEBUG("TFTP Server: ERROR packet received (%" PRIuSIZE " bytes)...\r\n",
+      length);
+
+   //Make sure the length of the ERROR packet is acceptable
+   if(length < sizeof(TftpErrorPacket))
+      return;
+
+   //Debug message
+   TRACE_DEBUG("  Opcode = %" PRIu16 "\r\n", ntohs(errorPacket->opcode));
+   TRACE_DEBUG("  Error Code = %" PRIu16 "\r\n", ntohs(errorPacket->errorCode));
+
+   //Compute the length of the error message
+   length -= sizeof(TftpErrorPacket);
+
+   //Make sure the error message is terminated with a zero byte
+   if(length > 1 && errorPacket->errorMsg[length - 1] == '\0')
+   {
+      //Debug message
+      TRACE_DEBUG("  Error Msg = %s\r\n", errorPacket->errorMsg);
+   }
+
+   //Close connection
+   tftpServerCloseConnection(connection);
+}
+
+
+/**
+ * @brief Send DATA packet
+ * @param[in] connection Pointer to the client connection
+ * @return Error code
+ **/
+
+error_t tftpServerSendDataPacket(TftpClientConnection *connection)
+{
+   error_t error;
+   size_t offset;
+   TftpDataPacket *dataPacket;
+
+   //Point the buffer where to format the packet
+   dataPacket = (TftpDataPacket *) connection->packet;
+
+   //Format DATA packet
+   dataPacket->opcode = HTONS(TFTP_OPCODE_DATA);
+   dataPacket->block = htons(connection->block);
+
+   //Read more data from the input file
+   if(connection->settings->readFileCallback != NULL)
+   {
+      //Calculate the offset relative to the beginning of the file
+      offset = (connection->block - 1) * TFTP_SERVER_BLOCK_SIZE;
+
+      //Invoke user callback function
+      error = connection->settings->readFileCallback(connection->file, offset,
+         dataPacket->data, TFTP_SERVER_BLOCK_SIZE, &connection->packetLen);
+   }
+   else
+   {
+      //No callback function defined
+      error = ERROR_READ_FAILED;
+   }
+
+   //End of file?
+   if(error == ERROR_END_OF_FILE || error == ERROR_END_OF_STREAM)
+   {
+      //Catch exception
+      error = NO_ERROR;
+      //This is the the last block of data
+      connection->packetLen = 0;
+   }
+
+   //Check status code
+   if(!error)
+   {
+      //A data packet of less than 512 bytes signals termination of the transfer
+      if(connection->packetLen < TFTP_SERVER_BLOCK_SIZE)
+      {
+         //Properly close the file
+         if(connection->settings->closeFileCallback != NULL)
+         {
+            //Invoke user callback function
+            connection->settings->closeFileCallback(connection->file);
+         }
+
+         //Mark the file as closed
+         connection->file = NULL;
+
+         //The host sending the last DATA must retransmit it until the packet
+         //is acknowledged or the sending host times out
+         connection->state = TFTP_STATE_READ_COMPLETE;
+      }
+
+      //Length of the DATA packet
+      connection->packetLen += sizeof(TftpAckPacket);
+
+      //Debug message
+      TRACE_DEBUG("TFTP Server: Sending DATA packet (%" PRIuSIZE " bytes)...\r\n", connection->packetLen);
+      TRACE_DEBUG("  Opcode = %" PRIu16 "\r\n", ntohs(dataPacket->opcode));
+      TRACE_DEBUG("  Block = %" PRIu16 "\r\n", ntohs(dataPacket->block));
+
+      //Send DATA packet
+      error = socketSend(connection->socket, connection->packet,
+         connection->packetLen, NULL, 0);
+
+      //Save the time at which the packet was sent
+      connection->timestamp = osGetSystemTime();
+      //Reset retransmission counter
+      connection->retransmitCount = 0;
+   }
+   else
+   {
+      //An error occurs during the transfer
+      tftpServerSendErrorPacket(connection, TFTP_ERROR_NOT_DEFINED,
+         "Failed to read file");
+
+      //A TFTP server may terminate after sending an error message
+      tftpServerCloseConnection(connection);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send ACK packet
+ * @param[in] connection Pointer to the client connection
+ * @return Error code
+ **/
+
+error_t tftpServerSendAckPacket(TftpClientConnection *connection)
+{
+   error_t error;
+   TftpAckPacket *ackPacket;
+
+   //Point the buffer where to format the packet
+   ackPacket = (TftpAckPacket *) connection->packet;
+
+   //Format ACK packet
+   ackPacket->opcode = HTONS(TFTP_OPCODE_ACK);
+   ackPacket->block = htons(connection->block);
+
+   //Length of the ACK packet
+   connection->packetLen = sizeof(TftpAckPacket);
+
+   //Debug message
+   TRACE_DEBUG("TFTP Server: Sending ACK packet (%" PRIuSIZE " bytes)...\r\n", connection->packetLen);
+   TRACE_DEBUG("  Opcode = %" PRIu16 "\r\n", ntohs(ackPacket->opcode));
+   TRACE_DEBUG("  Block = %" PRIu16 "\r\n", ntohs(ackPacket->block));
+
+   //Send ACK packet
+   error = socketSend(connection->socket, connection->packet,
+      connection->packetLen, NULL, 0);
+
+   //Save the time at which the packet was sent
+   connection->timestamp = osGetSystemTime();
+   //Reset retransmission counter
+   connection->retransmitCount = 0;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send ERROR packet
+ * @param[in] connection Pointer to the client connection
+ * @param[in] errorCode Integer indicating the nature of the error
+ * @param[in] errorMsg Error message intended for human consumption
+ * @return Error code
+ **/
+
+error_t tftpServerSendErrorPacket(TftpClientConnection *connection,
+   uint16_t errorCode, const char_t *errorMsg)
+{
+   error_t error;
+   TftpErrorPacket *errorPacket;
+
+   //Check the length of the error message
+   if(strlen(errorMsg) >= TFTP_SERVER_BLOCK_SIZE)
+      return ERROR_INVALID_PARAMETER;
+
+   //Point the buffer where to format the packet
+   errorPacket = (TftpErrorPacket *) connection->packet;
+
+   //Format ERROR packet
+   errorPacket->opcode = HTONS(TFTP_OPCODE_ERROR);
+   errorPacket->errorCode = htons(errorCode);
+
+   //Copy error message
+   strcpy(errorPacket->errorMsg, errorMsg);
+
+   //Length of the ERROR packet
+   connection->packetLen = sizeof(TftpErrorPacket) + strlen(errorMsg) + 1;
+
+   //Debug message
+   TRACE_DEBUG("TFTP Server: Sending ERROR packet (%" PRIuSIZE " bytes)...\r\n", connection->packetLen);
+   TRACE_DEBUG("  Opcode = %" PRIu16 "\r\n", ntohs(errorPacket->opcode));
+   TRACE_DEBUG("  Error Code = %" PRIu16 "\r\n", ntohs(errorPacket->errorCode));
+   TRACE_DEBUG("  Error Msg = %s\r\n", errorPacket->errorMsg);
+
+   //Send ERROR packet
+   error = socketSend(connection->socket, connection->packet,
+      connection->packetLen, NULL, 0);
+
+   //Save the time at which the packet was sent
+   connection->timestamp = osGetSystemTime();
+   //Reset retransmission counter
+   connection->retransmitCount = 0;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Retransmit the last packet
+ * @param[in] connection Pointer to the client connection
+ * @return Error code
+ **/
+
+error_t tftpServerRetransmitPacket(TftpClientConnection *connection)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_DEBUG("TFTP Server: Retransmitting packet (%" PRIuSIZE " bytes)...\r\n",
+      connection->packetLen);
+
+   //Retransmit the last packet
+   error = socketSend(connection->socket, connection->packet,
+      connection->packetLen, NULL, 0);
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Create client connection
+ * @param[in] context Pointer to the TFTP server context
+ * @param[in] clientIpAddr IP address of the client
+ * @param[in] clientPort Port number used by the client
+ * @return Pointer to the structure describing the connection
+ **/
+
+TftpClientConnection *tftpServerOpenConnection(TftpServerContext *context,
+   const IpAddr *clientIpAddr, uint16_t clientPort)
+{
+   error_t error;
+   uint_t i;
+   TftpClientConnection *connection;
+   TftpClientConnection *oldestConnection;
+
+   //Keep track of the oldest connection that is waiting to
+   //retransmit the final ACK
+   oldestConnection = NULL;
+
+   //Loop through the connection table
+   for(i = 0; i < TFTP_SERVER_MAX_CONNECTIONS; i++)
+   {
+      //Point to the current entry
+      connection = &context->connection[i];
+
+      //Check the state of the current connection
+      if(connection->state == TFTP_STATE_CLOSED)
+      {
+         //The current entry is available
+         break;
+      }
+      else if(connection->state == TFTP_STATE_WRITE_COMPLETE)
+      {
+         //Keep track of the oldest connection that is waiting to
+         //retransmit the final ACK
+         if(oldestConnection == NULL)
+         {
+            //Save current connection
+            oldestConnection = connection;
+         }
+         else if(timeCompare(connection->timestamp,
+            oldestConnection->timestamp) < 0)
+         {
+            //Save current connection
+            oldestConnection = connection;
+         }
+      }
+   }
+
+   //The oldest connection that is waiting to retransmit the final ACK
+   //can be reused when the connection table runs out of space
+   if(i >= TFTP_SERVER_MAX_CONNECTIONS)
+   {
+      //Close the oldest connection
+      tftpServerCloseConnection(oldestConnection);
+      //Reuse the connection
+      connection = oldestConnection;
+   }
+
+   //Failed to create a new connection?
+   if(connection == NULL)
+      return NULL;
+
+   //Clear the structure describing the connection
+   memset(connection, 0, sizeof(TftpClientConnection));
+
+   //Open a UDP socket
+   connection->socket = socketOpen(SOCKET_TYPE_DGRAM, SOCKET_IP_PROTO_UDP);
+
+   //Failed to open socket?
+   if(connection->socket == NULL)
+      return NULL;
+
+   //Associate the socket with the relevant interface
+   error = socketBindToInterface(connection->socket, context->settings.interface);
+
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects
+      socketClose(connection->socket);
+      connection->socket = NULL;
+      //Exit immediately
+      return NULL;
+   }
+
+   //Connect the socket to the remote TFTP client
+   error = socketConnect(connection->socket, clientIpAddr, clientPort);
+
+   //Any error to report?
+   if(error)
+   {
+      //Clean up side effects
+      socketClose(connection->socket);
+      connection->socket = NULL;
+      //Exit immediately
+      return NULL;
+   }
+
+   //Reference to the TFTP server settings
+   connection->settings = &context->settings;
+   //Update connection state
+   connection->state = TFTP_STATE_OPEN;
+
+   //Pointer to the structure describing the connection
+   return connection;
+}
+
+
+/**
+ * @brief Close client connection
+ * @param[in] connection Pointer to the client connection
+ **/
+
+void tftpServerCloseConnection(TftpClientConnection *connection)
+{
+   //Debug message
+   TRACE_INFO("TFTP Server: Closing connection...\r\n");
+
+   //Any active connection?
+   if(connection->socket != NULL)
+   {
+      //Close UDP socket
+      socketClose(connection->socket);
+      connection->socket = NULL;
+   }
+
+   //Check whether a read or write operation is in progress...
+   if(connection->file != NULL)
+   {
+      //Properly close the file before closing the connection
+      if(connection->settings->closeFileCallback != NULL)
+      {
+         //Invoke user callback function
+         connection->settings->closeFileCallback(connection->file);
+      }
+
+      //Mark the file as closed
+      connection->file = NULL;
+   }
+
+   //Mark the connection as closed
+   connection->state = TFTP_STATE_CLOSED;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/tftp/tftp_server.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,245 @@
+/**
+ * @file tftp_server.h
+ * @brief TFTP server
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TFTP_SERVER_H
+#define _TFTP_SERVER_H
+
+//Dependencies
+#include "core/net.h"
+#include "tftp_common.h"
+
+//TFTP server support
+#ifndef TFTP_SERVER_SUPPORT
+   #define TFTP_SERVER_SUPPORT ENABLED
+#elif (TFTP_SERVER_SUPPORT != ENABLED && TFTP_SERVER_SUPPORT != DISABLED)
+   #error TFTP_SERVER_SUPPORT parameter is not valid
+#endif
+
+//Stack size required to run the TFTP server
+#ifndef TFTP_SERVER_STACK_SIZE
+   #define TFTP_SERVER_STACK_SIZE 650
+#elif (TFTP_SERVER_STACK_SIZE < 1)
+   #error TFTP_SERVER_STACK_SIZE parameter is not valid
+#endif
+
+//Priority at which the TFTP server should run
+#ifndef TFTP_SERVER_PRIORITY
+   #define TFTP_SERVER_PRIORITY OS_TASK_PRIORITY_NORMAL
+#endif
+
+//Maximum number of simultaneous connections
+#ifndef TFTP_SERVER_MAX_CONNECTIONS
+   #define TFTP_SERVER_MAX_CONNECTIONS 2
+#elif (TFTP_SERVER_MAX_CONNECTIONS < 1)
+   #error TFTP_SERVER_MAX_CONNECTIONS parameter is not valid
+#endif
+
+//TFTP server tick interval
+#ifndef TFTP_SERVER_TICK_INTERVAL
+   #define TFTP_SERVER_TICK_INTERVAL 500
+#elif (TFTP_SERVER_TICK_INTERVAL < 100)
+   #error TFTP_SERVER_TICK_INTERVAL parameter is not valid
+#endif
+
+//Maximum number of retransmissions of packets
+#ifndef TFTP_SERVER_MAX_RETRIES
+   #define TFTP_SERVER_MAX_RETRIES 5
+#elif (TFTP_SERVER_MAX_RETRIES < 1)
+   #error TFTP_SERVER_MAX_RETRIES parameter is not valid
+#endif
+
+//Retransmission timeout
+#ifndef TFTP_SERVER_TIMEOUT
+   #define TFTP_SERVER_TIMEOUT 5000
+#elif (TFTP_SERVER_TIMEOUT < 1000)
+   #error TFTP_SERVER_TIMEOUT parameter is not valid
+#endif
+
+//Additional delay before closing the connection (when sending the final ACK)
+#ifndef TFTP_SERVER_FINAL_DELAY
+   #define TFTP_SERVER_FINAL_DELAY 10000
+#elif (TFTP_SERVER_FINAL_DELAY < 1000)
+   #error TFTP_SERVER_FINAL_DELAY parameter is not valid
+#endif
+
+//Block size
+#ifndef TFTP_SERVER_BLOCK_SIZE
+   #define TFTP_SERVER_BLOCK_SIZE 512
+#elif (TFTP_SERVER_BLOCK_SIZE < 512)
+   #error TFTP_SERVER_BLOCK_SIZE parameter is not valid
+#endif
+
+//Maximum size of TFTP packets
+#define TFTP_SERVER_MAX_PACKET_SIZE (sizeof(TftpDataPacket) + TFTP_SERVER_BLOCK_SIZE)
+
+//Forward declaration of TftpClientConnection structure
+struct _TftpClientConnection;
+#define TftpClientConnection struct _TftpClientConnection
+
+//Forward declaration of TftpServerContext structure
+struct _TftpServerContext;
+#define TftpServerContext struct _TftpServerContext
+
+
+/**
+ * @brief TFTP connection state
+ **/
+
+typedef enum
+{
+   TFTP_STATE_CLOSED         = 0,
+   TFTP_STATE_OPEN           = 1,
+   TFTP_STATE_READING        = 2,
+   TFTP_STATE_WRITING        = 3,
+   TFTP_STATE_READ_COMPLETE  = 4,
+   TFTP_STATE_WRITE_COMPLETE = 5
+} TftpConnectionState;
+
+
+/**
+ * @brief Open file callback function
+ **/
+
+typedef void *(*TftpServerOpenFileCallback)(const char_t *filename,
+   const char_t *mode, bool_t writeAccess);
+
+
+/**
+ * @brief Write file callback function
+ **/
+
+typedef error_t (*TftpServerWriteFileCallback)(void *file,
+   size_t offset, const uint8_t *data, size_t length);
+
+
+/**
+ * @brief Read file callback function
+ **/
+
+typedef error_t (*TftpServerReadFileCallback)(void *file,
+   size_t offset, uint8_t *data, size_t size, size_t *length);
+
+
+/**
+ * @brief Close file callback function
+ **/
+
+typedef void (*TftpServerCloseFileCallback)(void *file);
+
+
+/**
+ * @brief TFTP server settings
+ **/
+
+typedef struct
+{
+   NetInterface *interface;                       ///<Underlying network interface
+   uint16_t port;                                 ///<TFTP port number
+   TftpServerOpenFileCallback openFileCallback;   ///<Open file callback function
+   TftpServerWriteFileCallback writeFileCallback; ///<Write file callback function
+   TftpServerReadFileCallback readFileCallback;   ///<Read file callback function
+   TftpServerCloseFileCallback closeFileCallback; ///<Close file callback function
+} TftpServerSettings;
+
+
+/**
+ * @brief TFTP client connection
+ **/
+
+struct _TftpClientConnection
+{
+   TftpServerSettings *settings;                ///<User settings
+   TftpConnectionState state;                   ///<Connection state
+   Socket *socket;                              ///<Underlying socket
+   void *file;                                  ///<File pointer
+   uint16_t block;                              ///<Block number
+   systime_t timestamp;                         ///<Time stamp to manage retransmissions
+   uint_t retransmitCount;                      ///<Retransmission counter
+   uint8_t packet[TFTP_SERVER_MAX_PACKET_SIZE]; ///<Outgoing TFTP packet
+   size_t packetLen;                            ///<Length of the outgoing packet
+};
+
+
+/**
+ * @brief TFTP server context
+ **/
+
+struct _TftpServerContext
+{
+   TftpServerSettings settings;                                  ///<User settings
+   OsEvent event;                                                ///<Event object used to poll the sockets
+   Socket *socket;                                               ///<Listening socket
+   TftpClientConnection connection[TFTP_SERVER_MAX_CONNECTIONS]; ///<Client connections
+   SocketEventDesc eventDesc[TFTP_SERVER_MAX_CONNECTIONS + 1];   ///<The events the application is interested in
+   uint8_t packet[TFTP_SERVER_MAX_PACKET_SIZE];                  ///<Incoming TFTP packet
+};
+
+
+//TFTP server related functions
+void tftpServerGetDefaultSettings(TftpServerSettings *settings);
+error_t tftpServerInit(TftpServerContext *context, const TftpServerSettings *settings);
+error_t tftpServerStart(TftpServerContext *context);
+
+void tftpServerTask(TftpServerContext *context);
+void tftpServerTick(TftpServerContext *context);
+
+void tftpServerAcceptRequest(TftpServerContext *context);
+
+void tftpServerProcessPacket(TftpServerContext *context,
+   TftpClientConnection *connection);
+
+void tftpServerProcessRrqPacket(TftpServerContext *context, const IpAddr *clientIpAddr,
+   uint16_t clientPort, const TftpRrqPacket *rrqPacket, size_t length);
+
+void tftpServerProcessWrqPacket(TftpServerContext *context, const IpAddr *clientIpAddr,
+   uint16_t clientPort, const TftpWrqPacket *wrqPacket, size_t length);
+
+void tftpServerProcessDataPacket(TftpClientConnection *connection,
+   const TftpDataPacket *dataPacket, size_t length);
+
+void tftpServerProcessAckPacket(TftpClientConnection *connection,
+   const TftpAckPacket *ackPacket, size_t length);
+
+void tftpServerProcessErrorPacket(TftpClientConnection *connection,
+   const TftpErrorPacket *errorPacket, size_t length);
+
+error_t tftpServerSendDataPacket(TftpClientConnection *connection);
+error_t tftpServerSendAckPacket(TftpClientConnection *connection);
+
+error_t tftpServerSendErrorPacket(TftpClientConnection *connection,
+   uint16_t errorCode, const char_t *errorMsg);
+
+error_t tftpServerRetransmitPacket(TftpClientConnection *connection);
+
+TftpClientConnection *tftpServerOpenConnection(TftpServerContext *context,
+   const IpAddr *clientIpAddr, uint16_t clientPort);
+
+void tftpServerCloseConnection(TftpClientConnection *connection);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/web_socket/web_socket.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1552 @@
+/**
+ * @file web_socket.c
+ * @brief WebSocket API (client and server)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL WEB_SOCKET_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include "core/net.h"
+#include "web_socket/web_socket.h"
+#include "web_socket/web_socket_auth.h"
+#include "web_socket/web_socket_frame.h"
+#include "web_socket/web_socket_transport.h"
+#include "web_socket/web_socket_misc.h"
+#include "str.h"
+#include "base64.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (WEB_SOCKET_SUPPORT == ENABLED)
+
+//WebSocket table
+WebSocket webSocketTable[WEB_SOCKET_MAX_COUNT];
+//Random data generation callback function
+WebSocketRandCallback webSockRandCallback;
+
+
+/**
+ * @brief WebSocket related initialization
+ * @return Error code
+ **/
+
+error_t webSocketInit(void)
+{
+   //Initialize WebSockets
+   memset(webSocketTable, 0, sizeof(webSocketTable));
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Register RNG callback function
+ * @param[in] callback RNG callback function
+ * @return Error code
+ **/
+
+error_t webSocketRegisterRandCallback(WebSocketRandCallback callback)
+{
+   //Check parameter
+   if(callback == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save callback function
+   webSockRandCallback = callback;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Create a WebSocket
+ * @return Handle referencing the new WebSocket
+ **/
+
+WebSocket *webSocketOpen(void)
+{
+   uint_t i;
+   WebSocket *webSocket;
+
+   //Initialize WebSocket handle
+   webSocket = NULL;
+
+   //Get exclusive access
+   osAcquireMutex(&netMutex);
+
+   //Loop through WebSocket descriptors
+   for(i = 0; i < WEB_SOCKET_MAX_COUNT; i++)
+   {
+      //Unused WebSocket found?
+      if(webSocketTable[i].state == WS_STATE_UNUSED)
+      {
+         //Save socket handle
+         webSocket = &webSocketTable[i];
+
+         //Clear associated structure
+         memset(webSocket, 0, sizeof(WebSocket));
+         //Set the default timeout to be used
+         webSocket->timeout = INFINITE_DELAY;
+         //Enter the CLOSED state
+         webSocket->state = WS_STATE_CLOSED;
+
+         //We are done
+         break;
+      }
+   }
+
+   //Release exclusive access
+   osReleaseMutex(&netMutex);
+
+   //Return a handle to the freshly created WebSocket
+   return webSocket;
+}
+
+
+/**
+ * @brief Upgrade a socket to a WebSocket
+ * @param[in] socket Handle referencing the socket
+ * @return Handle referencing the new WebSocket
+ **/
+
+WebSocket *webSocketUpgradeSocket(Socket *socket)
+{
+   WebSocket *webSocket;
+
+   //Valid socket handle?
+   if(socket != NULL)
+   {
+      //Create a new WebSocket
+      webSocket = webSocketOpen();
+
+      //WebSocket successfully created?
+      if(webSocket != NULL)
+      {
+         //Attach the socket handle
+         webSocket->socket = socket;
+         //Initialize state
+         webSocket->state = WS_STATE_INIT;
+      }
+   }
+   else
+   {
+      //The specified socket is not valid...
+      webSocket = NULL;
+   }
+
+   //Return a handle to the freshly created WebSocket
+   return webSocket;
+}
+
+
+#if (WEB_SOCKET_TLS_SUPPORT == ENABLED)
+
+/**
+ * @brief Upgrade a secure socket to a secure WebSocket
+ * @param[in] socket Handle referencing the socket
+ * @param[in] tlsContext Pointer to the SSL/TLS context
+ * @return Handle referencing the new WebSocket
+ **/
+
+WebSocket *webSocketUpgradeSecureSocket(Socket *socket, TlsContext *tlsContext)
+{
+   WebSocket *webSocket;
+
+   //Valid SSL/TLS context?
+   if(tlsContext != NULL)
+   {
+      //Create a new WebSocket
+      webSocket = webSocketOpen();
+
+      //WebSocket successfully created?
+      if(webSocket != NULL)
+      {
+         //Attach the socket handle
+         webSocket->socket = socket;
+         //Attach the SSL/TLS context
+         webSocket->tlsContext = tlsContext;
+         //Initialize state
+         webSocket->state = WS_STATE_INIT;
+      }
+   }
+   else
+   {
+      //The specified socket is not valid...
+      webSocket = NULL;
+   }
+
+   //Return a handle to the freshly created WebSocket
+   return webSocket;
+}
+
+
+/**
+ * @brief Register TLS initialization callback function
+ * @param[in] webSocket Handle that identifies a WebSocket
+ * @param[in] callback TLS initialization callback function
+ * @return Error code
+ **/
+
+error_t webSocketRegisterTlsInitCallback(WebSocket *webSocket,
+   WebSocketTlsInitCallback callback)
+{
+   //Check parameters
+   if(webSocket == NULL || callback == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save callback function
+   webSocket->tlsInitCallback = callback;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+#endif
+
+
+/**
+ * @brief Set timeout value for blocking operations
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] timeout Maximum time to wait
+ * @return Error code
+ **/
+
+error_t webSocketSetTimeout(WebSocket *webSocket, systime_t timeout)
+{
+   //Make sure the WebSocket handle is valid
+   if(webSocket == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save timeout value
+   webSocket->timeout = timeout;
+
+   //Valid socket?
+   if(webSocket->socket != NULL)
+      socketSetTimeout(webSocket->socket, timeout);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set the hostname of the resource being requested
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] host NULL-terminated string containing the hostname
+ * @return Error code
+ **/
+
+error_t webSocketSetHost(WebSocket *webSocket, const char_t *host)
+{
+   //Check parameters
+   if(webSocket == NULL || host == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save the hostname
+   strSafeCopy(webSocket->host, host, WEB_SOCKET_HOST_MAX_LEN);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set the origin header field
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] origin NULL-terminated string containing the origin
+ * @return Error code
+ **/
+
+error_t webSocketSetOrigin(WebSocket *webSocket, const char_t *origin)
+{
+   //Check parameters
+   if(webSocket == NULL || origin == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save origin
+   strSafeCopy(webSocket->origin, origin, WEB_SOCKET_ORIGIN_MAX_LEN);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set the sub-protocol header field
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] subProtocol NULL-terminated string containing the sub-protocol
+ * @return Error code
+ **/
+
+error_t webSocketSetSubProtocol(WebSocket *webSocket, const char_t *subProtocol)
+{
+   //Check parameters
+   if(webSocket == NULL || subProtocol == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Save sub-protocol
+   strSafeCopy(webSocket->subProtocol, subProtocol, WEB_SOCKET_SUB_PROTOCOL_MAX_LEN);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Set authentication information
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] username NULL-terminated string containing the user name to be used
+ * @param[in] password NULL-terminated string containing the password to be used
+ * @param[in] allowedAuthModes Logic OR of allowed HTTP authentication modes
+ * @return Error code
+ **/
+
+error_t webSocketSetAuthInfo(WebSocket *webSocket, const char_t *username,
+   const char_t *password, uint_t allowedAuthModes)
+{
+#if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
+   WebSocketAuthContext *authContext;
+
+   //Check parameters
+   if(webSocket == NULL || username == NULL || password == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Point to the authentication context
+   authContext = &webSocket->authContext;
+
+   //Save user name
+   strSafeCopy(authContext->username, username, WEB_SOCKET_USERNAME_MAX_LEN);
+   //Save password
+   strSafeCopy(authContext->password, password, WEB_SOCKET_PASSWORD_MAX_LEN);
+   //Save the list of allowed HTTP authentication modes
+   authContext->allowedAuthModes = allowedAuthModes;
+#endif
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Bind the WebSocket to a particular network interface
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] interface Network interface to be used
+ * @return Error code
+ **/
+
+error_t webSocketBindToInterface(WebSocket *webSocket, NetInterface *interface)
+{
+   //Make sure the WebSocket handle is valid
+   if(webSocket == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Explicitly associate the WebSocket with the specified interface
+   webSocket->interface = interface;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Establish a WebSocket connection
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] serverIpAddr IP address of the WebSocket server to connect to
+ * @param[in] serverPort TCP port number that will be used to establish the
+ *   connection
+ * @param[in] uri NULL-terminated string that contains the resource name
+ * @return Error code
+ **/
+
+error_t webSocketConnect(WebSocket *webSocket, const IpAddr *serverIpAddr,
+   uint16_t serverPort, const char_t *uri)
+{
+   error_t error;
+   size_t n;
+   WebSocketFrameContext *txContext;
+
+   //Make sure the WebSocket handle is valid
+   if(webSocket == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Point to the TX context
+   txContext = &webSocket->txContext;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Establish connection
+   while(webSocket->state != WS_STATE_OPEN)
+   {
+      //Check current state
+      if(webSocket->state == WS_STATE_CLOSED)
+      {
+         //Check parameters
+         if(serverIpAddr == NULL || uri == NULL)
+         {
+            //Report an error
+            error = ERROR_INVALID_PARAMETER;
+         }
+         else
+         {
+            //A WebSocket client is a WebSocket endpoint that initiates a
+            //connection to a peer
+            webSocket->endpoint = WS_ENDPOINT_CLIENT;
+
+            //Save the URI
+            strSafeCopy(webSocket->uri, uri, WEB_SOCKET_URI_MAX_LEN);
+            //Reset retry counter
+            webSocket->retryCount = 0;
+
+#if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
+            //HTTP authentication is not used for the first connection attempt
+            webSocket->authContext.requiredAuthMode = WS_AUTH_MODE_NONE;
+            webSocket->authContext.selectedAuthMode = WS_AUTH_MODE_NONE;
+#endif
+            //Initialize the WebSocket connection
+            webSocketChangeState(webSocket, WS_STATE_INIT);
+         }
+      }
+      else if(webSocket->state == WS_STATE_INIT)
+      {
+         //Increment retry counter
+         webSocket->retryCount++;
+
+         //Limit the number of connection attempts
+         if(webSocket->retryCount > WEB_SOCKET_MAX_CONN_RETRIES)
+         {
+            //Report an error
+            error = ERROR_OPEN_FAILED;
+         }
+         else
+         {
+            //Open network connection
+            error = webSocketOpenConnection(webSocket);
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Establish connection
+            webSocketChangeState(webSocket, WS_STATE_CONNECTING);
+         }
+      }
+      else if(webSocket->state == WS_STATE_CONNECTING)
+      {
+         //Establish connection
+         error = webSocketEstablishConnection(webSocket,
+            serverIpAddr, serverPort);
+
+         //Check status code
+         if(!error)
+         {
+            //Generate client's key
+            error = webSocketGenerateClientKey(webSocket);
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Format client handshake
+            error = webSocketFormatClientHandshake(webSocket, serverPort);
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Send client handshake
+            webSocketChangeState(webSocket, WS_STATE_CLIENT_HANDSHAKE);
+         }
+      }
+      else if(webSocket->state == WS_STATE_CLIENT_HANDSHAKE)
+      {
+         //Any remaining data to be sent?
+         if(txContext->bufferPos < txContext->bufferLen)
+         {
+            //Send more data
+            error = webSocketSendData(webSocket,
+               txContext->buffer + txContext->bufferPos,
+               txContext->bufferLen - txContext->bufferPos, &n, 0);
+
+            //Advance data pointer
+            txContext->bufferPos += n;
+         }
+         else
+         {
+            //Wait for server handshake
+            webSocketChangeState(webSocket, WS_STATE_SERVER_HANDSHAKE);
+         }
+      }
+      else if(webSocket->state == WS_STATE_SERVER_HANDSHAKE)
+      {
+         //Parse the server handshake
+         error = webSocketParseHandshake(webSocket);
+      }
+      else if(webSocket->state == WS_STATE_SERVER_RESP_BODY)
+      {
+         //Check Connection header field
+         if(webSocket->handshakeContext.connectionClose)
+         {
+            //Close connection
+            webSocketCloseConnection(webSocket);
+            //Try to connect again
+            webSocketChangeState(webSocket, WS_STATE_INIT);
+         }
+         else
+         {
+            //Any remaining data to read in the response body?
+            if(webSocket->handshakeContext.contentLength > 0)
+            {
+               //Limit the number of bytes to read at a time
+               n = MIN(webSocket->handshakeContext.contentLength, WEB_SOCKET_BUFFER_SIZE);
+               //Discard any received data
+               error = webSocketReceiveData(webSocket, txContext->buffer, n, &n, 0);
+               //Decrement byte counter
+               webSocket->handshakeContext.contentLength -= n;
+            }
+            else
+            {
+               //Format client handshake
+               error = webSocketFormatClientHandshake(webSocket, serverPort);
+               //Try to authenticate again
+               webSocketChangeState(webSocket, WS_STATE_CLIENT_HANDSHAKE);
+            }
+         }
+      }
+      else
+      {
+         //Invalid state
+         error = ERROR_WRONG_STATE;
+      }
+
+      //Check whether authentication is required
+      if(error == ERROR_AUTH_REQUIRED)
+      {
+#if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED)
+         //Basic authentication?
+         if(webSocket->authContext.requiredAuthMode == WS_AUTH_MODE_BASIC)
+         {
+            //Check whether the basic authentication scheme is allowed
+            if(webSocket->authContext.allowedAuthModes & WS_AUTH_MODE_BASIC)
+            {
+               //Do not try to connect again if the credentials are not valid...
+               if(webSocket->authContext.selectedAuthMode == WS_AUTH_MODE_NONE)
+               {
+                  //Catch exception
+                  error = NO_ERROR;
+                  //Force the WebSocket client to use basic authentication
+                  webSocket->authContext.selectedAuthMode = WS_AUTH_MODE_BASIC;
+                  //Read response body, if any
+                  webSocketChangeState(webSocket, WS_STATE_SERVER_RESP_BODY);
+               }
+            }
+         }
+#endif
+#if (WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
+         //Digest authentication?
+         if(webSocket->authContext.requiredAuthMode == WS_AUTH_MODE_DIGEST)
+         {
+            //Check whether the digest authentication scheme is allowed
+            if(webSocket->authContext.allowedAuthModes & WS_AUTH_MODE_DIGEST)
+            {
+               //Do not try to connect again if the credentials are not valid...
+               if(webSocket->authContext.selectedAuthMode == WS_AUTH_MODE_NONE)
+               {
+                  //Force the WebSocket client to use digest authentication
+                  webSocket->authContext.selectedAuthMode = WS_AUTH_MODE_DIGEST;
+
+                  //Make sure that the RNG callback function has been registered
+                  if(webSockRandCallback != NULL)
+                  {
+                     //Generate a random cnonce
+                     error = webSockRandCallback(txContext->buffer,
+                        WEB_SOCKET_CNONCE_SIZE);
+                  }
+                  else
+                  {
+                     //A cryptographically strong random number generator
+                     //must be used to generate the cnonce
+                     error = ERROR_PRNG_NOT_READY;
+                  }
+
+                  //Convert the byte array to hex string
+                  webSocketConvertArrayToHexString(txContext->buffer,
+                     WEB_SOCKET_CNONCE_SIZE, webSocket->authContext.cnonce);
+
+                  //Read response body, if any
+                  webSocketChangeState(webSocket, WS_STATE_SERVER_RESP_BODY);
+               }
+            }
+         }
+#endif
+      }
+
+      //If an error occurred, then the client must fail the WebSocket
+      //connection
+      if(error)
+      {
+#if (NET_RTOS_SUPPORT == DISABLED)
+         //Timeout error?
+         if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
+            break;
+#endif
+         //Close connection
+         webSocketCloseConnection(webSocket);
+
+         //Switch to the CLOSED state
+         webSocketChangeState(webSocket, WS_STATE_CLOSED);
+         //Exit immediately
+         break;
+      }
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Set client's key
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] clientKey NULL-terminated string that holds the the client's key
+ * @return Error code
+ **/
+
+error_t webSocketSetClientKey(WebSocket *webSocket, const char_t *clientKey)
+{
+   error_t error;
+   size_t n;
+
+   //Check parameters
+   if(webSocket == NULL || clientKey == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Get the length of the client's key
+   n = strlen(clientKey);
+
+   //Check the length of the key
+   if(n > WEB_SOCKET_CLIENT_KEY_SIZE)
+      return ERROR_INVALID_LENGTH;
+
+   //Copy client's key
+   strcpy(webSocket->handshakeContext.clientKey, clientKey);
+
+   //a WebSocket server is a WebSocket endpoint that awaits
+   //connections from peers
+   webSocket->endpoint = WS_ENDPOINT_SERVER;
+
+   //Initialize status code
+   webSocket->statusCode = WS_STATUS_CODE_NO_STATUS_RCVD;
+
+   //Initialize handshake parameters
+   webSocket->handshakeContext.version = WS_HTTP_VERSION_1_1;
+   webSocket->handshakeContext.connectionUpgrade = TRUE;
+   webSocket->handshakeContext.upgradeWebSocket = TRUE;
+
+   //Initialize FIN flag
+   webSocket->rxContext.fin = TRUE;
+
+   //Verify client's key
+   error = webSocketVerifyClientKey(webSocket);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Generate server's key
+   error = webSocketGenerateServerKey(webSocket);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Format server handshake
+   error = webSocketFormatServerHandshake(webSocket);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Update FSM state
+   webSocket->state = WS_STATE_SERVER_HANDSHAKE;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse client's handshake
+ * @param[in] webSocket Handle that identifies a WebSocket
+ * @return Error code
+ **/
+
+error_t webSocketParseClientHandshake(WebSocket *webSocket)
+{
+   error_t error;
+
+   //Make sure the WebSocket handle is valid
+   if(webSocket == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //a WebSocket server is a WebSocket endpoint that awaits
+   //connections from peers
+   webSocket->endpoint = WS_ENDPOINT_SERVER;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Establish connection
+   while(webSocket->state != WS_STATE_SERVER_HANDSHAKE)
+   {
+      //Check current state
+      if(webSocket->state == WS_STATE_INIT)
+      {
+         //Open network connection
+         error = webSocketOpenConnection(webSocket);
+
+         //Check status code
+         if(!error)
+         {
+            //Establish connection
+            webSocketChangeState(webSocket, WS_STATE_CONNECTING);
+         }
+      }
+      else if(webSocket->state == WS_STATE_CONNECTING)
+      {
+#if (WEB_SOCKET_TLS_SUPPORT == ENABLED)
+         //Use SSL/TLS to secure the connection?
+         if(webSocket->tlsInitCallback != NULL)
+         {
+            //Establish a SSL/TLS connection
+            error = tlsConnect(webSocket->tlsContext);
+         }
+#endif
+         //Check status code
+         if(!error)
+         {
+            //Parse client handshake
+            webSocketChangeState(webSocket, WS_STATE_CLIENT_HANDSHAKE);
+         }
+      }
+      else if(webSocket->state == WS_STATE_CLIENT_HANDSHAKE)
+      {
+         //Parse the client handshake
+         error = webSocketParseHandshake(webSocket);
+
+         //Check status code
+         if(!error)
+         {
+            //Generate server's key
+            error = webSocketGenerateServerKey(webSocket);
+         }
+
+         //Check status code
+         if(!error)
+         {
+            //Format server handshake
+            error = webSocketFormatServerHandshake(webSocket);
+         }
+      }
+      else
+      {
+         //Invalid state
+         error = ERROR_WRONG_STATE;
+      }
+
+      //Any error to report?
+      if(error)
+         break;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send server's handshake
+ * @param[in] webSocket Handle that identifies a WebSocket
+ * @return Error code
+ **/
+
+error_t webSocketSendServerHandshake(WebSocket *webSocket)
+{
+   error_t error;
+   size_t n;
+   WebSocketFrameContext *txContext;
+
+   //Make sure the WebSocket handle is valid
+   if(webSocket == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Point to the TX context
+   txContext = &webSocket->txContext;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Establish connection
+   while(webSocket->state != WS_STATE_OPEN)
+   {
+      //Check current state
+      if(webSocket->state == WS_STATE_SERVER_HANDSHAKE)
+      {
+         //Any remaining data to be sent?
+         if(txContext->bufferPos < txContext->bufferLen)
+         {
+            //Send more data
+            error = webSocketSendData(webSocket,
+               txContext->buffer + txContext->bufferPos,
+               txContext->bufferLen - txContext->bufferPos, &n, 0);
+
+            //Advance data pointer
+            txContext->bufferPos += n;
+         }
+         else
+         {
+            //The WebSocket connection is established
+            webSocketChangeState(webSocket, WS_STATE_OPEN);
+         }
+      }
+      else
+      {
+         //Invalid state
+         error = ERROR_WRONG_STATE;
+      }
+
+      //Any error to report?
+      if(error)
+         break;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Send HTTP error response to the client
+ * @param[in] webSocket Handle that identifies a WebSocket
+ * @param[in] statusCode HTTP status code
+ * @param[in] message Text message
+ * @return Error code
+ **/
+
+error_t webSocketSendErrorResponse(WebSocket *webSocket,
+   uint_t statusCode, const char_t *message)
+{
+   error_t error;
+   size_t n;
+   WebSocketFrameContext *txContext;
+
+   //Make sure the WebSocket handle is valid
+   if(webSocket == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Point to the TX context
+   txContext = &webSocket->txContext;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Send HTTP error message
+   while(1)
+   {
+      //Check current state
+      if(txContext->state == WS_SUB_STATE_INIT)
+      {
+         //Format HTTP error response
+         error = webSocketFormatErrorResponse(webSocket, statusCode, message);
+
+         //Send the response
+         txContext->state = WS_SUB_STATE_FRAME_PAYLOAD;
+      }
+      else if(txContext->state == WS_SUB_STATE_FRAME_PAYLOAD)
+      {
+         //Any remaining data to be sent?
+         if(txContext->bufferPos < txContext->bufferLen)
+         {
+            //Send more data
+            error = webSocketSendData(webSocket,
+               txContext->buffer + txContext->bufferPos,
+               txContext->bufferLen - txContext->bufferPos, &n, 0);
+
+            //Advance data pointer
+            txContext->bufferPos += n;
+         }
+         else
+         {
+            //We are done
+            webSocketChangeState(webSocket, WS_STATE_SHUTDOWN);
+            break;
+         }
+      }
+      else
+      {
+         //Invalid state
+         error = ERROR_WRONG_STATE;
+      }
+
+      //Any error to report?
+      if(error)
+         break;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Transmit data over the WebSocket connection
+ * @param[in] webSocket Handle that identifies a WebSocket
+ * @param[in] data Pointer to a buffer containing the data to be transmitted
+ * @param[in] length Number of data bytes to send
+ * @param[in] type Frame type
+ * @param[out] written Actual number of bytes written (optional parameter)
+ * @return Error code
+ **/
+
+error_t webSocketSend(WebSocket *webSocket, const void *data,
+   size_t length, WebSocketFrameType type, size_t *written)
+{
+   //An unfragmented message consists of a single frame with the FIN bit
+   //set and an opcode other than 0
+   return webSocketSendEx(webSocket, data, length,
+      type, written, TRUE, TRUE);
+}
+
+
+/**
+ * @brief Transmit data over the WebSocket connection
+ * @param[in] webSocket Handle that identifies a WebSocket
+ * @param[in] data Pointer to a buffer containing the data to be transmitted
+ * @param[in] length Number of data bytes to send
+ * @param[in] type Frame type
+ * @param[out] written Actual number of bytes written (optional parameter)
+ * @param[in] firstFrag First fragment of the message
+ * @param[in] lastFrag Last fragment of the message
+ **/
+
+error_t webSocketSendEx(WebSocket *webSocket, const void *data, size_t length,
+   WebSocketFrameType type, size_t *written, bool_t firstFrag, bool_t lastFrag)
+{
+   error_t error;
+   size_t i;
+   size_t j;
+   size_t k;
+   size_t n;
+   const uint8_t *p;
+   WebSocketFrameContext *txContext;
+
+   //Check parameters
+   if(webSocket == NULL || data == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //A data frame may be transmitted by either the client or the server at
+   //any time after opening handshake completion and before that endpoint
+   //has sent a Close frame
+   if(webSocket->state != WS_STATE_OPEN)
+      return ERROR_NOT_CONNECTED;
+
+   //Point to the TX context
+   txContext = &webSocket->txContext;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Point to the application data to be written
+   p = (const uint8_t *) data;
+   //No data has been transmitted yet
+   i = 0;
+
+   //Send as much data as possible
+   while(1)
+   {
+      //Check current sub-state
+      if(txContext->state == WS_SUB_STATE_INIT)
+      {
+         //A fragmented message consists of a single frame with the FIN bit
+         //clear and an opcode other than 0, followed by zero or more frames
+         //with the FIN bit clear and the opcode set to 0, and terminated by
+         //a single frame with the FIN bit set and an opcode of 0
+         if(!firstFrag)
+            type = WS_FRAME_TYPE_CONTINUATION;
+
+         //Format WebSocket frame header
+         error = webSocketFormatFrameHeader(webSocket, lastFrag, type, length - i);
+
+         //Send the frame header
+         txContext->state = WS_SUB_STATE_FRAME_HEADER;
+      }
+      else if(txContext->state == WS_SUB_STATE_FRAME_HEADER)
+      {
+         //Any remaining data to be sent?
+         if(txContext->bufferPos < txContext->bufferLen)
+         {
+            //Send more data
+            error = webSocketSendData(webSocket,
+               txContext->buffer + txContext->bufferPos,
+               txContext->bufferLen - txContext->bufferPos, &n, 0);
+
+            //Advance data pointer
+            txContext->bufferPos += n;
+         }
+         else
+         {
+            //Flush the transmit buffer
+            txContext->payloadPos = 0;
+            txContext->bufferPos = 0;
+            txContext->bufferLen = 0;
+
+            //Send the payload of the WebSocket frame
+            txContext->state = WS_SUB_STATE_FRAME_PAYLOAD;
+         }
+      }
+      else if(txContext->state == WS_SUB_STATE_FRAME_PAYLOAD)
+      {
+         //Any remaining data to be sent?
+         if(txContext->bufferPos < txContext->bufferLen)
+         {
+            //Send more data
+            error = webSocketSendData(webSocket,
+               txContext->buffer + txContext->bufferPos,
+               txContext->bufferLen - txContext->bufferPos, &n, 0);
+
+            //Advance data pointer
+            txContext->payloadPos += n;
+            txContext->bufferPos += n;
+
+            //Total number of data that have been written
+            i += n;
+         }
+         else
+         {
+            //Send as much data as possible
+            if(txContext->payloadPos < txContext->payloadLen)
+            {
+               //Calculate the number of bytes that are pending
+               n = MIN(length - i, txContext->payloadLen - txContext->payloadPos);
+               //Limit the number of bytes to be copied at a time
+               n = MIN(n, WEB_SOCKET_BUFFER_SIZE);
+
+               //Copy application data to the transmit buffer
+               memcpy(txContext->buffer, p, n);
+
+               //All frames sent from the client to the server are masked
+               if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
+               {
+                  //Apply masking
+                  for(j = 0; j < n; j++)
+                  {
+                     //Index of the masking key to be applied
+                     k = (txContext->payloadPos + j) % 4;
+                     //Convert unmasked data into masked data
+                     txContext->buffer[j] = p[i + j] ^ txContext->maskingKey[k];
+                  }
+               }
+
+               //Rewind to the beginning of the buffer
+               txContext->bufferPos = 0;
+               //Update the number of data buffered but not yet sent
+               txContext->bufferLen = n;
+            }
+            else
+            {
+               //Prepare to send a new WebSocket frame
+               txContext->state = WS_SUB_STATE_INIT;
+
+               //Write operation complete?
+               if(i >= length)
+                  break;
+            }
+         }
+      }
+      else
+      {
+         //Invalid state
+         error = ERROR_WRONG_STATE;
+      }
+
+      //Any error to report?
+      if(error)
+         break;
+   }
+
+   //Total number of data that have been written
+   if(written != NULL)
+      *written = i;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Receive data from a WebSocket connection
+ * @param[in] webSocket Handle that identifies a WebSocket
+ * @param[out] data Buffer where to store the incoming data
+ * @param[in] size Maximum number of bytes that can be received
+ * @param[out] type Frame type
+ * @param[out] received Number of bytes that have been received
+ * @return Error code
+ **/
+
+error_t webSocketReceive(WebSocket *webSocket, void *data,
+   size_t size, WebSocketFrameType *type, size_t *received)
+{
+   bool_t firstFrag;
+   bool_t lastFrag;
+
+   return webSocketReceiveEx(webSocket, data, size,
+      type, received, &firstFrag, &lastFrag);
+}
+
+
+/**
+ * @brief Receive data from a WebSocket connection
+ * @param[in] webSocket Handle that identifies a WebSocket
+ * @param[out] data Buffer where to store the incoming data
+ * @param[in] size Maximum number of bytes that can be received
+ * @param[out] type Frame type
+ * @param[out] received Number of bytes that have been received
+ * @param[out] firstFrag First fragment of the message
+ * @param[out] lastFrag Last fragment of the message
+ * @return Error code
+ **/
+
+error_t webSocketReceiveEx(WebSocket *webSocket, void *data, size_t size,
+   WebSocketFrameType *type, size_t *received, bool_t *firstFrag, bool_t *lastFrag)
+{
+   error_t error;
+   size_t i;
+   size_t j;
+   size_t k;
+   size_t n;
+   WebSocketFrame *frame;
+   WebSocketFrameContext *rxContext;
+
+   //Make sure the WebSocket handle is valid
+   if(webSocket == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Check the state of the WebSocket connection
+   if(webSocket->state != WS_STATE_OPEN &&
+      webSocket->state != WS_STATE_CLOSING_RX)
+      return ERROR_NOT_CONNECTED;
+
+   //Point to the RX context
+   rxContext = &webSocket->rxContext;
+   //Point to the WebSocket frame header
+   frame = (WebSocketFrame *) rxContext->buffer;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Initialize flags
+   if(type != NULL)
+      *type = WS_FRAME_TYPE_CONTINUATION;
+   if(firstFrag != NULL)
+      *firstFrag = FALSE;
+   if(lastFrag != NULL)
+      *lastFrag = FALSE;
+
+   //No data has been read yet
+   i = 0;
+
+   //Read as much data as possible
+   while(i < size)
+   {
+      //Check current sub-state
+      if(rxContext->state == WS_SUB_STATE_INIT)
+      {
+         //Flush the receive buffer
+         rxContext->bufferPos = 0;
+         rxContext->bufferLen = sizeof(WebSocketFrame);
+
+         //Decode the frame header
+         rxContext->state = WS_SUB_STATE_FRAME_HEADER;
+      }
+      else if(rxContext->state == WS_SUB_STATE_FRAME_HEADER)
+      {
+         //Incomplete frame header?
+         if(rxContext->bufferPos < rxContext->bufferLen)
+         {
+            //Read more data
+            error = webSocketReceiveData(webSocket,
+               rxContext->buffer + rxContext->bufferPos,
+               rxContext->bufferLen - rxContext->bufferPos, &n, 0);
+
+            //Advance data pointer
+            rxContext->bufferPos += n;
+         }
+         else
+         {
+            //Check the Payload Length field
+            if(frame->payloadLen == 126)
+               rxContext->bufferLen += sizeof(uint16_t);
+            else if(frame->payloadLen == 127)
+               rxContext->bufferLen += sizeof(uint64_t);
+
+            //Check whether the masking key is present
+            if(frame->mask)
+               rxContext->bufferLen += sizeof(uint32_t);
+
+            //The Opcode field defines the interpretation of the payload data
+            if(frame->opcode == WS_FRAME_TYPE_CLOSE)
+            {
+               //All control frames must have a payload length of 125 bytes or less
+               if(frame->payloadLen <= 125)
+               {
+                  //Retrieve the length of the WebSocket frame
+                  rxContext->bufferLen += frame->payloadLen;
+               }
+               else
+               {
+                  //Report a protocol error
+                  webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
+                  //Terminate the WebSocket connection
+                  error = ERROR_INVALID_FRAME;
+               }
+            }
+
+            //Decode the extended payload length and the masking key, if any
+            rxContext->state = WS_SUB_STATE_FRAME_EXT_HEADER;
+         }
+      }
+      else if(rxContext->state == WS_SUB_STATE_FRAME_EXT_HEADER)
+      {
+         //Incomplete frame header?
+         if(rxContext->bufferPos < rxContext->bufferLen)
+         {
+            //Read more data
+            error = webSocketReceiveData(webSocket,
+               rxContext->buffer + rxContext->bufferPos,
+               rxContext->bufferLen - rxContext->bufferPos, &n, 0);
+
+            //Advance data pointer
+            rxContext->bufferPos += n;
+         }
+         else
+         {
+            //Parse the header of the WebSocket frame
+            error = webSocketParseFrameHeader(webSocket, frame, type);
+
+            //Check status code
+            if(error == ERROR_UNEXPECTED_MESSAGE)
+            {
+               error = NO_ERROR;
+               break;
+            }
+            else if(error == NO_ERROR)
+            {
+               if(firstFrag != NULL)
+                  *firstFrag = TRUE;
+            }
+
+            //Flush the receive buffer
+            rxContext->payloadPos = 0;
+            rxContext->bufferPos = 0;
+            rxContext->bufferLen = 0;
+
+            //Decode the payload of the WebSocket frame
+            rxContext->state = WS_SUB_STATE_FRAME_PAYLOAD;
+         }
+      }
+      else if(rxContext->state == WS_SUB_STATE_FRAME_PAYLOAD)
+      {
+         if(rxContext->payloadPos < rxContext->payloadLen)
+         {
+            //Limit the number of bytes to read at a time
+            n = MIN(size - i, rxContext->payloadLen - rxContext->payloadPos);
+            //Limit the number of bytes to be copied at a time
+            n = MIN(n, WEB_SOCKET_BUFFER_SIZE);
+
+            //Read more data
+            error = webSocketReceiveData(webSocket, rxContext->buffer, n, &n, 0);
+
+            //All frames sent from the client to the server are masked
+            if(rxContext->mask)
+            {
+               //Unmask the data
+               for(j = 0; j < n; j++)
+               {
+                  //Index of the masking key to be applied
+                  k = (rxContext->payloadPos + j) % 4;
+                  //Convert masked data into unmasked data
+                  rxContext->buffer[j] ^= rxContext->maskingKey[k];
+               }
+            }
+
+            //Text frame?
+            if(rxContext->dataFrameType == WS_FRAME_TYPE_TEXT &&
+               rxContext->controlFrameType == WS_FRAME_TYPE_CONTINUATION)
+            {
+               //Compute the number of remaining data bytes in the UTF-8 stream
+               if(rxContext->fin)
+                  k = rxContext->payloadLen - rxContext->payloadPos;
+               else
+                  k = 0;
+
+               //Invalid UTF-8 sequence?
+               if(!webSocketCheckUtf8Stream(&webSocket->utf8Context,
+                  rxContext->buffer, n, k))
+               {
+                  //The received data is not consistent with the type of the message
+                  webSocket->statusCode = WS_STATUS_CODE_INVALID_PAYLOAD_DATA;
+                  //The endpoint must fail the WebSocket connection
+                  error = ERROR_INVALID_FRAME;
+               }
+            }
+
+            //Sanity check
+            if(data != NULL)
+            {
+               //Copy application data
+               memcpy((uint8_t *) data + i, rxContext->buffer, n);
+            }
+
+            //Advance data pointer
+            rxContext->payloadPos += n;
+
+            //Total number of data that have been read
+            i += n;
+         }
+
+         if(rxContext->payloadPos == rxContext->payloadLen)
+         {
+            //Decode the next WebSocket frame
+            rxContext->state = WS_SUB_STATE_INIT;
+
+            //Last fragment of the message?
+            if(rxContext->fin || rxContext->controlFrameType != WS_FRAME_TYPE_CONTINUATION)
+            {
+               if(lastFrag != NULL)
+                  *lastFrag = TRUE;
+
+               //Exit immediately
+               break;
+            }
+         }
+      }
+      else
+      {
+         //Invalid state
+         error = ERROR_WRONG_STATE;
+      }
+
+      //Any error to report?
+      if(error)
+         break;
+   }
+
+   //Check status code
+   if(!error)
+   {
+      //Return the frame type
+      if(type != NULL)
+      {
+         //Control or data frame?
+         if(rxContext->controlFrameType != WS_FRAME_TYPE_CONTINUATION)
+            *type = rxContext->controlFrameType;
+         else
+            *type = rxContext->dataFrameType;
+      }
+   }
+
+   //Return the total number of data that have been read
+   if(received != NULL)
+      *received = i;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Check whether some data is available in the receive buffer
+ * @param[in] webSocket Handle to a WebSocket
+ * @return The function returns TRUE if some data is pending and can be read
+ *   immediately without blocking. Otherwise, FALSE is returned
+ **/
+
+bool_t webSocketIsRxReady(WebSocket *webSocket)
+{
+   bool_t available = FALSE;
+
+#if (WEB_SOCKET_TLS_SUPPORT == ENABLED)
+   //Check whether a secure connection is being used
+   if(webSocket->tlsContext != NULL)
+   {
+      //Check whether some data is pending in the receive buffer
+      if(webSocket->tlsContext->rxBufferLen > 0)
+         available = TRUE;
+   }
+#endif
+
+   //The function returns TRUE if some data can be read immediately
+   //without blocking
+   return available;
+}
+
+
+/**
+ * @brief Gracefully close a WebSocket connection
+ * @param[in] webSocket Handle to a WebSocket
+ **/
+
+error_t webSocketShutdown(WebSocket *webSocket)
+{
+   error_t error;
+   size_t n;
+   WebSocketFrameContext *txContext;
+
+   //Make sure the WebSocket handle is valid
+   if(webSocket == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Point to the TX context
+   txContext = &webSocket->txContext;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Closing handshake
+   while(webSocket->state != WS_STATE_CLOSED)
+   {
+      //Check current state
+      if(webSocket->state == WS_STATE_OPEN)
+      {
+         //Check whether the latest frame has been completely transmitted
+         if(txContext->payloadPos == txContext->payloadLen)
+         {
+            //Format Close frame
+            error = webSocketFormatCloseFrame(webSocket);
+            //Send Close frame
+            webSocket->state = WS_STATE_CLOSING_TX;
+         }
+         else
+         {
+            //The WebSocket connection cannot be closed until the
+            //transmission of the frame is complete...
+            error = ERROR_FAILURE;
+         }
+      }
+      else if(webSocket->state == WS_STATE_CLOSING_TX)
+      {
+         //Any remaining data to be sent?
+         if(txContext->bufferPos < txContext->bufferLen)
+         {
+            //Send more data
+            error = webSocketSendData(webSocket,
+               txContext->buffer + txContext->bufferPos,
+               txContext->bufferLen - txContext->bufferPos, &n, 0);
+
+            //Advance data pointer
+            txContext->bufferPos += n;
+         }
+         else
+         {
+            //Check whether a Close frame has been received from the peer
+            if(webSocket->handshakeContext.closingFrameReceived)
+               webSocket->state = WS_STATE_SHUTDOWN;
+            else
+               webSocket->state = WS_STATE_CLOSING_RX;
+         }
+      }
+      else if(webSocket->state == WS_STATE_CLOSING_RX)
+      {
+         //After receiving a control frame indicating the connection should
+         //be closed, a peer discards any further data received
+         error = webSocketReceive(webSocket, NULL, WEB_SOCKET_BUFFER_SIZE, NULL, 0);
+
+         //Check status code
+         if(error == NO_ERROR)
+         {
+            //Close frame received?
+            if(webSocket->handshakeContext.closingFrameReceived)
+            {
+               //Properly shutdown the network connection
+               webSocket->state = WS_STATE_SHUTDOWN;
+            }
+         }
+         else if(error == ERROR_INVALID_FRAME || error == ERROR_END_OF_STREAM)
+         {
+            //Catch exception
+            error = NO_ERROR;
+            //Properly shutdown the network connection
+            webSocket->state = WS_STATE_SHUTDOWN;
+         }
+      }
+      else if(webSocket->state == WS_STATE_SHUTDOWN)
+      {
+         //Properly dispose the network connection
+         error = webSocketShutdownConnection(webSocket);
+
+         //Check status code
+         if(!error)
+         {
+            //The connection has been properly closed
+            webSocket->state = WS_STATE_CLOSED;
+         }
+      }
+      else
+      {
+         //Invalid state
+         error = ERROR_WRONG_STATE;
+      }
+
+      //Any error to report?
+      if(error)
+         break;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Close a WebSocket connection
+ * @param[in] webSocket Handle identifying the WebSocket to close
+ **/
+
+void webSocketClose(WebSocket *webSocket)
+{
+   //Make sure the WebSocket handle is valid
+   if(webSocket != NULL)
+   {
+      //Close connection
+      webSocketCloseConnection(webSocket);
+      //Release the WebSocket
+      webSocketChangeState(webSocket, WS_STATE_UNUSED);
+   }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/web_socket/web_socket.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,507 @@
+/**
+ * @file web_socket.h
+ * @brief WebSocket API (client and server)
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _WEB_SOCKET_H
+#define _WEB_SOCKET_H
+
+//Dependencies
+#include "core/net.h"
+#include "core/socket.h"
+
+//WebSocket support
+#ifndef WEB_SOCKET_SUPPORT
+   #define WEB_SOCKET_SUPPORT DISABLED
+#elif (WEB_SOCKET_SUPPORT != ENABLED && WEB_SOCKET_SUPPORT != DISABLED)
+   #error WEB_SOCKET_SUPPORT parameter is not valid
+#endif
+
+//Number of WebSockets that can be opened simultaneously
+#ifndef WEB_SOCKET_MAX_COUNT
+   #define WEB_SOCKET_MAX_COUNT 4
+#elif (WEB_SOCKET_MAX_COUNT < 1)
+   #error WEB_SOCKET_MAX_COUNT parameter is not valid
+#endif
+
+//Support for WebSocket connections over SSL/TLS
+#ifndef WEB_SOCKET_TLS_SUPPORT
+   #define WEB_SOCKET_TLS_SUPPORT DISABLED
+#elif (WEB_SOCKET_TLS_SUPPORT != ENABLED && WEB_SOCKET_TLS_SUPPORT != DISABLED)
+   #error WEB_SOCKET_TLS_SUPPORT parameter is not valid
+#endif
+
+//Basic access authentication support
+#ifndef WEB_SOCKET_BASIC_AUTH_SUPPORT
+   #define WEB_SOCKET_BASIC_AUTH_SUPPORT DISABLED
+#elif (WEB_SOCKET_BASIC_AUTH_SUPPORT != ENABLED && WEB_SOCKET_BASIC_AUTH_SUPPORT != DISABLED)
+   #error WEB_SOCKET_BASIC_AUTH_SUPPORT parameter is not valid
+#endif
+
+//Digest access authentication support
+#ifndef WEB_SOCKET_DIGEST_AUTH_SUPPORT
+   #define WEB_SOCKET_DIGEST_AUTH_SUPPORT DISABLED
+#elif (WEB_SOCKET_DIGEST_AUTH_SUPPORT != ENABLED && WEB_SOCKET_DIGEST_AUTH_SUPPORT != DISABLED)
+   #error WEB_SOCKET_DIGEST_AUTH_SUPPORT parameter is not valid
+#endif
+
+//Maximum number of connection attempts
+#ifndef WEB_SOCKET_MAX_CONN_RETRIES
+   #define WEB_SOCKET_MAX_CONN_RETRIES 3
+#elif (WEB_SOCKET_MAX_CONN_RETRIES < 1)
+   #error WEB_SOCKET_MAX_CONN_RETRIES parameter is not valid
+#endif
+
+//Size of the WebSocket buffer
+#ifndef WEB_SOCKET_BUFFER_SIZE
+   #define WEB_SOCKET_BUFFER_SIZE 1024
+#elif (WEB_SOCKET_BUFFER_SIZE < 128)
+   #error WEB_SOCKET_BUFFER_SIZE parameter is not valid
+#endif
+
+//Maximum length of the hostname
+#ifndef WEB_SOCKET_HOST_MAX_LEN
+   #define WEB_SOCKET_HOST_MAX_LEN 32
+#elif (WEB_SOCKET_HOST_MAX_LEN < 1)
+   #error WEB_SOCKET_HOST_MAX_LEN parameter is not valid
+#endif
+
+//Maximum length of the origin header field
+#ifndef WEB_SOCKET_ORIGIN_MAX_LEN
+   #define WEB_SOCKET_ORIGIN_MAX_LEN 16
+#elif (WEB_SOCKET_ORIGIN_MAX_LEN < 1)
+   #error WEB_SOCKET_ORIGIN_MAX_LEN parameter is not valid
+#endif
+
+//Maximum length of the sub-protocol
+#ifndef WEB_SOCKET_SUB_PROTOCOL_MAX_LEN
+   #define WEB_SOCKET_SUB_PROTOCOL_MAX_LEN 8
+#elif (WEB_SOCKET_SUB_PROTOCOL_MAX_LEN < 1)
+   #error WEB_SOCKET_SUB_PROTOCOL_MAX_LEN parameter is not valid
+#endif
+
+//Maximum length of the URI
+#ifndef WEB_SOCKET_URI_MAX_LEN
+   #define WEB_SOCKET_URI_MAX_LEN 32
+#elif (WEB_SOCKET_URI_MAX_LEN < 1)
+   #error WEB_SOCKET_URI_MAX_LEN parameter is not valid
+#endif
+
+//Maximum length of the query string
+#ifndef WEB_SOCKET_QUERY_STRING_MAX_LEN
+   #define WEB_SOCKET_QUERY_STRING_MAX_LEN 32
+#elif (WEB_SOCKET_QUERY_STRING_MAX_LEN < 1)
+   #error WEB_SOCKET_QUERY_STRING_MAX_LEN parameter is not valid
+#endif
+
+//Maximum length of the realm
+#ifndef WEB_SOCKET_REALM_MAX_LEN
+   #define WEB_SOCKET_REALM_MAX_LEN 32
+#elif (WEB_SOCKET_REALM_MAX_LEN < 1)
+   #error WEB_SOCKET_REALM_MAX_LEN parameter is not valid
+#endif
+
+//Maximum length of the user name
+#ifndef WEB_SOCKET_USERNAME_MAX_LEN
+   #define WEB_SOCKET_USERNAME_MAX_LEN 16
+#elif (WEB_SOCKET_USERNAME_MAX_LEN < 1)
+   #error WEB_SOCKET_USERNAME_MAX_LEN parameter is not valid
+#endif
+
+//Maximum length of the password
+#ifndef WEB_SOCKET_PASSWORD_MAX_LEN
+   #define WEB_SOCKET_PASSWORD_MAX_LEN 16
+#elif (WEB_SOCKET_PASSWORD_MAX_LEN < 1)
+   #error WEB_SOCKET_PASSWORD_MAX_LEN parameter is not valid
+#endif
+
+//Maximum length of the nonce
+#ifndef WEB_SOCKET_NONCE_MAX_LEN
+   #define WEB_SOCKET_NONCE_MAX_LEN 32
+#elif (WEB_SOCKET_NONCE_MAX_LEN < 1)
+   #error WEB_SOCKET_NONCE_MAX_LEN parameter is not valid
+#endif
+
+//Maximum length of the opaque parameter
+#ifndef WEB_SOCKET_OPAQUE_MAX_LEN
+   #define WEB_SOCKET_OPAQUE_MAX_LEN 32
+#elif (WEB_SOCKET_OPAQUE_MAX_LEN < 1)
+   #error WEB_SOCKET_OPAQUE_MAX_LEN parameter is not valid
+#endif
+
+//Cnonce size
+#ifndef WEB_SOCKET_CNONCE_SIZE
+   #define WEB_SOCKET_CNONCE_SIZE 16
+#elif (WEB_SOCKET_CNONCE_SIZE < 1)
+   #error WEB_SOCKET_CNONCE_SIZE parameter is not valid
+#endif
+
+//TLS supported?
+#if (WEB_SOCKET_TLS_SUPPORT == ENABLED)
+   #include "crypto.h"
+   #include "tls.h"
+#endif
+
+//Client key size
+#define WEB_SOCKET_CLIENT_KEY_SIZE 24
+//Server key size
+#define WEB_SOCKET_SERVER_KEY_SIZE 28
+
+//Forward declaration of WebSocket structure
+struct _WebSocket;
+#define WebSocket struct _WebSocket
+
+
+/**
+ * @brief WebSocket endpoint types
+ **/
+
+typedef enum
+{
+   WS_ENDPOINT_CLIENT = 0,
+   WS_ENDPOINT_SERVER = 1
+} WebSocketEndpoint;
+
+
+/**
+ * @brief HTTP version numbers
+ **/
+
+typedef enum
+{
+   WS_HTTP_VERSION_0_9 = 0x0009,
+   WS_HTTP_VERSION_1_0 = 0x0100,
+   WS_HTTP_VERSION_1_1 = 0x0101
+} WebSocketHttpVersion;
+
+
+/**
+ * @brief Authentication schemes
+ **/
+
+typedef enum
+{
+   WS_AUTH_MODE_NONE   = 0x00,
+   WS_AUTH_MODE_BASIC  = 0x01,
+   WS_AUTH_MODE_DIGEST = 0x02
+} WebSocketAuthMode;
+
+
+/**
+ * @brief WebSocket states
+ **/
+
+typedef enum
+{
+   WS_STATE_UNUSED             = 0,
+   WS_STATE_CLOSED             = 1,
+   WS_STATE_INIT               = 2,
+   WS_STATE_CONNECTING         = 3,
+   WS_STATE_CLIENT_HANDSHAKE   = 4,
+   WS_STATE_SERVER_HANDSHAKE   = 5,
+   WS_STATE_SERVER_RESP_BODY   = 6,
+   WS_STATE_OPEN               = 7,
+   WS_STATE_CLOSING_TX         = 8,
+   WS_STATE_CLOSING_RX         = 9,
+   WS_STATE_SHUTDOWN           = 10,
+} WebSocketState;
+
+
+/**
+ * @brief WebSocket sub-states
+ **/
+
+typedef enum
+{
+   WS_SUB_STATE_INIT                   = 0,
+   //Handshake decoding
+   WS_SUB_STATE_HANDSHAKE_LEADING_LINE = 1,
+   WS_SUB_STATE_HANDSHAKE_HEADER_FIELD = 2,
+   WS_SUB_STATE_HANDSHAKE_LWSP         = 3,
+   //WebSocket frame decoding
+   WS_SUB_STATE_FRAME_HEADER           = 4,
+   WS_SUB_STATE_FRAME_EXT_HEADER       = 5,
+   WS_SUB_STATE_FRAME_PAYLOAD          = 6
+} WebSocketSubState;
+
+
+/**
+ * @brief WebSocket frame types
+ **/
+
+typedef enum
+{
+   WS_FRAME_TYPE_CONTINUATION = 0x00,
+   WS_FRAME_TYPE_TEXT         = 0x01,
+   WS_FRAME_TYPE_BINARY       = 0x02,
+   WS_FRAME_TYPE_CLOSE        = 0x08,
+   WS_FRAME_TYPE_PING         = 0x09,
+   WS_FRAME_TYPE_PONG         = 0x0A
+} WebSocketFrameType;
+
+
+/**
+ * @brief WebSocket status codes
+ **/
+
+typedef enum
+{
+   WS_STATUS_CODE_NORMAL_CLOSURE       = 1000,
+   WS_STATUS_CODE_GOING_AWAY           = 1001,
+   WS_STATUS_CODE_PROTOCOL_ERROR       = 1002,
+   WS_STATUS_CODE_UNSUPPORTED_DATA     = 1003,
+   WS_STATUS_CODE_NO_STATUS_RCVD       = 1005,
+   WS_STATUS_CODE_ABNORMAL_CLOSURE     = 1006,
+   WS_STATUS_CODE_INVALID_PAYLOAD_DATA = 1007,
+   WS_STATUS_CODE_POLICY_VIOLATION     = 1008,
+   WS_STATUS_CODE_MESSAGE_TOO_BIG      = 1009,
+   WS_STATUS_CODE_MANDATORY_EXT        = 1010,
+   WS_STATUS_CODE_INTERNAL_ERROR       = 1011,
+   WS_STATUS_CODE_TLS_HANDSHAKE        = 1015
+} WebSocketStatusCode;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(push, 1)
+#endif
+
+
+/**
+ * @brief WebSocket frame
+ **/
+
+typedef __start_packed struct
+{
+#ifdef _CPU_BIG_ENDIAN
+   uint8_t fin : 1;         //0
+   uint8_t reserved : 3;
+   uint8_t opcode : 4;
+   uint8_t mask : 1;        //1
+   uint8_t payloadLen: 7;
+#else
+   uint8_t opcode : 4;      //0
+   uint8_t reserved : 3;
+   uint8_t fin : 1;
+   uint8_t payloadLen: 7;   //1
+   uint8_t mask : 1;
+#endif
+   uint8_t extPayloadLen[]; //2
+} __end_packed WebSocketFrame;
+
+
+//CodeWarrior or Win32 compiler?
+#if defined(__CWCC__) || defined(_WIN32)
+   #pragma pack(pop)
+#endif
+
+
+/**
+ * @brief Random data generation callback function
+ **/
+
+typedef error_t (*WebSocketRandCallback)(uint8_t *data, size_t length);
+
+
+//WebSocket connections over SSL/TLS supported?
+#if (WEB_SOCKET_TLS_SUPPORT == ENABLED)
+
+/**
+ * @brief SSL/TLS initialization callback function
+ **/
+
+typedef error_t (*WebSocketTlsInitCallback)(WebSocket *webSocket,
+   TlsContext *tlsContext);
+
+#endif
+
+
+/**
+ * @brief Authentication context
+ **/
+
+typedef struct
+{
+   uint_t allowedAuthModes;
+   WebSocketAuthMode requiredAuthMode;
+   WebSocketAuthMode selectedAuthMode;
+   char_t username[WEB_SOCKET_USERNAME_MAX_LEN + 1];
+   char_t password[WEB_SOCKET_PASSWORD_MAX_LEN + 1];
+   char_t realm[WEB_SOCKET_REALM_MAX_LEN + 1];
+#if (WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
+   uint32_t nc;
+   char_t nonce[WEB_SOCKET_NONCE_MAX_LEN + 1];
+   char_t cnonce[WEB_SOCKET_CNONCE_SIZE * 2 + 1];
+   char_t opaque[WEB_SOCKET_OPAQUE_MAX_LEN + 1];
+   bool_t stale;
+#endif
+} WebSocketAuthContext;
+
+
+/**
+ * @brief Handshake context
+ **/
+
+typedef struct
+{
+   uint_t version;
+   uint_t statusCode;
+   bool_t upgradeWebSocket;
+   bool_t connectionUpgrade;
+   bool_t connectionClose;
+   size_t contentLength;
+   char_t clientKey[WEB_SOCKET_CLIENT_KEY_SIZE + 1];
+   char_t serverKey[WEB_SOCKET_SERVER_KEY_SIZE + 1];
+   bool_t closingFrameSent;
+   bool_t closingFrameReceived;
+} WebSocketHandshakeContext;
+
+
+/**
+ * @brief Frame encoding/decoding context
+ **/
+
+typedef struct
+{
+   WebSocketSubState state;                ///FSM state
+   WebSocketFrameType dataFrameType;       ///<Data frame type
+   WebSocketFrameType controlFrameType;    ///<Control frame type
+   bool_t fin;                             ///<Final fragment in a message
+   bool_t mask;                            ///<Defines whether the payload data is masked
+   uint8_t maskingKey[4];                  ///<Masking key
+   size_t payloadLen;                      ///<Payload length
+   size_t payloadPos;                      ///<Current position
+   uint8_t buffer[WEB_SOCKET_BUFFER_SIZE]; ///<Data buffer
+   size_t bufferLen;                       ///<Length of the data buffer
+   size_t bufferPos;                       ///<Current position
+} WebSocketFrameContext;
+
+
+/**
+ * @brief UTF-8 decoding context
+ **/
+
+typedef struct
+{
+   uint_t utf8CharSize;
+   uint_t utf8CharIndex;
+   uint32_t utf8CodePoint;
+} WebSocketUtf8Context;
+
+
+/**
+ * @brief Structure describing a WebSocket
+ **/
+
+struct _WebSocket
+{
+   WebSocketEndpoint endpoint;               ///<Endpoint type (client or server)
+   WebSocketState state;                     ///<WebSocket connection state
+   uint16_t statusCode;
+   systime_t timestamp;
+   uint_t retryCount;
+   char_t host[WEB_SOCKET_HOST_MAX_LEN + 1];
+   char_t origin[WEB_SOCKET_ORIGIN_MAX_LEN + 1];
+   char_t subProtocol[WEB_SOCKET_SUB_PROTOCOL_MAX_LEN + 1];
+   char_t uri[WEB_SOCKET_URI_MAX_LEN + 1];
+   char_t queryString[WEB_SOCKET_QUERY_STRING_MAX_LEN + 1];
+   systime_t timeout;                        ///<timeout value for blocking operations
+   NetInterface *interface;                  ///<Underlying network interface
+   Socket *socket;                           ///<Underlying TCP socket
+#if (WEB_SOCKET_TLS_SUPPORT == ENABLED)
+   TlsContext *tlsContext;                   ///<SSL/TLS context
+   TlsSession tlsSession;                    ///<SSL/TLS session
+   WebSocketTlsInitCallback tlsInitCallback; ///<SSL/TLS initialization callback function
+#endif
+#if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
+   WebSocketAuthContext authContext;
+#endif
+   WebSocketHandshakeContext handshakeContext;
+   WebSocketFrameContext txContext;
+   WebSocketFrameContext rxContext;
+   WebSocketUtf8Context utf8Context;
+};
+
+
+//Random data generation callback function
+extern WebSocketRandCallback webSockRandCallback;
+
+//WebSocket related functions
+error_t webSocketInit(void);
+
+error_t webSocketRegisterRandCallback(WebSocketRandCallback callback);
+
+WebSocket *webSocketOpen(void);
+WebSocket *webSocketUpgradeSocket(Socket *socket);
+
+#if (WEB_SOCKET_TLS_SUPPORT == ENABLED)
+
+WebSocket *webSocketUpgradeSecureSocket(Socket *socket, TlsContext *tlsContext);
+
+error_t webSocketRegisterTlsInitCallback(WebSocket *webSocket,
+   WebSocketTlsInitCallback callback);
+
+#endif
+
+error_t webSocketSetTimeout(WebSocket *webSocket, systime_t timeout);
+
+error_t webSocketSetHost(WebSocket *webSocket, const char_t *host);
+error_t webSocketSetOrigin(WebSocket *webSocket, const char_t *origin);
+error_t webSocketSetSubProtocol(WebSocket *webSocket, const char_t *subProtocol);
+
+error_t webSocketSetAuthInfo(WebSocket *webSocket, const char_t *username,
+   const char_t *password, uint_t allowedAuthModes);
+
+error_t webSocketBindToInterface(WebSocket *webSocket, NetInterface *interface);
+
+error_t webSocketConnect(WebSocket *webSocket, const IpAddr *serverIpAddr,
+   uint16_t serverPort, const char_t *uri);
+
+error_t webSocketSetClientKey(WebSocket *webSocket, const char_t *clientKey);
+error_t webSocketParseClientHandshake(WebSocket *webSocket);
+error_t webSocketSendServerHandshake(WebSocket *webSocket);
+
+error_t webSocketSendErrorResponse(WebSocket *webSocket,
+   uint_t statusCode, const char_t *message);
+
+error_t webSocketSend(WebSocket *webSocket, const void *data,
+   size_t length, WebSocketFrameType type, size_t *written);
+
+error_t webSocketSendEx(WebSocket *webSocket, const void *data, size_t length,
+   WebSocketFrameType type, size_t *written, bool_t firstFrag, bool_t lastFrag);
+
+error_t webSocketReceive(WebSocket *webSocket, void *data,
+   size_t size, WebSocketFrameType *type, size_t *received);
+
+error_t webSocketReceiveEx(WebSocket *webSocket, void *data, size_t size,
+   WebSocketFrameType *type, size_t *received, bool_t *firstFrag, bool_t *lastFrag);
+
+bool_t webSocketIsRxReady(WebSocket *webSocket);
+error_t webSocketShutdown(WebSocket *webSocket);
+void webSocketClose(WebSocket *webSocket);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/web_socket/web_socket_auth.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,335 @@
+/**
+ * @file web_socket_auth.c
+ * @brief HTTP authentication for WebSockets
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL WEB_SOCKET_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include "core/net.h"
+#include "web_socket/web_socket.h"
+#include "web_socket/web_socket_auth.h"
+#include "base64.h"
+#include "md5.h"
+#include "str.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (WEB_SOCKET_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Parse WWW-Authenticate header field
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] value NULL-terminated string that contains the value of header field
+ * @return Error code
+ **/
+
+error_t webSocketParseAuthenticateField(WebSocket *webSocket, char_t *value)
+{
+#if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
+   size_t n;
+   char_t *p;
+   char_t *token;
+   char_t *separator;
+   char_t *name;
+   WebSocketAuthContext *authContext;
+
+   //Point to the handshake context
+   authContext = &webSocket->authContext;
+
+   //Retrieve the authentication scheme
+   token = strtok_r(value, " \t", &p);
+
+   //Any parsing error?
+   if(token == NULL)
+      return ERROR_INVALID_SYNTAX;
+
+   //Basic access authentication?
+   if(!strcmp(token, "Basic"))
+   {
+      //Basic authentication is required by the WebSocket server
+      authContext->requiredAuthMode = WS_AUTH_MODE_BASIC;
+   }
+   //Digest access authentication?
+   else if(!strcasecmp(token, "Digest"))
+   {
+      //Digest authentication is required by the WebSocket server
+      authContext->requiredAuthMode = WS_AUTH_MODE_DIGEST;
+   }
+   //Unknown authentication scheme?
+   else
+   {
+      //Report an error
+      return ERROR_INVALID_SYNTAX;
+   }
+
+   //Get the first parameter
+   token = strtok_r(NULL, ",", &p);
+
+   //Parse the WWW-Authenticate field
+   while(token != NULL)
+   {
+      //Check whether a separator is present
+      separator = strchr(token, '=');
+
+      //Separator found?
+      if(separator != NULL)
+      {
+         //Split the string
+         *separator = '\0';
+
+         //Get field name and value
+         name = strTrimWhitespace(token);
+         value = strTrimWhitespace(separator + 1);
+
+         //Retrieve the length of the value field
+         n = strlen(value);
+
+         //Discard the surrounding quotes
+         if(n > 0 && value[n - 1] == '\"')
+            value[n - 1] = '\0';
+         if(value[0] == '\"')
+            value++;
+
+         //Check parameter name
+         if(!strcasecmp(name, "realm"))
+         {
+            //Save realm
+            strSafeCopy(authContext->realm, value, WEB_SOCKET_REALM_MAX_LEN);
+         }
+#if (WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
+         else if(!strcasecmp(name, "nonce"))
+         {
+            //Save nonce
+            strSafeCopy(authContext->nonce, value, WEB_SOCKET_NONCE_MAX_LEN + 1);
+         }
+         else if(!strcasecmp(name, "opaque"))
+         {
+            //Save nonce
+            strSafeCopy(authContext->opaque, value, WEB_SOCKET_OPAQUE_MAX_LEN + 1);
+         }
+         else if(!strcasecmp(name, "stale"))
+         {
+            //Save stale flag
+            if(!strcasecmp(value, "true"))
+               authContext->stale = TRUE;
+            else
+               authContext->stale = FALSE;
+         }
+#endif
+
+         //Get next parameter
+         token = strtok_r(NULL, ",", &p);
+      }
+   }
+#endif
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format Authorization header field
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[out] output Buffer where to format the header field
+ * @return Total length of the header field
+ **/
+
+size_t webSocketAddAuthorizationField(WebSocket *webSocket, char_t *output)
+{
+   size_t n;
+
+#if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
+   WebSocketAuthContext *authContext;
+
+   //Point to the handshake context
+   authContext = &webSocket->authContext;
+#endif
+
+#if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED)
+   //Basic authentication scheme?
+   if(authContext->selectedAuthMode == WS_AUTH_MODE_BASIC)
+   {
+      size_t k;
+      char_t *temp;
+
+      //Temporary buffer
+      temp = (char_t *) webSocket->rxContext.buffer;
+
+      //Format Authorization header field
+      n = sprintf(output, "Authorization: Basic ");
+
+      //The client sends the userid and password, separated by a single colon
+      //character, within a Base64 encoded string in the credentials
+      k = sprintf(temp, "%s:%s", authContext->username,
+         authContext->password);
+
+      //Encode the resulting string using Base64
+      base64Encode(temp, k, output + n, &k);
+      //Update the total length of the header field
+      n += k;
+
+      //Properly terminate the Authorization header field
+      n += sprintf(output + n, "\r\n");
+   }
+   else
+#endif
+#if (WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
+   //Digest authentication scheme?
+   if(authContext->selectedAuthMode == WS_AUTH_MODE_DIGEST)
+   {
+      Md5Context md5Context;
+      char_t ha1[2 * MD5_DIGEST_SIZE + 1];
+      char_t ha2[2 * MD5_DIGEST_SIZE + 1];
+      char_t nc[9];
+
+      //Count of the number of requests (including the current request)
+      //that the client has sent with the nonce value in this request
+      authContext->nc++;
+
+      //Convert the value to hex string
+      sprintf(nc, "%08x", authContext->nc);
+
+      //Compute HA1 = MD5(username : realm : password)
+      md5Init(&md5Context);
+      md5Update(&md5Context, authContext->username, strlen(authContext->username));
+      md5Update(&md5Context, ":", 1);
+      md5Update(&md5Context, authContext->realm, strlen(authContext->realm));
+      md5Update(&md5Context, ":", 1);
+      md5Update(&md5Context, authContext->password, strlen(authContext->password));
+      md5Final(&md5Context, NULL);
+
+      //Convert MD5 hash to hex string
+      webSocketConvertArrayToHexString(md5Context.digest, MD5_DIGEST_SIZE, ha1);
+      //Debug message
+      TRACE_DEBUG("  HA1: %s\r\n", ha1);
+
+      //Compute HA2 = MD5(method : uri)
+      md5Init(&md5Context);
+      md5Update(&md5Context, "GET", 3);
+      md5Update(&md5Context, ":", 1);
+      md5Update(&md5Context, webSocket->uri, strlen(webSocket->uri));
+      md5Final(&md5Context, NULL);
+
+      //Convert MD5 hash to hex string
+      webSocketConvertArrayToHexString(md5Context.digest, MD5_DIGEST_SIZE, ha2);
+      //Debug message
+      TRACE_DEBUG("  HA2: %s\r\n", ha2);
+
+      //Compute MD5(HA1 : nonce : nc : cnonce : qop : HA1)
+      md5Init(&md5Context);
+      md5Update(&md5Context, ha1, strlen(ha1));
+      md5Update(&md5Context, ":", 1);
+      md5Update(&md5Context, authContext->nonce, strlen(authContext->nonce));
+      md5Update(&md5Context, ":", 1);
+      md5Update(&md5Context, nc, strlen(nc));
+      md5Update(&md5Context, ":", 1);
+      md5Update(&md5Context, authContext->cnonce, strlen(authContext->cnonce));
+      md5Update(&md5Context, ":", 1);
+      md5Update(&md5Context, "auth", 4);
+      md5Update(&md5Context, ":", 1);
+      md5Update(&md5Context, ha2, strlen(ha2));
+      md5Final(&md5Context, NULL);
+
+      //Convert MD5 hash to hex string
+      webSocketConvertArrayToHexString(md5Context.digest, MD5_DIGEST_SIZE, ha1);
+      //Debug message
+      TRACE_DEBUG("  response: %s\r\n", ha1);
+
+      //Format Authorization header field
+      n = sprintf(output, "Authorization: Digest\r\n");
+
+      //Username
+      n += sprintf(output + n, "  username=\"%s\",\r\n", authContext->username);
+      //Realm
+      n += sprintf(output + n, "  realm=\"%s\",\r\n", authContext->realm);
+      //Nonce value
+      n += sprintf(output + n, "  nonce=\"%s\",\r\n", authContext->nonce);
+      //URI
+      n += sprintf(output + n, "  uri=\"%s\",\r\n", webSocket->uri);
+      //Quality of protection
+      n += sprintf(output + n, "  qop=\"auth\",\r\n");
+      //Nonce count
+      n += sprintf(output + n, "  nc=\"%08x\",\r\n", authContext->nc);
+      //Cnonce value
+      n += sprintf(output + n, "  cnonce=\"%s\",\r\n", authContext->cnonce);
+      //Response
+      n += sprintf(output + n, "  response=\"%s\",\r\n", ha1);
+      //Opaque parameter
+      n += sprintf(output + n, "  opaque=\"%s\",\r\n", authContext->opaque);
+   }
+   else
+#endif
+   //Unknown authentication scheme?
+   {
+      //No need to add the Authorization header field
+      n = 0;
+   }
+
+   //Return the total length of the Authorization header field
+   return n;
+}
+
+
+/**
+ * @brief Convert byte array to hex string
+ * @param[in] input Point to the byte array
+ * @param[in] inputLength Length of the byte array
+ * @param[out] output NULL-terminated string resulting from the conversion
+ * @return Error code
+ **/
+
+void webSocketConvertArrayToHexString(const uint8_t *input,
+   size_t inputLength, char_t *output)
+{
+   size_t i;
+
+   //Hex conversion table
+   static const char_t hexDigit[] =
+   {
+      '0', '1', '2', '3', '4', '5', '6', '7',
+      '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+   };
+
+   //Process byte array
+   for(i = 0; i < inputLength; i++)
+   {
+      //Convert upper nibble
+      output[i * 2] = hexDigit[(input[i] >> 4) & 0x0F];
+      //Then convert lower nibble
+      output[i * 2 + 1] = hexDigit[input[i] & 0x0F];
+   }
+
+   //Properly terminate the string with a NULL character
+   output[i * 2] = '\0';
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/web_socket/web_socket_auth.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,45 @@
+/**
+ * @file web_socket_auth.h
+ * @brief HTTP authentication for WebSockets
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _WEB_SOCKET_AUTH_H
+#define _WEB_SOCKET_AUTH_H
+
+//Dependencies
+#include "core/net.h"
+#include "web_socket/web_socket.h"
+
+//WebSocket related functions
+error_t webSocketParseAuthenticateField(WebSocket *webSocket, char_t *value);
+
+size_t webSocketAddAuthorizationField(WebSocket *webSocket, char_t *output);
+
+void webSocketConvertArrayToHexString(const uint8_t *input,
+   size_t inputLength, char_t *output);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/web_socket/web_socket_frame.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,478 @@
+/**
+ * @file web_socket_frame.c
+ * @brief WebSocket frame parsing and formatting
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL WEB_SOCKET_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "web_socket/web_socket.h"
+#include "web_socket/web_socket_frame.h"
+#include "web_socket/web_socket_transport.h"
+#include "web_socket/web_socket_misc.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (WEB_SOCKET_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Format WebSocket frame header
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] fin FIN flag
+ * @param[in] type Frame type
+ * @param[in] payloadLen Length of the payload data
+ * @return Error code
+ **/
+
+error_t webSocketFormatFrameHeader(WebSocket *webSocket,
+   bool_t fin, WebSocketFrameType type, size_t payloadLen)
+{
+   error_t error;
+   WebSocketFrameContext *txContext;
+   WebSocketFrame *frame;
+
+   //Point to the TX context
+   txContext = &webSocket->txContext;
+
+   //Flush the transmit buffer
+   txContext->bufferPos = 0;
+   txContext->bufferLen = 0;
+
+   //The endpoint must encapsulate the data in a WebSocket frame
+   frame = (WebSocketFrame *) txContext->buffer;
+
+   //The frame needs to be formatted according to the WebSocket framing
+   //format
+   frame->fin = fin;
+   frame->reserved = 0;
+   frame->opcode = type;
+
+   //All frames sent from the client to the server are masked by a 32-bit
+   //value that is contained within the frame
+   if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
+   {
+      //All frames sent from client to server have the Mask bit set to 1
+      frame->mask = TRUE;
+
+      //Make sure that the RNG callback function has been registered
+      if(webSockRandCallback != NULL)
+      {
+         //Generate a random masking key
+         error = webSockRandCallback(txContext->maskingKey, sizeof(uint32_t));
+         //Any error to report?
+         if(error)
+            return error;
+      }
+      else
+      {
+         //A cryptographically strong random number generator
+         //must be used to generate the masking key
+         return ERROR_PRNG_NOT_READY;
+      }
+   }
+   else
+   {
+      //Clear the Mask bit
+      frame->mask = FALSE;
+   }
+
+   //Size of the frame header
+   txContext->bufferLen = sizeof(WebSocketFrame);
+
+   //Compute the number of application data to be transmitted
+   txContext->payloadLen = payloadLen;
+
+   //Check the length of the payload
+   if(payloadLen <= 125)
+   {
+      //Payload length
+      frame->payloadLen = payloadLen;
+   }
+   else if(payloadLen <= 65535)
+   {
+      //If the Payload Length field is set to 126, then the following
+      //2 bytes are interpreted as a 16-bit unsigned integer
+      frame->payloadLen = 126;
+
+      //Save the length of the payload data
+      STORE16BE(payloadLen, frame->extPayloadLen);
+
+      //Adjust the length of the frame header
+      txContext->bufferLen += sizeof(uint16_t);
+   }
+   else
+   {
+      //If the Payload Length field is set to 127, then the following
+      //8 bytes are interpreted as a 64-bit unsigned integer
+      frame->payloadLen = 127;
+
+      //Save the length of the payload data
+      STORE64BE(payloadLen, frame->extPayloadLen);
+
+      //Adjust the length of the frame header
+      txContext->bufferLen += sizeof(uint64_t);
+   }
+
+   //Debug message
+   TRACE_DEBUG("WebSocket: Sending frame\r\n");
+   TRACE_DEBUG("  FIN = %u\r\n", frame->fin);
+   TRACE_DEBUG("  Reserved = %u\r\n", frame->reserved);
+   TRACE_DEBUG("  Opcode = %u\r\n", frame->opcode);
+   TRACE_DEBUG("  Mask = %u\r\n", frame->mask);
+   TRACE_DEBUG("  Payload Length = %u\r\n", txContext->payloadLen);
+
+   //The Masking Key field is present the mask bit is set to 1
+   if(frame->mask)
+   {
+      //Debug message
+      TRACE_DEBUG_ARRAY("  Masking Key = ", txContext->maskingKey, sizeof(uint32_t));
+
+      //Copy the masking key
+      memcpy(txContext->buffer + txContext->bufferLen,
+         txContext->maskingKey, sizeof(uint32_t));
+
+      //Adjust the length of the frame header
+      txContext->bufferLen += sizeof(uint32_t);
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse WebSocket frame header
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] frame Pointer to the WebSocket frame header
+ * @param[out] type Frame type
+ * @return Error code
+ **/
+
+error_t webSocketParseFrameHeader(WebSocket *webSocket,
+   const WebSocketFrame *frame, WebSocketFrameType *type)
+{
+   size_t j;
+   size_t k;
+   size_t n;
+   uint16_t statusCode;
+   WebSocketFrameContext *rxContext;
+
+   //Point to the RX context
+   rxContext = &webSocket->rxContext;
+
+   //Point to the Extended Payload Length
+   n = sizeof(WebSocketFrame);
+
+   //Check frame type
+   if(type != NULL)
+   {
+      if(*type != WS_FRAME_TYPE_CONTINUATION)
+      {
+         if(frame->opcode != WS_FRAME_TYPE_CONTINUATION &&
+            frame->opcode != *type)
+         {
+            return ERROR_UNEXPECTED_MESSAGE;
+         }
+      }
+   }
+
+   //Check the Payload Length field
+   if(frame->payloadLen == 126)
+   {
+      //If the Payload Length field is set to 126, then the following
+      //2 bytes are interpreted as a 16-bit unsigned integer
+      rxContext->payloadLen = LOAD16BE(frame->extPayloadLen);
+
+      //Point to the next field
+      n += sizeof(uint16_t);
+   }
+   else if(frame->payloadLen == 127)
+   {
+      //If the Payload Length field is set to 127, then the following
+      //8 bytes are interpreted as a 64-bit unsigned integer
+      rxContext->payloadLen = LOAD64BE(frame->extPayloadLen);
+
+      //Point to the next field
+      n += sizeof(uint64_t);
+   }
+   else
+   {
+      //Retrieve the length of the payload data
+      rxContext->payloadLen = frame->payloadLen;
+   }
+
+   //Debug message
+   TRACE_DEBUG("WebSocket: frame received...\r\n");
+   TRACE_DEBUG("  FIN = %u\r\n", frame->fin);
+   TRACE_DEBUG("  Reserved = %u\r\n", frame->reserved);
+   TRACE_DEBUG("  Opcode = %u\r\n", frame->opcode);
+   TRACE_DEBUG("  Mask = %u\r\n", frame->mask);
+   TRACE_DEBUG("  Payload Length = %u\r\n", rxContext->payloadLen);
+
+   //Check whether the payload data is masked
+   if(frame->mask)
+   {
+      //Save the masking key
+      memcpy(rxContext->maskingKey, (uint8_t *) frame + n, sizeof(uint32_t));
+      //Debug message
+      TRACE_DEBUG_ARRAY("  Masking Key = ", rxContext->maskingKey, sizeof(uint32_t));
+
+      //Point to the payload data
+      n += sizeof(uint32_t);
+   }
+
+   //Text or Close frame received?
+   if(frame->opcode == WS_FRAME_TYPE_TEXT ||
+      frame->opcode == WS_FRAME_TYPE_CLOSE)
+   {
+      //Reinitialize UTF-8 decoding context
+      webSocket->utf8Context.utf8CharSize = 0;
+      webSocket->utf8Context.utf8CharIndex = 0;
+      webSocket->utf8Context.utf8CodePoint = 0;
+   }
+
+   //If the RSV field is a nonzero value and none of the negotiated extensions
+   //defines the meaning of such a nonzero value, the receiving endpoint must
+   //fail the WebSocket connection
+   if(frame->reserved != 0)
+   {
+      //Report a protocol error
+      webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
+      //Terminate the WebSocket connection
+      return ERROR_INVALID_FRAME;
+   }
+
+   //The Opcode field defines the interpretation of the payload data
+   if(frame->opcode == WS_FRAME_TYPE_CONTINUATION)
+   {
+      //A Continuation frame cannot be the first frame of a fragmented message
+      if(rxContext->fin)
+         webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
+
+      rxContext->controlFrameType = WS_FRAME_TYPE_CONTINUATION;
+   }
+   else if(frame->opcode == WS_FRAME_TYPE_TEXT)
+   {
+      //The Opcode must be 0 in subsequent fragmented frames
+      if(!rxContext->fin)
+         webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
+
+      //Save the Opcode field
+      rxContext->dataFrameType = WS_FRAME_TYPE_TEXT;
+      rxContext->controlFrameType = WS_FRAME_TYPE_CONTINUATION;
+   }
+   else if(frame->opcode == WS_FRAME_TYPE_BINARY)
+   {
+      //The Opcode must be 0 in subsequent fragmented frames
+      if(!rxContext->fin)
+         webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
+
+      //Save the Opcode field
+      rxContext->dataFrameType = WS_FRAME_TYPE_BINARY;
+      rxContext->controlFrameType = WS_FRAME_TYPE_CONTINUATION;
+   }
+   else if(frame->opcode == WS_FRAME_TYPE_CLOSE)
+   {
+      //Check the length of the payload data
+      if(rxContext->payloadLen == 0)
+      {
+         //The Close frame does not contain any body
+         webSocket->statusCode = WS_STATUS_CODE_NORMAL_CLOSURE;
+      }
+      else if(rxContext->payloadLen == 1)
+      {
+         //Report a protocol error
+         webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
+      }
+      else
+      {
+         //All frames sent from the client to the server are masked
+         if(frame->mask)
+         {
+            //Unmask the data
+            for(j = 0; j < rxContext->payloadLen; j++)
+            {
+               //Index of the masking key to be applied
+               k = j % 4;
+               //Convert masked data into unmasked data
+               *((uint8_t *) frame + n + j) ^= rxContext->maskingKey[k];
+            }
+         }
+
+         //If there is a body, the first two bytes of the body must be
+         //a 2-byte unsigned integer representing a status code
+         statusCode = LOAD16BE((uint8_t *) frame + n);
+
+         //Debug message
+         TRACE_DEBUG("  Status Code = %u\r\n", statusCode);
+
+         //When sending a Close frame in response, the endpoint typically
+         //echos the status code it received
+         if(webSocketCheckStatusCode(statusCode))
+            webSocket->statusCode = statusCode;
+         else
+            webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
+
+         //The body may contain UTF-8-encoded data
+         if(rxContext->payloadLen > 2)
+         {
+            //Compute the length of the data
+            k = rxContext->payloadLen - 2;
+
+            //Invalid UTF-8 sequence?
+            if(!webSocketCheckUtf8Stream(&webSocket->utf8Context,
+               (uint8_t *) frame + n + 2, k, k))
+            {
+               //The received data is not consistent with the type of the message
+               webSocket->statusCode = WS_STATUS_CODE_INVALID_PAYLOAD_DATA;
+            }
+         }
+      }
+
+      //A Close frame has been received
+      webSocket->handshakeContext.closingFrameReceived = TRUE;
+      //Exit immediately
+      return ERROR_END_OF_STREAM;
+   }
+   else if(frame->opcode == WS_FRAME_TYPE_PING)
+   {
+      //Save the Opcode field
+      rxContext->controlFrameType = WS_FRAME_TYPE_PING;
+
+      //Control frames must not be fragmented
+      if(!frame->fin)
+         webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
+
+      //All control frames must have a payload length of 125 bytes or less
+      if(frame->payloadLen > 125)
+         webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
+   }
+   else if(frame->opcode == WS_FRAME_TYPE_PONG)
+   {
+      //Save the Opcode field
+      rxContext->controlFrameType = WS_FRAME_TYPE_PONG;
+
+      //Control frames must not be fragmented
+      if(!frame->fin)
+         webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
+
+      //All control frames must have a payload length of 125 bytes or less
+      if(frame->payloadLen > 125)
+         webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
+   }
+   else
+   {
+      //If an unknown opcode is received, the receiving endpoint must fail
+      //the WebSocket connection
+      webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
+   }
+
+   //Save the Mask flag
+   rxContext->mask = frame->mask;
+
+   //Control frame?
+   if(rxContext->controlFrameType != WS_FRAME_TYPE_CONTINUATION)
+   {
+      //Return frame type
+      if(type != NULL)
+         *type = rxContext->controlFrameType;
+   }
+   else
+   {
+      //Save the FIN flag
+      rxContext->fin = frame->fin;
+
+      //Return frame type
+      if(type != NULL)
+         *type = rxContext->dataFrameType;
+   }
+
+   //Check status code
+   if(webSocket->statusCode == WS_STATUS_CODE_NO_STATUS_RCVD)
+      return NO_ERROR;
+   else if(webSocket->statusCode == WS_STATUS_CODE_NORMAL_CLOSURE)
+      return ERROR_END_OF_STREAM;
+   else if(webSocket->statusCode == WS_STATUS_CODE_PROTOCOL_ERROR)
+      return ERROR_INVALID_FRAME;
+   else
+      return ERROR_FAILURE;
+}
+
+
+/**
+ * @brief Format a Close frame
+ * @param[in] webSocket Handle to a WebSocket
+ * @return Error code
+ **/
+
+error_t webSocketFormatCloseFrame(WebSocket *webSocket)
+{
+   error_t error;
+   uint8_t *p;
+
+   //Format Close frame
+   error = webSocketFormatFrameHeader(webSocket,
+      TRUE, WS_FRAME_TYPE_CLOSE, sizeof(uint16_t));
+
+   //Check status code
+   if(!error)
+   {
+      //1005 is a reserved value and must not be set as a status code in
+      //a Close control frame by an endpoint
+      if(webSocket->statusCode == WS_STATUS_CODE_NO_STATUS_RCVD)
+         webSocket->statusCode = WS_STATUS_CODE_NORMAL_CLOSURE;
+
+      //Debug message
+      TRACE_DEBUG("  Status Code = %u\r\n", webSocket->statusCode);
+
+      //Point to end of the WebSocket frame header
+      p = webSocket->txContext.buffer + webSocket->txContext.bufferLen;
+
+      //Write status code
+      p[0] = MSB(webSocket->statusCode);
+      p[1] = LSB(webSocket->statusCode);
+
+      //All frames sent from the client to the server are masked
+      if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
+      {
+         //Apply masking
+         p[0] ^= webSocket->txContext.maskingKey[0];
+         p[1] ^= webSocket->txContext.maskingKey[1];
+      }
+
+      //Adjust the length of the frame
+      webSocket->txContext.bufferLen += sizeof(uint16_t);
+   }
+
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/web_socket/web_socket_frame.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,46 @@
+/**
+ * @file web_socket_frame.h
+ * @brief WebSocket frame parsing and formatting
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _WEB_SOCKET_FRAME_H
+#define _WEB_SOCKET_FRAME_H
+
+//Dependencies
+#include "core/net.h"
+#include "web_socket/web_socket.h"
+
+//WebSocket related functions
+error_t webSocketFormatFrameHeader(WebSocket *webSocket,
+   bool_t fin, WebSocketFrameType type, size_t payloadLen);
+
+error_t webSocketParseFrameHeader(WebSocket *webSocket,
+   const WebSocketFrame *frame, WebSocketFrameType *type);
+
+error_t webSocketFormatCloseFrame(WebSocket *webSocket);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/web_socket/web_socket_misc.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,1361 @@
+/**
+ * @file web_socket_misc.c
+ * @brief Helper functions for WebSockets
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL WEB_SOCKET_TRACE_LEVEL
+
+//Dependencies
+#include <stdlib.h>
+#include "core/net.h"
+#include "web_socket/web_socket.h"
+#include "web_socket/web_socket_auth.h"
+#include "web_socket/web_socket_frame.h"
+#include "web_socket/web_socket_transport.h"
+#include "web_socket/web_socket_misc.h"
+#include "base64.h"
+#include "sha1.h"
+#include "str.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (WEB_SOCKET_SUPPORT == ENABLED)
+
+//WebSocket GUID
+const char_t webSocketGuid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+
+
+/**
+ * @brief HTTP status codes
+ **/
+
+static const WebSocketStatusCodeDesc statusCodeList[] =
+{
+   //Success
+   {200, "OK"},
+   {201, "Created"},
+   {202, "Accepted"},
+   {204, "No Content"},
+   //Redirection
+   {301, "Moved Permanently"},
+   {302, "Found"},
+   {304, "Not Modified"},
+   //Client error
+   {400, "Bad Request"},
+   {401, "Unauthorized"},
+   {403, "Forbidden"},
+   {404, "Not Found"},
+   //Server error
+   {500, "Internal Server Error"},
+   {501, "Not Implemented"},
+   {502, "Bad Gateway"},
+   {503, "Service Unavailable"}
+};
+
+
+/**
+ * @brief Update WebSocket state
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] newState New state to switch to
+ **/
+
+void webSocketChangeState(WebSocket *webSocket, WebSocketState newState)
+{
+   //Switch to the new state
+   webSocket->state = newState;
+   //Save current time;
+   webSocket->timestamp = osGetSystemTime();
+
+   //Reset sub-state
+   webSocket->txContext.state = WS_SUB_STATE_INIT;
+   webSocket->rxContext.state = WS_SUB_STATE_INIT;
+}
+
+
+/**
+ * @brief Parse client or server handshake
+ * @param[in] webSocket Handle to a WebSocket
+ * @return Error code
+ **/
+
+error_t webSocketParseHandshake(WebSocket *webSocket)
+{
+   error_t error;
+   size_t n;
+   WebSocketFrameContext *rxContext;
+
+   //Point to the RX context
+   rxContext = &webSocket->rxContext;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+   //Wait for the handshake to complete
+   while(1)
+   {
+      //Client or server operation?
+      if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
+      {
+         if(webSocket->state == WS_STATE_OPEN)
+            break;
+      }
+      else
+      {
+         if(webSocket->state == WS_STATE_SERVER_HANDSHAKE)
+            break;
+      }
+
+      //Check current sub-state
+      if(rxContext->state == WS_SUB_STATE_INIT)
+      {
+         //Initialize status code
+         webSocket->statusCode = WS_STATUS_CODE_NO_STATUS_RCVD;
+
+         //Initialize FIN flag
+         rxContext->fin = TRUE;
+
+         //Flush the receive buffer
+         rxContext->bufferPos = 0;
+         rxContext->bufferLen = 0;
+
+         //Initialize variables
+         webSocket->handshakeContext.statusCode = 0;
+         webSocket->handshakeContext.upgradeWebSocket = FALSE;
+         webSocket->handshakeContext.connectionUpgrade = FALSE;
+         webSocket->handshakeContext.connectionClose = FALSE;
+         webSocket->handshakeContext.contentLength = 0;
+         webSocket->handshakeContext.closingFrameSent = FALSE;
+         webSocket->handshakeContext.closingFrameReceived = FALSE;
+
+#if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
+         webSocket->authContext.requiredAuthMode = WS_AUTH_MODE_NONE;
+#endif
+
+#if (WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
+         strcpy(webSocket->authContext.nonce, "");
+         strcpy(webSocket->authContext.opaque, "");
+         webSocket->authContext.stale = FALSE;
+#endif
+
+         //Client or server operation?
+         if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
+         {
+            //Clear server key
+            strcpy(webSocket->handshakeContext.serverKey, "");
+
+            //Debug message
+            TRACE_DEBUG("WebSocket: server handshake\r\n");
+         }
+         else
+         {
+            //Clear client key
+            strcpy(webSocket->handshakeContext.clientKey, "");
+
+            //Debug message
+            TRACE_DEBUG("WebSocket: client handshake\r\n");
+         }
+
+         //Decode the leading line
+         rxContext->state = WS_SUB_STATE_HANDSHAKE_LEADING_LINE;
+      }
+      else if(rxContext->state == WS_SUB_STATE_HANDSHAKE_LEADING_LINE)
+      {
+         //Check whether more data is required
+         if(rxContext->bufferLen < 2)
+         {
+            //Limit the number of characters to read at a time
+            n = WEB_SOCKET_BUFFER_SIZE - 1 - rxContext->bufferLen;
+
+            //Read data until a CLRF character is encountered
+            error = webSocketReceiveData(webSocket, rxContext->buffer +
+               rxContext->bufferLen, n, &n, SOCKET_FLAG_BREAK_CRLF);
+
+            //Update the length of the buffer
+            rxContext->bufferLen += n;
+         }
+         else if(rxContext->bufferLen >= (WEB_SOCKET_BUFFER_SIZE - 1))
+         {
+            //Report an error
+            error = ERROR_INVALID_REQUEST;
+         }
+         else if(strncmp((char_t *) rxContext->buffer + rxContext->bufferLen - 2, "\r\n", 2))
+         {
+            //Limit the number of characters to read at a time
+            n = WEB_SOCKET_BUFFER_SIZE - 1 - rxContext->bufferLen;
+
+            //Read data until a CLRF character is encountered
+            error = webSocketReceiveData(webSocket, rxContext->buffer +
+               rxContext->bufferLen, n, &n, SOCKET_FLAG_BREAK_CRLF);
+
+            //Update the length of the buffer
+            rxContext->bufferLen += n;
+         }
+         else
+         {
+            //Properly terminate the string with a NULL character
+            rxContext->buffer[rxContext->bufferLen] = '\0';
+
+            //Client or server operation?
+            if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
+            {
+               //The leading line from the server follows the Status-Line format
+               error = webSocketParseStatusLine(webSocket, (char_t *) rxContext->buffer);
+            }
+            else
+            {
+               //The leading line from the client follows the Request-Line format
+               error = webSocketParseRequestLine(webSocket, (char_t *) rxContext->buffer);
+            }
+
+            //Flush the receive buffer
+            rxContext->bufferPos = 0;
+            rxContext->bufferLen = 0;
+
+            //Parse the header fields of the handshake
+            rxContext->state = WS_SUB_STATE_HANDSHAKE_HEADER_FIELD;
+         }
+      }
+      else if(rxContext->state == WS_SUB_STATE_HANDSHAKE_HEADER_FIELD)
+      {
+         //Check whether more data is required
+         if(rxContext->bufferLen < 2)
+         {
+            //Limit the number of characters to read at a time
+            n = WEB_SOCKET_BUFFER_SIZE - 1 - rxContext->bufferLen;
+
+            //Read data until a CLRF character is encountered
+            error = webSocketReceiveData(webSocket, rxContext->buffer +
+               rxContext->bufferLen, n, &n, SOCKET_FLAG_BREAK_CRLF);
+
+            //Update the length of the buffer
+            rxContext->bufferLen += n;
+         }
+         else if(rxContext->bufferLen >= (WEB_SOCKET_BUFFER_SIZE - 1))
+         {
+            //Report an error
+            error = ERROR_INVALID_REQUEST;
+         }
+         else if(strncmp((char_t *) rxContext->buffer + rxContext->bufferLen - 2, "\r\n", 2))
+         {
+            //Limit the number of characters to read at a time
+            n = WEB_SOCKET_BUFFER_SIZE - 1 - rxContext->bufferLen;
+
+            //Read data until a CLRF character is encountered
+            error = webSocketReceiveData(webSocket, rxContext->buffer +
+               rxContext->bufferLen, n, &n, SOCKET_FLAG_BREAK_CRLF);
+
+            //Update the length of the buffer
+            rxContext->bufferLen += n;
+         }
+         else
+         {
+            //Properly terminate the string with a NULL character
+            rxContext->buffer[rxContext->bufferLen] = '\0';
+
+            //An empty line indicates the end of the header fields
+            if(!strcmp((char_t *) rxContext->buffer, "\r\n"))
+            {
+               //Client or server operation?
+               if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
+               {
+                  //Verify server's handshake
+                  error = webSocketVerifyServerHandshake(webSocket);
+               }
+               else
+               {
+                  //Verify client's handshake
+                  error = webSocketVerifyClientHandshake(webSocket);
+               }
+            }
+            else
+            {
+               //Read the next character to detect if the CRLF is immediately
+               //followed by a LWSP character
+               rxContext->state = WS_SUB_STATE_HANDSHAKE_LWSP;
+            }
+         }
+      }
+      else if(rxContext->state == WS_SUB_STATE_HANDSHAKE_LWSP)
+      {
+         char_t nextChar;
+
+         //Read the next character
+         error = webSocketReceiveData(webSocket, &nextChar, sizeof(char_t), &n, 0);
+
+         //Successful read operation?
+         if(!error && n == sizeof(char_t))
+         {
+            //LWSP character found?
+            if(nextChar == ' ' || nextChar == '\t')
+            {
+               //Unfolding is accomplished by regarding CRLF immediately
+               //followed by a LWSP as equivalent to the LWSP character
+               if(rxContext->bufferLen >= 2)
+               {
+                  //Remove trailing CRLF sequence
+                  rxContext->bufferLen -= 2;
+               }
+
+               //The header field spans multiple line
+               rxContext->state = WS_SUB_STATE_HANDSHAKE_HEADER_FIELD;
+            }
+            else
+            {
+               //Parse header field
+               error = webSocketParseHeaderField(webSocket, (char_t *) rxContext->buffer);
+
+               //Restore the very first character of the header field
+               rxContext->buffer[0] = nextChar;
+               //Adjust the length of the receive buffer
+               rxContext->bufferLen = sizeof(char_t);
+
+               //Decode the next header field
+               rxContext->state = WS_SUB_STATE_HANDSHAKE_HEADER_FIELD;
+            }
+         }
+      }
+      else
+      {
+         //Invalid state
+         error = ERROR_WRONG_STATE;
+      }
+
+      //Any error to report?
+      if(error)
+         break;
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Parse the Request-Line of the client's handshake
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] line NULL-terminated string that contains the Request-Line
+ * @return Error code
+ **/
+
+error_t webSocketParseRequestLine(WebSocket *webSocket, char_t *line)
+{
+   error_t error;
+   char_t *token;
+   char_t *p;
+   char_t *s;
+
+   //Debug message
+   TRACE_DEBUG("%s", line);
+
+   //The Request-Line begins with a method token
+   token = strtok_r(line, " \r\n", &p);
+   //Unable to retrieve the method?
+   if(token == NULL)
+      return ERROR_INVALID_REQUEST;
+
+   //The method of the request must be GET
+   if(strcasecmp(token, "GET"))
+      return ERROR_INVALID_REQUEST;
+
+   //The Request-URI is following the method token
+   token = strtok_r(NULL, " \r\n", &p);
+   //Unable to retrieve the Request-URI?
+   if(token == NULL)
+      return ERROR_INVALID_REQUEST;
+
+   //Check whether a query string is present
+   s = strchr(token, '?');
+
+   //Query string found?
+   if(s != NULL)
+   {
+      //Split the string
+      *s = '\0';
+
+      //Save the Request-URI
+      error = webSocketDecodePercentEncodedString(token,
+         webSocket->uri, WEB_SOCKET_URI_MAX_LEN);
+      //Any error to report?
+      if(error)
+         return ERROR_INVALID_REQUEST;
+
+      //Check the length of the query string
+      if(strlen(s + 1) > WEB_SOCKET_QUERY_STRING_MAX_LEN)
+         return ERROR_INVALID_REQUEST;
+
+      //Save the query string
+      strcpy(webSocket->queryString, s + 1);
+   }
+   else
+   {
+      //Save the Request-URI
+      error = webSocketDecodePercentEncodedString(token,
+         webSocket->uri, WEB_SOCKET_URI_MAX_LEN);
+      //Any error to report?
+      if(error)
+         return ERROR_INVALID_REQUEST;
+
+      //No query string
+      webSocket->queryString[0] = '\0';
+   }
+
+   //The protocol version is following the Request-URI
+   token = strtok_r(NULL, " \r\n", &p);
+
+   //HTTP version 0.9?
+   if(token == NULL)
+   {
+      //Save version number
+      webSocket->handshakeContext.version = WS_HTTP_VERSION_0_9;
+      //Persistent connections are not supported
+      webSocket->handshakeContext.connectionClose = TRUE;
+   }
+   //HTTP version 1.0?
+   else if(!strcasecmp(token, "HTTP/1.0"))
+   {
+      //Save version number
+      webSocket->handshakeContext.version = WS_HTTP_VERSION_1_0;
+      //By default connections are not persistent
+      webSocket->handshakeContext.connectionClose = TRUE;
+   }
+   //HTTP version 1.1?
+   else if(!strcasecmp(token, "HTTP/1.1"))
+   {
+      //Save version number
+      webSocket->handshakeContext.version = WS_HTTP_VERSION_1_1;
+      //HTTP 1.1 makes persistent connections the default
+      webSocket->handshakeContext.connectionClose = FALSE;
+   }
+   //HTTP version not supported?
+   else
+   {
+      //Report an error
+      return ERROR_INVALID_REQUEST;
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse the Status-Line of the server's handshake
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] line NULL-terminated string that contains the Status-Line
+ * @return Error code
+ **/
+
+error_t webSocketParseStatusLine(WebSocket *webSocket, char_t *line)
+{
+   char_t *p;
+   char_t *token;
+
+   //Debug message
+   TRACE_DEBUG("%s", line);
+
+   //Retrieve the HTTP-Version field
+   token = strtok_r(line, " ", &p);
+   //Any parsing error?
+   if(token == NULL)
+      return ERROR_INVALID_SYNTAX;
+
+   //Retrieve the Status-Code field
+   token = strtok_r(NULL, " ", &p);
+   //Any parsing error?
+   if(token == NULL)
+      return ERROR_INVALID_SYNTAX;
+
+   //Convert the status code
+   webSocket->handshakeContext.statusCode = strtoul(token, &p, 10);
+   //Any parsing error?
+   if(*p != '\0')
+      return ERROR_INVALID_SYNTAX;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse a header field
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] line NULL-terminated string that contains the header field
+ * @return Error code
+ **/
+
+error_t webSocketParseHeaderField(WebSocket *webSocket, char_t *line)
+{
+   char_t *separator;
+   char_t *name;
+   char_t *value;
+   WebSocketHandshakeContext *handshakeContext;
+
+   //Point to the handshake context
+   handshakeContext = &webSocket->handshakeContext;
+
+   //Debug message
+   TRACE_DEBUG("%s", line);
+
+   //Check whether a separator is present
+   separator = strchr(line, ':');
+
+   //Separator found?
+   if(separator != NULL)
+   {
+      //Split the line
+      *separator = '\0';
+
+      //Get field name and value
+      name = strTrimWhitespace(line);
+      value = strTrimWhitespace(separator + 1);
+
+      //Upgrade header field found?
+      if(!strcasecmp(name, "Upgrade"))
+      {
+         if(!strcasecmp(value, "websocket"))
+            handshakeContext->upgradeWebSocket = TRUE;
+
+      }
+      //Connection header field found?
+      else if(!strcasecmp(name, "Connection"))
+      {
+         //Parse Connection header field
+         webSocketParseConnectionField(webSocket, value);
+      }
+      //Sec-WebSocket-Key header field found?
+      else if(!strcasecmp(name, "Sec-WebSocket-Key"))
+      {
+         //Server operation?
+         if(webSocket->endpoint == WS_ENDPOINT_SERVER)
+         {
+            //Save the contents of the Sec-WebSocket-Key header field
+            strSafeCopy(handshakeContext->clientKey, value,
+               WEB_SOCKET_CLIENT_KEY_SIZE + 1);
+         }
+      }
+      //Sec-WebSocket-Accept header field found?
+      else if(!strcasecmp(name, "Sec-WebSocket-Accept"))
+      {
+         //Client operation?
+         if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
+         {
+            //Save the contents of the Sec-WebSocket-Accept header field
+            strSafeCopy(handshakeContext->serverKey, value,
+               WEB_SOCKET_SERVER_KEY_SIZE + 1);
+         }
+      }
+#if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
+      //WWW-Authenticate header field found?
+      else if(!strcasecmp(name, "WWW-Authenticate"))
+      {
+         //Parse WWW-Authenticate header field
+         webSocketParseAuthenticateField(webSocket, value);
+      }
+#endif
+      //Content-Length header field found?
+      else if(!strcasecmp(name, "Content-Length"))
+      {
+         handshakeContext->contentLength = strtoul(value, NULL, 10);
+      }
+   }
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Parse Connection header field
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] value NULL-terminated string that contains the value of header field
+ **/
+
+void webSocketParseConnectionField(WebSocket *webSocket, char_t *value)
+{
+   char_t *p;
+   char_t *token;
+
+   //Get the first value of the list
+   token = strtok_r(value, ",", &p);
+
+   //Parse the comma-separated list
+   while(token != NULL)
+   {
+      //Trim whitespace characters
+      value = strTrimWhitespace(token);
+
+      //Check current value
+      if(!strcasecmp(value, "keep-alive"))
+      {
+         //The connection is persistent
+         webSocket->handshakeContext.connectionClose = FALSE;
+      }
+      else if(!strcasecmp(value, "close"))
+      {
+         //The connection will be closed after completion of the response
+         webSocket->handshakeContext.connectionClose = TRUE;
+      }
+      else if(!strcasecmp(value, "upgrade"))
+      {
+         //Upgrade the connection
+         webSocket->handshakeContext.connectionUpgrade = TRUE;
+      }
+
+      //Get next value
+      token = strtok_r(NULL, ",", &p);
+   }
+}
+
+
+/**
+ * @brief Format client's handshake
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] serverPort TCP port number used to establish the connection
+ * @return Error code
+ **/
+
+error_t webSocketFormatClientHandshake(WebSocket *webSocket, uint16_t serverPort)
+{
+   char_t *p;
+   WebSocketFrameContext *txContext;
+
+   //Point to the TX context
+   txContext = &webSocket->txContext;
+   //Point to the buffer where to format the client's handshake
+   p = (char_t *) txContext->buffer;
+
+   //The Request-Line begins with a method token, followed by the
+   //Request-URI and the protocol version, and ending with CRLF
+   p += sprintf(p, "GET %s HTTP/1.1\r\n", webSocket->uri);
+
+   //Add Host header field
+   if(webSocket->host[0] != '\0')
+   {
+      //The Host header field specifies the Internet host and port number of
+      //the resource being requested
+      p += sprintf(p, "Host: %s:%d\r\n", webSocket->host, serverPort);
+   }
+   else
+   {
+      //If the requested URI does not include a host name for the service being
+      //requested, then the Host header field must be given with an empty value
+      p += sprintf(p, "Host:\r\n");
+   }
+
+#if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
+   //Check whether authentication is required
+   if(webSocket->authContext.selectedAuthMode != WS_AUTH_MODE_NONE)
+   {
+      //Add Authorization header field
+      p += webSocketAddAuthorizationField(webSocket, p);
+   }
+#endif
+
+   //Add Origin header field
+   if(webSocket->origin[0] != '\0')
+      p += sprintf(p, "Origin: %s\r\n", webSocket->origin);
+   else
+      p += sprintf(p, "Origin: null\r\n");
+
+   //Add Upgrade header field
+   p += sprintf(p, "Upgrade: websocket\r\n");
+   //Add Connection header field
+   p += sprintf(p, "Connection: Upgrade\r\n");
+
+   //Add Sec-WebSocket-Protocol header field
+   if(webSocket->subProtocol[0] != '\0')
+      p += sprintf(p, "Sec-WebSocket-Protocol: %s\r\n", webSocket->subProtocol);
+
+   //Add Sec-WebSocket-Key header field
+   p += sprintf(p, "Sec-WebSocket-Key: %s\r\n",
+      webSocket->handshakeContext.clientKey);
+
+   //Add Sec-WebSocket-Version header field
+   p += sprintf(p, "Sec-WebSocket-Version: 13\r\n");
+   //An empty line indicates the end of the header fields
+   p += sprintf(p, "\r\n");
+
+   //Debug message
+   TRACE_DEBUG("\r\n");
+   TRACE_DEBUG("WebSocket: client handshake\r\n");
+   TRACE_DEBUG("%s", txContext->buffer);
+
+   //Rewind to the beginning of the buffer
+   txContext->bufferPos = 0;
+   //Update the number of data buffered but not yet sent
+   txContext->bufferLen = strlen((char_t *) txContext->buffer);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format server's handshake
+ * @param[in] webSocket Handle to a WebSocket
+ * @return Error code
+ **/
+
+error_t webSocketFormatServerHandshake(WebSocket *webSocket)
+{
+   char_t *p;
+   WebSocketFrameContext *txContext;
+
+   //Point to the TX context
+   txContext = &webSocket->txContext;
+   //Point to the buffer where to format the client's handshake
+   p = (char_t *) txContext->buffer;
+
+   //The first line is an HTTP Status-Line, with the status code 101
+   p += sprintf(p, "HTTP/1.1 101 Switching Protocols\r\n");
+
+   //Add Upgrade header field
+   p += sprintf(p, "Upgrade: websocket\r\n");
+   //Add Connection header field
+   p += sprintf(p, "Connection: Upgrade\r\n");
+
+   //Add Sec-WebSocket-Protocol header field
+   if(webSocket->subProtocol[0] != '\0')
+      p += sprintf(p, "Sec-WebSocket-Protocol: %s\r\n", webSocket->subProtocol);
+
+   //Add Sec-WebSocket-Accept header field
+   p += sprintf(p, "Sec-WebSocket-Accept: %s\r\n",
+      webSocket->handshakeContext.serverKey);
+
+   //An empty line indicates the end of the header fields
+   p += sprintf(p, "\r\n");
+
+   //Debug message
+   TRACE_DEBUG("\r\n");
+   TRACE_DEBUG("WebSocket: server handshake\r\n");
+   TRACE_DEBUG("%s", txContext->buffer);
+
+   //Rewind to the beginning of the buffer
+   txContext->bufferPos = 0;
+   //Update the number of data buffered but not yet sent
+   txContext->bufferLen = strlen((char_t *) txContext->buffer);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Format HTTP error response
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] statusCode HTTP status code
+ * @param[in] message User message
+ * @return Error code
+ **/
+
+error_t webSocketFormatErrorResponse(WebSocket *webSocket,
+   uint_t statusCode, const char_t *message)
+{
+   uint_t i;
+   size_t length;
+   char_t *p;
+   WebSocketFrameContext *txContext;
+
+   //HTML response template
+   static const char_t template[] =
+      "<!doctype html>\r\n"
+      "<html>\r\n"
+      "<head><title>Error %03d</title></head>\r\n"
+      "<body>\r\n"
+      "<h2>Error %03d</h2>\r\n"
+      "<p>%s</p>\r\n"
+      "</body>\r\n"
+      "</html>\r\n";
+
+   //Point to the TX context
+   txContext = &webSocket->txContext;
+   //Point to the buffer where to format the client's handshake
+   p = (char_t *) txContext->buffer;
+
+   //The first line of a response message is the Status-Line, consisting
+   //of the protocol version followed by a numeric status code and its
+   //associated textual phrase
+   p += sprintf(p, "HTTP/%u.%u %u ", MSB(webSocket->handshakeContext.version),
+      LSB(webSocket->handshakeContext.version), statusCode);
+
+   //Retrieve the Reason-Phrase that corresponds to the Status-Code
+   for(i = 0; i < arraysize(statusCodeList); i++)
+   {
+      //Check the status code
+      if(statusCodeList[i].value == statusCode)
+      {
+         //Append the textual phrase to the Status-Line
+         p += sprintf(p, statusCodeList[i].message);
+         //Break the loop and continue processing
+         break;
+      }
+   }
+
+   //Properly terminate the Status-Line
+   p += sprintf(p, "\r\n");
+
+   //Content type
+   p += sprintf(p, "Content-Type: %s\r\n", "text/html");
+
+   //Compute the length of the response
+   length = strlen(template) + strlen(message) - 4;
+   //Set Content-Length field
+   p += sprintf(p, "Content-Length: %" PRIuSIZE "\r\n", length);
+
+   //Terminate the header with an empty line
+   p += sprintf(p, "\r\n");
+
+   //Format HTML response
+   p += sprintf(p, template, statusCode, statusCode, message);
+
+   //Rewind to the beginning of the buffer
+   txContext->bufferPos = 0;
+   //Update the number of data buffered but not yet sent
+   txContext->bufferLen = strlen((char_t *) txContext->buffer);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Verify client's handshake
+ * @param[in] webSocket Handle to a WebSocket
+ * @return Error code
+ **/
+
+error_t webSocketVerifyClientHandshake(WebSocket *webSocket)
+{
+   error_t error;
+   WebSocketHandshakeContext *handshakeContext;
+
+   //Debug message
+   TRACE_DEBUG("WebSocket: verifying client handshake\r\n");
+
+   //Point to the handshake context
+   handshakeContext = &webSocket->handshakeContext;
+
+   //The HTTP version must be at least 1.1
+   if(handshakeContext->version < WS_HTTP_VERSION_1_1)
+      return ERROR_INVALID_REQUEST;
+
+   //The request must contain an Upgrade header field whose value
+   //must include the "websocket" keyword
+   if(!handshakeContext->upgradeWebSocket)
+      return ERROR_INVALID_REQUEST;
+
+   //The request must contain a Connection header field whose value
+   //must include the "Upgrade" token
+   if(!handshakeContext->connectionUpgrade)
+      return ERROR_INVALID_REQUEST;
+
+   //The request must include a header field with the name Sec-WebSocket-Key
+   if(handshakeContext->clientKey[0] == 0)
+      return ERROR_INVALID_REQUEST;
+
+   //Check the Sec-WebSocket-Key header field
+   error = webSocketVerifyClientKey(webSocket);
+   //Verification failed?
+   if(error)
+      return error;
+
+   //Generate the server part of the handshake
+   webSocketChangeState(webSocket, WS_STATE_SERVER_HANDSHAKE);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Verify server's handshake
+ * @param[in] webSocket Handle to a WebSocket
+ * @return Error code
+ **/
+
+error_t webSocketVerifyServerHandshake(WebSocket *webSocket)
+{
+   error_t error;
+   WebSocketHandshakeContext *handshakeContext;
+
+   //Debug message
+   TRACE_DEBUG("WebSocket: verifying server handshake\r\n");
+
+   //Point to the handshake context
+   handshakeContext = &webSocket->handshakeContext;
+
+   //If the status code received from the server is not 101, the client
+   //handles the response per HTTP procedures
+   if(handshakeContext->statusCode == 401)
+   {
+      //Authorization required
+      return ERROR_AUTH_REQUIRED;
+   }
+   else if(handshakeContext->statusCode != 101)
+   {
+      //Unknown status code
+      return ERROR_INVALID_STATUS;
+   }
+
+   //If the response lacks an Upgrade header field or the Upgrade header field
+   //contains a value that is not an ASCII case-insensitive match for the
+   //value "websocket", the client must fail the WebSocket connection
+   if(!handshakeContext->upgradeWebSocket)
+      return ERROR_INVALID_SYNTAX;
+
+   //If the response lacks a Connection header field or the Connection header
+   //field doesn't contain a token that is an ASCII case-insensitive match for
+   //the value "Upgrade", the client must fail the WebSocket connection
+   if(!handshakeContext->connectionUpgrade)
+      return ERROR_INVALID_SYNTAX;
+
+   //If the response lacks a Sec-WebSocket-Accept header field, the client
+   //must fail the WebSocket connection
+   if(strlen(handshakeContext->serverKey) == 0)
+      return ERROR_INVALID_SYNTAX;
+
+   //Check the Sec-WebSocket-Accept header field
+   error = webSocketVerifyServerKey(webSocket);
+   //Verification failed?
+   if(error)
+      return error;
+
+   //If the server's response is validated as provided for above, it is
+   //said that the WebSocket connection is established and that the
+   //WebSocket connection is in the OPEN state
+   webSocketChangeState(webSocket, WS_STATE_OPEN);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Generate client's key
+ * @param[in] webSocket Handle to a WebSocket
+ * @return Error code
+ **/
+
+error_t webSocketGenerateClientKey(WebSocket *webSocket)
+{
+   error_t error;
+   size_t n;
+   uint8_t nonce[16];
+   WebSocketHandshakeContext *handshakeContext;
+
+   //Debug message
+   TRACE_DEBUG("WebSocket: Generating client's key...\r\n");
+
+   //Point to the handshake context
+   handshakeContext = &webSocket->handshakeContext;
+
+   //Make sure that the RNG callback function has been registered
+   if(webSockRandCallback == NULL)
+   {
+      //A cryptographically strong random number generator
+      //must be used to generate the nonce
+      return ERROR_PRNG_NOT_READY;
+   }
+
+   //A nonce must be selected randomly for each connection
+   error = webSockRandCallback(nonce, sizeof(nonce));
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Encode the client's key
+   base64Encode(nonce, sizeof(nonce), handshakeContext->clientKey, &n);
+
+   //Debug message
+   TRACE_DEBUG("  Client key: %s\r\n", handshakeContext->clientKey);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Generate server's key
+ * @param[in] webSocket Handle to a WebSocket
+ * @return Error code
+ **/
+
+error_t webSocketGenerateServerKey(WebSocket *webSocket)
+{
+   size_t n;
+   WebSocketHandshakeContext *handshakeContext;
+   Sha1Context sha1Context;
+
+   //Debug message
+   TRACE_DEBUG("WebSocket: Generating server's key...\r\n");
+
+   //Point to the handshake context
+   handshakeContext = &webSocket->handshakeContext;
+
+   //Retrieve the length of the client key
+   n = strlen(handshakeContext->clientKey);
+
+   //Concatenate the Sec-WebSocket-Key with the GUID string and digest
+   //the resulting string using SHA-1
+   sha1Init(&sha1Context);
+   sha1Update(&sha1Context, handshakeContext->clientKey, n);
+   sha1Update(&sha1Context, webSocketGuid, strlen(webSocketGuid));
+   sha1Final(&sha1Context, NULL);
+
+   //Encode the result using Base64
+   base64Encode(sha1Context.digest, SHA1_DIGEST_SIZE,
+      handshakeContext->serverKey, &n);
+
+   //Debug message
+   TRACE_DEBUG("  Server key: %s\r\n", handshakeContext->serverKey);
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Verify client's key
+ * @param[in] webSocket Handle to a WebSocket
+ * @return Error code
+ **/
+
+error_t webSocketVerifyClientKey(WebSocket *webSocket)
+{
+   error_t error;
+   size_t n;
+   char_t *buffer;
+   WebSocketHandshakeContext *handshakeContext;
+
+   //Debug message
+   TRACE_DEBUG("WebSocket: Verifying client's key...\r\n");
+
+   //Temporary buffer
+   buffer = (char_t *) webSocket->txContext.buffer;
+
+   //Point to the handshake context
+   handshakeContext = &webSocket->handshakeContext;
+
+   //Retrieve the length of the client's key
+   n = strlen(handshakeContext->clientKey);
+
+   //The value of the Sec-WebSocket-Key header field must be a 16-byte
+   //value that has been Base64-encoded
+   error = base64Decode(handshakeContext->clientKey, n, buffer, &n);
+   //Decoding failed?
+   if(error)
+      return ERROR_INVALID_KEY;
+
+   //Check the length of the resulting value
+   if(n != 16)
+      return ERROR_INVALID_KEY;
+
+   //Successful verification
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Verify server's key
+ * @param[in] webSocket Handle to a WebSocket
+ * @return Error code
+ **/
+
+error_t webSocketVerifyServerKey(WebSocket *webSocket)
+{
+   size_t n;
+   char_t *buffer;
+   WebSocketHandshakeContext *handshakeContext;
+   Sha1Context sha1Context;
+
+   //Debug message
+   TRACE_DEBUG("WebSocket: Verifying server's key...\r\n");
+
+   //Temporary buffer
+   buffer = (char_t *) webSocket->txContext.buffer;
+
+   //Point to the handshake context
+   handshakeContext = &webSocket->handshakeContext;
+
+   //Retrieve the length of the client's key
+   n = strlen(handshakeContext->clientKey);
+
+   //Concatenate the Sec-WebSocket-Key with the GUID string and digest
+   //the resulting string using SHA-1
+   sha1Init(&sha1Context);
+   sha1Update(&sha1Context, handshakeContext->clientKey, n);
+   sha1Update(&sha1Context, webSocketGuid, strlen(webSocketGuid));
+   sha1Final(&sha1Context, NULL);
+
+   //Encode the result using Base64
+   base64Encode(sha1Context.digest, SHA1_DIGEST_SIZE, buffer, &n);
+
+   //Debug message
+   TRACE_DEBUG("  Client key: %s\r\n", handshakeContext->clientKey);
+   TRACE_DEBUG("  Server key: %s\r\n", handshakeContext->serverKey);
+   TRACE_DEBUG("  Calculated key: %s\r\n", webSocket->txContext.buffer);
+
+   //Check whether the server's key is valid
+   if(strcmp(handshakeContext->serverKey, buffer))
+      return ERROR_INVALID_KEY;
+
+   //Successful verification
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Check whether a status code is valid
+ * @param[in] statusCode Status code
+ * @return The function returns TRUE is the specified status code is
+ *   valid. Otherwise, FALSE is returned
+ **/
+
+bool_t webSocketCheckStatusCode(uint16_t statusCode)
+{
+   bool_t valid;
+
+   //Check status code
+   if(statusCode == WS_STATUS_CODE_NORMAL_CLOSURE ||
+      statusCode == WS_STATUS_CODE_GOING_AWAY ||
+      statusCode == WS_STATUS_CODE_PROTOCOL_ERROR ||
+      statusCode == WS_STATUS_CODE_UNSUPPORTED_DATA ||
+      statusCode == WS_STATUS_CODE_INVALID_PAYLOAD_DATA ||
+      statusCode == WS_STATUS_CODE_POLICY_VIOLATION ||
+      statusCode == WS_STATUS_CODE_MESSAGE_TOO_BIG ||
+      statusCode == WS_STATUS_CODE_MANDATORY_EXT ||
+      statusCode == WS_STATUS_CODE_INTERNAL_ERROR)
+   {
+      valid = TRUE;
+   }
+   else if(statusCode >= 3000)
+   {
+      valid = TRUE;
+   }
+   else
+   {
+      valid = FALSE;
+   }
+
+   //The function returns TRUE is the specified status code is valid
+   return valid;
+}
+
+
+/**
+ * @brief Decode a percent-encoded string
+ * @param[in] input NULL-terminated string to be decoded
+ * @param[out] output NULL-terminated string resulting from the decoding process
+ * @param[in] outputSize Size of the output buffer in bytes
+ * @return Error code
+ **/
+
+error_t webSocketDecodePercentEncodedString(const char_t *input,
+   char_t *output, size_t outputSize)
+{
+   size_t i;
+   char_t buffer[3];
+
+   //Check parameters
+   if(input == NULL || output == NULL)
+      return ERROR_INVALID_PARAMETER;
+
+   //Decode the percent-encoded string
+   for(i = 0; *input != '\0' && i < outputSize; i++)
+   {
+      //Check current character
+      if(*input == '+')
+      {
+         //Replace '+' characters with spaces
+         output[i] = ' ';
+         //Advance data pointer
+         input++;
+      }
+      else if(input[0] == '%' && input[1] != '\0' && input[2] != '\0')
+      {
+         //Process percent-encoded characters
+         buffer[0] = input[1];
+         buffer[1] = input[2];
+         buffer[2] = '\0';
+         //String to integer conversion
+         output[i] = (uint8_t) strtoul(buffer, NULL, 16);
+         //Advance data pointer
+         input += 3;
+      }
+      else
+      {
+         //Copy any other characters
+         output[i] = *input;
+         //Advance data pointer
+         input++;
+      }
+   }
+
+   //Check whether the output buffer runs out of space
+   if(i >= outputSize)
+      return ERROR_FAILURE;
+
+   //Properly terminate the resulting string
+   output[i] = '\0';
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Check whether a an UTF-8 stream is valid
+ * @param[in] context UTF-8 decoding context
+ * @param[in] data Pointer to the chunk of data to be processed
+ * @param[in] length Data chunk length
+ * @param[in] remaining number of remaining bytes in the UTF-8 stream
+ * @return The function returns TRUE is the specified UTF-8 stream is
+ *   valid. Otherwise, FALSE is returned
+ **/
+
+bool_t webSocketCheckUtf8Stream(WebSocketUtf8Context *context,
+   const uint8_t *data, size_t length, size_t remaining)
+{
+   size_t i;
+   bool_t valid;
+
+   //Initialize flag
+   valid = TRUE;
+
+   //Interpret the byte stream as UTF-8
+   for(i = 0; i < length && valid; i++)
+   {
+      //Leading or continuation byte?
+      if(context->utf8CharIndex == 0)
+      {
+         //7-bit code point?
+         if((data[i] & 0x80) == 0x00)
+         {
+            //The code point consist of a single byte
+            context->utf8CharSize = 1;
+            //Decode the first byte of the sequence
+            context->utf8CodePoint = data[i] & 0x7F;
+         }
+         //11-bit code point?
+         else if((data[i] & 0xE0) == 0xC0)
+         {
+            //The code point consist of a 2 bytes
+            context->utf8CharSize = 2;
+            //Decode the first byte of the sequence
+            context->utf8CodePoint = (data[i] & 0x1F) << 6;
+         }
+         //16-bit code point?
+         else if((data[i] & 0xF0) == 0xE0)
+         {
+            //The code point consist of a 3 bytes
+            context->utf8CharSize = 3;
+            //Decode the first byte of the sequence
+            context->utf8CodePoint = (data[i] & 0x0F) << 12;
+         }
+         //21-bit code point?
+         else if((data[i] & 0xF8) == 0xF0)
+         {
+            //The code point consist of a 3 bytes
+            context->utf8CharSize = 4;
+            //Decode the first byte of the sequence
+            context->utf8CodePoint = (data[i] & 0x07) << 18;
+         }
+         else
+         {
+            //The UTF-8 stream is not valid
+            valid = FALSE;
+         }
+
+         //This test only applies to frames that are not fragmented
+         if(length <= remaining)
+         {
+            //Make sure the UTF-8 stream is properly terminated
+            if((i + context->utf8CharSize) > remaining)
+            {
+               //The UTF-8 stream is not valid
+               valid = FALSE;
+            }
+         }
+
+         //Decode the next byte of the sequence
+         context->utf8CharIndex = context->utf8CharSize - 1;
+      }
+      else
+      {
+         //Continuation bytes all have 10 in the high-order position
+         if((data[i] & 0xC0) == 0x80)
+         {
+            //Decode the multi-byte sequence
+            context->utf8CharIndex--;
+            //All continuation bytes contain exactly 6 bits from the code point
+            context->utf8CodePoint |= (data[i] & 0x3F) << (context->utf8CharIndex * 6);
+
+            //The correct encoding of a code point use only the minimum number
+            //of bytes required to hold the significant bits of the code point
+            if(context->utf8CharSize == 2)
+            {
+               //Overlong encoding is not supported
+               if((context->utf8CodePoint & ~0x7F) == 0)
+                  valid = FALSE;
+            }
+            if(context->utf8CharSize == 3 && context->utf8CharIndex < 2)
+            {
+               //Overlong encoding is not supported
+               if((context->utf8CodePoint & ~0x7FF) == 0)
+                  valid = FALSE;
+            }
+            if(context->utf8CharSize == 4 && context->utf8CharIndex < 3)
+            {
+               //Overlong encoding is not supported
+               if((context->utf8CodePoint & ~0xFFFF) == 0)
+                  valid = FALSE;
+            }
+
+            //According to the UTF-8 definition (RFC 3629) the high and low
+            //surrogate halves used by UTF-16 (U+D800 through U+DFFF) are not
+            //legal Unicode values, and their UTF-8 encoding should be treated
+            //as an invalid byte sequence
+            if(context->utf8CodePoint >= 0xD800 && context->utf8CodePoint < 0xE000)
+               valid = FALSE;
+
+            //Code points greater than U+10FFFF are not valid
+            if(context->utf8CodePoint >= 0x110000)
+               valid = FALSE;
+         }
+         else
+         {
+            //The start byte is not followed by enough continuation bytes
+            valid = FALSE;
+         }
+      }
+   }
+
+   //The function returns TRUE is the specified UTF-8 stream is valid
+   return valid;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/web_socket/web_socket_misc.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,82 @@
+/**
+ * @file web_socket_misc.h
+ * @brief Helper functions for WebSockets
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _WEB_SOCKET_MISC_H
+#define _WEB_SOCKET_MISC_H
+
+//Dependencies
+#include "core/net.h"
+#include "web_socket/web_socket.h"
+
+
+/**
+ * @brief HTTP status code
+ **/
+
+typedef struct
+{
+   uint_t value;
+   const char_t message[28];
+} WebSocketStatusCodeDesc;
+
+
+//WebSocket related functions
+void webSocketChangeState(WebSocket *webSocket, WebSocketState newState);
+
+error_t webSocketParseHandshake(WebSocket *webSocket);
+error_t webSocketParseRequestLine(WebSocket *webSocket, char_t *line);
+error_t webSocketParseStatusLine(WebSocket *webSocket, char_t *line);
+error_t webSocketParseHeaderField(WebSocket *webSocket, char_t *line);
+
+void webSocketParseConnectionField(WebSocket *webSocket, char_t *value);
+
+error_t webSocketFormatClientHandshake(WebSocket *webSocket, uint16_t serverPort);
+error_t webSocketFormatServerHandshake(WebSocket *webSocket);
+
+error_t webSocketFormatErrorResponse(WebSocket *webSocket,
+   uint_t statusCode, const char_t *message);
+
+error_t webSocketVerifyClientHandshake(WebSocket *webSocket);
+error_t webSocketVerifyServerHandshake(WebSocket *webSocket);
+
+error_t webSocketGenerateClientKey(WebSocket *webSocket);
+error_t webSocketGenerateServerKey(WebSocket *webSocket);
+
+error_t webSocketVerifyClientKey(WebSocket *webSocket);
+error_t webSocketVerifyServerKey(WebSocket *webSocket);
+
+bool_t webSocketCheckStatusCode(uint16_t statusCode);
+
+error_t webSocketDecodePercentEncodedString(const char_t *input,
+   char_t *output, size_t outputSize);
+
+bool_t webSocketCheckUtf8Stream(WebSocketUtf8Context *context,
+   const uint8_t *data, size_t length, size_t remaining);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/web_socket/web_socket_transport.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,310 @@
+/**
+ * @file web_socket_transport.c
+ * @brief WebSocket transport layer
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Switch to the appropriate trace level
+#define TRACE_LEVEL WEB_SOCKET_TRACE_LEVEL
+
+//Dependencies
+#include "core/net.h"
+#include "web_socket/web_socket.h"
+#include "web_socket/web_socket_transport.h"
+#include "debug.h"
+
+//Check TCP/IP stack configuration
+#if (WEB_SOCKET_SUPPORT == ENABLED)
+
+
+/**
+ * @brief Open network connection
+ * @param[in] webSocket Handle to a WebSocket
+ * @return Error code
+ **/
+
+error_t webSocketOpenConnection(WebSocket *webSocket)
+{
+   error_t error;
+
+   //Invalid socket handle?
+   if(webSocket->socket == NULL)
+   {
+      //Open a TCP socket
+      webSocket->socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
+      //Failed to open socket?
+      if(webSocket->socket == NULL)
+         return ERROR_OPEN_FAILED;
+
+      //Associate the WebSocket with the relevant interface
+      error = socketBindToInterface(webSocket->socket, webSocket->interface);
+      //Any error to report?
+      if(error)
+         return error;
+   }
+
+   //Set timeout
+   error = socketSetTimeout(webSocket->socket, webSocket->timeout);
+   //Any error to report?
+   if(error)
+      return error;
+
+#if (WEB_SOCKET_TLS_SUPPORT == ENABLED)
+      //Use SSL/TLS to secure the connection?
+      if(webSocket->tlsInitCallback != NULL)
+      {
+         TlsConnectionEnd connectionEnd;
+
+         //Allocate SSL/TLS context
+         webSocket->tlsContext = tlsInit();
+         //Failed to allocate SSL/TLS context?
+         if(webSocket->tlsContext == NULL)
+            return ERROR_OUT_OF_MEMORY;
+
+         //Client or server operation?
+         if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
+            connectionEnd = TLS_CONNECTION_END_CLIENT;
+         else
+            connectionEnd = TLS_CONNECTION_END_SERVER;
+
+         //Select the relevant operation mode
+         error = tlsSetConnectionEnd(webSocket->tlsContext, connectionEnd);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //Bind TLS to the relevant socket
+         error = tlsSetSocket(webSocket->tlsContext, webSocket->socket);
+         //Any error to report?
+         if(error)
+            return error;
+
+         //Restore SSL/TLS session, if any
+         if(webSocket->tlsSession.idLength > 0)
+         {
+            //Restore SSL/TLS session
+            error = tlsRestoreSession(webSocket->tlsContext, &webSocket->tlsSession);
+            //Any error to report?
+            if(error)
+               return error;
+         }
+
+         //Invoke user-defined callback, if any
+         if(webSocket->tlsInitCallback != NULL)
+         {
+            //Perform SSL/TLS related initialization
+            error = webSocket->tlsInitCallback(webSocket, webSocket->tlsContext);
+            //Any error to report?
+            if(error)
+               return error;
+         }
+      }
+#endif
+
+   //Successful initialization
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Establish network connection
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] serverIpAddr IP address of the WebSocket server to connect to
+ * @param[in] serverPort TCP port number that will be used to establish the
+ *   connection
+ * @return Error code
+ **/
+
+error_t webSocketEstablishConnection(WebSocket *webSocket,
+   const IpAddr *serverIpAddr, uint16_t serverPort)
+{
+   error_t error;
+
+   //Connect to WebSocket server
+   error = socketConnect(webSocket->socket, serverIpAddr, serverPort);
+
+#if (WEB_SOCKET_TLS_SUPPORT == ENABLED)
+   //Use SSL/TLS to secure the connection?
+   if(webSocket->tlsInitCallback != NULL)
+   {
+      //Check status code
+      if(!error)
+      {
+         //Establish a SSL/TLS connection
+         error = tlsConnect(webSocket->tlsContext);
+      }
+   }
+#endif
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Shutdown network connection
+ * @param[in] webSocket Handle to a WebSocket
+ * @return Error code
+ **/
+
+error_t webSocketShutdownConnection(WebSocket *webSocket)
+{
+   error_t error;
+   size_t n;
+
+   //Initialize status code
+   error = NO_ERROR;
+
+#if (WEB_SOCKET_TLS_SUPPORT == ENABLED)
+   //Check whether a secure connection is being used
+   if(webSocket->tlsContext != NULL)
+   {
+      //Shutdown SSL/TLS connection
+      error = tlsShutdown(webSocket->tlsContext);
+   }
+#endif
+
+   //Check status code
+   if(!error)
+   {
+      //Further transmissions are disallowed
+      error = socketShutdown(webSocket->socket, SOCKET_SD_SEND);
+   }
+
+   //Receive data until until the peer has also performed an orderly shutdown
+   while(!error)
+   {
+      //Discard data
+      error = socketReceive(webSocket->socket, webSocket->rxContext.buffer,
+         WEB_SOCKET_BUFFER_SIZE, &n, 0);
+   }
+
+   //Check whether the connection has been properly closed
+   if(error == ERROR_END_OF_STREAM)
+      error = NO_ERROR;
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Close network connection
+ * @param[in] webSocket Handle to a WebSocket
+ **/
+
+void webSocketCloseConnection(WebSocket *webSocket)
+{
+#if (WEB_SOCKET_TLS_SUPPORT == ENABLED)
+   //Check whether a secure connection is being used
+   if(webSocket->tlsContext != NULL)
+   {
+      //Save SSL/TLS session
+      tlsSaveSession(webSocket->tlsContext, &webSocket->tlsSession);
+
+      //Release SSL/TLS context
+      tlsFree(webSocket->tlsContext);
+      webSocket->tlsContext = NULL;
+   }
+#endif
+
+   //Close TCP connection
+   if(webSocket->socket != NULL)
+   {
+      socketClose(webSocket->socket);
+      webSocket->socket = NULL;
+   }
+}
+
+
+/**
+ * @brief Send data using the relevant transport protocol
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[in] data Pointer to a buffer containing the data to be transmitted
+ * @param[in] length Number of bytes to be transmitted
+ * @param[out] written Actual number of bytes written (optional parameter)
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t webSocketSendData(WebSocket *webSocket, const void *data,
+   size_t length, size_t *written, uint_t flags)
+{
+   error_t error;
+
+#if (WEB_SOCKET_TLS_SUPPORT == ENABLED)
+   //Check whether a secure connection is being used
+   if(webSocket->tlsContext != NULL)
+   {
+      //Use SSL/TLS to transmit data to the client
+      error = tlsWrite(webSocket->tlsContext, data, length, written, flags);
+   }
+   else
+#endif
+   {
+      //Transmit data
+      error = socketSend(webSocket->socket, data, length, written, flags);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief Receive data using the relevant transport protocol
+ * @param[in] webSocket Handle to a WebSocket
+ * @param[out] data Buffer into which received data will be placed
+ * @param[in] size Maximum number of bytes that can be received
+ * @param[out] received Number of bytes that have been received
+ * @param[in] flags Set of flags that influences the behavior of this function
+ * @return Error code
+ **/
+
+error_t webSocketReceiveData(WebSocket *webSocket, void *data,
+   size_t size, size_t *received, uint_t flags)
+{
+   error_t error;
+
+#if (WEB_SOCKET_TLS_SUPPORT == ENABLED)
+   //Check whether a secure connection is being used
+   if(webSocket->tlsContext != NULL)
+   {
+      //Use SSL/TLS to receive data from the client
+      error = tlsRead(webSocket->tlsContext, data, size, received, flags);
+   }
+   else
+#endif
+   {
+      //Receive data
+      error = socketReceive(webSocket->socket, data, size, received, flags);
+   }
+
+   //Return status code
+   return error;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyclone_tcp/web_socket/web_socket_transport.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,52 @@
+/**
+ * @file web_socket_transport.h
+ * @brief WebSocket transport layer
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _WEB_SOCKET_TRANSPORT_H
+#define _WEB_SOCKET_TRANSPORT_H
+
+//Dependencies
+#include "core/net.h"
+#include "web_socket/web_socket.h"
+
+//WebSocket related functions
+error_t webSocketOpenConnection(WebSocket *webSocket);
+
+error_t webSocketEstablishConnection(WebSocket *webSocket,
+   const IpAddr *serverIpAddr, uint16_t serverPort);
+
+error_t webSocketShutdownConnection(WebSocket *webSocket);
+void webSocketCloseConnection(WebSocket *webSocket);
+
+error_t webSocketSendData(WebSocket *webSocket, const void *data,
+   size_t length, size_t *written, uint_t flags);
+
+error_t webSocketReceiveData(WebSocket *webSocket, void *data,
+   size_t size, size_t *received, uint_t flags);
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ftp_client_demo/src/FreeRTOSConfig.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,132 @@
+/*
+    FreeRTOS V6.0.1 - Copyright (C) 2009 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 FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+//Keil MDK-ARM, IAR EWARM or GCC compiler?
+#if (defined(__CC_ARM) || defined(__ICCARM__) || defined(__GNUC__))
+
+#include <stdint.h>
+extern uint32_t SystemCoreClock;
+
+#endif
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+#define configUSE_PREEMPTION		1
+#define configUSE_IDLE_HOOK			0
+#define configUSE_TICK_HOOK			0
+#define configCPU_CLOCK_HZ			( SystemCoreClock )
+#define configTICK_RATE_HZ			( ( portTickType ) 1000 )
+#define configMAX_PRIORITIES		( 5 )
+#define configMINIMAL_STACK_SIZE	( ( unsigned short ) 400 )
+#define configMAX_TASK_NAME_LEN		( 16 )
+#define configUSE_16_BIT_TICKS		0
+#define configIDLE_SHOULD_YIELD		1
+#define configUSE_APPLICATION_TASK_TAG      1
+#define configUSE_MUTEXES               1
+#define configUSE_COUNTING_SEMAPHORES 1
+
+#define configCHECK_FOR_STACK_OVERFLOW  0
+#define configGENERATE_RUN_TIME_STATS               0
+//#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS      init_us_timer
+//#define portGET_RUN_TIME_COUNTER_VALUE              get_us_time
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES 		0
+#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
+
+/* Set the following definitions to 1 to include the API function, or zero
+to exclude the API function. */
+
+#define INCLUDE_vTaskPrioritySet		1
+#define INCLUDE_uxTaskPriorityGet		1
+#define INCLUDE_vTaskDelete				1
+#define INCLUDE_vTaskCleanUpResources	1
+#define INCLUDE_vTaskSuspend			1
+#define INCLUDE_vTaskDelayUntil			1
+#define INCLUDE_vTaskDelay				1
+#define INCLUDE_uxTaskGetStackHighWaterMark     1
+#define INCLUDE_xTaskGetIdleTaskHandle  1
+#define INCLUDE_xTaskGetSchedulerState 1
+
+/* This is the raw value as per the Cortex-M3 NVIC.  Values can be 255
+(lowest) to 0 (1?) (highest). */
+#define configKERNEL_INTERRUPT_PRIORITY 		255
+#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	191 /* equivalent to 0xb0, or priority 11. */
+
+/* This is the value being used as per the ST library which permits 16
+priority values, 0 to 15.  This must correspond to the
+configKERNEL_INTERRUPT_PRIORITY setting.  Here 15 corresponds to the lowest
+NVIC value of 255. */
+#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY	15
+
+/* Redefine functions names to match the standard peripheral library */
+//#define xPortSysTickHandler     SysTick_Handler
+#define xPortPendSVHandler      PendSV_Handler
+#define vPortSVCHandler         SVC_Handler
+
+#endif /* FREERTOS_CONFIG_H */
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ftp_client_demo/src/debug.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,134 @@
+/**
+ * @file debug.c
+ * @brief Debugging facilities
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Dependencies
+#include "stm32f7xx.h"
+#include "stm32f7xx_hal.h"
+#include "debug.h"
+
+//Variable declaration
+static UART_HandleTypeDef UART_Handle;
+
+
+/**
+ * @brief Debug UART initialization
+ * @param[in] baudrate UART baudrate
+ **/
+
+void debugInit(uint32_t baudrate)
+{
+   GPIO_InitTypeDef GPIO_InitStructure;
+
+   //Enable GPIOD clock
+   __HAL_RCC_GPIOD_CLK_ENABLE();
+   //Enable USART3 clock
+   __HAL_RCC_USART3_CLK_ENABLE();
+
+   //Configure USART3_TX (PD8)
+   GPIO_InitStructure.Pin = GPIO_PIN_8;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_PULLUP;
+   GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
+   GPIO_InitStructure.Alternate = GPIO_AF7_USART3;
+   HAL_GPIO_Init(GPIOD, &GPIO_InitStructure);
+
+   //Configure USART3_RX (PD9)
+   GPIO_InitStructure.Pin = GPIO_PIN_9;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_PULLUP;
+   GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
+   GPIO_InitStructure.Alternate = GPIO_AF7_USART3;
+   HAL_GPIO_Init(GPIOD, &GPIO_InitStructure);
+
+   //Configure USART3
+   UART_Handle.Instance = USART3;
+   UART_Handle.Init.BaudRate = baudrate;
+   UART_Handle.Init.WordLength = UART_WORDLENGTH_8B;
+   UART_Handle.Init.StopBits = UART_STOPBITS_1;
+   UART_Handle.Init.Parity = UART_PARITY_NONE;
+   UART_Handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
+   UART_Handle.Init.Mode = UART_MODE_TX_RX;
+   HAL_UART_Init(&UART_Handle);
+}
+
+
+/**
+ * @brief Display the contents of an array
+ * @param[in] stream Pointer to a FILE object that identifies an output stream
+ * @param[in] prepend String to prepend to the left of each line
+ * @param[in] data Pointer to the data array
+ * @param[in] length Number of bytes to display
+ **/
+
+void debugDisplayArray(FILE *stream,
+   const char_t *prepend, const void *data, size_t length)
+{
+   uint_t i;
+
+   for(i = 0; i < length; i++)
+   {
+      //Beginning of a new line?
+      if((i % 16) == 0)
+         fprintf(stream, "%s", prepend);
+      //Display current data byte
+      fprintf(stream, "%02" PRIX8 " ", *((uint8_t *) data + i));
+      //End of current line?
+      if((i % 16) == 15 || i == (length - 1))
+         fprintf(stream, "\r\n");
+   }
+}
+
+
+/**
+ * @brief Write character to stream
+ * @param[in] c The character to be written
+ * @param[in] stream Pointer to a FILE object that identifies an output stream
+ * @return On success, the character written is returned. If a writing
+ *   error occurs, EOF is returned
+ **/
+
+int_t fputc(int_t c, FILE *stream)
+{
+   //Standard output or error output?
+   if(stream == stdout || stream == stderr)
+   {
+      //Character to be written
+      uint8_t ch = c;
+
+      //Transmit data
+      HAL_UART_Transmit(&UART_Handle, &ch, 1, HAL_MAX_DELAY);
+
+      //On success, the character written is returned
+      return c;
+   }
+   //Unknown output?
+   else
+   {
+      //If a writing error occurs, EOF is returned
+      return EOF;
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ftp_client_demo/src/main.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,490 @@
+/**
+ * @file main.c
+ * @brief Main routine
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Dependencies
+#include <stdlib.h>
+#include "stm32f7xx.h"
+#include "stm32f7xx_hal.h"
+#include "stm32f7xx_nucleo_144.h"
+#include "os_port.h"
+#include "core/net.h"
+#include "drivers/stm32f7xx_eth.h"
+#include "drivers/lan8742.h"
+#include "dhcp/dhcp_client.h"
+#include "ipv6/slaac.h"
+#include "ftp/ftp_client.h"
+#include "error.h"
+#include "debug.h"
+
+//Application configuration
+#define APP_MAC_ADDR "00-AB-CD-EF-07-46"
+
+#define APP_USE_DHCP ENABLED
+#define APP_IPV4_HOST_ADDR "192.168.0.20"
+#define APP_IPV4_SUBNET_MASK "255.255.255.0"
+#define APP_IPV4_DEFAULT_GATEWAY "192.168.0.254"
+#define APP_IPV4_PRIMARY_DNS "8.8.8.8"
+#define APP_IPV4_SECONDARY_DNS "8.8.4.4"
+
+#define APP_USE_SLAAC ENABLED
+#define APP_IPV6_LINK_LOCAL_ADDR "fe80::746"
+#define APP_IPV6_PREFIX "2001:db8::"
+#define APP_IPV6_PREFIX_LENGTH 64
+#define APP_IPV6_GLOBAL_ADDR "2001:db8::746"
+#define APP_IPV6_ROUTER "fe80::1"
+#define APP_IPV6_PRIMARY_DNS "2001:4860:4860::8888"
+#define APP_IPV6_SECONDARY_DNS "2001:4860:4860::8844"
+
+//Global variables
+DhcpClientSettings dhcpClientSettings;
+DhcpClientContext dhcpClientContext;
+SlaacSettings slaacSettings;
+SlaacContext slaacContext;
+
+
+/**
+ * @brief System clock configuration
+ **/
+
+void SystemClock_Config(void)
+{
+   RCC_OscInitTypeDef RCC_OscInitStruct;
+   RCC_ClkInitTypeDef RCC_ClkInitStruct;
+
+   //Enable Power Control clock
+   __HAL_RCC_PWR_CLK_ENABLE();
+
+   //Enable HSE Oscillator and activate PLL with HSE as source
+   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
+   RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;
+   RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
+   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
+   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
+   RCC_OscInitStruct.PLL.PLLM = 8;
+   RCC_OscInitStruct.PLL.PLLN = 432;
+   RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
+   RCC_OscInitStruct.PLL.PLLQ = 9;
+   HAL_RCC_OscConfig(&RCC_OscInitStruct);
+
+   //Enable overdrive mode
+   HAL_PWREx_EnableOverDrive();
+
+   //Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
+   //clocks dividers
+   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
+   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
+   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
+   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
+   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
+   HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7);
+}
+
+
+/**
+ * @brief MPU configuration
+ **/
+
+void MPU_Config(void)
+{
+   MPU_Region_InitTypeDef MPU_InitStruct;
+
+   //Disable MPU
+   HAL_MPU_Disable();
+
+   //SRAM
+   MPU_InitStruct.Enable = MPU_REGION_ENABLE;
+   MPU_InitStruct.Number = MPU_REGION_NUMBER0;
+   MPU_InitStruct.BaseAddress = 0x20000000;
+   MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
+   MPU_InitStruct.SubRegionDisable = 0;
+   MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
+   MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
+   MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
+   MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
+   MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
+   MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
+   HAL_MPU_ConfigRegion(&MPU_InitStruct);
+
+   //SRAM2
+   MPU_InitStruct.Enable = MPU_REGION_ENABLE;
+   MPU_InitStruct.Number = MPU_REGION_NUMBER1;
+   MPU_InitStruct.BaseAddress = 0x2004C000;
+   MPU_InitStruct.Size = MPU_REGION_SIZE_16KB;
+   MPU_InitStruct.SubRegionDisable = 0;
+   MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
+   MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
+   MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
+   MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
+   MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
+   MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
+   HAL_MPU_ConfigRegion(&MPU_InitStruct);
+
+   //Enable MPU
+   HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
+}
+
+
+/**
+ * @brief FTP client test routine
+ * @return Error code
+ **/
+
+error_t ftpClientTest(void)
+{
+   error_t error;
+   size_t length;
+   IpAddr ipAddr;
+   FtpClientContext ftpContext;
+   static char_t buffer[256];
+
+   //Debug message
+   TRACE_INFO("\r\n\r\nResolving server name...\r\n");
+   //Resolve FTP server name
+   error = getHostByName(NULL, "ftp.gnu.org", &ipAddr, 0);
+
+   //Any error to report?
+   if(error)
+   {
+      //Debug message
+      TRACE_INFO("Failed to resolve server name!\r\n");
+      //Exit immediately
+      return error;
+   }
+
+   //Debug message
+   TRACE_INFO("Connecting to FTP server %s\r\n", ipAddrToString(&ipAddr, NULL));
+   //Connect to the FTP server
+   error = ftpConnect(&ftpContext, NULL, &ipAddr, 21, FTP_NO_SECURITY | FTP_PASSIVE_MODE);
+
+   //Any error to report?
+   if(error)
+   {
+      //Debug message
+      TRACE_INFO("Failed to connect to FTP server!\r\n");
+      //Exit immediately
+      return error;
+   }
+
+   //Debug message
+   TRACE_INFO("Successful connection\r\n");
+
+   //Start of exception handling block
+   do
+   {
+      //Login to the FTP server using the provided username and password
+      error = ftpLogin(&ftpContext, "anonymous", "password", "");
+      //Any error to report?
+      if(error) break;
+
+      //Open the specified file for reading
+      error = ftpOpenFile(&ftpContext, "welcome.msg", FTP_FOR_READING | FTP_BINARY_TYPE);
+      //Any error to report?
+      if(error) break;
+
+      //Dump the contents of the file
+      while(1)
+      {
+         //Read data
+         error = ftpReadFile(&ftpContext, buffer, sizeof(buffer) - 1, &length, 0);
+         //End of file?
+         if(error) break;
+
+         //Properly terminate the string with a NULL character
+         buffer[length] = '\0';
+         //Dump current data
+         TRACE_INFO("%s", buffer);
+      }
+
+      //End the string with a line feed
+      TRACE_INFO("\r\n");
+      //Close the file
+      error = ftpCloseFile(&ftpContext);
+
+      //End of exception handling block
+   } while(0);
+
+   //Close the connection
+   ftpClose(&ftpContext);
+   //Debug message
+   TRACE_INFO("Connection closed...\r\n");
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief User task
+ **/
+
+void userTask(void *param)
+{
+   //Endless loop
+   while(1)
+   {
+      //User button pressed?
+      if(BSP_PB_GetState(BUTTON_KEY))
+      {
+         //FTP client test routine
+         ftpClientTest();
+
+         //Wait for the user button to be released
+         while(BSP_PB_GetState(BUTTON_KEY));
+      }
+
+      //Loop delay
+      osDelayTask(100);
+   }
+}
+
+
+/**
+ * @brief LED blinking task
+ **/
+
+void blinkTask(void *param)
+{
+   //Endless loop
+   while(1)
+   {
+      BSP_LED_On(LED1);
+      osDelayTask(100);
+      BSP_LED_Off(LED1);
+      osDelayTask(900);
+   }
+}
+
+
+/**
+ * @brief Main entry point
+ * @return Unused value
+ **/
+
+int_t main(void)
+{
+   error_t error;
+   NetInterface *interface;
+   OsTask *task;
+   MacAddr macAddr;
+#if (APP_USE_DHCP == DISABLED)
+   Ipv4Addr ipv4Addr;
+#endif
+#if (APP_USE_SLAAC == DISABLED)
+   Ipv6Addr ipv6Addr;
+#endif
+
+   //MPU configuration
+   MPU_Config();
+   //HAL library initialization
+   HAL_Init();
+   //Configure the system clock
+   SystemClock_Config();
+
+   //Enable I-cache and D-cache
+   SCB_EnableICache();
+   SCB_EnableDCache();
+
+   //Initialize kernel
+   osInitKernel();
+   //Configure debug UART
+   debugInit(115200);
+
+   //Start-up message
+   TRACE_INFO("\r\n");
+   TRACE_INFO("**********************************\r\n");
+   TRACE_INFO("*** CycloneTCP FTP Client Demo ***\r\n");
+   TRACE_INFO("**********************************\r\n");
+   TRACE_INFO("Copyright: 2010-2017 Oryx Embedded SARL\r\n");
+   TRACE_INFO("Compiled: %s %s\r\n", __DATE__, __TIME__);
+   TRACE_INFO("Target: STM32F746\r\n");
+   TRACE_INFO("\r\n");
+
+   //LED configuration
+   BSP_LED_Init(LED1);
+   BSP_LED_Init(LED2);
+   BSP_LED_Init(LED3);
+
+   //Clear LEDs
+   BSP_LED_Off(LED1);
+   BSP_LED_Off(LED2);
+   BSP_LED_Off(LED3);
+
+   //Initialize user button
+   BSP_PB_Init(BUTTON_KEY, BUTTON_MODE_GPIO);
+
+   //TCP/IP stack initialization
+   error = netInit();
+   //Any error to report?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to initialize TCP/IP stack!\r\n");
+   }
+
+   //Configure the first Ethernet interface
+   interface = &netInterface[0];
+
+   //Set interface name
+   netSetInterfaceName(interface, "eth0");
+   //Set host name
+   netSetHostname(interface, "FTPClientDemo");
+   //Select the relevant network adapter
+   netSetDriver(interface, &stm32f7xxEthDriver);
+   netSetPhyDriver(interface, &lan8742PhyDriver);
+   //Set host MAC address
+   macStringToAddr(APP_MAC_ADDR, &macAddr);
+   netSetMacAddr(interface, &macAddr);
+
+   //Initialize network interface
+   error = netConfigInterface(interface);
+   //Any error to report?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to configure interface %s!\r\n", interface->name);
+   }
+
+#if (IPV4_SUPPORT == ENABLED)
+#if (APP_USE_DHCP == ENABLED)
+   //Get default settings
+   dhcpClientGetDefaultSettings(&dhcpClientSettings);
+   //Set the network interface to be configured by DHCP
+   dhcpClientSettings.interface = interface;
+   //Disable rapid commit option
+   dhcpClientSettings.rapidCommit = FALSE;
+
+   //DHCP client initialization
+   error = dhcpClientInit(&dhcpClientContext, &dhcpClientSettings);
+   //Failed to initialize DHCP client?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to initialize DHCP client!\r\n");
+   }
+
+   //Start DHCP client
+   error = dhcpClientStart(&dhcpClientContext);
+   //Failed to start DHCP client?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to start DHCP client!\r\n");
+   }
+#else
+   //Set IPv4 host address
+   ipv4StringToAddr(APP_IPV4_HOST_ADDR, &ipv4Addr);
+   ipv4SetHostAddr(interface, ipv4Addr);
+
+   //Set subnet mask
+   ipv4StringToAddr(APP_IPV4_SUBNET_MASK, &ipv4Addr);
+   ipv4SetSubnetMask(interface, ipv4Addr);
+
+   //Set default gateway
+   ipv4StringToAddr(APP_IPV4_DEFAULT_GATEWAY, &ipv4Addr);
+   ipv4SetDefaultGateway(interface, ipv4Addr);
+
+   //Set primary and secondary DNS servers
+   ipv4StringToAddr(APP_IPV4_PRIMARY_DNS, &ipv4Addr);
+   ipv4SetDnsServer(interface, 0, ipv4Addr);
+   ipv4StringToAddr(APP_IPV4_SECONDARY_DNS, &ipv4Addr);
+   ipv4SetDnsServer(interface, 1, ipv4Addr);
+#endif
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+#if (APP_USE_SLAAC == ENABLED)
+   //Get default settings
+   slaacGetDefaultSettings(&slaacSettings);
+   //Set the network interface to be configured
+   slaacSettings.interface = interface;
+
+   //SLAAC initialization
+   error = slaacInit(&slaacContext, &slaacSettings);
+   //Failed to initialize SLAAC?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to initialize SLAAC!\r\n");
+   }
+
+   //Start IPv6 address autoconfiguration process
+   error = slaacStart(&slaacContext);
+   //Failed to start SLAAC process?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to start SLAAC!\r\n");
+   }
+#else
+   //Set link-local address
+   ipv6StringToAddr(APP_IPV6_LINK_LOCAL_ADDR, &ipv6Addr);
+   ipv6SetLinkLocalAddr(interface, &ipv6Addr);
+
+   //Set IPv6 prefix
+   ipv6StringToAddr(APP_IPV6_PREFIX, &ipv6Addr);
+   ipv6SetPrefix(interface, 0, &ipv6Addr, APP_IPV6_PREFIX_LENGTH);
+
+   //Set global address
+   ipv6StringToAddr(APP_IPV6_GLOBAL_ADDR, &ipv6Addr);
+   ipv6SetGlobalAddr(interface, 0, &ipv6Addr);
+
+   //Set default router
+   ipv6StringToAddr(APP_IPV6_ROUTER, &ipv6Addr);
+   ipv6SetDefaultRouter(interface, 0, &ipv6Addr);
+
+   //Set primary and secondary DNS servers
+   ipv6StringToAddr(APP_IPV6_PRIMARY_DNS, &ipv6Addr);
+   ipv6SetDnsServer(interface, 0, &ipv6Addr);
+   ipv6StringToAddr(APP_IPV6_SECONDARY_DNS, &ipv6Addr);
+   ipv6SetDnsServer(interface, 1, &ipv6Addr);
+#endif
+#endif
+
+   //Create user task
+   task = osCreateTask("User Task", userTask, NULL, 500, OS_TASK_PRIORITY_NORMAL);
+   //Failed to create the task?
+   if(task == OS_INVALID_HANDLE)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to create task!\r\n");
+   }
+
+   //Create a task to blink the LED
+   task = osCreateTask("Blink", blinkTask, NULL, 500, OS_TASK_PRIORITY_NORMAL);
+   //Failed to create the task?
+   if(task == OS_INVALID_HANDLE)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to create task!\r\n");
+   }
+
+   //Start the execution of tasks
+   osStartKernel();
+
+   //This function should never return
+   return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ftp_client_demo/src/net_config.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,140 @@
+/**
+ * @file net_config.h
+ * @brief CycloneTCP configuration file
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _NET_CONFIG_H
+#define _NET_CONFIG_H
+
+//Trace level for TCP/IP stack debugging
+#define MEM_TRACE_LEVEL          4
+#define NIC_TRACE_LEVEL          4
+#define ETH_TRACE_LEVEL          2
+#define ARP_TRACE_LEVEL          2
+#define IP_TRACE_LEVEL           2
+#define IPV4_TRACE_LEVEL         2
+#define IPV6_TRACE_LEVEL         2
+#define ICMP_TRACE_LEVEL         2
+#define IGMP_TRACE_LEVEL         2
+#define ICMPV6_TRACE_LEVEL       2
+#define MLD_TRACE_LEVEL          2
+#define NDP_TRACE_LEVEL          2
+#define UDP_TRACE_LEVEL          2
+#define TCP_TRACE_LEVEL          2
+#define SOCKET_TRACE_LEVEL       2
+#define RAW_SOCKET_TRACE_LEVEL   2
+#define BSD_SOCKET_TRACE_LEVEL   2
+#define SLAAC_TRACE_LEVEL        4
+#define DHCP_TRACE_LEVEL         4
+#define DHCPV6_TRACE_LEVEL       4
+#define DNS_TRACE_LEVEL          4
+#define MDNS_TRACE_LEVEL         4
+#define NBNS_TRACE_LEVEL         2
+#define LLMNR_TRACE_LEVEL        4
+#define FTP_TRACE_LEVEL          5
+#define HTTP_TRACE_LEVEL         4
+#define SMTP_TRACE_LEVEL         5
+#define SNTP_TRACE_LEVEL         4
+#define STD_SERVICES_TRACE_LEVEL 5
+
+//Number of network adapters
+#define NET_INTERFACE_COUNT 1
+
+//Size of the multicast MAC filter
+#define MAC_MULTICAST_FILTER_SIZE 12
+
+//IPv4 support
+#define IPV4_SUPPORT ENABLED
+//Size of the IPv4 multicast filter
+#define IPV4_MULTICAST_FILTER_SIZE 4
+
+//IPv4 fragmentation support
+#define IPV4_FRAG_SUPPORT ENABLED
+//Maximum number of fragmented packets the host will accept
+//and hold in the reassembly queue simultaneously
+#define IPV4_MAX_FRAG_DATAGRAMS 4
+//Maximum datagram size the host will accept when reassembling fragments
+#define IPV4_MAX_FRAG_DATAGRAM_SIZE 8192
+
+//Size of ARP cache
+#define ARP_CACHE_SIZE 8
+//Maximum number of packets waiting for address resolution to complete
+#define ARP_MAX_PENDING_PACKETS 2
+
+//IGMP support
+#define IGMP_SUPPORT ENABLED
+
+//IPv6 support
+#define IPV6_SUPPORT ENABLED
+//Size of the IPv6 multicast filter
+#define IPV6_MULTICAST_FILTER_SIZE 8
+
+//IPv6 fragmentation support
+#define IPV6_FRAG_SUPPORT ENABLED
+//Maximum number of fragmented packets the host will accept
+//and hold in the reassembly queue simultaneously
+#define IPV6_MAX_FRAG_DATAGRAMS 4
+//Maximum datagram size the host will accept when reassembling fragments
+#define IPV6_MAX_FRAG_DATAGRAM_SIZE 8192
+
+//MLD support
+#define MLD_SUPPORT ENABLED
+
+//Neighbor cache size
+#define NDP_NEIGHBOR_CACHE_SIZE 8
+//Destination cache size
+#define NDP_DEST_CACHE_SIZE 8
+//Maximum number of packets waiting for address resolution to complete
+#define NDP_MAX_PENDING_PACKETS 2
+
+//TCP support
+#define TCP_SUPPORT ENABLED
+//Default buffer size for transmission
+#define TCP_DEFAULT_TX_BUFFER_SIZE (1430*2)
+//Default buffer size for reception
+#define TCP_DEFAULT_RX_BUFFER_SIZE (1430*2)
+//Default SYN queue size for listening sockets
+#define TCP_DEFAULT_SYN_QUEUE_SIZE 4
+//Maximum number of retransmissions
+#define TCP_MAX_RETRIES 5
+//Selective acknowledgment support
+#define TCP_SACK_SUPPORT DISABLED
+
+//UDP support
+#define UDP_SUPPORT ENABLED
+//Receive queue depth for connectionless sockets
+#define UDP_RX_QUEUE_SIZE 4
+
+//Raw socket support
+#define RAW_SOCKET_SUPPORT DISABLED
+//Receive queue depth for raw sockets
+#define RAW_SOCKET_RX_QUEUE_SIZE 4
+
+//Number of sockets that can be opened simultaneously
+#define SOCKET_MAX_COUNT 10
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ftp_client_demo/src/os_port_config.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,34 @@
+/**
+ * @file os_port_config.h
+ * @brief RTOS port configuration file
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _OS_PORT_CONFIG_H
+#define _OS_PORT_CONFIG_H
+
+//Select underlying RTOS
+#define USE_FREERTOS
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ftp_client_demo/src/stm32f7xx_hal_conf.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,427 @@
+/**
+  ******************************************************************************
+  * @file    stm32f7xx_hal_conf.h
+  * @author  MCD Application Team
+  * @version V1.0.1
+  * @date    22-April-2016
+  * @brief   HAL configuration file.
+  ******************************************************************************
+  * @attention
+  *
+  * <h2><center>&copy; COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
+  *
+  * Redistribution and use in source and binary forms, with or without modification,
+  * are permitted provided that the following conditions are met:
+  *   1. Redistributions of source code must retain the above copyright notice,
+  *      this list of conditions and the following disclaimer.
+  *   2. Redistributions in binary form must reproduce 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.
+  *
+  ******************************************************************************
+  */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32F7xx_HAL_CONF_H
+#define __STM32F7xx_HAL_CONF_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+/* ########################## Module Selection ############################## */
+/**
+  * @brief This is the list of modules to be used in the HAL driver
+  */
+#define HAL_MODULE_ENABLED
+#define HAL_ADC_MODULE_ENABLED
+#define HAL_CAN_MODULE_ENABLED
+#define HAL_CEC_MODULE_ENABLED
+#define HAL_CRC_MODULE_ENABLED
+#define HAL_CRYP_MODULE_ENABLED
+#define HAL_DAC_MODULE_ENABLED
+#define HAL_DCMI_MODULE_ENABLED
+#define HAL_DMA_MODULE_ENABLED
+#define HAL_DMA2D_MODULE_ENABLED
+#define HAL_ETH_MODULE_ENABLED
+#define HAL_FLASH_MODULE_ENABLED
+#define HAL_NAND_MODULE_ENABLED
+#define HAL_NOR_MODULE_ENABLED
+#define HAL_SRAM_MODULE_ENABLED
+#define HAL_SDRAM_MODULE_ENABLED
+#define HAL_HASH_MODULE_ENABLED
+#define HAL_GPIO_MODULE_ENABLED
+#define HAL_I2C_MODULE_ENABLED
+#define HAL_I2S_MODULE_ENABLED
+#define HAL_IWDG_MODULE_ENABLED
+#define HAL_LPTIM_MODULE_ENABLED
+#define HAL_LTDC_MODULE_ENABLED
+#define HAL_PWR_MODULE_ENABLED
+#define HAL_QSPI_MODULE_ENABLED
+#define HAL_RCC_MODULE_ENABLED
+#define HAL_RNG_MODULE_ENABLED
+#define HAL_RTC_MODULE_ENABLED
+#define HAL_SAI_MODULE_ENABLED
+#define HAL_SD_MODULE_ENABLED
+#define HAL_SPDIFRX_MODULE_ENABLED
+#define HAL_SPI_MODULE_ENABLED
+#define HAL_TIM_MODULE_ENABLED
+#define HAL_UART_MODULE_ENABLED
+#define HAL_USART_MODULE_ENABLED
+#define HAL_IRDA_MODULE_ENABLED
+#define HAL_SMARTCARD_MODULE_ENABLED
+#define HAL_WWDG_MODULE_ENABLED
+#define HAL_CORTEX_MODULE_ENABLED
+#define HAL_PCD_MODULE_ENABLED
+#define HAL_HCD_MODULE_ENABLED
+
+
+/* ########################## HSE/HSI Values adaptation ##################### */
+/**
+  * @brief Adjust the value of External High Speed oscillator (HSE) used in your application.
+  *        This value is used by the RCC HAL module to compute the system frequency
+  *        (when HSE is used as system clock source, directly or through the PLL).
+  */
+#if !defined  (HSE_VALUE)
+  #define HSE_VALUE    ((uint32_t)8000000U) /*!< Value of the External oscillator in Hz */
+#endif /* HSE_VALUE */
+
+#if !defined  (HSE_STARTUP_TIMEOUT)
+  #define HSE_STARTUP_TIMEOUT    100U   /*!< Time out for HSE start up, in ms */
+#endif /* HSE_STARTUP_TIMEOUT */
+
+/**
+  * @brief Internal High Speed oscillator (HSI) value.
+  *        This value is used by the RCC HAL module to compute the system frequency
+  *        (when HSI is used as system clock source, directly or through the PLL).
+  */
+#if !defined  (HSI_VALUE)
+  #define HSI_VALUE    16000000U /*!< Value of the Internal oscillator in Hz*/
+#endif /* HSI_VALUE */
+
+/**
+  * @brief Internal Low Speed oscillator (LSI) value.
+  */
+#if !defined  (LSI_VALUE)
+ #define LSI_VALUE  32000U                  /*!< LSI Typical Value in Hz*/
+#endif /* LSI_VALUE */                      /*!< Value of the Internal Low Speed oscillator in Hz
+                                             The real value may vary depending on the variations
+                                             in voltage and temperature.  */
+/**
+  * @brief External Low Speed oscillator (LSE) value.
+  */
+#if !defined  (LSE_VALUE)
+ #define LSE_VALUE  32768U    /*!< Value of the External Low Speed oscillator in Hz */
+#endif /* LSE_VALUE */
+
+#if !defined  (LSE_STARTUP_TIMEOUT)
+  #define LSE_STARTUP_TIMEOUT    5000U   /*!< Time out for LSE start up, in ms */
+#endif /* LSE_STARTUP_TIMEOUT */
+
+/**
+  * @brief External clock source for I2S peripheral
+  *        This value is used by the I2S HAL module to compute the I2S clock source
+  *        frequency, this source is inserted directly through I2S_CKIN pad.
+  */
+#if !defined  (EXTERNAL_CLOCK_VALUE)
+  #define EXTERNAL_CLOCK_VALUE    12288000U /*!< Value of the Internal oscillator in Hz*/
+#endif /* EXTERNAL_CLOCK_VALUE */
+
+/* Tip: To avoid modifying this file each time you need to use different HSE,
+   ===  you can define the HSE value in your toolchain compiler preprocessor. */
+
+/* ########################### System Configuration ######################### */
+/**
+  * @brief This is the HAL system configuration section
+  */
+#define  VDD_VALUE                    3300U /*!< Value of VDD in mv */
+#define  TICK_INT_PRIORITY            0x0FU /*!< tick interrupt priority */
+#define  USE_RTOS                     0U
+#define  PREFETCH_ENABLE              1U
+#define  ART_ACCLERATOR_ENABLE        1U /* To enable instruction cache and prefetch */
+
+/* ########################## Assert Selection ############################## */
+/**
+  * @brief Uncomment the line below to expanse the "assert_param" macro in the
+  *        HAL drivers code
+  */
+/* #define USE_FULL_ASSERT    1 */
+
+/* ################## Ethernet peripheral configuration for NUCLEO 144 board ##################### */
+
+/* Section 1 : Ethernet peripheral configuration */
+
+/* MAC ADDRESS: MAC_ADDR0:MAC_ADDR1:MAC_ADDR2:MAC_ADDR3:MAC_ADDR4:MAC_ADDR5 */
+#define MAC_ADDR0   2
+#define MAC_ADDR1   0
+#define MAC_ADDR2   0
+#define MAC_ADDR3   0
+#define MAC_ADDR4   0
+#define MAC_ADDR5   0
+
+/* Definition of the Ethernet driver buffers size and count */
+#define ETH_RX_BUF_SIZE                ETH_MAX_PACKET_SIZE /* buffer size for receive               */
+#define ETH_TX_BUF_SIZE                ETH_MAX_PACKET_SIZE /* buffer size for transmit              */
+#define ETH_RXBUFNB                    ((uint32_t)5)       /* 5 Rx buffers of size ETH_RX_BUF_SIZE  */
+#define ETH_TXBUFNB                    ((uint32_t)5)       /* 5 Tx buffers of size ETH_TX_BUF_SIZE  */
+
+/* Section 2: PHY configuration section */
+/* LAN8742A PHY Address*/
+#define LAN8742A_PHY_ADDRESS            0x00
+/* PHY Reset delay these values are based on a 1 ms Systick interrupt*/
+#define PHY_RESET_DELAY                 ((uint32_t)0x00000FFF)
+/* PHY Configuration delay */
+#define PHY_CONFIG_DELAY                ((uint32_t)0x00000FFF)
+
+#define PHY_READ_TO                     ((uint32_t)0x0000FFFF)
+#define PHY_WRITE_TO                    ((uint32_t)0x0000FFFF)
+
+/* Section 3: Common PHY Registers */
+
+#define PHY_BCR                         ((uint16_t)0x00)    /*!< Transceiver Basic Control Register   */
+#define PHY_BSR                         ((uint16_t)0x01)    /*!< Transceiver Basic Status Register    */
+
+#define PHY_RESET                       ((uint16_t)0x8000)  /*!< PHY Reset */
+#define PHY_LOOPBACK                    ((uint16_t)0x4000)  /*!< Select loop-back mode */
+#define PHY_FULLDUPLEX_100M             ((uint16_t)0x2100)  /*!< Set the full-duplex mode at 100 Mb/s */
+#define PHY_HALFDUPLEX_100M             ((uint16_t)0x2000)  /*!< Set the half-duplex mode at 100 Mb/s */
+#define PHY_FULLDUPLEX_10M              ((uint16_t)0x0100)  /*!< Set the full-duplex mode at 10 Mb/s  */
+#define PHY_HALFDUPLEX_10M              ((uint16_t)0x0000)  /*!< Set the half-duplex mode at 10 Mb/s  */
+#define PHY_AUTONEGOTIATION             ((uint16_t)0x1000)  /*!< Enable auto-negotiation function     */
+#define PHY_RESTART_AUTONEGOTIATION     ((uint16_t)0x0200)  /*!< Restart auto-negotiation function    */
+#define PHY_POWERDOWN                   ((uint16_t)0x0800)  /*!< Select the power down mode           */
+#define PHY_ISOLATE                     ((uint16_t)0x0400)  /*!< Isolate PHY from MII                 */
+
+#define PHY_AUTONEGO_COMPLETE           ((uint16_t)0x0020)  /*!< Auto-Negotiation process completed   */
+#define PHY_LINKED_STATUS               ((uint16_t)0x0004)  /*!< Valid link established               */
+#define PHY_JABBER_DETECTION            ((uint16_t)0x0002)  /*!< Jabber condition detected            */
+
+/* Section 4: Extended PHY Registers */
+
+#define PHY_SR                          ((uint16_t)0x1F)    /*!< PHY special control/ status register Offset     */
+
+#define PHY_SPEED_STATUS                ((uint16_t)0x0004)  /*!< PHY Speed mask                                  */
+#define PHY_DUPLEX_STATUS               ((uint16_t)0x0010)  /*!< PHY Duplex mask                                 */
+
+
+#define PHY_ISFR                        ((uint16_t)0x1D)    /*!< PHY Interrupt Source Flag register Offset       */
+#define PHY_ISFR_INT4                   ((uint16_t)0x0010)  /*!< PHY Link down inturrupt                         */
+
+/* ################## SPI peripheral configuration ########################## */
+
+/* CRC FEATURE: Use to activate CRC feature inside HAL SPI Driver
+* Activated: CRC code is present inside driver
+* Deactivated: CRC code cleaned from driver
+*/
+
+#define USE_SPI_CRC                     1U
+
+/* Includes ------------------------------------------------------------------*/
+/**
+  * @brief Include module's header file
+  */
+
+#ifdef HAL_RCC_MODULE_ENABLED
+  #include "stm32f7xx_hal_rcc.h"
+#endif /* HAL_RCC_MODULE_ENABLED */
+
+#ifdef HAL_GPIO_MODULE_ENABLED
+  #include "stm32f7xx_hal_gpio.h"
+#endif /* HAL_GPIO_MODULE_ENABLED */
+
+#ifdef HAL_DMA_MODULE_ENABLED
+  #include "stm32f7xx_hal_dma.h"
+#endif /* HAL_DMA_MODULE_ENABLED */
+
+#ifdef HAL_CORTEX_MODULE_ENABLED
+  #include "stm32f7xx_hal_cortex.h"
+#endif /* HAL_CORTEX_MODULE_ENABLED */
+
+#ifdef HAL_ADC_MODULE_ENABLED
+  #include "stm32f7xx_hal_adc.h"
+#endif /* HAL_ADC_MODULE_ENABLED */
+
+#ifdef HAL_CAN_MODULE_ENABLED
+  #include "stm32f7xx_hal_can.h"
+#endif /* HAL_CAN_MODULE_ENABLED */
+
+#ifdef HAL_CEC_MODULE_ENABLED
+  #include "stm32f7xx_hal_cec.h"
+#endif /* HAL_CEC_MODULE_ENABLED */
+
+#ifdef HAL_CRC_MODULE_ENABLED
+  #include "stm32f7xx_hal_crc.h"
+#endif /* HAL_CRC_MODULE_ENABLED */
+
+#ifdef HAL_CRYP_MODULE_ENABLED
+  #include "stm32f7xx_hal_cryp.h"
+#endif /* HAL_CRYP_MODULE_ENABLED */
+
+#ifdef HAL_DMA2D_MODULE_ENABLED
+  #include "stm32f7xx_hal_dma2d.h"
+#endif /* HAL_DMA2D_MODULE_ENABLED */
+
+#ifdef HAL_DAC_MODULE_ENABLED
+  #include "stm32f7xx_hal_dac.h"
+#endif /* HAL_DAC_MODULE_ENABLED */
+
+#ifdef HAL_DCMI_MODULE_ENABLED
+  #include "stm32f7xx_hal_dcmi.h"
+#endif /* HAL_DCMI_MODULE_ENABLED */
+
+#ifdef HAL_ETH_MODULE_ENABLED
+  #include "stm32f7xx_hal_eth.h"
+#endif /* HAL_ETH_MODULE_ENABLED */
+
+#ifdef HAL_FLASH_MODULE_ENABLED
+  #include "stm32f7xx_hal_flash.h"
+#endif /* HAL_FLASH_MODULE_ENABLED */
+
+#ifdef HAL_SRAM_MODULE_ENABLED
+  #include "stm32f7xx_hal_sram.h"
+#endif /* HAL_SRAM_MODULE_ENABLED */
+
+#ifdef HAL_NOR_MODULE_ENABLED
+  #include "stm32f7xx_hal_nor.h"
+#endif /* HAL_NOR_MODULE_ENABLED */
+
+#ifdef HAL_NAND_MODULE_ENABLED
+  #include "stm32f7xx_hal_nand.h"
+#endif /* HAL_NAND_MODULE_ENABLED */
+
+#ifdef HAL_SDRAM_MODULE_ENABLED
+  #include "stm32f7xx_hal_sdram.h"
+#endif /* HAL_SDRAM_MODULE_ENABLED */
+
+#ifdef HAL_HASH_MODULE_ENABLED
+ #include "stm32f7xx_hal_hash.h"
+#endif /* HAL_HASH_MODULE_ENABLED */
+
+#ifdef HAL_I2C_MODULE_ENABLED
+ #include "stm32f7xx_hal_i2c.h"
+#endif /* HAL_I2C_MODULE_ENABLED */
+
+#ifdef HAL_I2S_MODULE_ENABLED
+ #include "stm32f7xx_hal_i2s.h"
+#endif /* HAL_I2S_MODULE_ENABLED */
+
+#ifdef HAL_IWDG_MODULE_ENABLED
+ #include "stm32f7xx_hal_iwdg.h"
+#endif /* HAL_IWDG_MODULE_ENABLED */
+
+#ifdef HAL_LPTIM_MODULE_ENABLED
+ #include "stm32f7xx_hal_lptim.h"
+#endif /* HAL_LPTIM_MODULE_ENABLED */
+
+#ifdef HAL_LTDC_MODULE_ENABLED
+ #include "stm32f7xx_hal_ltdc.h"
+#endif /* HAL_LTDC_MODULE_ENABLED */
+
+#ifdef HAL_PWR_MODULE_ENABLED
+ #include "stm32f7xx_hal_pwr.h"
+#endif /* HAL_PWR_MODULE_ENABLED */
+
+#ifdef HAL_QSPI_MODULE_ENABLED
+ #include "stm32f7xx_hal_qspi.h"
+#endif /* HAL_QSPI_MODULE_ENABLED */
+
+#ifdef HAL_RNG_MODULE_ENABLED
+ #include "stm32f7xx_hal_rng.h"
+#endif /* HAL_RNG_MODULE_ENABLED */
+
+#ifdef HAL_RTC_MODULE_ENABLED
+ #include "stm32f7xx_hal_rtc.h"
+#endif /* HAL_RTC_MODULE_ENABLED */
+
+#ifdef HAL_SAI_MODULE_ENABLED
+ #include "stm32f7xx_hal_sai.h"
+#endif /* HAL_SAI_MODULE_ENABLED */
+
+#ifdef HAL_SD_MODULE_ENABLED
+ #include "stm32f7xx_hal_sd.h"
+#endif /* HAL_SD_MODULE_ENABLED */
+
+#ifdef HAL_SPDIFRX_MODULE_ENABLED
+ #include "stm32f7xx_hal_spdifrx.h"
+#endif /* HAL_SPDIFRX_MODULE_ENABLED */
+
+#ifdef HAL_SPI_MODULE_ENABLED
+ #include "stm32f7xx_hal_spi.h"
+#endif /* HAL_SPI_MODULE_ENABLED */
+
+#ifdef HAL_TIM_MODULE_ENABLED
+ #include "stm32f7xx_hal_tim.h"
+#endif /* HAL_TIM_MODULE_ENABLED */
+
+#ifdef HAL_UART_MODULE_ENABLED
+ #include "stm32f7xx_hal_uart.h"
+#endif /* HAL_UART_MODULE_ENABLED */
+
+#ifdef HAL_USART_MODULE_ENABLED
+ #include "stm32f7xx_hal_usart.h"
+#endif /* HAL_USART_MODULE_ENABLED */
+
+#ifdef HAL_IRDA_MODULE_ENABLED
+ #include "stm32f7xx_hal_irda.h"
+#endif /* HAL_IRDA_MODULE_ENABLED */
+
+#ifdef HAL_SMARTCARD_MODULE_ENABLED
+ #include "stm32f7xx_hal_smartcard.h"
+#endif /* HAL_SMARTCARD_MODULE_ENABLED */
+
+#ifdef HAL_WWDG_MODULE_ENABLED
+ #include "stm32f7xx_hal_wwdg.h"
+#endif /* HAL_WWDG_MODULE_ENABLED */
+
+#ifdef HAL_PCD_MODULE_ENABLED
+ #include "stm32f7xx_hal_pcd.h"
+#endif /* HAL_PCD_MODULE_ENABLED */
+
+#ifdef HAL_HCD_MODULE_ENABLED
+ #include "stm32f7xx_hal_hcd.h"
+#endif /* HAL_HCD_MODULE_ENABLED */
+
+/* Exported macro ------------------------------------------------------------*/
+#ifdef  USE_FULL_ASSERT
+/**
+  * @brief  The assert_param macro is used for function's parameters check.
+  * @param  expr: If expr is false, it calls assert_failed function
+  *         which reports the name of the source file and the source
+  *         line number of the call that failed.
+  *         If expr is true, it returns no value.
+  * @retval None
+  */
+  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
+/* Exported functions ------------------------------------------------------- */
+  void assert_failed(uint8_t* file, uint32_t line);
+#else
+  #define assert_param(expr) ((void)0)
+#endif /* USE_FULL_ASSERT */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __STM32F7xx_HAL_CONF_H */
+
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ftp_client_demo/src/stm32f7xx_it.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,195 @@
+/**
+  ******************************************************************************
+  * @file    Templates/stm32f7xx_it.c
+  * @author  MCD Application Team
+  * @version V1.0.3
+  * @date    22-April-2016
+  * @brief   Main Interrupt Service Routines.
+  *          This file provides template for all exceptions handler and
+  *          peripherals interrupt service routine.
+  ******************************************************************************
+  * @attention
+  *
+  * <h2><center>&copy; COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
+  *
+  * Redistribution and use in source and binary forms, with or without modification,
+  * are permitted provided that the following conditions are met:
+  *   1. Redistributions of source code must retain the above copyright notice,
+  *      this list of conditions and the following disclaimer.
+  *   2. Redistributions in binary form must reproduce 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.
+  *
+  ******************************************************************************
+  */
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32f7xx_it.h"
+#include "os_port.h"
+
+/** @addtogroup STM32F7xx_HAL_Examples
+  * @{
+  */
+
+/** @addtogroup Templates
+  * @{
+  */
+
+/* Private typedef -----------------------------------------------------------*/
+/* Private define ------------------------------------------------------------*/
+/* Private macro -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Private function prototypes -----------------------------------------------*/
+extern void HAL_IncTick(void);
+extern void xPortSysTickHandler(void);
+
+/* Private functions ---------------------------------------------------------*/
+
+/******************************************************************************/
+/*            Cortex-M7 Processor Exceptions Handlers                         */
+/******************************************************************************/
+
+/**
+  * @brief   This function handles NMI exception.
+  * @param  None
+  * @retval None
+  */
+void NMI_Handler(void)
+{
+}
+
+/**
+  * @brief  This function handles Hard Fault exception.
+  * @param  None
+  * @retval None
+  */
+void HardFault_Handler(void)
+{
+  /* Go to infinite loop when Hard Fault exception occurs */
+  while (1)
+  {
+  }
+}
+
+/**
+  * @brief  This function handles Memory Manage exception.
+  * @param  None
+  * @retval None
+  */
+void MemManage_Handler(void)
+{
+  /* Go to infinite loop when Memory Manage exception occurs */
+  while (1)
+  {
+  }
+}
+
+/**
+  * @brief  This function handles Bus Fault exception.
+  * @param  None
+  * @retval None
+  */
+void BusFault_Handler(void)
+{
+  /* Go to infinite loop when Bus Fault exception occurs */
+  while (1)
+  {
+  }
+}
+
+/**
+  * @brief  This function handles Usage Fault exception.
+  * @param  None
+  * @retval None
+  */
+void UsageFault_Handler(void)
+{
+  /* Go to infinite loop when Usage Fault exception occurs */
+  while (1)
+  {
+  }
+}
+
+/**
+  * @brief  This function handles SVCall exception.
+  * @param  None
+  * @retval None
+  */
+#if 0
+void SVC_Handler(void)
+{
+}
+#endif
+
+/**
+  * @brief  This function handles Debug Monitor exception.
+  * @param  None
+  * @retval None
+  */
+void DebugMon_Handler(void)
+{
+}
+
+/**
+  * @brief  This function handles PendSVC exception.
+  * @param  None
+  * @retval None
+  */
+#if 0
+void PendSV_Handler(void)
+{
+}
+#endif
+
+/**
+  * @brief  This function handles SysTick Handler.
+  * @param  None
+  * @retval None
+  */
+void SysTick_Handler(void)
+{
+   HAL_IncTick();
+
+   if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
+   {
+      xPortSysTickHandler();
+   }
+}
+
+/******************************************************************************/
+/*                 STM32F7xx Peripherals Interrupt Handlers                   */
+/*  Add here the Interrupt Handler for the used peripheral(s) (PPP), for the  */
+/*  available peripheral interrupt handler's name please refer to the startup */
+/*  file (startup_stm32f7xx.s).                                               */
+/******************************************************************************/
+
+/**
+  * @brief  This function handles PPP interrupt request.
+  * @param  None
+  * @retval None
+  */
+/*void PPP_IRQHandler(void)
+{
+}*/
+
+/**
+  * @}
+  */
+
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ftp_client_demo/src/stm32f7xx_it.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,69 @@
+/**
+  ******************************************************************************
+  * @file    Templates/stm32f7xx_it.h
+  * @author  MCD Application Team
+  * @version V1.0.3
+  * @date    22-April-2016
+  * @brief   This file contains the headers of the interrupt handlers.
+  ******************************************************************************
+  * @attention
+  *
+  * <h2><center>&copy; COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
+  *
+  * Redistribution and use in source and binary forms, with or without modification,
+  * are permitted provided that the following conditions are met:
+  *   1. Redistributions of source code must retain the above copyright notice,
+  *      this list of conditions and the following disclaimer.
+  *   2. Redistributions in binary form must reproduce 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.
+  *
+  ******************************************************************************
+  */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32F7xx_IT_H
+#define __STM32F7xx_IT_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+/* Exported macro ------------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+void NMI_Handler(void);
+void HardFault_Handler(void);
+void MemManage_Handler(void);
+void BusFault_Handler(void);
+void UsageFault_Handler(void);
+void SVC_Handler(void);
+void DebugMon_Handler(void);
+void PendSV_Handler(void);
+void SysTick_Handler(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __STM32F7xx_IT_H */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ftp_client_demo/src/system_stm32f7xx.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,281 @@
+/**
+  ******************************************************************************
+  * @file    Templates/system_stm32f7xx.c
+  * @author  MCD Application Team
+  * @version V1.0.1
+  * @date    22-April-2016
+  * @brief   - CMSIS Cortex-M7 Device Peripheral Access Layer System Source File.
+  *          - This file is dedicated only for STM32F746 NUCLEO 144 boards.
+  *
+  *   This file provides two functions and one global variable to be called from
+  *   user application:
+  *      - SystemInit(): This function is called at startup just after reset and
+  *                      before branch to main program. This call is made inside
+  *                      the "startup_stm32f7xx.s" file.
+  *
+  *      - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
+  *                                  by the user application to setup the SysTick
+  *                                  timer or configure other parameters.
+  *
+  *      - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
+  *                                 be called whenever the core clock is changed
+  *                                 during program execution.
+  *
+  *
+  ******************************************************************************
+  * @attention
+  *
+  * <h2><center>&copy; COPYRIGHT 2016 STMicroelectronics</center></h2>
+  *
+  * Redistribution and use in source and binary forms, with or without modification,
+  * are permitted provided that the following conditions are met:
+  *   1. Redistributions of source code must retain the above copyright notice,
+  *      this list of conditions and the following disclaimer.
+  *   2. Redistributions in binary form must reproduce 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.
+  *
+  ******************************************************************************
+  */
+
+/** @addtogroup CMSIS
+  * @{
+  */
+
+/** @addtogroup stm32f7xx_system
+  * @{
+  */
+
+/** @addtogroup STM32F7xx_System_Private_Includes
+  * @{
+  */
+
+#include "stm32f7xx.h"
+
+#if !defined  (HSE_VALUE)
+  #define HSE_VALUE    ((uint32_t)8000000) /*!< Default value of the External oscillator in Hz */
+#endif /* HSE_VALUE */
+
+#if !defined  (HSI_VALUE)
+  #define HSI_VALUE    ((uint32_t)16000000) /*!< Value of the Internal oscillator in Hz*/
+#endif /* HSI_VALUE */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F7xx_System_Private_TypesDefinitions
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F7xx_System_Private_Defines
+  * @{
+  */
+
+/************************* Miscellaneous Configuration ************************/
+/*!< Uncomment the following line if you need to relocate your vector Table in
+     Internal SRAM. */
+/* #define VECT_TAB_SRAM */
+#define VECT_TAB_OFFSET  0x00 /*!< Vector Table base offset field.
+                                   This value must be a multiple of 0x200. */
+/******************************************************************************/
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F7xx_System_Private_Macros
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F7xx_System_Private_Variables
+  * @{
+  */
+
+  /* This variable is updated in three ways:
+      1) by calling CMSIS function SystemCoreClockUpdate()
+      2) by calling HAL API function HAL_RCC_GetHCLKFreq()
+      3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency
+         Note: If you use this function to configure the system clock; then there
+               is no need to call the 2 first functions listed above, since SystemCoreClock
+               variable is updated automatically.
+  */
+  uint32_t SystemCoreClock = 16000000;
+  const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
+  const uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4};
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F7xx_System_Private_FunctionPrototypes
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F7xx_System_Private_Functions
+  * @{
+  */
+
+/**
+  * @brief  Setup the microcontroller system
+  *         Initialize the Embedded Flash Interface, the PLL and update the
+  *         SystemFrequency variable.
+  * @param  None
+  * @retval None
+  */
+void SystemInit(void)
+{
+  /* FPU settings ------------------------------------------------------------*/
+  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
+    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
+  #endif
+  /* Reset the RCC clock configuration to the default reset state ------------*/
+  /* Set HSION bit */
+  RCC->CR |= (uint32_t)0x00000001;
+
+  /* Reset CFGR register */
+  RCC->CFGR = 0x00000000;
+
+  /* Reset HSEON, CSSON and PLLON bits */
+  RCC->CR &= (uint32_t)0xFEF6FFFF;
+
+  /* Reset PLLCFGR register */
+  RCC->PLLCFGR = 0x24003010;
+
+  /* Reset HSEBYP bit */
+  RCC->CR &= (uint32_t)0xFFFBFFFF;
+
+  /* Disable all interrupts */
+  RCC->CIR = 0x00000000;
+
+  /* Configure the Vector Table location add offset address ------------------*/
+#ifdef VECT_TAB_SRAM
+  //SCB->VTOR = SRAM1_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
+#else
+  //SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
+#endif
+}
+
+/**
+   * @brief  Update SystemCoreClock variable according to Clock Register Values.
+  *         The SystemCoreClock variable contains the core clock (HCLK), it can
+  *         be used by the user application to setup the SysTick timer or configure
+  *         other parameters.
+  *
+  * @note   Each time the core clock (HCLK) changes, this function must be called
+  *         to update SystemCoreClock variable value. Otherwise, any configuration
+  *         based on this variable will be incorrect.
+  *
+  * @note   - The system frequency computed by this function is not the real
+  *           frequency in the chip. It is calculated based on the predefined
+  *           constant and the selected clock source:
+  *
+  *           - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
+  *
+  *           - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)
+  *
+  *           - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**)
+  *             or HSI_VALUE(*) multiplied/divided by the PLL factors.
+  *
+  *         (*) HSI_VALUE is a constant defined in stm32f7xx.h file (default value
+  *             16 MHz) but the real value may vary depending on the variations
+  *             in voltage and temperature.
+  *
+  *         (**) HSE_VALUE is a constant defined in stm32f7xx.h file (default value
+  *              25 MHz), user has to ensure that HSE_VALUE is same as the real
+  *              frequency of the crystal used. Otherwise, this function may
+  *              have wrong result.
+  *
+  *         - The result of this function could be not correct when using fractional
+  *           value for HSE crystal.
+  *
+  * @param  None
+  * @retval None
+  */
+void SystemCoreClockUpdate(void)
+{
+  uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2;
+
+  /* Get SYSCLK source -------------------------------------------------------*/
+  tmp = RCC->CFGR & RCC_CFGR_SWS;
+
+  switch (tmp)
+  {
+    case 0x00:  /* HSI used as system clock source */
+      SystemCoreClock = HSI_VALUE;
+      break;
+    case 0x04:  /* HSE used as system clock source */
+      SystemCoreClock = HSE_VALUE;
+      break;
+    case 0x08:  /* PLL used as system clock source */
+
+      /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N
+         SYSCLK = PLL_VCO / PLL_P
+         */
+      pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22;
+      pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;
+
+      if (pllsource != 0)
+      {
+        /* HSE used as PLL clock source */
+        pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
+      }
+      else
+      {
+        /* HSI used as PLL clock source */
+        pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
+      }
+
+      pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2;
+      SystemCoreClock = pllvco/pllp;
+      break;
+    default:
+      SystemCoreClock = HSI_VALUE;
+      break;
+  }
+  /* Compute HCLK frequency --------------------------------------------------*/
+  /* Get HCLK prescaler */
+  tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
+  /* HCLK frequency */
+  SystemCoreClock >>= tmp;
+}
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mqtt_client_demo/src/FreeRTOSConfig.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,132 @@
+/*
+    FreeRTOS V6.0.1 - Copyright (C) 2009 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 FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+//Keil MDK-ARM, IAR EWARM or GCC compiler?
+#if (defined(__CC_ARM) || defined(__ICCARM__) || defined(__GNUC__))
+
+#include <stdint.h>
+extern uint32_t SystemCoreClock;
+
+#endif
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+#define configUSE_PREEMPTION		1
+#define configUSE_IDLE_HOOK			0
+#define configUSE_TICK_HOOK			0
+#define configCPU_CLOCK_HZ			( SystemCoreClock )
+#define configTICK_RATE_HZ			( ( portTickType ) 1000 )
+#define configMAX_PRIORITIES		( 5 )
+#define configMINIMAL_STACK_SIZE	( ( unsigned short ) 400 )
+#define configMAX_TASK_NAME_LEN		( 16 )
+#define configUSE_16_BIT_TICKS		0
+#define configIDLE_SHOULD_YIELD		1
+#define configUSE_APPLICATION_TASK_TAG      1
+#define configUSE_MUTEXES               1
+#define configUSE_COUNTING_SEMAPHORES 1
+
+#define configCHECK_FOR_STACK_OVERFLOW  0
+#define configGENERATE_RUN_TIME_STATS               0
+//#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS      init_us_timer
+//#define portGET_RUN_TIME_COUNTER_VALUE              get_us_time
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES 		0
+#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
+
+/* Set the following definitions to 1 to include the API function, or zero
+to exclude the API function. */
+
+#define INCLUDE_vTaskPrioritySet		1
+#define INCLUDE_uxTaskPriorityGet		1
+#define INCLUDE_vTaskDelete				1
+#define INCLUDE_vTaskCleanUpResources	1
+#define INCLUDE_vTaskSuspend			1
+#define INCLUDE_vTaskDelayUntil			1
+#define INCLUDE_vTaskDelay				1
+#define INCLUDE_uxTaskGetStackHighWaterMark     1
+#define INCLUDE_xTaskGetIdleTaskHandle  1
+#define INCLUDE_xTaskGetSchedulerState 1
+
+/* This is the raw value as per the Cortex-M3 NVIC.  Values can be 255
+(lowest) to 0 (1?) (highest). */
+#define configKERNEL_INTERRUPT_PRIORITY 		255
+#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	191 /* equivalent to 0xb0, or priority 11. */
+
+/* This is the value being used as per the ST library which permits 16
+priority values, 0 to 15.  This must correspond to the
+configKERNEL_INTERRUPT_PRIORITY setting.  Here 15 corresponds to the lowest
+NVIC value of 255. */
+#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY	15
+
+/* Redefine functions names to match the standard peripheral library */
+//#define xPortSysTickHandler     SysTick_Handler
+#define xPortPendSVHandler      PendSV_Handler
+#define vPortSVCHandler         SVC_Handler
+
+#endif /* FREERTOS_CONFIG_H */
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mqtt_client_demo/src/crypto_config.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,139 @@
+/**
+ * @file crypto_config.h
+ * @brief CycloneCrypto configuration file
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneCrypto Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _CRYPTO_CONFIG_H
+#define _CRYPTO_CONFIG_H
+
+//Desired trace level (for debugging purposes)
+#define CRYPTO_TRACE_LEVEL TRACE_LEVEL_INFO
+
+//Assembly optimizations for time-critical routines
+#define MPI_ASM_SUPPORT DISABLED
+
+//Base64 encoding support
+#define BASE64_SUPPORT ENABLED
+
+//MD2 hash support
+#define MD2_SUPPORT DISABLED
+//MD4 hash support
+#define MD4_SUPPORT DISABLED
+//MD5 hash support
+#define MD5_SUPPORT ENABLED
+//RIPEMD-128 hash support
+#define RIPEMD128_SUPPORT DISABLED
+//RIPEMD-160 hash support
+#define RIPEMD160_SUPPORT DISABLED
+//SHA-1 hash support
+#define SHA1_SUPPORT ENABLED
+//SHA-224 hash support
+#define SHA224_SUPPORT ENABLED
+//SHA-256 hash support
+#define SHA256_SUPPORT ENABLED
+//SHA-384 hash support
+#define SHA384_SUPPORT ENABLED
+//SHA-512 hash support
+#define SHA512_SUPPORT ENABLED
+//SHA-512/224 hash support
+#define SHA512_224_SUPPORT DISABLED
+//SHA-512/256 hash support
+#define SHA512_256_SUPPORT DISABLED
+//SHA3-224 hash support
+#define SHA3_224_SUPPORT DISABLED
+//SHA3-256 hash support
+#define SHA3_256_SUPPORT DISABLED
+//SHA3-384 hash support
+#define SHA3_384_SUPPORT DISABLED
+//SHA3-512 hash support
+#define SHA3_512_SUPPORT DISABLED
+//Keccak support
+#define KECCAK_SUPPORT DISABLED
+//Tiger hash support
+#define TIGER_SUPPORT DISABLED
+//Whirlpool hash support
+#define WHIRLPOOL_SUPPORT DISABLED
+
+//HMAC support
+#define HMAC_SUPPORT ENABLED
+
+//RC4 support
+#define RC4_SUPPORT ENABLED
+//RC6 support
+#define RC6_SUPPORT DISABLED
+//IDEA support
+#define IDEA_SUPPORT ENABLED
+//DES support
+#define DES_SUPPORT ENABLED
+//Triple DES support
+#define DES3_SUPPORT ENABLED
+//AES support
+#define AES_SUPPORT ENABLED
+//Camellia support
+#define CAMELLIA_SUPPORT ENABLED
+//SEED support
+#define SEED_SUPPORT ENABLED
+//ARIA support
+#define ARIA_SUPPORT ENABLED
+
+//ECB mode support
+#define ECB_SUPPORT DISABLED
+//CBC mode support
+#define CBC_SUPPORT ENABLED
+//CFB mode support
+#define CFB_SUPPORT DISABLED
+//OFB mode support
+#define OFB_SUPPORT DISABLED
+//CTR mode support
+#define CTR_SUPPORT DISABLED
+//CCM mode support
+#define CCM_SUPPORT ENABLED
+//GCM mode support
+#define GCM_SUPPORT ENABLED
+
+//Chacha support
+#define CHACHA_SUPPORT ENABLED
+//Poly1305 support
+#define POLY1305_SUPPORT ENABLED
+//Chacha20Poly1305 support
+#define CHACHA20_POLY1305_SUPPORT ENABLED
+
+//Diffie-Hellman support
+#define DH_SUPPORT ENABLED
+//RSA support
+#define RSA_SUPPORT ENABLED
+//DSA support
+#define DSA_SUPPORT ENABLED
+
+//Elliptic curve cryptography support
+#define EC_SUPPORT ENABLED
+//ECDH support
+#define ECDH_SUPPORT ENABLED
+//ECDSA support
+#define ECDSA_SUPPORT ENABLED
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mqtt_client_demo/src/debug.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,134 @@
+/**
+ * @file debug.c
+ * @brief Debugging facilities
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Dependencies
+#include "stm32f7xx.h"
+#include "stm32f7xx_hal.h"
+#include "debug.h"
+
+//Variable declaration
+static UART_HandleTypeDef UART_Handle;
+
+
+/**
+ * @brief Debug UART initialization
+ * @param[in] baudrate UART baudrate
+ **/
+
+void debugInit(uint32_t baudrate)
+{
+   GPIO_InitTypeDef GPIO_InitStructure;
+
+   //Enable GPIOD clock
+   __HAL_RCC_GPIOD_CLK_ENABLE();
+   //Enable USART3 clock
+   __HAL_RCC_USART3_CLK_ENABLE();
+
+   //Configure USART3_TX (PD8)
+   GPIO_InitStructure.Pin = GPIO_PIN_8;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_PULLUP;
+   GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
+   GPIO_InitStructure.Alternate = GPIO_AF7_USART3;
+   HAL_GPIO_Init(GPIOD, &GPIO_InitStructure);
+
+   //Configure USART3_RX (PD9)
+   GPIO_InitStructure.Pin = GPIO_PIN_9;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_PULLUP;
+   GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
+   GPIO_InitStructure.Alternate = GPIO_AF7_USART3;
+   HAL_GPIO_Init(GPIOD, &GPIO_InitStructure);
+
+   //Configure USART3
+   UART_Handle.Instance = USART3;
+   UART_Handle.Init.BaudRate = baudrate;
+   UART_Handle.Init.WordLength = UART_WORDLENGTH_8B;
+   UART_Handle.Init.StopBits = UART_STOPBITS_1;
+   UART_Handle.Init.Parity = UART_PARITY_NONE;
+   UART_Handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
+   UART_Handle.Init.Mode = UART_MODE_TX_RX;
+   HAL_UART_Init(&UART_Handle);
+}
+
+
+/**
+ * @brief Display the contents of an array
+ * @param[in] stream Pointer to a FILE object that identifies an output stream
+ * @param[in] prepend String to prepend to the left of each line
+ * @param[in] data Pointer to the data array
+ * @param[in] length Number of bytes to display
+ **/
+
+void debugDisplayArray(FILE *stream,
+   const char_t *prepend, const void *data, size_t length)
+{
+   uint_t i;
+
+   for(i = 0; i < length; i++)
+   {
+      //Beginning of a new line?
+      if((i % 16) == 0)
+         fprintf(stream, "%s", prepend);
+      //Display current data byte
+      fprintf(stream, "%02" PRIX8 " ", *((uint8_t *) data + i));
+      //End of current line?
+      if((i % 16) == 15 || i == (length - 1))
+         fprintf(stream, "\r\n");
+   }
+}
+
+
+/**
+ * @brief Write character to stream
+ * @param[in] c The character to be written
+ * @param[in] stream Pointer to a FILE object that identifies an output stream
+ * @return On success, the character written is returned. If a writing
+ *   error occurs, EOF is returned
+ **/
+
+int_t fputc(int_t c, FILE *stream)
+{
+   //Standard output or error output?
+   if(stream == stdout || stream == stderr)
+   {
+      //Character to be written
+      uint8_t ch = c;
+
+      //Transmit data
+      HAL_UART_Transmit(&UART_Handle, &ch, 1, HAL_MAX_DELAY);
+
+      //On success, the character written is returned
+      return c;
+   }
+   //Unknown output?
+   else
+   {
+      //If a writing error occurs, EOF is returned
+      return EOF;
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mqtt_client_demo/src/main.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,775 @@
+/**
+ * @file main.c
+ * @brief Main routine
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Dependencies
+#include <stdlib.h>
+#include "stm32f7xx.h"
+#include "stm32f7xx_hal.h"
+#include "stm32f7xx_nucleo_144.h"
+#include "os_port.h"
+#include "core/net.h"
+#include "drivers/stm32f7xx_eth.h"
+#include "drivers/lan8742.h"
+#include "dhcp/dhcp_client.h"
+#include "ipv6/slaac.h"
+#include "mqtt/mqtt_client.h"
+#include "yarrow.h"
+#include "debug.h"
+
+//Application configuration
+#define APP_MAC_ADDR "00-AB-CD-EF-07-46"
+
+#define APP_USE_DHCP ENABLED
+#define APP_IPV4_HOST_ADDR "192.168.0.20"
+#define APP_IPV4_SUBNET_MASK "255.255.255.0"
+#define APP_IPV4_DEFAULT_GATEWAY "192.168.0.254"
+#define APP_IPV4_PRIMARY_DNS "8.8.8.8"
+#define APP_IPV4_SECONDARY_DNS "8.8.4.4"
+
+#define APP_USE_SLAAC ENABLED
+#define APP_IPV6_LINK_LOCAL_ADDR "fe80::746"
+#define APP_IPV6_PREFIX "2001:db8::"
+#define APP_IPV6_PREFIX_LENGTH 64
+#define APP_IPV6_GLOBAL_ADDR "2001:db8::746"
+#define APP_IPV6_ROUTER "fe80::1"
+#define APP_IPV6_PRIMARY_DNS "2001:4860:4860::8888"
+#define APP_IPV6_SECONDARY_DNS "2001:4860:4860::8844"
+
+//MQTT server name
+#define APP_SERVER_NAME "iot.eclipse.org"
+
+//MQTT server port
+#define APP_SERVER_PORT 1883   //MQTT over TCP
+//#define APP_SERVER_PORT 8883 //MQTT over SSL/TLS
+//#define APP_SERVER_PORT 80   //MQTT over WebSocket
+//#define APP_SERVER_PORT 443  //MQTT over secure WebSocket
+
+//URI (for MQTT over WebSocket only)
+#define APP_SERVER_URI "/ws"
+
+//List of trusted CA certificates
+char_t trustedCaList[] =
+   "-----BEGIN CERTIFICATE-----"
+   "MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/"
+   "MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT"
+   "DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow"
+   "PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD"
+   "Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB"
+   "AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O"
+   "rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq"
+   "OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b"
+   "xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw"
+   "7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD"
+   "aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV"
+   "HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG"
+   "SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69"
+   "ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr"
+   "AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz"
+   "R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5"
+   "JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo"
+   "Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ"
+   "-----END CERTIFICATE-----";
+
+//Connection states
+#define APP_STATE_NOT_CONNECTED 0
+#define APP_STATE_CONNECTING    1
+#define APP_STATE_CONNECTED     2
+
+//Global variables
+uint_t connectionState = APP_STATE_NOT_CONNECTED;
+
+RNG_HandleTypeDef RNG_Handle;
+
+DhcpClientSettings dhcpClientSettings;
+DhcpClientContext dhcpClientContext;
+SlaacSettings slaacSettings;
+SlaacContext slaacContext;
+MqttClientContext mqttClientContext;
+YarrowContext yarrowContext;
+uint8_t seed[32];
+
+
+/**
+ * @brief System clock configuration
+ **/
+
+void SystemClock_Config(void)
+{
+   RCC_OscInitTypeDef RCC_OscInitStruct;
+   RCC_ClkInitTypeDef RCC_ClkInitStruct;
+
+   //Enable Power Control clock
+   __HAL_RCC_PWR_CLK_ENABLE();
+
+   //Enable HSE Oscillator and activate PLL with HSE as source
+   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
+   RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;
+   RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
+   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
+   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
+   RCC_OscInitStruct.PLL.PLLM = 8;
+   RCC_OscInitStruct.PLL.PLLN = 432;
+   RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
+   RCC_OscInitStruct.PLL.PLLQ = 9;
+   HAL_RCC_OscConfig(&RCC_OscInitStruct);
+
+   //Enable overdrive mode
+   HAL_PWREx_EnableOverDrive();
+
+   //Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
+   //clocks dividers
+   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
+   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
+   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
+   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
+   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
+   HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7);
+}
+
+
+/**
+ * @brief MPU configuration
+ **/
+
+void MPU_Config(void)
+{
+   MPU_Region_InitTypeDef MPU_InitStruct;
+
+   //Disable MPU
+   HAL_MPU_Disable();
+
+   //SRAM
+   MPU_InitStruct.Enable = MPU_REGION_ENABLE;
+   MPU_InitStruct.Number = MPU_REGION_NUMBER0;
+   MPU_InitStruct.BaseAddress = 0x20000000;
+   MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
+   MPU_InitStruct.SubRegionDisable = 0;
+   MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
+   MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
+   MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
+   MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
+   MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
+   MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
+   HAL_MPU_ConfigRegion(&MPU_InitStruct);
+
+   //SRAM2
+   MPU_InitStruct.Enable = MPU_REGION_ENABLE;
+   MPU_InitStruct.Number = MPU_REGION_NUMBER1;
+   MPU_InitStruct.BaseAddress = 0x2004C000;
+   MPU_InitStruct.Size = MPU_REGION_SIZE_16KB;
+   MPU_InitStruct.SubRegionDisable = 0;
+   MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
+   MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
+   MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
+   MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
+   MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
+   MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
+   HAL_MPU_ConfigRegion(&MPU_InitStruct);
+
+   //Enable MPU
+   HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
+}
+
+
+/**
+ * @brief Random data generation callback function
+ * @param[out] data Buffer where to store the random data
+ * @param[in] lenght Number of bytes that are required
+ * @return Error code
+ **/
+
+error_t webSocketRngCallback(uint8_t *data, size_t length)
+{
+   //Generate some random data
+   return yarrowRead(&yarrowContext, data, length);
+}
+
+
+/**
+ * @brief SSL/TLS initialization callback
+ * @param[in] context Pointer to the FTP client context
+ * @param[in] tlsContext Pointer to the SSL/TLS context
+ * @return Error code
+ **/
+
+error_t mqttTlsInitCallback(MqttClientContext *context,
+   TlsContext *tlsContext)
+{
+   error_t error;
+
+   //Debug message
+   TRACE_INFO("MQTT: TLS initialization callback\r\n");
+
+   //Set the PRNG algorithm to be used
+   error = tlsSetPrng(tlsContext, YARROW_PRNG_ALGO, &yarrowContext);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Set the fully qualified domain name of the server
+   error = tlsSetServerName(tlsContext, APP_SERVER_NAME);
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Import the list of trusted CA certificates
+   error = tlsSetTrustedCaList(tlsContext, trustedCaList, strlen(trustedCaList));
+   //Any error to report?
+   if(error)
+      return error;
+
+   //Successful processing
+   return NO_ERROR;
+}
+
+
+/**
+ * @brief Publish callback function
+ * @param[in] context Pointer to the MQTT client context
+ * @param[in] topic Topic name
+ * @param[in] message Message payload
+ * @param[in] length Length of the message payload
+ * @param[in] dup Duplicate delivery of the PUBLISH packet
+ * @param[in] qos QoS level used to publish the message
+ * @param[in] retain This flag specifies if the message is to be retained
+ * @param[in] packetId Packet identifier
+ **/
+
+void mqttPublishCallback(MqttClientContext *context,
+   const char_t *topic, const uint8_t *message, size_t length,
+   bool_t dup, MqttQosLevel qos, bool_t retain, uint16_t packetId)
+{
+   //Debug message
+   TRACE_INFO("PUBLISH packet received...\r\n");
+   TRACE_INFO("  Dup: %u\r\n", dup);
+   TRACE_INFO("  QoS: %u\r\n", qos);
+   TRACE_INFO("  Retain: %u\r\n", retain);
+   TRACE_INFO("  Packet Identifier: %u\r\n", packetId);
+   TRACE_INFO("  Topic: %s\r\n", topic);
+   TRACE_INFO("  Message (%" PRIuSIZE " bytes):\r\n", length);
+   TRACE_INFO_ARRAY("    ", message, length);
+
+   //Check topic name
+   if(!strcmp(topic, "board/leds/1"))
+   {
+      if(length == 6 && !strncasecmp((char_t *) message, "toggle", 6))
+         BSP_LED_Toggle(LED2);
+      else if(length == 2 && !strncasecmp((char_t *) message, "on", 2))
+         BSP_LED_On(LED2);
+      else
+         BSP_LED_Off(LED2);
+   }
+   else if(!strcmp(topic, "board/leds/2"))
+   {
+      if(length == 6 && !strncasecmp((char_t *) message, "toggle", 6))
+         BSP_LED_Toggle(LED3);
+      else if(length == 2 && !strncasecmp((char_t *) message, "on", 2))
+         BSP_LED_On(LED3);
+      else
+         BSP_LED_Off(LED3);
+   }
+}
+
+
+/**
+ * @brief Establish MQTT connection
+ **/
+
+error_t mqttConnect(void)
+{
+   error_t error;
+   IpAddr ipAddr;
+   MqttClientCallbacks mqttClientCallbacks;
+
+   //Debug message
+   TRACE_INFO("\r\n\r\nResolving server name...\r\n");
+
+   //Resolve MQTT server name
+   error = getHostByName(NULL, APP_SERVER_NAME, &ipAddr, 0);
+   //Any error to report?
+   if(error)
+      return error;
+
+#if (APP_SERVER_PORT == 80 || APP_SERVER_PORT == 443)
+   //Register RNG callback
+   webSocketRegisterRandCallback(webSocketRngCallback);
+#endif
+
+   //Initialize MQTT client context
+   mqttClientInit(&mqttClientContext);
+   //Initialize MQTT client callbacks
+   mqttClientInitCallbacks(&mqttClientCallbacks);
+
+   //Attach application-specific callback functions
+   mqttClientCallbacks.publishCallback = mqttPublishCallback;
+#if (APP_SERVER_PORT == 8883 || APP_SERVER_PORT == 443)
+   mqttClientCallbacks.tlsInitCallback = mqttTlsInitCallback;
+#endif
+
+   //Register MQTT client callbacks
+   mqttClientRegisterCallbacks(&mqttClientContext, &mqttClientCallbacks);
+
+   //Set the MQTT version to be used
+   mqttClientSetProtocolLevel(&mqttClientContext,
+      MQTT_PROTOCOL_LEVEL_3_1_1);
+
+#if (APP_SERVER_PORT == 1883)
+   //MQTT over TCP
+   mqttClientSetTransportProtocol(&mqttClientContext, MQTT_TRANSPORT_PROTOCOL_TCP);
+#elif (APP_SERVER_PORT == 8883)
+   //MQTT over SSL/TLS
+   mqttClientSetTransportProtocol(&mqttClientContext, MQTT_TRANSPORT_PROTOCOL_TLS);
+#elif (APP_SERVER_PORT == 80)
+   //MQTT over WebSocket
+   mqttClientSetTransportProtocol(&mqttClientContext, MQTT_TRANSPORT_PROTOCOL_WS);
+#elif (APP_SERVER_PORT == 443)
+   //MQTT over secure WebSocket
+   mqttClientSetTransportProtocol(&mqttClientContext, MQTT_TRANSPORT_PROTOCOL_WSS);
+#endif
+
+   //Set keep-alive value
+   mqttClientSetKeepAlive(&mqttClientContext, 3600);
+
+#if (APP_SERVER_PORT == 80 || APP_SERVER_PORT == 443)
+   //Set the hostname of the resource being requested
+   mqttClientSetHost(&mqttClientContext, APP_SERVER_NAME);
+   //Set the name of the resource being requested
+   mqttClientSetUri(&mqttClientContext, APP_SERVER_URI);
+#endif
+
+   //Set client identifier
+   //mqttClientSetIdentifier(&mqttClientContext, "client12345678");
+
+   //Set user name and password
+   //mqttClientSetAuthInfo(&mqttClientContext, "username", "password");
+
+   //Set Will message
+   mqttClientSetWillMessage(&mqttClientContext, "board/status",
+      "offline", 7, MQTT_QOS_LEVEL_0, FALSE);
+
+   //Debug message
+   TRACE_INFO("Connecting to MQTT server %s...\r\n", ipAddrToString(&ipAddr, NULL));
+
+   //Start of exception handling block
+   do
+   {
+      //Establish connection with the MQTT server
+      error = mqttClientConnect(&mqttClientContext,
+         &ipAddr, APP_SERVER_PORT, TRUE);
+      //Any error to report
+      if(error)
+         break;
+
+      //Subscribe to the desired topics
+      error = mqttClientSubscribe(&mqttClientContext,
+         "board/leds/+", MQTT_QOS_LEVEL_1, NULL);
+      //Any error to report
+      if(error)
+         break;
+
+      //Send PUBLISH packet
+      error = mqttClientPublish(&mqttClientContext, "board/status",
+         "online", 6, MQTT_QOS_LEVEL_1, TRUE, NULL);
+      //Any error to report
+      if(error)
+         break;
+
+      //End of exception handling block
+   } while(0);
+
+   //Check status code
+   if(error)
+   {
+      //Close connection
+      mqttClientClose(&mqttClientContext);
+   }
+
+   //Return status code
+   return error;
+}
+
+
+/**
+ * @brief MQTT test task
+ **/
+
+void mqttTestTask(void *param)
+{
+   error_t error;
+   uint_t buttonState;
+   uint_t prevButtonState;
+   char_t buffer[16];
+
+   //Initialize variables
+   prevButtonState = 0;
+
+   //Endless loop
+   while(1)
+   {
+      //Check connection state
+      if(connectionState == APP_STATE_NOT_CONNECTED)
+      {
+         //Update connection state
+         connectionState = APP_STATE_CONNECTING;
+
+         //Try to connect to the MQTT server
+         error = mqttConnect();
+
+         //Failed to connect to the MQTT server?
+         if(error)
+         {
+            //Update connection state
+            connectionState = APP_STATE_NOT_CONNECTED;
+            //Recovery delay
+            osDelayTask(2000);
+         }
+         else
+         {
+            //Update connection state
+            connectionState = APP_STATE_CONNECTED;
+         }
+      }
+      else
+      {
+         //Process incoming events
+         error = mqttClientProcessEvents(&mqttClientContext, 100);
+
+         //Connection to MQTT server lost?
+         if(error != NO_ERROR && error != ERROR_TIMEOUT)
+         {
+            //Close connection
+            mqttClientClose(&mqttClientContext);
+            //Update connection state
+            connectionState = APP_STATE_NOT_CONNECTED;
+            //Recovery delay
+            osDelayTask(2000);
+         }
+         else
+         {
+            //Initialize status code
+            error = NO_ERROR;
+
+            //Get user button state
+            buttonState = BSP_PB_GetState(BUTTON_KEY);
+
+            //Any change detected?
+            if(buttonState != prevButtonState)
+            {
+               if(buttonState)
+                  strcpy(buffer, "pressed");
+               else
+                  strcpy(buffer, "released");
+
+               //Send PUBLISH packet
+               error = mqttClientPublish(&mqttClientContext, "board/buttons/1",
+                  buffer, strlen(buffer), MQTT_QOS_LEVEL_1, TRUE, NULL);
+
+               //Save current state
+               prevButtonState = buttonState;
+            }
+
+            //Failed to publish data?
+            if(error)
+            {
+               //Close connection
+               mqttClientClose(&mqttClientContext);
+               //Update connection state
+               connectionState = APP_STATE_NOT_CONNECTED;
+               //Recovery delay
+               osDelayTask(2000);
+            }
+         }
+      }
+   }
+}
+
+
+/**
+ * @brief LED blinking task
+ **/
+
+void blinkTask(void *param)
+{
+   //Endless loop
+   while(1)
+   {
+      BSP_LED_On(LED1);
+      osDelayTask(100);
+      BSP_LED_Off(LED1);
+      osDelayTask(900);
+   }
+}
+
+
+/**
+ * @brief Main entry point
+ * @return Unused value
+ **/
+
+int_t main(void)
+{
+   error_t error;
+   uint_t i;
+   uint32_t value;
+   NetInterface *interface;
+   OsTask *task;
+   MacAddr macAddr;
+#if (APP_USE_DHCP == DISABLED)
+   Ipv4Addr ipv4Addr;
+#endif
+#if (APP_USE_SLAAC == DISABLED)
+   Ipv6Addr ipv6Addr;
+#endif
+
+   //MPU configuration
+   MPU_Config();
+   //HAL library initialization
+   HAL_Init();
+   //Configure the system clock
+   SystemClock_Config();
+
+   //Enable I-cache and D-cache
+   SCB_EnableICache();
+   SCB_EnableDCache();
+
+   //Initialize kernel
+   osInitKernel();
+   //Configure debug UART
+   debugInit(115200);
+
+   //Start-up message
+   TRACE_INFO("\r\n");
+   TRACE_INFO("***********************************\r\n");
+   TRACE_INFO("*** CycloneTCP MQTT Client Demo ***\r\n");
+   TRACE_INFO("***********************************\r\n");
+   TRACE_INFO("Copyright: 2010-2017 Oryx Embedded SARL\r\n");
+   TRACE_INFO("Compiled: %s %s\r\n", __DATE__, __TIME__);
+   TRACE_INFO("Target: STM32F746\r\n");
+   TRACE_INFO("\r\n");
+
+   //LED configuration
+   BSP_LED_Init(LED1);
+   BSP_LED_Init(LED2);
+   BSP_LED_Init(LED3);
+
+   //Clear LEDs
+   BSP_LED_Off(LED1);
+   BSP_LED_Off(LED2);
+   BSP_LED_Off(LED3);
+
+   //Initialize user button
+   BSP_PB_Init(BUTTON_KEY, BUTTON_MODE_GPIO);
+
+   //Enable RNG peripheral clock
+   __HAL_RCC_RNG_CLK_ENABLE();
+   //Initialize RNG
+   RNG_Handle.Instance = RNG;
+   HAL_RNG_Init(&RNG_Handle);
+
+   //Generate a random seed
+   for(i = 0; i < 32; i += 4)
+   {
+      //Get 32-bit random value
+      HAL_RNG_GenerateRandomNumber(&RNG_Handle, &value);
+
+      //Copy random value
+      seed[i] = value & 0xFF;
+      seed[i + 1] = (value >> 8) & 0xFF;
+      seed[i + 2] = (value >> 16) & 0xFF;
+      seed[i + 3] = (value >> 24) & 0xFF;
+   }
+
+   //PRNG initialization
+   error = yarrowInit(&yarrowContext);
+   //Any error to report?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to initialize PRNG!\r\n");
+   }
+
+   //Properly seed the PRNG
+   error = yarrowSeed(&yarrowContext, seed, sizeof(seed));
+   //Any error to report?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to seed PRNG!\r\n");
+   }
+
+   //TCP/IP stack initialization
+   error = netInit();
+   //Any error to report?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to initialize TCP/IP stack!\r\n");
+   }
+
+   //Configure the first Ethernet interface
+   interface = &netInterface[0];
+
+   //Set interface name
+   netSetInterfaceName(interface, "eth0");
+   //Set host name
+   netSetHostname(interface, "MQTTClientDemo");
+   //Select the relevant network adapter
+   netSetDriver(interface, &stm32f7xxEthDriver);
+   netSetPhyDriver(interface, &lan8742PhyDriver);
+   //Set host MAC address
+   macStringToAddr(APP_MAC_ADDR, &macAddr);
+   netSetMacAddr(interface, &macAddr);
+
+   //Initialize network interface
+   error = netConfigInterface(interface);
+   //Any error to report?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to configure interface %s!\r\n", interface->name);
+   }
+
+#if (IPV4_SUPPORT == ENABLED)
+#if (APP_USE_DHCP == ENABLED)
+   //Get default settings
+   dhcpClientGetDefaultSettings(&dhcpClientSettings);
+   //Set the network interface to be configured by DHCP
+   dhcpClientSettings.interface = interface;
+   //Disable rapid commit option
+   dhcpClientSettings.rapidCommit = FALSE;
+
+   //DHCP client initialization
+   error = dhcpClientInit(&dhcpClientContext, &dhcpClientSettings);
+   //Failed to initialize DHCP client?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to initialize DHCP client!\r\n");
+   }
+
+   //Start DHCP client
+   error = dhcpClientStart(&dhcpClientContext);
+   //Failed to start DHCP client?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to start DHCP client!\r\n");
+   }
+#else
+   //Set IPv4 host address
+   ipv4StringToAddr(APP_IPV4_HOST_ADDR, &ipv4Addr);
+   ipv4SetHostAddr(interface, ipv4Addr);
+
+   //Set subnet mask
+   ipv4StringToAddr(APP_IPV4_SUBNET_MASK, &ipv4Addr);
+   ipv4SetSubnetMask(interface, ipv4Addr);
+
+   //Set default gateway
+   ipv4StringToAddr(APP_IPV4_DEFAULT_GATEWAY, &ipv4Addr);
+   ipv4SetDefaultGateway(interface, ipv4Addr);
+
+   //Set primary and secondary DNS servers
+   ipv4StringToAddr(APP_IPV4_PRIMARY_DNS, &ipv4Addr);
+   ipv4SetDnsServer(interface, 0, ipv4Addr);
+   ipv4StringToAddr(APP_IPV4_SECONDARY_DNS, &ipv4Addr);
+   ipv4SetDnsServer(interface, 1, ipv4Addr);
+#endif
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+#if (APP_USE_SLAAC == ENABLED)
+   //Get default settings
+   slaacGetDefaultSettings(&slaacSettings);
+   //Set the network interface to be configured
+   slaacSettings.interface = interface;
+
+   //SLAAC initialization
+   error = slaacInit(&slaacContext, &slaacSettings);
+   //Failed to initialize SLAAC?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to initialize SLAAC!\r\n");
+   }
+
+   //Start IPv6 address autoconfiguration process
+   error = slaacStart(&slaacContext);
+   //Failed to start SLAAC process?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to start SLAAC!\r\n");
+   }
+#else
+   //Set link-local address
+   ipv6StringToAddr(APP_IPV6_LINK_LOCAL_ADDR, &ipv6Addr);
+   ipv6SetLinkLocalAddr(interface, &ipv6Addr);
+
+   //Set IPv6 prefix
+   ipv6StringToAddr(APP_IPV6_PREFIX, &ipv6Addr);
+   ipv6SetPrefix(interface, 0, &ipv6Addr, APP_IPV6_PREFIX_LENGTH);
+
+   //Set global address
+   ipv6StringToAddr(APP_IPV6_GLOBAL_ADDR, &ipv6Addr);
+   ipv6SetGlobalAddr(interface, 0, &ipv6Addr);
+
+   //Set default router
+   ipv6StringToAddr(APP_IPV6_ROUTER, &ipv6Addr);
+   ipv6SetDefaultRouter(interface, 0, &ipv6Addr);
+
+   //Set primary and secondary DNS servers
+   ipv6StringToAddr(APP_IPV6_PRIMARY_DNS, &ipv6Addr);
+   ipv6SetDnsServer(interface, 0, &ipv6Addr);
+   ipv6StringToAddr(APP_IPV6_SECONDARY_DNS, &ipv6Addr);
+   ipv6SetDnsServer(interface, 1, &ipv6Addr);
+#endif
+#endif
+
+   //Create MQTT test task
+   task = osCreateTask("MQTT", mqttTestTask, NULL, 750, OS_TASK_PRIORITY_NORMAL);
+   //Failed to create the task?
+   if(task == OS_INVALID_HANDLE)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to create task!\r\n");
+   }
+
+   //Create a task to blink the LED
+   task = osCreateTask("Blink", blinkTask, NULL, 500, OS_TASK_PRIORITY_NORMAL);
+   //Failed to create the task?
+   if(task == OS_INVALID_HANDLE)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to create task!\r\n");
+   }
+
+   //Start the execution of tasks
+   osStartKernel();
+
+   //This function should never return
+   return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mqtt_client_demo/src/net_config.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,152 @@
+/**
+ * @file net_config.h
+ * @brief CycloneTCP configuration file
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _NET_CONFIG_H
+#define _NET_CONFIG_H
+
+//Trace level for TCP/IP stack debugging
+#define MEM_TRACE_LEVEL          4
+#define NIC_TRACE_LEVEL          4
+#define ETH_TRACE_LEVEL          2
+#define ARP_TRACE_LEVEL          2
+#define IP_TRACE_LEVEL           2
+#define IPV4_TRACE_LEVEL         2
+#define IPV6_TRACE_LEVEL         2
+#define ICMP_TRACE_LEVEL         2
+#define IGMP_TRACE_LEVEL         2
+#define ICMPV6_TRACE_LEVEL       2
+#define MLD_TRACE_LEVEL          2
+#define NDP_TRACE_LEVEL          2
+#define UDP_TRACE_LEVEL          2
+#define TCP_TRACE_LEVEL          2
+#define SOCKET_TRACE_LEVEL       2
+#define RAW_SOCKET_TRACE_LEVEL   2
+#define BSD_SOCKET_TRACE_LEVEL   2
+#define WEB_SOCKET_TRACE_LEVEL   2
+#define SLAAC_TRACE_LEVEL        4
+#define DHCP_TRACE_LEVEL         4
+#define DHCPV6_TRACE_LEVEL       4
+#define DNS_TRACE_LEVEL          4
+#define MDNS_TRACE_LEVEL         4
+#define NBNS_TRACE_LEVEL         2
+#define LLMNR_TRACE_LEVEL        4
+#define FTP_TRACE_LEVEL          5
+#define HTTP_TRACE_LEVEL         4
+#define MQTT_TRACE_LEVEL         4
+#define SMTP_TRACE_LEVEL         5
+#define SNTP_TRACE_LEVEL         4
+#define STD_SERVICES_TRACE_LEVEL 5
+
+//Number of network adapters
+#define NET_INTERFACE_COUNT 1
+
+//Size of the multicast MAC filter
+#define MAC_MULTICAST_FILTER_SIZE 12
+
+//IPv4 support
+#define IPV4_SUPPORT ENABLED
+//Size of the IPv4 multicast filter
+#define IPV4_MULTICAST_FILTER_SIZE 4
+
+//IPv4 fragmentation support
+#define IPV4_FRAG_SUPPORT ENABLED
+//Maximum number of fragmented packets the host will accept
+//and hold in the reassembly queue simultaneously
+#define IPV4_MAX_FRAG_DATAGRAMS 4
+//Maximum datagram size the host will accept when reassembling fragments
+#define IPV4_MAX_FRAG_DATAGRAM_SIZE 8192
+
+//Size of ARP cache
+#define ARP_CACHE_SIZE 8
+//Maximum number of packets waiting for address resolution to complete
+#define ARP_MAX_PENDING_PACKETS 2
+
+//IGMP support
+#define IGMP_SUPPORT ENABLED
+
+//IPv6 support
+#define IPV6_SUPPORT ENABLED
+//Size of the IPv6 multicast filter
+#define IPV6_MULTICAST_FILTER_SIZE 8
+
+//IPv6 fragmentation support
+#define IPV6_FRAG_SUPPORT ENABLED
+//Maximum number of fragmented packets the host will accept
+//and hold in the reassembly queue simultaneously
+#define IPV6_MAX_FRAG_DATAGRAMS 4
+//Maximum datagram size the host will accept when reassembling fragments
+#define IPV6_MAX_FRAG_DATAGRAM_SIZE 8192
+
+//MLD support
+#define MLD_SUPPORT ENABLED
+
+//Neighbor cache size
+#define NDP_NEIGHBOR_CACHE_SIZE 8
+//Destination cache size
+#define NDP_DEST_CACHE_SIZE 8
+//Maximum number of packets waiting for address resolution to complete
+#define NDP_MAX_PENDING_PACKETS 2
+
+//TCP support
+#define TCP_SUPPORT ENABLED
+//Default buffer size for transmission
+#define TCP_DEFAULT_TX_BUFFER_SIZE (1430*2)
+//Default buffer size for reception
+#define TCP_DEFAULT_RX_BUFFER_SIZE (1430*2)
+//Default SYN queue size for listening sockets
+#define TCP_DEFAULT_SYN_QUEUE_SIZE 4
+//Maximum number of retransmissions
+#define TCP_MAX_RETRIES 5
+//Selective acknowledgment support
+#define TCP_SACK_SUPPORT DISABLED
+
+//UDP support
+#define UDP_SUPPORT ENABLED
+//Receive queue depth for connectionless sockets
+#define UDP_RX_QUEUE_SIZE 4
+
+//Raw socket support
+#define RAW_SOCKET_SUPPORT DISABLED
+//Receive queue depth for raw sockets
+#define RAW_SOCKET_RX_QUEUE_SIZE 4
+
+//Number of sockets that can be opened simultaneously
+#define SOCKET_MAX_COUNT 10
+
+//WebSocket support
+#define WEB_SOCKET_SUPPORT ENABLED
+//Support for WebSocket connections over SSL/TLS
+#define WEB_SOCKET_TLS_SUPPORT ENABLED
+
+//MQTT over SSL/TLS
+#define MQTT_CLIENT_TLS_SUPPORT ENABLED
+//MQTT over WebSocket
+#define MQTT_CLIENT_WS_SUPPORT ENABLED
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mqtt_client_demo/src/os_port_config.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,34 @@
+/**
+ * @file os_port_config.h
+ * @brief RTOS port configuration file
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _OS_PORT_CONFIG_H
+#define _OS_PORT_CONFIG_H
+
+//Select underlying RTOS
+#define USE_FREERTOS
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mqtt_client_demo/src/stm32f7xx_hal_conf.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,427 @@
+/**
+  ******************************************************************************
+  * @file    stm32f7xx_hal_conf.h
+  * @author  MCD Application Team
+  * @version V1.0.1
+  * @date    22-April-2016
+  * @brief   HAL configuration file.
+  ******************************************************************************
+  * @attention
+  *
+  * <h2><center>&copy; COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
+  *
+  * Redistribution and use in source and binary forms, with or without modification,
+  * are permitted provided that the following conditions are met:
+  *   1. Redistributions of source code must retain the above copyright notice,
+  *      this list of conditions and the following disclaimer.
+  *   2. Redistributions in binary form must reproduce 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.
+  *
+  ******************************************************************************
+  */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32F7xx_HAL_CONF_H
+#define __STM32F7xx_HAL_CONF_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+/* ########################## Module Selection ############################## */
+/**
+  * @brief This is the list of modules to be used in the HAL driver
+  */
+#define HAL_MODULE_ENABLED
+#define HAL_ADC_MODULE_ENABLED
+#define HAL_CAN_MODULE_ENABLED
+#define HAL_CEC_MODULE_ENABLED
+#define HAL_CRC_MODULE_ENABLED
+#define HAL_CRYP_MODULE_ENABLED
+#define HAL_DAC_MODULE_ENABLED
+#define HAL_DCMI_MODULE_ENABLED
+#define HAL_DMA_MODULE_ENABLED
+#define HAL_DMA2D_MODULE_ENABLED
+#define HAL_ETH_MODULE_ENABLED
+#define HAL_FLASH_MODULE_ENABLED
+#define HAL_NAND_MODULE_ENABLED
+#define HAL_NOR_MODULE_ENABLED
+#define HAL_SRAM_MODULE_ENABLED
+#define HAL_SDRAM_MODULE_ENABLED
+#define HAL_HASH_MODULE_ENABLED
+#define HAL_GPIO_MODULE_ENABLED
+#define HAL_I2C_MODULE_ENABLED
+#define HAL_I2S_MODULE_ENABLED
+#define HAL_IWDG_MODULE_ENABLED
+#define HAL_LPTIM_MODULE_ENABLED
+#define HAL_LTDC_MODULE_ENABLED
+#define HAL_PWR_MODULE_ENABLED
+#define HAL_QSPI_MODULE_ENABLED
+#define HAL_RCC_MODULE_ENABLED
+#define HAL_RNG_MODULE_ENABLED
+#define HAL_RTC_MODULE_ENABLED
+#define HAL_SAI_MODULE_ENABLED
+#define HAL_SD_MODULE_ENABLED
+#define HAL_SPDIFRX_MODULE_ENABLED
+#define HAL_SPI_MODULE_ENABLED
+#define HAL_TIM_MODULE_ENABLED
+#define HAL_UART_MODULE_ENABLED
+#define HAL_USART_MODULE_ENABLED
+#define HAL_IRDA_MODULE_ENABLED
+#define HAL_SMARTCARD_MODULE_ENABLED
+#define HAL_WWDG_MODULE_ENABLED
+#define HAL_CORTEX_MODULE_ENABLED
+#define HAL_PCD_MODULE_ENABLED
+#define HAL_HCD_MODULE_ENABLED
+
+
+/* ########################## HSE/HSI Values adaptation ##################### */
+/**
+  * @brief Adjust the value of External High Speed oscillator (HSE) used in your application.
+  *        This value is used by the RCC HAL module to compute the system frequency
+  *        (when HSE is used as system clock source, directly or through the PLL).
+  */
+#if !defined  (HSE_VALUE)
+  #define HSE_VALUE    ((uint32_t)8000000U) /*!< Value of the External oscillator in Hz */
+#endif /* HSE_VALUE */
+
+#if !defined  (HSE_STARTUP_TIMEOUT)
+  #define HSE_STARTUP_TIMEOUT    100U   /*!< Time out for HSE start up, in ms */
+#endif /* HSE_STARTUP_TIMEOUT */
+
+/**
+  * @brief Internal High Speed oscillator (HSI) value.
+  *        This value is used by the RCC HAL module to compute the system frequency
+  *        (when HSI is used as system clock source, directly or through the PLL).
+  */
+#if !defined  (HSI_VALUE)
+  #define HSI_VALUE    16000000U /*!< Value of the Internal oscillator in Hz*/
+#endif /* HSI_VALUE */
+
+/**
+  * @brief Internal Low Speed oscillator (LSI) value.
+  */
+#if !defined  (LSI_VALUE)
+ #define LSI_VALUE  32000U                  /*!< LSI Typical Value in Hz*/
+#endif /* LSI_VALUE */                      /*!< Value of the Internal Low Speed oscillator in Hz
+                                             The real value may vary depending on the variations
+                                             in voltage and temperature.  */
+/**
+  * @brief External Low Speed oscillator (LSE) value.
+  */
+#if !defined  (LSE_VALUE)
+ #define LSE_VALUE  32768U    /*!< Value of the External Low Speed oscillator in Hz */
+#endif /* LSE_VALUE */
+
+#if !defined  (LSE_STARTUP_TIMEOUT)
+  #define LSE_STARTUP_TIMEOUT    5000U   /*!< Time out for LSE start up, in ms */
+#endif /* LSE_STARTUP_TIMEOUT */
+
+/**
+  * @brief External clock source for I2S peripheral
+  *        This value is used by the I2S HAL module to compute the I2S clock source
+  *        frequency, this source is inserted directly through I2S_CKIN pad.
+  */
+#if !defined  (EXTERNAL_CLOCK_VALUE)
+  #define EXTERNAL_CLOCK_VALUE    12288000U /*!< Value of the Internal oscillator in Hz*/
+#endif /* EXTERNAL_CLOCK_VALUE */
+
+/* Tip: To avoid modifying this file each time you need to use different HSE,
+   ===  you can define the HSE value in your toolchain compiler preprocessor. */
+
+/* ########################### System Configuration ######################### */
+/**
+  * @brief This is the HAL system configuration section
+  */
+#define  VDD_VALUE                    3300U /*!< Value of VDD in mv */
+#define  TICK_INT_PRIORITY            0x0FU /*!< tick interrupt priority */
+#define  USE_RTOS                     0U
+#define  PREFETCH_ENABLE              1U
+#define  ART_ACCLERATOR_ENABLE        1U /* To enable instruction cache and prefetch */
+
+/* ########################## Assert Selection ############################## */
+/**
+  * @brief Uncomment the line below to expanse the "assert_param" macro in the
+  *        HAL drivers code
+  */
+/* #define USE_FULL_ASSERT    1 */
+
+/* ################## Ethernet peripheral configuration for NUCLEO 144 board ##################### */
+
+/* Section 1 : Ethernet peripheral configuration */
+
+/* MAC ADDRESS: MAC_ADDR0:MAC_ADDR1:MAC_ADDR2:MAC_ADDR3:MAC_ADDR4:MAC_ADDR5 */
+#define MAC_ADDR0   2
+#define MAC_ADDR1   0
+#define MAC_ADDR2   0
+#define MAC_ADDR3   0
+#define MAC_ADDR4   0
+#define MAC_ADDR5   0
+
+/* Definition of the Ethernet driver buffers size and count */
+#define ETH_RX_BUF_SIZE                ETH_MAX_PACKET_SIZE /* buffer size for receive               */
+#define ETH_TX_BUF_SIZE                ETH_MAX_PACKET_SIZE /* buffer size for transmit              */
+#define ETH_RXBUFNB                    ((uint32_t)5)       /* 5 Rx buffers of size ETH_RX_BUF_SIZE  */
+#define ETH_TXBUFNB                    ((uint32_t)5)       /* 5 Tx buffers of size ETH_TX_BUF_SIZE  */
+
+/* Section 2: PHY configuration section */
+/* LAN8742A PHY Address*/
+#define LAN8742A_PHY_ADDRESS            0x00
+/* PHY Reset delay these values are based on a 1 ms Systick interrupt*/
+#define PHY_RESET_DELAY                 ((uint32_t)0x00000FFF)
+/* PHY Configuration delay */
+#define PHY_CONFIG_DELAY                ((uint32_t)0x00000FFF)
+
+#define PHY_READ_TO                     ((uint32_t)0x0000FFFF)
+#define PHY_WRITE_TO                    ((uint32_t)0x0000FFFF)
+
+/* Section 3: Common PHY Registers */
+
+#define PHY_BCR                         ((uint16_t)0x00)    /*!< Transceiver Basic Control Register   */
+#define PHY_BSR                         ((uint16_t)0x01)    /*!< Transceiver Basic Status Register    */
+
+#define PHY_RESET                       ((uint16_t)0x8000)  /*!< PHY Reset */
+#define PHY_LOOPBACK                    ((uint16_t)0x4000)  /*!< Select loop-back mode */
+#define PHY_FULLDUPLEX_100M             ((uint16_t)0x2100)  /*!< Set the full-duplex mode at 100 Mb/s */
+#define PHY_HALFDUPLEX_100M             ((uint16_t)0x2000)  /*!< Set the half-duplex mode at 100 Mb/s */
+#define PHY_FULLDUPLEX_10M              ((uint16_t)0x0100)  /*!< Set the full-duplex mode at 10 Mb/s  */
+#define PHY_HALFDUPLEX_10M              ((uint16_t)0x0000)  /*!< Set the half-duplex mode at 10 Mb/s  */
+#define PHY_AUTONEGOTIATION             ((uint16_t)0x1000)  /*!< Enable auto-negotiation function     */
+#define PHY_RESTART_AUTONEGOTIATION     ((uint16_t)0x0200)  /*!< Restart auto-negotiation function    */
+#define PHY_POWERDOWN                   ((uint16_t)0x0800)  /*!< Select the power down mode           */
+#define PHY_ISOLATE                     ((uint16_t)0x0400)  /*!< Isolate PHY from MII                 */
+
+#define PHY_AUTONEGO_COMPLETE           ((uint16_t)0x0020)  /*!< Auto-Negotiation process completed   */
+#define PHY_LINKED_STATUS               ((uint16_t)0x0004)  /*!< Valid link established               */
+#define PHY_JABBER_DETECTION            ((uint16_t)0x0002)  /*!< Jabber condition detected            */
+
+/* Section 4: Extended PHY Registers */
+
+#define PHY_SR                          ((uint16_t)0x1F)    /*!< PHY special control/ status register Offset     */
+
+#define PHY_SPEED_STATUS                ((uint16_t)0x0004)  /*!< PHY Speed mask                                  */
+#define PHY_DUPLEX_STATUS               ((uint16_t)0x0010)  /*!< PHY Duplex mask                                 */
+
+
+#define PHY_ISFR                        ((uint16_t)0x1D)    /*!< PHY Interrupt Source Flag register Offset       */
+#define PHY_ISFR_INT4                   ((uint16_t)0x0010)  /*!< PHY Link down inturrupt                         */
+
+/* ################## SPI peripheral configuration ########################## */
+
+/* CRC FEATURE: Use to activate CRC feature inside HAL SPI Driver
+* Activated: CRC code is present inside driver
+* Deactivated: CRC code cleaned from driver
+*/
+
+#define USE_SPI_CRC                     1U
+
+/* Includes ------------------------------------------------------------------*/
+/**
+  * @brief Include module's header file
+  */
+
+#ifdef HAL_RCC_MODULE_ENABLED
+  #include "stm32f7xx_hal_rcc.h"
+#endif /* HAL_RCC_MODULE_ENABLED */
+
+#ifdef HAL_GPIO_MODULE_ENABLED
+  #include "stm32f7xx_hal_gpio.h"
+#endif /* HAL_GPIO_MODULE_ENABLED */
+
+#ifdef HAL_DMA_MODULE_ENABLED
+  #include "stm32f7xx_hal_dma.h"
+#endif /* HAL_DMA_MODULE_ENABLED */
+
+#ifdef HAL_CORTEX_MODULE_ENABLED
+  #include "stm32f7xx_hal_cortex.h"
+#endif /* HAL_CORTEX_MODULE_ENABLED */
+
+#ifdef HAL_ADC_MODULE_ENABLED
+  #include "stm32f7xx_hal_adc.h"
+#endif /* HAL_ADC_MODULE_ENABLED */
+
+#ifdef HAL_CAN_MODULE_ENABLED
+  #include "stm32f7xx_hal_can.h"
+#endif /* HAL_CAN_MODULE_ENABLED */
+
+#ifdef HAL_CEC_MODULE_ENABLED
+  #include "stm32f7xx_hal_cec.h"
+#endif /* HAL_CEC_MODULE_ENABLED */
+
+#ifdef HAL_CRC_MODULE_ENABLED
+  #include "stm32f7xx_hal_crc.h"
+#endif /* HAL_CRC_MODULE_ENABLED */
+
+#ifdef HAL_CRYP_MODULE_ENABLED
+  #include "stm32f7xx_hal_cryp.h"
+#endif /* HAL_CRYP_MODULE_ENABLED */
+
+#ifdef HAL_DMA2D_MODULE_ENABLED
+  #include "stm32f7xx_hal_dma2d.h"
+#endif /* HAL_DMA2D_MODULE_ENABLED */
+
+#ifdef HAL_DAC_MODULE_ENABLED
+  #include "stm32f7xx_hal_dac.h"
+#endif /* HAL_DAC_MODULE_ENABLED */
+
+#ifdef HAL_DCMI_MODULE_ENABLED
+  #include "stm32f7xx_hal_dcmi.h"
+#endif /* HAL_DCMI_MODULE_ENABLED */
+
+#ifdef HAL_ETH_MODULE_ENABLED
+  #include "stm32f7xx_hal_eth.h"
+#endif /* HAL_ETH_MODULE_ENABLED */
+
+#ifdef HAL_FLASH_MODULE_ENABLED
+  #include "stm32f7xx_hal_flash.h"
+#endif /* HAL_FLASH_MODULE_ENABLED */
+
+#ifdef HAL_SRAM_MODULE_ENABLED
+  #include "stm32f7xx_hal_sram.h"
+#endif /* HAL_SRAM_MODULE_ENABLED */
+
+#ifdef HAL_NOR_MODULE_ENABLED
+  #include "stm32f7xx_hal_nor.h"
+#endif /* HAL_NOR_MODULE_ENABLED */
+
+#ifdef HAL_NAND_MODULE_ENABLED
+  #include "stm32f7xx_hal_nand.h"
+#endif /* HAL_NAND_MODULE_ENABLED */
+
+#ifdef HAL_SDRAM_MODULE_ENABLED
+  #include "stm32f7xx_hal_sdram.h"
+#endif /* HAL_SDRAM_MODULE_ENABLED */
+
+#ifdef HAL_HASH_MODULE_ENABLED
+ #include "stm32f7xx_hal_hash.h"
+#endif /* HAL_HASH_MODULE_ENABLED */
+
+#ifdef HAL_I2C_MODULE_ENABLED
+ #include "stm32f7xx_hal_i2c.h"
+#endif /* HAL_I2C_MODULE_ENABLED */
+
+#ifdef HAL_I2S_MODULE_ENABLED
+ #include "stm32f7xx_hal_i2s.h"
+#endif /* HAL_I2S_MODULE_ENABLED */
+
+#ifdef HAL_IWDG_MODULE_ENABLED
+ #include "stm32f7xx_hal_iwdg.h"
+#endif /* HAL_IWDG_MODULE_ENABLED */
+
+#ifdef HAL_LPTIM_MODULE_ENABLED
+ #include "stm32f7xx_hal_lptim.h"
+#endif /* HAL_LPTIM_MODULE_ENABLED */
+
+#ifdef HAL_LTDC_MODULE_ENABLED
+ #include "stm32f7xx_hal_ltdc.h"
+#endif /* HAL_LTDC_MODULE_ENABLED */
+
+#ifdef HAL_PWR_MODULE_ENABLED
+ #include "stm32f7xx_hal_pwr.h"
+#endif /* HAL_PWR_MODULE_ENABLED */
+
+#ifdef HAL_QSPI_MODULE_ENABLED
+ #include "stm32f7xx_hal_qspi.h"
+#endif /* HAL_QSPI_MODULE_ENABLED */
+
+#ifdef HAL_RNG_MODULE_ENABLED
+ #include "stm32f7xx_hal_rng.h"
+#endif /* HAL_RNG_MODULE_ENABLED */
+
+#ifdef HAL_RTC_MODULE_ENABLED
+ #include "stm32f7xx_hal_rtc.h"
+#endif /* HAL_RTC_MODULE_ENABLED */
+
+#ifdef HAL_SAI_MODULE_ENABLED
+ #include "stm32f7xx_hal_sai.h"
+#endif /* HAL_SAI_MODULE_ENABLED */
+
+#ifdef HAL_SD_MODULE_ENABLED
+ #include "stm32f7xx_hal_sd.h"
+#endif /* HAL_SD_MODULE_ENABLED */
+
+#ifdef HAL_SPDIFRX_MODULE_ENABLED
+ #include "stm32f7xx_hal_spdifrx.h"
+#endif /* HAL_SPDIFRX_MODULE_ENABLED */
+
+#ifdef HAL_SPI_MODULE_ENABLED
+ #include "stm32f7xx_hal_spi.h"
+#endif /* HAL_SPI_MODULE_ENABLED */
+
+#ifdef HAL_TIM_MODULE_ENABLED
+ #include "stm32f7xx_hal_tim.h"
+#endif /* HAL_TIM_MODULE_ENABLED */
+
+#ifdef HAL_UART_MODULE_ENABLED
+ #include "stm32f7xx_hal_uart.h"
+#endif /* HAL_UART_MODULE_ENABLED */
+
+#ifdef HAL_USART_MODULE_ENABLED
+ #include "stm32f7xx_hal_usart.h"
+#endif /* HAL_USART_MODULE_ENABLED */
+
+#ifdef HAL_IRDA_MODULE_ENABLED
+ #include "stm32f7xx_hal_irda.h"
+#endif /* HAL_IRDA_MODULE_ENABLED */
+
+#ifdef HAL_SMARTCARD_MODULE_ENABLED
+ #include "stm32f7xx_hal_smartcard.h"
+#endif /* HAL_SMARTCARD_MODULE_ENABLED */
+
+#ifdef HAL_WWDG_MODULE_ENABLED
+ #include "stm32f7xx_hal_wwdg.h"
+#endif /* HAL_WWDG_MODULE_ENABLED */
+
+#ifdef HAL_PCD_MODULE_ENABLED
+ #include "stm32f7xx_hal_pcd.h"
+#endif /* HAL_PCD_MODULE_ENABLED */
+
+#ifdef HAL_HCD_MODULE_ENABLED
+ #include "stm32f7xx_hal_hcd.h"
+#endif /* HAL_HCD_MODULE_ENABLED */
+
+/* Exported macro ------------------------------------------------------------*/
+#ifdef  USE_FULL_ASSERT
+/**
+  * @brief  The assert_param macro is used for function's parameters check.
+  * @param  expr: If expr is false, it calls assert_failed function
+  *         which reports the name of the source file and the source
+  *         line number of the call that failed.
+  *         If expr is true, it returns no value.
+  * @retval None
+  */
+  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
+/* Exported functions ------------------------------------------------------- */
+  void assert_failed(uint8_t* file, uint32_t line);
+#else
+  #define assert_param(expr) ((void)0)
+#endif /* USE_FULL_ASSERT */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __STM32F7xx_HAL_CONF_H */
+
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mqtt_client_demo/src/stm32f7xx_it.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,195 @@
+/**
+  ******************************************************************************
+  * @file    Templates/stm32f7xx_it.c
+  * @author  MCD Application Team
+  * @version V1.0.3
+  * @date    22-April-2016
+  * @brief   Main Interrupt Service Routines.
+  *          This file provides template for all exceptions handler and
+  *          peripherals interrupt service routine.
+  ******************************************************************************
+  * @attention
+  *
+  * <h2><center>&copy; COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
+  *
+  * Redistribution and use in source and binary forms, with or without modification,
+  * are permitted provided that the following conditions are met:
+  *   1. Redistributions of source code must retain the above copyright notice,
+  *      this list of conditions and the following disclaimer.
+  *   2. Redistributions in binary form must reproduce 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.
+  *
+  ******************************************************************************
+  */
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32f7xx_it.h"
+#include "os_port.h"
+
+/** @addtogroup STM32F7xx_HAL_Examples
+  * @{
+  */
+
+/** @addtogroup Templates
+  * @{
+  */
+
+/* Private typedef -----------------------------------------------------------*/
+/* Private define ------------------------------------------------------------*/
+/* Private macro -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Private function prototypes -----------------------------------------------*/
+extern void HAL_IncTick(void);
+extern void xPortSysTickHandler(void);
+
+/* Private functions ---------------------------------------------------------*/
+
+/******************************************************************************/
+/*            Cortex-M7 Processor Exceptions Handlers                         */
+/******************************************************************************/
+
+/**
+  * @brief   This function handles NMI exception.
+  * @param  None
+  * @retval None
+  */
+void NMI_Handler(void)
+{
+}
+
+/**
+  * @brief  This function handles Hard Fault exception.
+  * @param  None
+  * @retval None
+  */
+void HardFault_Handler(void)
+{
+  /* Go to infinite loop when Hard Fault exception occurs */
+  while (1)
+  {
+  }
+}
+
+/**
+  * @brief  This function handles Memory Manage exception.
+  * @param  None
+  * @retval None
+  */
+void MemManage_Handler(void)
+{
+  /* Go to infinite loop when Memory Manage exception occurs */
+  while (1)
+  {
+  }
+}
+
+/**
+  * @brief  This function handles Bus Fault exception.
+  * @param  None
+  * @retval None
+  */
+void BusFault_Handler(void)
+{
+  /* Go to infinite loop when Bus Fault exception occurs */
+  while (1)
+  {
+  }
+}
+
+/**
+  * @brief  This function handles Usage Fault exception.
+  * @param  None
+  * @retval None
+  */
+void UsageFault_Handler(void)
+{
+  /* Go to infinite loop when Usage Fault exception occurs */
+  while (1)
+  {
+  }
+}
+
+/**
+  * @brief  This function handles SVCall exception.
+  * @param  None
+  * @retval None
+  */
+#if 0
+void SVC_Handler(void)
+{
+}
+#endif
+
+/**
+  * @brief  This function handles Debug Monitor exception.
+  * @param  None
+  * @retval None
+  */
+void DebugMon_Handler(void)
+{
+}
+
+/**
+  * @brief  This function handles PendSVC exception.
+  * @param  None
+  * @retval None
+  */
+#if 0
+void PendSV_Handler(void)
+{
+}
+#endif
+
+/**
+  * @brief  This function handles SysTick Handler.
+  * @param  None
+  * @retval None
+  */
+void SysTick_Handler(void)
+{
+   HAL_IncTick();
+
+   if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
+   {
+      xPortSysTickHandler();
+   }
+}
+
+/******************************************************************************/
+/*                 STM32F7xx Peripherals Interrupt Handlers                   */
+/*  Add here the Interrupt Handler for the used peripheral(s) (PPP), for the  */
+/*  available peripheral interrupt handler's name please refer to the startup */
+/*  file (startup_stm32f7xx.s).                                               */
+/******************************************************************************/
+
+/**
+  * @brief  This function handles PPP interrupt request.
+  * @param  None
+  * @retval None
+  */
+/*void PPP_IRQHandler(void)
+{
+}*/
+
+/**
+  * @}
+  */
+
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mqtt_client_demo/src/stm32f7xx_it.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,69 @@
+/**
+  ******************************************************************************
+  * @file    Templates/stm32f7xx_it.h
+  * @author  MCD Application Team
+  * @version V1.0.3
+  * @date    22-April-2016
+  * @brief   This file contains the headers of the interrupt handlers.
+  ******************************************************************************
+  * @attention
+  *
+  * <h2><center>&copy; COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
+  *
+  * Redistribution and use in source and binary forms, with or without modification,
+  * are permitted provided that the following conditions are met:
+  *   1. Redistributions of source code must retain the above copyright notice,
+  *      this list of conditions and the following disclaimer.
+  *   2. Redistributions in binary form must reproduce 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.
+  *
+  ******************************************************************************
+  */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32F7xx_IT_H
+#define __STM32F7xx_IT_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+/* Exported macro ------------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+void NMI_Handler(void);
+void HardFault_Handler(void);
+void MemManage_Handler(void);
+void BusFault_Handler(void);
+void UsageFault_Handler(void);
+void SVC_Handler(void);
+void DebugMon_Handler(void);
+void PendSV_Handler(void);
+void SysTick_Handler(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __STM32F7xx_IT_H */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mqtt_client_demo/src/system_stm32f7xx.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,281 @@
+/**
+  ******************************************************************************
+  * @file    Templates/system_stm32f7xx.c
+  * @author  MCD Application Team
+  * @version V1.0.1
+  * @date    22-April-2016
+  * @brief   - CMSIS Cortex-M7 Device Peripheral Access Layer System Source File.
+  *          - This file is dedicated only for STM32F746 NUCLEO 144 boards.
+  *
+  *   This file provides two functions and one global variable to be called from
+  *   user application:
+  *      - SystemInit(): This function is called at startup just after reset and
+  *                      before branch to main program. This call is made inside
+  *                      the "startup_stm32f7xx.s" file.
+  *
+  *      - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
+  *                                  by the user application to setup the SysTick
+  *                                  timer or configure other parameters.
+  *
+  *      - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
+  *                                 be called whenever the core clock is changed
+  *                                 during program execution.
+  *
+  *
+  ******************************************************************************
+  * @attention
+  *
+  * <h2><center>&copy; COPYRIGHT 2016 STMicroelectronics</center></h2>
+  *
+  * Redistribution and use in source and binary forms, with or without modification,
+  * are permitted provided that the following conditions are met:
+  *   1. Redistributions of source code must retain the above copyright notice,
+  *      this list of conditions and the following disclaimer.
+  *   2. Redistributions in binary form must reproduce 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.
+  *
+  ******************************************************************************
+  */
+
+/** @addtogroup CMSIS
+  * @{
+  */
+
+/** @addtogroup stm32f7xx_system
+  * @{
+  */
+
+/** @addtogroup STM32F7xx_System_Private_Includes
+  * @{
+  */
+
+#include "stm32f7xx.h"
+
+#if !defined  (HSE_VALUE)
+  #define HSE_VALUE    ((uint32_t)8000000) /*!< Default value of the External oscillator in Hz */
+#endif /* HSE_VALUE */
+
+#if !defined  (HSI_VALUE)
+  #define HSI_VALUE    ((uint32_t)16000000) /*!< Value of the Internal oscillator in Hz*/
+#endif /* HSI_VALUE */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F7xx_System_Private_TypesDefinitions
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F7xx_System_Private_Defines
+  * @{
+  */
+
+/************************* Miscellaneous Configuration ************************/
+/*!< Uncomment the following line if you need to relocate your vector Table in
+     Internal SRAM. */
+/* #define VECT_TAB_SRAM */
+#define VECT_TAB_OFFSET  0x00 /*!< Vector Table base offset field.
+                                   This value must be a multiple of 0x200. */
+/******************************************************************************/
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F7xx_System_Private_Macros
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F7xx_System_Private_Variables
+  * @{
+  */
+
+  /* This variable is updated in three ways:
+      1) by calling CMSIS function SystemCoreClockUpdate()
+      2) by calling HAL API function HAL_RCC_GetHCLKFreq()
+      3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency
+         Note: If you use this function to configure the system clock; then there
+               is no need to call the 2 first functions listed above, since SystemCoreClock
+               variable is updated automatically.
+  */
+  uint32_t SystemCoreClock = 16000000;
+  const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
+  const uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4};
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F7xx_System_Private_FunctionPrototypes
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F7xx_System_Private_Functions
+  * @{
+  */
+
+/**
+  * @brief  Setup the microcontroller system
+  *         Initialize the Embedded Flash Interface, the PLL and update the
+  *         SystemFrequency variable.
+  * @param  None
+  * @retval None
+  */
+void SystemInit(void)
+{
+  /* FPU settings ------------------------------------------------------------*/
+  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
+    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
+  #endif
+  /* Reset the RCC clock configuration to the default reset state ------------*/
+  /* Set HSION bit */
+  RCC->CR |= (uint32_t)0x00000001;
+
+  /* Reset CFGR register */
+  RCC->CFGR = 0x00000000;
+
+  /* Reset HSEON, CSSON and PLLON bits */
+  RCC->CR &= (uint32_t)0xFEF6FFFF;
+
+  /* Reset PLLCFGR register */
+  RCC->PLLCFGR = 0x24003010;
+
+  /* Reset HSEBYP bit */
+  RCC->CR &= (uint32_t)0xFFFBFFFF;
+
+  /* Disable all interrupts */
+  RCC->CIR = 0x00000000;
+
+  /* Configure the Vector Table location add offset address ------------------*/
+#ifdef VECT_TAB_SRAM
+  //SCB->VTOR = SRAM1_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
+#else
+  //SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
+#endif
+}
+
+/**
+   * @brief  Update SystemCoreClock variable according to Clock Register Values.
+  *         The SystemCoreClock variable contains the core clock (HCLK), it can
+  *         be used by the user application to setup the SysTick timer or configure
+  *         other parameters.
+  *
+  * @note   Each time the core clock (HCLK) changes, this function must be called
+  *         to update SystemCoreClock variable value. Otherwise, any configuration
+  *         based on this variable will be incorrect.
+  *
+  * @note   - The system frequency computed by this function is not the real
+  *           frequency in the chip. It is calculated based on the predefined
+  *           constant and the selected clock source:
+  *
+  *           - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
+  *
+  *           - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)
+  *
+  *           - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**)
+  *             or HSI_VALUE(*) multiplied/divided by the PLL factors.
+  *
+  *         (*) HSI_VALUE is a constant defined in stm32f7xx.h file (default value
+  *             16 MHz) but the real value may vary depending on the variations
+  *             in voltage and temperature.
+  *
+  *         (**) HSE_VALUE is a constant defined in stm32f7xx.h file (default value
+  *              25 MHz), user has to ensure that HSE_VALUE is same as the real
+  *              frequency of the crystal used. Otherwise, this function may
+  *              have wrong result.
+  *
+  *         - The result of this function could be not correct when using fractional
+  *           value for HSE crystal.
+  *
+  * @param  None
+  * @retval None
+  */
+void SystemCoreClockUpdate(void)
+{
+  uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2;
+
+  /* Get SYSCLK source -------------------------------------------------------*/
+  tmp = RCC->CFGR & RCC_CFGR_SWS;
+
+  switch (tmp)
+  {
+    case 0x00:  /* HSI used as system clock source */
+      SystemCoreClock = HSI_VALUE;
+      break;
+    case 0x04:  /* HSE used as system clock source */
+      SystemCoreClock = HSE_VALUE;
+      break;
+    case 0x08:  /* PLL used as system clock source */
+
+      /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N
+         SYSCLK = PLL_VCO / PLL_P
+         */
+      pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22;
+      pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;
+
+      if (pllsource != 0)
+      {
+        /* HSE used as PLL clock source */
+        pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
+      }
+      else
+      {
+        /* HSI used as PLL clock source */
+        pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
+      }
+
+      pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2;
+      SystemCoreClock = pllvco/pllp;
+      break;
+    default:
+      SystemCoreClock = HSI_VALUE;
+      break;
+  }
+  /* Compute HCLK frequency --------------------------------------------------*/
+  /* Get HCLK prescaler */
+  tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
+  /* HCLK frequency */
+  SystemCoreClock >>= tmp;
+}
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mqtt_client_demo/src/tls_config.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,161 @@
+/**
+ * @file tls_config.h
+ * @brief CycloneSSL configuration file
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneSSL Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _TLS_CONFIG_H
+#define _TLS_CONFIG_H
+
+//Desired trace level (for debugging purposes)
+#define TLS_TRACE_LEVEL TRACE_LEVEL_INFO
+
+//Enable SSL/TLS support
+#define TLS_SUPPORT ENABLED
+//Client mode of operation
+#define TLS_CLIENT_SUPPORT ENABLED
+//Server mode of operation
+#define TLS_SERVER_SUPPORT DISABLED
+
+//Minimum version that can be negotiated
+#define TLS_MIN_VERSION SSL_VERSION_3_0
+//Maximum version that can be negotiated
+#define TLS_MAX_VERSION TLS_VERSION_1_2
+
+//Session resumption mechanism
+#define TLS_SESSION_RESUME_SUPPORT ENABLED
+//Lifetime of session cache entries
+#define TLS_SESSION_CACHE_LIFETIME 3600000
+
+//SNI (Server Name Indication) extension
+#define TLS_SNI_SUPPORT ENABLED
+//ALPN (Application-Layer Protocol Negotiation) extension
+#define TLS_ALPN_SUPPORT DISABLED
+
+//Maximum number of certificates the end entity can load
+#define TLS_MAX_CERTIFICATES 3
+
+//RSA key exchange support
+#define TLS_RSA_SUPPORT ENABLED
+//DHE_RSA key exchange support
+#define TLS_DHE_RSA_SUPPORT DISABLED
+//DHE_DSS key exchange support
+#define TLS_DHE_DSS_SUPPORT DISABLED
+//DH_anon key exchange support
+#define TLS_DH_ANON_SUPPORT DISABLED
+//ECDHE_RSA key exchange support
+#define TLS_ECDHE_RSA_SUPPORT ENABLED
+//ECDHE_ECDSA key exchange support
+#define TLS_ECDHE_ECDSA_SUPPORT DISABLED
+//ECDH_anon key exchange support
+#define TLS_ECDH_ANON_SUPPORT DISABLED
+//PSK key exchange support
+#define TLS_PSK_SUPPORT DISABLED
+//RSA_PSK key exchange support
+#define TLS_RSA_PSK_SUPPORT DISABLED
+//DHE_PSK key exchange support
+#define TLS_DHE_PSK_SUPPORT DISABLED
+//ECDHE_PSK key exchange support
+#define TLS_ECDHE_PSK_SUPPORT DISABLED
+
+//RSA signature capability
+#define TLS_RSA_SIGN_SUPPORT ENABLED
+//DSA signature capability
+#define TLS_DSA_SIGN_SUPPORT ENABLED
+//ECDSA signature capability
+#define TLS_ECDSA_SIGN_SUPPORT ENABLED
+
+//Stream cipher support
+#define TLS_STREAM_CIPHER_SUPPORT ENABLED
+//CBC block cipher support
+#define TLS_CBC_CIPHER_SUPPORT ENABLED
+//CCM AEAD support
+#define TLS_CCM_CIPHER_SUPPORT ENABLED
+//GCM AEAD support
+#define TLS_GCM_CIPHER_SUPPORT ENABLED
+//ChaCha20Poly1305 AEAD support
+#define TLS_CHACHA20_POLY1305_SUPPORT ENABLED
+
+//RC4 cipher support
+#define TLS_RC4_SUPPORT ENABLED
+//IDEA cipher support
+#define TLS_IDEA_SUPPORT DISABLED
+//DES cipher support
+#define TLS_DES_SUPPORT DISABLED
+//Triple DES cipher support
+#define TLS_3DES_SUPPORT ENABLED
+//AES cipher support
+#define TLS_AES_SUPPORT ENABLED
+//Camellia cipher support
+#define TLS_CAMELLIA_SUPPORT ENABLED
+//SEED cipher support
+#define TLS_SEED_SUPPORT ENABLED
+//ARIA cipher support
+#define TLS_ARIA_SUPPORT ENABLED
+
+//MD5 hash support
+#define TLS_MD5_SUPPORT ENABLED
+//SHA-1 hash support
+#define TLS_SHA1_SUPPORT ENABLED
+//SHA-224 hash support
+#define TLS_SHA224_SUPPORT ENABLED
+//SHA-256 hash support
+#define TLS_SHA256_SUPPORT ENABLED
+//SHA-384 hash support
+#define TLS_SHA384_SUPPORT ENABLED
+//SHA-512 hash support
+#define TLS_SHA512_SUPPORT ENABLED
+
+//secp160k1 elliptic curve support
+#define TLS_SECP160K1_SUPPORT DISABLED
+//secp160r1 elliptic curve support
+#define TLS_SECP160R1_SUPPORT DISABLED
+//secp160r2 elliptic curve support
+#define TLS_SECP160R2_SUPPORT DISABLED
+//secp192k1 elliptic curve support
+#define TLS_SECP192K1_SUPPORT DISABLED
+//secp192r1 elliptic curve support
+#define TLS_SECP192R1_SUPPORT ENABLED
+//secp224k1 elliptic curve support
+#define TLS_SECP224K1_SUPPORT DISABLED
+//secp224r1 elliptic curve support
+#define TLS_SECP224R1_SUPPORT ENABLED
+//secp256k1 elliptic curve support
+#define TLS_SECP256K1_SUPPORT DISABLED
+//secp256r1 elliptic curve support
+#define TLS_SECP256R1_SUPPORT ENABLED
+//secp384r1 elliptic curve support
+#define TLS_SECP384R1_SUPPORT ENABLED
+//secp521r1 elliptic curve support
+#define TLS_SECP521R1_SUPPORT DISABLED
+//brainpoolP256r1 elliptic curve support
+#define TLS_BRAINPOOLP256R1_SUPPORT DISABLED
+//brainpoolP384r1 elliptic curve support
+#define TLS_BRAINPOOLP384R1_SUPPORT DISABLED
+//brainpoolP512r1 elliptic curve support
+#define TLS_BRAINPOOLP512R1_SUPPORT DISABLED
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web_server_demo/resources/www/css/demo.css	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,181 @@
+body {
+	padding: 10px;
+	font-family: Verdana, Arial, sans-serif;
+	font-size: 14px;
+}
+
+h2 {
+	font-size: 14px;
+	font-weight: bold;
+	text-align: left;
+	margin: 5px 20px 5px 20px;
+}
+
+p {
+	text-align: left;
+	padding: 10px 30px;
+}
+
+ul {
+	text-align: left;
+	padding: 5px 60px;
+}
+
+li {
+	list-style-type: disc;
+}
+
+span {
+	display: inline-block;
+}
+
+form {
+	text-align: left;
+	padding: 0px 30px;
+}
+
+#slider {
+	width: 800px;
+	height: 600px;
+}
+
+.anythingSlider-default.activeSlider .anythingWindow {
+	border-color: #FFFFFF;
+}
+
+#center {
+	width: 800px;
+	/*height: 600px;*/
+	margin-left: auto;
+	margin-right: auto;
+}
+
+div.header {
+	height: 90px;
+	line-height: 90px;
+	width: 100%;
+	background: #000000 url(../images/cyclone_tcp_white.png) no-repeat 30px 5px;
+	text-align: right;
+	border-radius: 30px 30px 0px 0px;
+	-moz-border-radius: 30px 30px 0px 0px;
+	-webkit-border-radius: 30px 30px 0px 0px;
+}
+
+div.headerText {
+	margin-right: 30px;
+	font-size: 24px;
+	font-weight: bold;
+	color: white;
+}
+
+div.wrapper {
+	min-height: 100%;
+	height: auto !important;
+	height: 100%;
+	/*margin: 0 auto -160px*/;
+	background-color: #FAFAFA;
+	text-align: center;
+	/*padding: 10px;*/
+}
+
+div.footer {
+	height: 60px;
+	line-height: 60px;
+	width: 100%;
+	background: #000000 url(../images/oryx_embedded_white.png) no-repeat 30px 5px;
+	text-align: right;
+	border-radius: 0px 0px 30px 30px;
+	-moz-border-radius: 0px 0px 30px 30px;
+	-webkit-border-radius: 0px 0px 30px 30px;
+}
+
+div.footerText {
+	margin-right: 30px;
+	font-size: 14px;
+	color: white;
+}
+
+table.dataTable {
+	background: none;
+	border: 0px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+.dataTable th {
+	border: 0px;
+	padding: 5px;
+	background-color: #000000;
+	color: #FFFFFF;
+	font-weight: normal;
+}
+
+.dataTable td {
+	border: 0px;
+	padding: 3px;
+	background-color: #FFFFFF;
+	text-align: left;
+}
+
+#mailForm span {
+	width: 100px;
+	text-align: left;
+}
+
+#mailForm  textarea {
+	margin: 2px 0px;
+	padding: 1px 2px;
+	border: 1px solid #CCCCCC;
+	resize: none;
+	-webkit-border-radius: 5px;
+	-moz-border-radius: 5px;
+	border-radius: 5px;
+}
+
+#mailForm  input[type=text], #mailForm  input[type=password] {
+	margin: 2px 0px;
+	padding: 1px 2px;
+	border: 1px solid #CCCCCC;
+	-webkit-border-radius: 5px;
+	-moz-border-radius: 5px;
+	border-radius: 5px;
+}
+
+#mailForm  input:hover, #mailForm  textarea:hover {
+	border: 1px solid #FCB400;
+}
+
+.color1 {
+	color: #D62408;
+}
+
+.color2 {
+	color: #21AA29;
+}
+
+.color3 {
+	color: #2159D6;
+}
+
+.color4 {
+	/*color: #FFCF00;*/
+	color: #FCB400;
+}
+
+div.bkColor1, th.bkColor1 {
+	background-color: #D62408;
+}
+
+div.bkColor2, th.bkColor2 {
+	background-color: #21AA29;
+}
+
+div.bkColor3, th.bkColor3 {
+	background-color: #2159D6;
+}
+
+div.bkColor4, th.bkColor4 {
+	/*background-color: #FFCF00;*/
+	background-color: #FCB400;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web_server_demo/resources/www/css/reset.css	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,48 @@
+/* http://meyerweb.com/eric/tools/css/reset/ 
+   v2.0 | 20110126
+   License: none (public domain)
+*/
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend, input, textarea,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed, 
+figure, figcaption, footer, header, hgroup, 
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+	margin: 0;
+	padding: 0;
+	border: 0;
+	font-size: 100%;
+	font: inherit;
+	vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure, 
+footer, header, hgroup, menu, nav, section {
+	display: block;
+}
+body {
+	line-height: 1;
+}
+ol, ul {
+	list-style: none;
+}
+blockquote, q {
+	quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+	content: '';
+	content: none;
+}
+table {
+	border-collapse: collapse;
+	border-spacing: 0;
+}
Binary file web_server_demo/resources/www/favicon.ico has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web_server_demo/resources/www/index.html	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,51 @@
+Web Server Demo
+The Web server supports SSI (Server-Sides Includes) and CGI scripting for dynamic contents. The following properties are dynamically generated each time the page is refreshed (press F5). If your system supports IPv6, try to access the server using its IPv6 link-local address or global address and discover your own IPv6 host address!
+
+System Information
+Board
+
+MAC Address
+
+System Time
+
+
+IPv4 Configuration
+IPv4 Address
+
+Subnet Mask
+
+Default Gateway
+
+Primary DNS
+
+Secondary DNS
+
+HTTP Connection
+Remote Address
+
+Remote Port
+
+Server Address
+
+Server Port
+
+Document URI
+
+Query String
+
+
+IPv6 Configuration
+Link-Local Addr
+
+Global Address
+
+Prefix
+
+Router
+
+Primary DNS
+
+Secondary DNS
+
+This page has been accessed .
+Copyright 2010-2017 Oryx Embedded - www.oryx-embedded.com
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web_server_demo/resources/www/index.shtm	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<meta http-equiv="Content-Type" content="text/html">
+	<meta charset="utf-8">
+	
+	<title>CycloneTCP Web Server Demo</title>
+	
+	<link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
+	
+	<!--
+	<link rel="stylesheet" href="css/reset.css">
+	<link rel="stylesheet" href="css/demo.css">
+	-->
+	
+	<style>
+		<!--#include file="css/reset.css" -->
+		<!--#include file="css/demo.css" -->
+	</style>
+</head>
+<body>
+	<div id="center">
+		<div class="header bkColor3"><div class="headerText">Web Server Demo</div></div>
+		<div class="wrapper">
+			<p>The Web server supports SSI (Server-Sides Includes) and CGI scripting for dynamic contents.
+			The following properties are dynamically generated each time the page is refreshed (press F5).
+			If your system supports IPv6, try to access the server using its IPv6 link-local address or
+			global address and discover your own IPv6 host address!</p>
+			<br>
+			
+			<table>
+				<tr><td width="320px">
+				<table class="dataTable" style="width: 300px; margin-left: 10px;">
+					<tr><th class="bkColor3" colspan=2><center>System Information</center></td></tr>
+					<tr><td width=130px>Board</td><td width=170px><!--#exec cgi="BOARD_NAME" --></td></tr>
+					<tr><td>MAC Address</td><td><!--#exec cgi="MAC_ADDR" --></td></tr>
+					<tr><td>System Time</td><td><!--#exec cgi="SYSTEM_TIME" --></td></tr>
+				</table>
+				<br>
+				<table class="dataTable" style="width: 300px; margin-left: 10px;">
+					<tr><th class="bkColor1" colspan=2><center>IPv4 Configuration</center></td></tr>
+					<tr><td width=130px>IPv4 Address</td><td width=170px><!--#exec cgi="IPV4_ADDR" --></td></tr>
+					<tr><td>Subnet Mask</td><td><!--#exec cgi="SUBNET_MASK" --></td></tr>
+					<tr><td>Default Gateway</td><td><!--#exec cgi="DEFAULT_GATEWAY" --></td></tr>
+					<tr><td>Primary DNS</td><td><!--#exec cgi="IPV4_PRIMARY_DNS" --></td></tr>
+					<tr><td>Secondary DNS</td><td><!--#exec cgi="IPV4_SECONDARY_DNS" --></td></tr>
+				</table>
+			</td><td width="480px">
+				<table class="dataTable" style="width: 470px; margin-right: 10px;">
+					<tr><th class="bkColor2" colspan=2><center>HTTP Connection</center></td></tr>
+					<tr><td width=135px>Remote Address</td><td width=345px><!--#echo var="REMOTE_ADDR" --></td></tr>
+					<tr><td>Remote Port</td><td><!--#echo var="REMOTE_PORT" --></td></tr>
+					<tr><td>Server Address</td><td><!--#echo var="SERVER_ADDR" --></td></tr>
+					<tr><td>Server Port</td><td><!--#echo var="SERVER_PORT" --></td></tr>
+					<tr><td>Document URI</td><td><!--#echo var="DOCUMENT_URI" --></td></tr>
+					<tr><td>Query String</td><td><!--#echo var="QUERY_STRING" --></td></tr>
+				</table>
+				<br>
+				<table class="dataTable" style="width: 470px; margin-right: 10px;">
+					<tr><th class="bkColor4" colspan=2><center>IPv6 Configuration</center></td></tr>
+					<tr><td width=135px>Link-Local Addr</td><td width=345px><!--#exec cgi="LINK_LOCAL_ADDR" --></td></tr>
+					<tr><td>Global Address</td><td><!--#exec cgi="GLOBAL_ADDR" --></td></tr>
+					<tr><td>Prefix</td><td><!--#exec cgi="IPV6_PREFIX" --></td></tr>
+					<tr><td>Router</td><td><!--#exec cgi="ROUTER" --></td></tr>
+					<tr><td>Primary DNS</td><td><!--#exec cgi="IPV6_PRIMARY_DNS" --></td></tr>
+					<tr><td>Secondary DNS</td><td><!--#exec cgi="IPV6_SECONDARY_DNS" --></td></tr>
+				</table>
+			</td>
+			</table>
+			
+			<p>This page has been accessed <span class="color3"><!--#exec cgi="PAGE_COUNTER" --></span>.</p>
+		</div>
+		<div class="footer bkColor3"><div class="footerText">Copyright 2010-2017 Oryx Embedded - www.oryx-embedded.com</div></div>
+	</div>
+</body>
+</html>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web_server_demo/src/FreeRTOSConfig.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,132 @@
+/*
+    FreeRTOS V6.0.1 - Copyright (C) 2009 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 FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+//Keil MDK-ARM, IAR EWARM or GCC compiler?
+#if (defined(__CC_ARM) || defined(__ICCARM__) || defined(__GNUC__))
+
+#include <stdint.h>
+extern uint32_t SystemCoreClock;
+
+#endif
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+#define configUSE_PREEMPTION		1
+#define configUSE_IDLE_HOOK			0
+#define configUSE_TICK_HOOK			0
+#define configCPU_CLOCK_HZ			( SystemCoreClock )
+#define configTICK_RATE_HZ			( ( portTickType ) 1000 )
+#define configMAX_PRIORITIES		( 5 )
+#define configMINIMAL_STACK_SIZE	( ( unsigned short ) 400 )
+#define configMAX_TASK_NAME_LEN		( 16 )
+#define configUSE_16_BIT_TICKS		0
+#define configIDLE_SHOULD_YIELD		1
+#define configUSE_APPLICATION_TASK_TAG      1
+#define configUSE_MUTEXES               1
+#define configUSE_COUNTING_SEMAPHORES 1
+
+#define configCHECK_FOR_STACK_OVERFLOW  0
+#define configGENERATE_RUN_TIME_STATS               0
+//#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS      init_us_timer
+//#define portGET_RUN_TIME_COUNTER_VALUE              get_us_time
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES 		0
+#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
+
+/* Set the following definitions to 1 to include the API function, or zero
+to exclude the API function. */
+
+#define INCLUDE_vTaskPrioritySet		1
+#define INCLUDE_uxTaskPriorityGet		1
+#define INCLUDE_vTaskDelete				1
+#define INCLUDE_vTaskCleanUpResources	1
+#define INCLUDE_vTaskSuspend			1
+#define INCLUDE_vTaskDelayUntil			1
+#define INCLUDE_vTaskDelay				1
+#define INCLUDE_uxTaskGetStackHighWaterMark     1
+#define INCLUDE_xTaskGetIdleTaskHandle  1
+#define INCLUDE_xTaskGetSchedulerState 1
+
+/* This is the raw value as per the Cortex-M3 NVIC.  Values can be 255
+(lowest) to 0 (1?) (highest). */
+#define configKERNEL_INTERRUPT_PRIORITY 		255
+#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	191 /* equivalent to 0xb0, or priority 11. */
+
+/* This is the value being used as per the ST library which permits 16
+priority values, 0 to 15.  This must correspond to the
+configKERNEL_INTERRUPT_PRIORITY setting.  Here 15 corresponds to the lowest
+NVIC value of 255. */
+#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY	15
+
+/* Redefine functions names to match the standard peripheral library */
+//#define xPortSysTickHandler     SysTick_Handler
+#define xPortPendSVHandler      PendSV_Handler
+#define vPortSVCHandler         SVC_Handler
+
+#endif /* FREERTOS_CONFIG_H */
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web_server_demo/src/debug.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,134 @@
+/**
+ * @file debug.c
+ * @brief Debugging facilities
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Dependencies
+#include "stm32f7xx.h"
+#include "stm32f7xx_hal.h"
+#include "debug.h"
+
+//Variable declaration
+static UART_HandleTypeDef UART_Handle;
+
+
+/**
+ * @brief Debug UART initialization
+ * @param[in] baudrate UART baudrate
+ **/
+
+void debugInit(uint32_t baudrate)
+{
+   GPIO_InitTypeDef GPIO_InitStructure;
+
+   //Enable GPIOD clock
+   __HAL_RCC_GPIOD_CLK_ENABLE();
+   //Enable USART3 clock
+   __HAL_RCC_USART3_CLK_ENABLE();
+
+   //Configure USART3_TX (PD8)
+   GPIO_InitStructure.Pin = GPIO_PIN_8;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_PULLUP;
+   GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
+   GPIO_InitStructure.Alternate = GPIO_AF7_USART3;
+   HAL_GPIO_Init(GPIOD, &GPIO_InitStructure);
+
+   //Configure USART3_RX (PD9)
+   GPIO_InitStructure.Pin = GPIO_PIN_9;
+   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+   GPIO_InitStructure.Pull = GPIO_PULLUP;
+   GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
+   GPIO_InitStructure.Alternate = GPIO_AF7_USART3;
+   HAL_GPIO_Init(GPIOD, &GPIO_InitStructure);
+
+   //Configure USART3
+   UART_Handle.Instance = USART3;
+   UART_Handle.Init.BaudRate = baudrate;
+   UART_Handle.Init.WordLength = UART_WORDLENGTH_8B;
+   UART_Handle.Init.StopBits = UART_STOPBITS_1;
+   UART_Handle.Init.Parity = UART_PARITY_NONE;
+   UART_Handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
+   UART_Handle.Init.Mode = UART_MODE_TX_RX;
+   HAL_UART_Init(&UART_Handle);
+}
+
+
+/**
+ * @brief Display the contents of an array
+ * @param[in] stream Pointer to a FILE object that identifies an output stream
+ * @param[in] prepend String to prepend to the left of each line
+ * @param[in] data Pointer to the data array
+ * @param[in] length Number of bytes to display
+ **/
+
+void debugDisplayArray(FILE *stream,
+   const char_t *prepend, const void *data, size_t length)
+{
+   uint_t i;
+
+   for(i = 0; i < length; i++)
+   {
+      //Beginning of a new line?
+      if((i % 16) == 0)
+         fprintf(stream, "%s", prepend);
+      //Display current data byte
+      fprintf(stream, "%02" PRIX8 " ", *((uint8_t *) data + i));
+      //End of current line?
+      if((i % 16) == 15 || i == (length - 1))
+         fprintf(stream, "\r\n");
+   }
+}
+
+
+/**
+ * @brief Write character to stream
+ * @param[in] c The character to be written
+ * @param[in] stream Pointer to a FILE object that identifies an output stream
+ * @return On success, the character written is returned. If a writing
+ *   error occurs, EOF is returned
+ **/
+
+int_t fputc(int_t c, FILE *stream)
+{
+   //Standard output or error output?
+   if(stream == stdout || stream == stderr)
+   {
+      //Character to be written
+      uint8_t ch = c;
+
+      //Transmit data
+      HAL_UART_Transmit(&UART_Handle, &ch, 1, HAL_MAX_DELAY);
+
+      //On success, the character written is returned
+      return c;
+   }
+   //Unknown output?
+   else
+   {
+      //If a writing error occurs, EOF is returned
+      return EOF;
+   }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web_server_demo/src/main.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,549 @@
+/**
+ * @file main.c
+ * @brief Main routine
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+//Dependencies
+#include <stdlib.h>
+#include "stm32f7xx.h"
+#include "stm32f7xx_hal.h"
+#include "stm32f7xx_nucleo_144.h"
+#include "os_port.h"
+#include "core/net.h"
+#include "drivers/stm32f7xx_eth.h"
+#include "drivers/lan8742.h"
+#include "dhcp/dhcp_client.h"
+#include "ipv6/slaac.h"
+#include "http/http_server.h"
+#include "http/mime.h"
+#include "path.h"
+#include "date_time.h"
+#include "resource_manager.h"
+#include "debug.h"
+
+//Application configuration
+#define APP_MAC_ADDR "00-AB-CD-EF-07-46"
+
+#define APP_USE_DHCP ENABLED
+#define APP_IPV4_HOST_ADDR "192.168.0.20"
+#define APP_IPV4_SUBNET_MASK "255.255.255.0"
+#define APP_IPV4_DEFAULT_GATEWAY "192.168.0.254"
+#define APP_IPV4_PRIMARY_DNS "8.8.8.8"
+#define APP_IPV4_SECONDARY_DNS "8.8.4.4"
+
+#define APP_USE_SLAAC ENABLED
+#define APP_IPV6_LINK_LOCAL_ADDR "fe80::746"
+#define APP_IPV6_PREFIX "2001:db8::"
+#define APP_IPV6_PREFIX_LENGTH 64
+#define APP_IPV6_GLOBAL_ADDR "2001:db8::746"
+#define APP_IPV6_ROUTER "fe80::1"
+#define APP_IPV6_PRIMARY_DNS "2001:4860:4860::8888"
+#define APP_IPV6_SECONDARY_DNS "2001:4860:4860::8844"
+
+#define APP_HTTP_MAX_CONNECTIONS 8
+
+//Global variables
+DhcpClientSettings dhcpClientSettings;
+DhcpClientContext dhcpClientContext;
+SlaacSettings slaacSettings;
+SlaacContext slaacContext;
+HttpServerSettings httpServerSettings;
+HttpServerContext httpServerContext;
+HttpConnection httpConnections[APP_HTTP_MAX_CONNECTIONS];
+
+//Forward declaration of functions
+error_t httpServerCgiCallback(HttpConnection *connection,
+   const char_t *param);
+
+error_t httpServerUriNotFoundCallback(HttpConnection *connection,
+   const char_t *uri);
+
+
+/**
+ * @brief System clock configuration
+ **/
+
+void SystemClock_Config(void)
+{
+   RCC_OscInitTypeDef RCC_OscInitStruct;
+   RCC_ClkInitTypeDef RCC_ClkInitStruct;
+
+   //Enable Power Control clock
+   __HAL_RCC_PWR_CLK_ENABLE();
+
+   //Enable HSE Oscillator and activate PLL with HSE as source
+   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
+   RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;
+   RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
+   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
+   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
+   RCC_OscInitStruct.PLL.PLLM = 8;
+   RCC_OscInitStruct.PLL.PLLN = 432;
+   RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
+   RCC_OscInitStruct.PLL.PLLQ = 9;
+   HAL_RCC_OscConfig(&RCC_OscInitStruct);
+
+   //Enable overdrive mode
+   HAL_PWREx_EnableOverDrive();
+
+   //Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
+   //clocks dividers
+   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
+   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
+   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
+   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
+   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
+   HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7);
+}
+
+
+/**
+ * @brief MPU configuration
+ **/
+
+void MPU_Config(void)
+{
+   MPU_Region_InitTypeDef MPU_InitStruct;
+
+   //Disable MPU
+   HAL_MPU_Disable();
+
+   //SRAM
+   MPU_InitStruct.Enable = MPU_REGION_ENABLE;
+   MPU_InitStruct.Number = MPU_REGION_NUMBER0;
+   MPU_InitStruct.BaseAddress = 0x20000000;
+   MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
+   MPU_InitStruct.SubRegionDisable = 0;
+   MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
+   MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
+   MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
+   MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
+   MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
+   MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
+   HAL_MPU_ConfigRegion(&MPU_InitStruct);
+
+   //SRAM2
+   MPU_InitStruct.Enable = MPU_REGION_ENABLE;
+   MPU_InitStruct.Number = MPU_REGION_NUMBER1;
+   MPU_InitStruct.BaseAddress = 0x2004C000;
+   MPU_InitStruct.Size = MPU_REGION_SIZE_16KB;
+   MPU_InitStruct.SubRegionDisable = 0;
+   MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
+   MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
+   MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
+   MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
+   MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
+   MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
+   HAL_MPU_ConfigRegion(&MPU_InitStruct);
+
+   //Enable MPU
+   HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
+}
+
+
+/**
+ * @brief LED blinking task
+ **/
+
+void blinkTask(void *param)
+{
+   //Endless loop
+   while(1)
+   {
+      BSP_LED_On(LED1);
+      osDelayTask(100);
+      BSP_LED_Off(LED1);
+      osDelayTask(900);
+   }
+}
+
+
+/**
+ * @brief Main entry point
+ * @return Unused value
+ **/
+
+int_t main(void)
+{
+   error_t error;
+   NetInterface *interface;
+   OsTask *task;
+   MacAddr macAddr;
+#if (APP_USE_DHCP == DISABLED)
+   Ipv4Addr ipv4Addr;
+#endif
+#if (APP_USE_SLAAC == DISABLED)
+   Ipv6Addr ipv6Addr;
+#endif
+
+   //MPU configuration
+   MPU_Config();
+   //HAL library initialization
+   HAL_Init();
+   //Configure the system clock
+   SystemClock_Config();
+
+   //Enable I-cache and D-cache
+   SCB_EnableICache();
+   SCB_EnableDCache();
+
+   //Initialize kernel
+   osInitKernel();
+   //Configure debug UART
+   debugInit(115200);
+
+   //Start-up message
+   TRACE_INFO("\r\n");
+   TRACE_INFO("**********************************\r\n");
+   TRACE_INFO("*** CycloneTCP Web Server Demo ***\r\n");
+   TRACE_INFO("**********************************\r\n");
+   TRACE_INFO("Copyright: 2010-2017 Oryx Embedded SARL\r\n");
+   TRACE_INFO("Compiled: %s %s\r\n", __DATE__, __TIME__);
+   TRACE_INFO("Target: STM32F746\r\n");
+   TRACE_INFO("\r\n");
+
+   //LED configuration
+   BSP_LED_Init(LED1);
+   BSP_LED_Init(LED2);
+   BSP_LED_Init(LED3);
+
+   //Clear LEDs
+   BSP_LED_Off(LED1);
+   BSP_LED_Off(LED2);
+   BSP_LED_Off(LED3);
+
+   //Initialize user button
+   BSP_PB_Init(BUTTON_KEY, BUTTON_MODE_GPIO);
+
+   //TCP/IP stack initialization
+   error = netInit();
+   //Any error to report?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to initialize TCP/IP stack!\r\n");
+   }
+
+   //Configure the first Ethernet interface
+   interface = &netInterface[0];
+
+   //Set interface name
+   netSetInterfaceName(interface, "eth0");
+   //Set host name
+   netSetHostname(interface, "WebServerDemo");
+   //Select the relevant network adapter
+   netSetDriver(interface, &stm32f7xxEthDriver);
+   netSetPhyDriver(interface, &lan8742PhyDriver);
+   //Set host MAC address
+   macStringToAddr(APP_MAC_ADDR, &macAddr);
+   netSetMacAddr(interface, &macAddr);
+
+   //Initialize network interface
+   error = netConfigInterface(interface);
+   //Any error to report?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to configure interface %s!\r\n", interface->name);
+   }
+
+#if (IPV4_SUPPORT == ENABLED)
+#if (APP_USE_DHCP == ENABLED)
+   //Get default settings
+   dhcpClientGetDefaultSettings(&dhcpClientSettings);
+   //Set the network interface to be configured by DHCP
+   dhcpClientSettings.interface = interface;
+   //Disable rapid commit option
+   dhcpClientSettings.rapidCommit = FALSE;
+
+   //DHCP client initialization
+   error = dhcpClientInit(&dhcpClientContext, &dhcpClientSettings);
+   //Failed to initialize DHCP client?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to initialize DHCP client!\r\n");
+   }
+
+   //Start DHCP client
+   error = dhcpClientStart(&dhcpClientContext);
+   //Failed to start DHCP client?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to start DHCP client!\r\n");
+   }
+#else
+   //Set IPv4 host address
+   ipv4StringToAddr(APP_IPV4_HOST_ADDR, &ipv4Addr);
+   ipv4SetHostAddr(interface, ipv4Addr);
+
+   //Set subnet mask
+   ipv4StringToAddr(APP_IPV4_SUBNET_MASK, &ipv4Addr);
+   ipv4SetSubnetMask(interface, ipv4Addr);
+
+   //Set default gateway
+   ipv4StringToAddr(APP_IPV4_DEFAULT_GATEWAY, &ipv4Addr);
+   ipv4SetDefaultGateway(interface, ipv4Addr);
+
+   //Set primary and secondary DNS servers
+   ipv4StringToAddr(APP_IPV4_PRIMARY_DNS, &ipv4Addr);
+   ipv4SetDnsServer(interface, 0, ipv4Addr);
+   ipv4StringToAddr(APP_IPV4_SECONDARY_DNS, &ipv4Addr);
+   ipv4SetDnsServer(interface, 1, ipv4Addr);
+#endif
+#endif
+
+#if (IPV6_SUPPORT == ENABLED)
+#if (APP_USE_SLAAC == ENABLED)
+   //Get default settings
+   slaacGetDefaultSettings(&slaacSettings);
+   //Set the network interface to be configured
+   slaacSettings.interface = interface;
+
+   //SLAAC initialization
+   error = slaacInit(&slaacContext, &slaacSettings);
+   //Failed to initialize SLAAC?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to initialize SLAAC!\r\n");
+   }
+
+   //Start IPv6 address autoconfiguration process
+   error = slaacStart(&slaacContext);
+   //Failed to start SLAAC process?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to start SLAAC!\r\n");
+   }
+#else
+   //Set link-local address
+   ipv6StringToAddr(APP_IPV6_LINK_LOCAL_ADDR, &ipv6Addr);
+   ipv6SetLinkLocalAddr(interface, &ipv6Addr);
+
+   //Set IPv6 prefix
+   ipv6StringToAddr(APP_IPV6_PREFIX, &ipv6Addr);
+   ipv6SetPrefix(interface, 0, &ipv6Addr, APP_IPV6_PREFIX_LENGTH);
+
+   //Set global address
+   ipv6StringToAddr(APP_IPV6_GLOBAL_ADDR, &ipv6Addr);
+   ipv6SetGlobalAddr(interface, 0, &ipv6Addr);
+
+   //Set default router
+   ipv6StringToAddr(APP_IPV6_ROUTER, &ipv6Addr);
+   ipv6SetDefaultRouter(interface, 0, &ipv6Addr);
+
+   //Set primary and secondary DNS servers
+   ipv6StringToAddr(APP_IPV6_PRIMARY_DNS, &ipv6Addr);
+   ipv6SetDnsServer(interface, 0, &ipv6Addr);
+   ipv6StringToAddr(APP_IPV6_SECONDARY_DNS, &ipv6Addr);
+   ipv6SetDnsServer(interface, 1, &ipv6Addr);
+#endif
+#endif
+
+   //Get default settings
+   httpServerGetDefaultSettings(&httpServerSettings);
+   //Bind HTTP server to the desired interface
+   httpServerSettings.interface = &netInterface[0];
+   //Listen to port 80
+   httpServerSettings.port = HTTP_PORT;
+   //Client connections
+   httpServerSettings.maxConnections = APP_HTTP_MAX_CONNECTIONS;
+   httpServerSettings.connections = httpConnections;
+   //Specify the server's root directory
+   strcpy(httpServerSettings.rootDirectory, "/www/");
+   //Set default home page
+   strcpy(httpServerSettings.defaultDocument, "index.shtm");
+   //Callback functions
+   httpServerSettings.cgiCallback = httpServerCgiCallback;
+   httpServerSettings.uriNotFoundCallback = httpServerUriNotFoundCallback;
+
+   //HTTP server initialization
+   error = httpServerInit(&httpServerContext, &httpServerSettings);
+   //Failed to initialize HTTP server?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to initialize HTTP server!\r\n");
+   }
+
+   //Start HTTP server
+   error = httpServerStart(&httpServerContext);
+   //Failed to start HTTP server?
+   if(error)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to start HTTP server!\r\n");
+   }
+
+   //Create a task to blink the LED
+   task = osCreateTask("Blink", blinkTask, NULL, 500, OS_TASK_PRIORITY_NORMAL);
+   //Failed to create the task?
+   if(task == OS_INVALID_HANDLE)
+   {
+      //Debug message
+      TRACE_ERROR("Failed to create task!\r\n");
+   }
+
+   //Start the execution of tasks
+   osStartKernel();
+
+   //This function should never return
+   return 0;
+}
+
+
+/**
+ * @brief CGI callback function
+ * @param[in] connection Handle referencing a client connection
+ * @param[in] param NULL-terminated string that contains the CGI parameter
+ * @return Error code
+ **/
+
+error_t httpServerCgiCallback(HttpConnection *connection,
+   const char_t *param)
+{
+   static uint_t pageCounter = 0;
+   uint_t length;
+   MacAddr macAddr;
+#if (IPV4_SUPPORT == ENABLED)
+   Ipv4Addr ipv4Addr;
+#endif
+#if (IPV6_SUPPORT == ENABLED)
+   uint_t n;
+   Ipv6Addr ipv6Addr;
+#endif
+
+   //Underlying network interface
+   NetInterface *interface = connection->socket->interface;
+
+   //Check parameter name
+   if(!strcasecmp(param, "PAGE_COUNTER"))
+   {
+      pageCounter++;
+      sprintf(connection->buffer, "%u time%s", pageCounter, (pageCounter >= 2) ? "s" : "");
+   }
+   else if(!strcasecmp(param, "BOARD_NAME"))
+   {
+      strcpy(connection->buffer, "Nucleo-F746ZG");
+   }
+   else if(!strcasecmp(param, "SYSTEM_TIME"))
+   {
+      systime_t time = osGetSystemTime();
+      formatSystemTime(time, connection->buffer);
+   }
+   else if(!strcasecmp(param, "MAC_ADDR"))
+   {
+      netGetMacAddr(interface, &macAddr);
+      macAddrToString(&macAddr, connection->buffer);
+   }
+   else if(!strcasecmp(param, "IPV4_ADDR"))
+   {
+      ipv4GetHostAddr(interface, &ipv4Addr);
+      ipv4AddrToString(ipv4Addr, connection->buffer);
+   }
+   else if(!strcasecmp(param, "SUBNET_MASK"))
+   {
+      ipv4GetSubnetMask(interface, &ipv4Addr);
+      ipv4AddrToString(ipv4Addr, connection->buffer);
+   }
+   else if(!strcasecmp(param, "DEFAULT_GATEWAY"))
+   {
+      ipv4GetDefaultGateway(interface, &ipv4Addr);
+      ipv4AddrToString(ipv4Addr, connection->buffer);
+   }
+   else if(!strcasecmp(param, "IPV4_PRIMARY_DNS"))
+   {
+      ipv4GetDnsServer(interface, 0, &ipv4Addr);
+      ipv4AddrToString(ipv4Addr, connection->buffer);
+   }
+   else if(!strcasecmp(param, "IPV4_SECONDARY_DNS"))
+   {
+      ipv4GetDnsServer(interface, 1, &ipv4Addr);
+      ipv4AddrToString(ipv4Addr, connection->buffer);
+   }
+#if (IPV6_SUPPORT == ENABLED)
+   else if(!strcasecmp(param, "LINK_LOCAL_ADDR"))
+   {
+      ipv6GetLinkLocalAddr(interface, &ipv6Addr);
+      ipv6AddrToString(&ipv6Addr, connection->buffer);
+   }
+   else if(!strcasecmp(param, "GLOBAL_ADDR"))
+   {
+      ipv6GetGlobalAddr(interface, 0, &ipv6Addr);
+      ipv6AddrToString(&ipv6Addr, connection->buffer);
+   }
+   else if(!strcasecmp(param, "IPV6_PREFIX"))
+   {
+      ipv6GetPrefix(interface, 0, &ipv6Addr, &n);
+      ipv6AddrToString(&ipv6Addr, connection->buffer);
+      length = strlen(connection->buffer);
+      sprintf(connection->buffer + length, "/%u", n);
+   }
+   else if(!strcasecmp(param, "ROUTER"))
+   {
+      ipv6GetDefaultRouter(interface, 0, &ipv6Addr);
+      ipv6AddrToString(&ipv6Addr, connection->buffer);
+   }
+   else if(!strcasecmp(param, "IPV6_PRIMARY_DNS"))
+   {
+      ipv6GetDnsServer(interface, 0, &ipv6Addr);
+      ipv6AddrToString(&ipv6Addr, connection->buffer);
+   }
+   else if(!strcasecmp(param, "IPV6_SECONDARY_DNS"))
+   {
+      ipv6GetDnsServer(interface, 1, &ipv6Addr);
+      ipv6AddrToString(&ipv6Addr, connection->buffer);
+   }
+#endif
+   else
+   {
+      return ERROR_INVALID_TAG;
+   }
+
+   //Get the length of the resulting string
+   length = strlen(connection->buffer);
+
+   //Send the contents of the specified environment variable
+   return httpWriteStream(connection, connection->buffer, length);
+}
+
+
+/**
+ * @brief URI not found callback
+ * @param[in] connection Handle referencing a client connection
+ * @param[in] uri NULL-terminated string containing the path to the requested resource
+ * @return Error code
+ **/
+
+error_t httpServerUriNotFoundCallback(HttpConnection *connection,
+   const char_t *uri)
+{
+   //Not implemented
+   return ERROR_NOT_FOUND;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web_server_demo/src/net_config.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,143 @@
+/**
+ * @file net_config.h
+ * @brief CycloneTCP configuration file
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
+ *
+ * This file is part of CycloneTCP Open.
+ *
+ * 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _NET_CONFIG_H
+#define _NET_CONFIG_H
+
+//Trace level for TCP/IP stack debugging
+#define MEM_TRACE_LEVEL          4
+#define NIC_TRACE_LEVEL          4
+#define ETH_TRACE_LEVEL          2
+#define ARP_TRACE_LEVEL          2
+#define IP_TRACE_LEVEL           2
+#define IPV4_TRACE_LEVEL         2
+#define IPV6_TRACE_LEVEL         2
+#define ICMP_TRACE_LEVEL         2
+#define IGMP_TRACE_LEVEL         2
+#define ICMPV6_TRACE_LEVEL       2
+#define MLD_TRACE_LEVEL          2
+#define NDP_TRACE_LEVEL          2
+#define UDP_TRACE_LEVEL          2
+#define TCP_TRACE_LEVEL          2
+#define SOCKET_TRACE_LEVEL       2
+#define RAW_SOCKET_TRACE_LEVEL   2
+#define BSD_SOCKET_TRACE_LEVEL   2
+#define SLAAC_TRACE_LEVEL        4
+#define DHCP_TRACE_LEVEL         4
+#define DHCPV6_TRACE_LEVEL       4
+#define DNS_TRACE_LEVEL          4
+#define MDNS_TRACE_LEVEL         4
+#define NBNS_TRACE_LEVEL         2
+#define LLMNR_TRACE_LEVEL        4
+#define FTP_TRACE_LEVEL          5
+#define HTTP_TRACE_LEVEL         4
+#define SMTP_TRACE_LEVEL         5
+#define SNTP_TRACE_LEVEL         4
+#define STD_SERVICES_TRACE_LEVEL 5
+
+//Number of network adapters
+#define NET_INTERFACE_COUNT 1
+
+//Size of the multicast MAC filter
+#define MAC_MULTICAST_FILTER_SIZE 12
+
+//IPv4 support
+#define IPV4_SUPPORT ENABLED
+//Size of the IPv4 multicast filter
+#define IPV4_MULTICAST_FILTER_SIZE 4
+
+//IPv4 fragmentation support
+#define IPV4_FRAG_SUPPORT ENABLED
+//Maximum number of fragmented packets the host will accept
+//and hold in the reassembly queue simultaneously
+#define IPV4_MAX_FRAG_DATAGRAMS 4
+//Maximum datagram size the host will accept when reassembling fragments
+#define IPV4_MAX_FRAG_DATAGRAM_SIZE 8192
+
+//Size of ARP cache
+#define ARP_CACHE_SIZE 8
+//Maximum number of packets waiting for address resolution to complete
+#define ARP_MAX_PENDING_PACKETS 2
+
+//IGMP support
+#define IGMP_SUPPORT ENABLED
+
+//IPv6 support
+#define IPV6_SUPPORT ENABLED
+//Size of the IPv6 multicast filter
+#define IPV6_MULTICAST_FILTER_SIZE 8
+
+//IPv6 fragmentation support
+#define IPV6_FRAG_SUPPORT ENABLED
+//Maximum number of fragmented packets the host will accept
+//and hold in the reassembly queue simultaneously
+#define IPV6_MAX_FRAG_DATAGRAMS 4
+//Maximum datagram size the host will accept when reassembling fragments
+#define IPV6_MAX_FRAG_DATAGRAM_SIZE 8192
+
+//MLD support
+#define MLD_SUPPORT ENABLED
+
+//Neighbor cache size
+#define NDP_NEIGHBOR_CACHE_SIZE 8
+//Destination cache size
+#define NDP_DEST_CACHE_SIZE 8
+//Maximum number of packets waiting for address resolution to complete
+#define NDP_MAX_PENDING_PACKETS 2
+
+//TCP support
+#define TCP_SUPPORT ENABLED
+//Default buffer size for transmission
+#define TCP_DEFAULT_TX_BUFFER_SIZE (1430*2)
+//Default buffer size for reception
+#define TCP_DEFAULT_RX_BUFFER_SIZE (1430*2)
+//Default SYN queue size for listening sockets
+#define TCP_DEFAULT_SYN_QUEUE_SIZE 4
+//Maximum number of retransmissions
+#define TCP_MAX_RETRIES 5
+//Selective acknowledgment support
+#define TCP_SACK_SUPPORT DISABLED
+
+//UDP support
+#define UDP_SUPPORT ENABLED
+//Receive queue depth for connectionless sockets
+#define UDP_RX_QUEUE_SIZE 4
+
+//Raw socket support
+#define RAW_SOCKET_SUPPORT DISABLED
+//Receive queue depth for raw sockets
+#define RAW_SOCKET_RX_QUEUE_SIZE 4
+
+//Number of sockets that can be opened simultaneously
+#define SOCKET_MAX_COUNT 16
+
+//Server Side Includes support
+#define HTTP_SERVER_SSI_SUPPORT ENABLED
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web_server_demo/src/os_port_config.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,34 @@
+/**
+ * @file os_port_config.h
+ * @brief RTOS port configuration file
+ *
+ * @section License
+ *
+ * Copyright (C) 2010-2017 Oryx Embedded SARL. 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.
+ *
+ * @author Oryx Embedded SARL (www.oryx-embedded.com)
+ * @version 1.7.6
+ **/
+
+#ifndef _OS_PORT_CONFIG_H
+#define _OS_PORT_CONFIG_H
+
+//Select underlying RTOS
+#define USE_FREERTOS
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web_server_demo/src/stm32f7xx_hal_conf.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,427 @@
+/**
+  ******************************************************************************
+  * @file    stm32f7xx_hal_conf.h
+  * @author  MCD Application Team
+  * @version V1.0.1
+  * @date    22-April-2016
+  * @brief   HAL configuration file.
+  ******************************************************************************
+  * @attention
+  *
+  * <h2><center>&copy; COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
+  *
+  * Redistribution and use in source and binary forms, with or without modification,
+  * are permitted provided that the following conditions are met:
+  *   1. Redistributions of source code must retain the above copyright notice,
+  *      this list of conditions and the following disclaimer.
+  *   2. Redistributions in binary form must reproduce 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.
+  *
+  ******************************************************************************
+  */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32F7xx_HAL_CONF_H
+#define __STM32F7xx_HAL_CONF_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+/* ########################## Module Selection ############################## */
+/**
+  * @brief This is the list of modules to be used in the HAL driver
+  */
+#define HAL_MODULE_ENABLED
+#define HAL_ADC_MODULE_ENABLED
+#define HAL_CAN_MODULE_ENABLED
+#define HAL_CEC_MODULE_ENABLED
+#define HAL_CRC_MODULE_ENABLED
+#define HAL_CRYP_MODULE_ENABLED
+#define HAL_DAC_MODULE_ENABLED
+#define HAL_DCMI_MODULE_ENABLED
+#define HAL_DMA_MODULE_ENABLED
+#define HAL_DMA2D_MODULE_ENABLED
+#define HAL_ETH_MODULE_ENABLED
+#define HAL_FLASH_MODULE_ENABLED
+#define HAL_NAND_MODULE_ENABLED
+#define HAL_NOR_MODULE_ENABLED
+#define HAL_SRAM_MODULE_ENABLED
+#define HAL_SDRAM_MODULE_ENABLED
+#define HAL_HASH_MODULE_ENABLED
+#define HAL_GPIO_MODULE_ENABLED
+#define HAL_I2C_MODULE_ENABLED
+#define HAL_I2S_MODULE_ENABLED
+#define HAL_IWDG_MODULE_ENABLED
+#define HAL_LPTIM_MODULE_ENABLED
+#define HAL_LTDC_MODULE_ENABLED
+#define HAL_PWR_MODULE_ENABLED
+#define HAL_QSPI_MODULE_ENABLED
+#define HAL_RCC_MODULE_ENABLED
+#define HAL_RNG_MODULE_ENABLED
+#define HAL_RTC_MODULE_ENABLED
+#define HAL_SAI_MODULE_ENABLED
+#define HAL_SD_MODULE_ENABLED
+#define HAL_SPDIFRX_MODULE_ENABLED
+#define HAL_SPI_MODULE_ENABLED
+#define HAL_TIM_MODULE_ENABLED
+#define HAL_UART_MODULE_ENABLED
+#define HAL_USART_MODULE_ENABLED
+#define HAL_IRDA_MODULE_ENABLED
+#define HAL_SMARTCARD_MODULE_ENABLED
+#define HAL_WWDG_MODULE_ENABLED
+#define HAL_CORTEX_MODULE_ENABLED
+#define HAL_PCD_MODULE_ENABLED
+#define HAL_HCD_MODULE_ENABLED
+
+
+/* ########################## HSE/HSI Values adaptation ##################### */
+/**
+  * @brief Adjust the value of External High Speed oscillator (HSE) used in your application.
+  *        This value is used by the RCC HAL module to compute the system frequency
+  *        (when HSE is used as system clock source, directly or through the PLL).
+  */
+#if !defined  (HSE_VALUE)
+  #define HSE_VALUE    ((uint32_t)8000000U) /*!< Value of the External oscillator in Hz */
+#endif /* HSE_VALUE */
+
+#if !defined  (HSE_STARTUP_TIMEOUT)
+  #define HSE_STARTUP_TIMEOUT    100U   /*!< Time out for HSE start up, in ms */
+#endif /* HSE_STARTUP_TIMEOUT */
+
+/**
+  * @brief Internal High Speed oscillator (HSI) value.
+  *        This value is used by the RCC HAL module to compute the system frequency
+  *        (when HSI is used as system clock source, directly or through the PLL).
+  */
+#if !defined  (HSI_VALUE)
+  #define HSI_VALUE    16000000U /*!< Value of the Internal oscillator in Hz*/
+#endif /* HSI_VALUE */
+
+/**
+  * @brief Internal Low Speed oscillator (LSI) value.
+  */
+#if !defined  (LSI_VALUE)
+ #define LSI_VALUE  32000U                  /*!< LSI Typical Value in Hz*/
+#endif /* LSI_VALUE */                      /*!< Value of the Internal Low Speed oscillator in Hz
+                                             The real value may vary depending on the variations
+                                             in voltage and temperature.  */
+/**
+  * @brief External Low Speed oscillator (LSE) value.
+  */
+#if !defined  (LSE_VALUE)
+ #define LSE_VALUE  32768U    /*!< Value of the External Low Speed oscillator in Hz */
+#endif /* LSE_VALUE */
+
+#if !defined  (LSE_STARTUP_TIMEOUT)
+  #define LSE_STARTUP_TIMEOUT    5000U   /*!< Time out for LSE start up, in ms */
+#endif /* LSE_STARTUP_TIMEOUT */
+
+/**
+  * @brief External clock source for I2S peripheral
+  *        This value is used by the I2S HAL module to compute the I2S clock source
+  *        frequency, this source is inserted directly through I2S_CKIN pad.
+  */
+#if !defined  (EXTERNAL_CLOCK_VALUE)
+  #define EXTERNAL_CLOCK_VALUE    12288000U /*!< Value of the Internal oscillator in Hz*/
+#endif /* EXTERNAL_CLOCK_VALUE */
+
+/* Tip: To avoid modifying this file each time you need to use different HSE,
+   ===  you can define the HSE value in your toolchain compiler preprocessor. */
+
+/* ########################### System Configuration ######################### */
+/**
+  * @brief This is the HAL system configuration section
+  */
+#define  VDD_VALUE                    3300U /*!< Value of VDD in mv */
+#define  TICK_INT_PRIORITY            0x0FU /*!< tick interrupt priority */
+#define  USE_RTOS                     0U
+#define  PREFETCH_ENABLE              1U
+#define  ART_ACCLERATOR_ENABLE        1U /* To enable instruction cache and prefetch */
+
+/* ########################## Assert Selection ############################## */
+/**
+  * @brief Uncomment the line below to expanse the "assert_param" macro in the
+  *        HAL drivers code
+  */
+/* #define USE_FULL_ASSERT    1 */
+
+/* ################## Ethernet peripheral configuration for NUCLEO 144 board ##################### */
+
+/* Section 1 : Ethernet peripheral configuration */
+
+/* MAC ADDRESS: MAC_ADDR0:MAC_ADDR1:MAC_ADDR2:MAC_ADDR3:MAC_ADDR4:MAC_ADDR5 */
+#define MAC_ADDR0   2
+#define MAC_ADDR1   0
+#define MAC_ADDR2   0
+#define MAC_ADDR3   0
+#define MAC_ADDR4   0
+#define MAC_ADDR5   0
+
+/* Definition of the Ethernet driver buffers size and count */
+#define ETH_RX_BUF_SIZE                ETH_MAX_PACKET_SIZE /* buffer size for receive               */
+#define ETH_TX_BUF_SIZE                ETH_MAX_PACKET_SIZE /* buffer size for transmit              */
+#define ETH_RXBUFNB                    ((uint32_t)5)       /* 5 Rx buffers of size ETH_RX_BUF_SIZE  */
+#define ETH_TXBUFNB                    ((uint32_t)5)       /* 5 Tx buffers of size ETH_TX_BUF_SIZE  */
+
+/* Section 2: PHY configuration section */
+/* LAN8742A PHY Address*/
+#define LAN8742A_PHY_ADDRESS            0x00
+/* PHY Reset delay these values are based on a 1 ms Systick interrupt*/
+#define PHY_RESET_DELAY                 ((uint32_t)0x00000FFF)
+/* PHY Configuration delay */
+#define PHY_CONFIG_DELAY                ((uint32_t)0x00000FFF)
+
+#define PHY_READ_TO                     ((uint32_t)0x0000FFFF)
+#define PHY_WRITE_TO                    ((uint32_t)0x0000FFFF)
+
+/* Section 3: Common PHY Registers */
+
+#define PHY_BCR                         ((uint16_t)0x00)    /*!< Transceiver Basic Control Register   */
+#define PHY_BSR                         ((uint16_t)0x01)    /*!< Transceiver Basic Status Register    */
+
+#define PHY_RESET                       ((uint16_t)0x8000)  /*!< PHY Reset */
+#define PHY_LOOPBACK                    ((uint16_t)0x4000)  /*!< Select loop-back mode */
+#define PHY_FULLDUPLEX_100M             ((uint16_t)0x2100)  /*!< Set the full-duplex mode at 100 Mb/s */
+#define PHY_HALFDUPLEX_100M             ((uint16_t)0x2000)  /*!< Set the half-duplex mode at 100 Mb/s */
+#define PHY_FULLDUPLEX_10M              ((uint16_t)0x0100)  /*!< Set the full-duplex mode at 10 Mb/s  */
+#define PHY_HALFDUPLEX_10M              ((uint16_t)0x0000)  /*!< Set the half-duplex mode at 10 Mb/s  */
+#define PHY_AUTONEGOTIATION             ((uint16_t)0x1000)  /*!< Enable auto-negotiation function     */
+#define PHY_RESTART_AUTONEGOTIATION     ((uint16_t)0x0200)  /*!< Restart auto-negotiation function    */
+#define PHY_POWERDOWN                   ((uint16_t)0x0800)  /*!< Select the power down mode           */
+#define PHY_ISOLATE                     ((uint16_t)0x0400)  /*!< Isolate PHY from MII                 */
+
+#define PHY_AUTONEGO_COMPLETE           ((uint16_t)0x0020)  /*!< Auto-Negotiation process completed   */
+#define PHY_LINKED_STATUS               ((uint16_t)0x0004)  /*!< Valid link established               */
+#define PHY_JABBER_DETECTION            ((uint16_t)0x0002)  /*!< Jabber condition detected            */
+
+/* Section 4: Extended PHY Registers */
+
+#define PHY_SR                          ((uint16_t)0x1F)    /*!< PHY special control/ status register Offset     */
+
+#define PHY_SPEED_STATUS                ((uint16_t)0x0004)  /*!< PHY Speed mask                                  */
+#define PHY_DUPLEX_STATUS               ((uint16_t)0x0010)  /*!< PHY Duplex mask                                 */
+
+
+#define PHY_ISFR                        ((uint16_t)0x1D)    /*!< PHY Interrupt Source Flag register Offset       */
+#define PHY_ISFR_INT4                   ((uint16_t)0x0010)  /*!< PHY Link down inturrupt                         */
+
+/* ################## SPI peripheral configuration ########################## */
+
+/* CRC FEATURE: Use to activate CRC feature inside HAL SPI Driver
+* Activated: CRC code is present inside driver
+* Deactivated: CRC code cleaned from driver
+*/
+
+#define USE_SPI_CRC                     1U
+
+/* Includes ------------------------------------------------------------------*/
+/**
+  * @brief Include module's header file
+  */
+
+#ifdef HAL_RCC_MODULE_ENABLED
+  #include "stm32f7xx_hal_rcc.h"
+#endif /* HAL_RCC_MODULE_ENABLED */
+
+#ifdef HAL_GPIO_MODULE_ENABLED
+  #include "stm32f7xx_hal_gpio.h"
+#endif /* HAL_GPIO_MODULE_ENABLED */
+
+#ifdef HAL_DMA_MODULE_ENABLED
+  #include "stm32f7xx_hal_dma.h"
+#endif /* HAL_DMA_MODULE_ENABLED */
+
+#ifdef HAL_CORTEX_MODULE_ENABLED
+  #include "stm32f7xx_hal_cortex.h"
+#endif /* HAL_CORTEX_MODULE_ENABLED */
+
+#ifdef HAL_ADC_MODULE_ENABLED
+  #include "stm32f7xx_hal_adc.h"
+#endif /* HAL_ADC_MODULE_ENABLED */
+
+#ifdef HAL_CAN_MODULE_ENABLED
+  #include "stm32f7xx_hal_can.h"
+#endif /* HAL_CAN_MODULE_ENABLED */
+
+#ifdef HAL_CEC_MODULE_ENABLED
+  #include "stm32f7xx_hal_cec.h"
+#endif /* HAL_CEC_MODULE_ENABLED */
+
+#ifdef HAL_CRC_MODULE_ENABLED
+  #include "stm32f7xx_hal_crc.h"
+#endif /* HAL_CRC_MODULE_ENABLED */
+
+#ifdef HAL_CRYP_MODULE_ENABLED
+  #include "stm32f7xx_hal_cryp.h"
+#endif /* HAL_CRYP_MODULE_ENABLED */
+
+#ifdef HAL_DMA2D_MODULE_ENABLED
+  #include "stm32f7xx_hal_dma2d.h"
+#endif /* HAL_DMA2D_MODULE_ENABLED */
+
+#ifdef HAL_DAC_MODULE_ENABLED
+  #include "stm32f7xx_hal_dac.h"
+#endif /* HAL_DAC_MODULE_ENABLED */
+
+#ifdef HAL_DCMI_MODULE_ENABLED
+  #include "stm32f7xx_hal_dcmi.h"
+#endif /* HAL_DCMI_MODULE_ENABLED */
+
+#ifdef HAL_ETH_MODULE_ENABLED
+  #include "stm32f7xx_hal_eth.h"
+#endif /* HAL_ETH_MODULE_ENABLED */
+
+#ifdef HAL_FLASH_MODULE_ENABLED
+  #include "stm32f7xx_hal_flash.h"
+#endif /* HAL_FLASH_MODULE_ENABLED */
+
+#ifdef HAL_SRAM_MODULE_ENABLED
+  #include "stm32f7xx_hal_sram.h"
+#endif /* HAL_SRAM_MODULE_ENABLED */
+
+#ifdef HAL_NOR_MODULE_ENABLED
+  #include "stm32f7xx_hal_nor.h"
+#endif /* HAL_NOR_MODULE_ENABLED */
+
+#ifdef HAL_NAND_MODULE_ENABLED
+  #include "stm32f7xx_hal_nand.h"
+#endif /* HAL_NAND_MODULE_ENABLED */
+
+#ifdef HAL_SDRAM_MODULE_ENABLED
+  #include "stm32f7xx_hal_sdram.h"
+#endif /* HAL_SDRAM_MODULE_ENABLED */
+
+#ifdef HAL_HASH_MODULE_ENABLED
+ #include "stm32f7xx_hal_hash.h"
+#endif /* HAL_HASH_MODULE_ENABLED */
+
+#ifdef HAL_I2C_MODULE_ENABLED
+ #include "stm32f7xx_hal_i2c.h"
+#endif /* HAL_I2C_MODULE_ENABLED */
+
+#ifdef HAL_I2S_MODULE_ENABLED
+ #include "stm32f7xx_hal_i2s.h"
+#endif /* HAL_I2S_MODULE_ENABLED */
+
+#ifdef HAL_IWDG_MODULE_ENABLED
+ #include "stm32f7xx_hal_iwdg.h"
+#endif /* HAL_IWDG_MODULE_ENABLED */
+
+#ifdef HAL_LPTIM_MODULE_ENABLED
+ #include "stm32f7xx_hal_lptim.h"
+#endif /* HAL_LPTIM_MODULE_ENABLED */
+
+#ifdef HAL_LTDC_MODULE_ENABLED
+ #include "stm32f7xx_hal_ltdc.h"
+#endif /* HAL_LTDC_MODULE_ENABLED */
+
+#ifdef HAL_PWR_MODULE_ENABLED
+ #include "stm32f7xx_hal_pwr.h"
+#endif /* HAL_PWR_MODULE_ENABLED */
+
+#ifdef HAL_QSPI_MODULE_ENABLED
+ #include "stm32f7xx_hal_qspi.h"
+#endif /* HAL_QSPI_MODULE_ENABLED */
+
+#ifdef HAL_RNG_MODULE_ENABLED
+ #include "stm32f7xx_hal_rng.h"
+#endif /* HAL_RNG_MODULE_ENABLED */
+
+#ifdef HAL_RTC_MODULE_ENABLED
+ #include "stm32f7xx_hal_rtc.h"
+#endif /* HAL_RTC_MODULE_ENABLED */
+
+#ifdef HAL_SAI_MODULE_ENABLED
+ #include "stm32f7xx_hal_sai.h"
+#endif /* HAL_SAI_MODULE_ENABLED */
+
+#ifdef HAL_SD_MODULE_ENABLED
+ #include "stm32f7xx_hal_sd.h"
+#endif /* HAL_SD_MODULE_ENABLED */
+
+#ifdef HAL_SPDIFRX_MODULE_ENABLED
+ #include "stm32f7xx_hal_spdifrx.h"
+#endif /* HAL_SPDIFRX_MODULE_ENABLED */
+
+#ifdef HAL_SPI_MODULE_ENABLED
+ #include "stm32f7xx_hal_spi.h"
+#endif /* HAL_SPI_MODULE_ENABLED */
+
+#ifdef HAL_TIM_MODULE_ENABLED
+ #include "stm32f7xx_hal_tim.h"
+#endif /* HAL_TIM_MODULE_ENABLED */
+
+#ifdef HAL_UART_MODULE_ENABLED
+ #include "stm32f7xx_hal_uart.h"
+#endif /* HAL_UART_MODULE_ENABLED */
+
+#ifdef HAL_USART_MODULE_ENABLED
+ #include "stm32f7xx_hal_usart.h"
+#endif /* HAL_USART_MODULE_ENABLED */
+
+#ifdef HAL_IRDA_MODULE_ENABLED
+ #include "stm32f7xx_hal_irda.h"
+#endif /* HAL_IRDA_MODULE_ENABLED */
+
+#ifdef HAL_SMARTCARD_MODULE_ENABLED
+ #include "stm32f7xx_hal_smartcard.h"
+#endif /* HAL_SMARTCARD_MODULE_ENABLED */
+
+#ifdef HAL_WWDG_MODULE_ENABLED
+ #include "stm32f7xx_hal_wwdg.h"
+#endif /* HAL_WWDG_MODULE_ENABLED */
+
+#ifdef HAL_PCD_MODULE_ENABLED
+ #include "stm32f7xx_hal_pcd.h"
+#endif /* HAL_PCD_MODULE_ENABLED */
+
+#ifdef HAL_HCD_MODULE_ENABLED
+ #include "stm32f7xx_hal_hcd.h"
+#endif /* HAL_HCD_MODULE_ENABLED */
+
+/* Exported macro ------------------------------------------------------------*/
+#ifdef  USE_FULL_ASSERT
+/**
+  * @brief  The assert_param macro is used for function's parameters check.
+  * @param  expr: If expr is false, it calls assert_failed function
+  *         which reports the name of the source file and the source
+  *         line number of the call that failed.
+  *         If expr is true, it returns no value.
+  * @retval None
+  */
+  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
+/* Exported functions ------------------------------------------------------- */
+  void assert_failed(uint8_t* file, uint32_t line);
+#else
+  #define assert_param(expr) ((void)0)
+#endif /* USE_FULL_ASSERT */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __STM32F7xx_HAL_CONF_H */
+
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web_server_demo/src/stm32f7xx_it.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,195 @@
+/**
+  ******************************************************************************
+  * @file    Templates/stm32f7xx_it.c
+  * @author  MCD Application Team
+  * @version V1.0.3
+  * @date    22-April-2016
+  * @brief   Main Interrupt Service Routines.
+  *          This file provides template for all exceptions handler and
+  *          peripherals interrupt service routine.
+  ******************************************************************************
+  * @attention
+  *
+  * <h2><center>&copy; COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
+  *
+  * Redistribution and use in source and binary forms, with or without modification,
+  * are permitted provided that the following conditions are met:
+  *   1. Redistributions of source code must retain the above copyright notice,
+  *      this list of conditions and the following disclaimer.
+  *   2. Redistributions in binary form must reproduce 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.
+  *
+  ******************************************************************************
+  */
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32f7xx_it.h"
+#include "os_port.h"
+
+/** @addtogroup STM32F7xx_HAL_Examples
+  * @{
+  */
+
+/** @addtogroup Templates
+  * @{
+  */
+
+/* Private typedef -----------------------------------------------------------*/
+/* Private define ------------------------------------------------------------*/
+/* Private macro -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Private function prototypes -----------------------------------------------*/
+extern void HAL_IncTick(void);
+extern void xPortSysTickHandler(void);
+
+/* Private functions ---------------------------------------------------------*/
+
+/******************************************************************************/
+/*            Cortex-M7 Processor Exceptions Handlers                         */
+/******************************************************************************/
+
+/**
+  * @brief   This function handles NMI exception.
+  * @param  None
+  * @retval None
+  */
+void NMI_Handler(void)
+{
+}
+
+/**
+  * @brief  This function handles Hard Fault exception.
+  * @param  None
+  * @retval None
+  */
+void HardFault_Handler(void)
+{
+  /* Go to infinite loop when Hard Fault exception occurs */
+  while (1)
+  {
+  }
+}
+
+/**
+  * @brief  This function handles Memory Manage exception.
+  * @param  None
+  * @retval None
+  */
+void MemManage_Handler(void)
+{
+  /* Go to infinite loop when Memory Manage exception occurs */
+  while (1)
+  {
+  }
+}
+
+/**
+  * @brief  This function handles Bus Fault exception.
+  * @param  None
+  * @retval None
+  */
+void BusFault_Handler(void)
+{
+  /* Go to infinite loop when Bus Fault exception occurs */
+  while (1)
+  {
+  }
+}
+
+/**
+  * @brief  This function handles Usage Fault exception.
+  * @param  None
+  * @retval None
+  */
+void UsageFault_Handler(void)
+{
+  /* Go to infinite loop when Usage Fault exception occurs */
+  while (1)
+  {
+  }
+}
+
+/**
+  * @brief  This function handles SVCall exception.
+  * @param  None
+  * @retval None
+  */
+#if 0
+void SVC_Handler(void)
+{
+}
+#endif
+
+/**
+  * @brief  This function handles Debug Monitor exception.
+  * @param  None
+  * @retval None
+  */
+void DebugMon_Handler(void)
+{
+}
+
+/**
+  * @brief  This function handles PendSVC exception.
+  * @param  None
+  * @retval None
+  */
+#if 0
+void PendSV_Handler(void)
+{
+}
+#endif
+
+/**
+  * @brief  This function handles SysTick Handler.
+  * @param  None
+  * @retval None
+  */
+void SysTick_Handler(void)
+{
+   HAL_IncTick();
+
+   if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
+   {
+      xPortSysTickHandler();
+   }
+}
+
+/******************************************************************************/
+/*                 STM32F7xx Peripherals Interrupt Handlers                   */
+/*  Add here the Interrupt Handler for the used peripheral(s) (PPP), for the  */
+/*  available peripheral interrupt handler's name please refer to the startup */
+/*  file (startup_stm32f7xx.s).                                               */
+/******************************************************************************/
+
+/**
+  * @brief  This function handles PPP interrupt request.
+  * @param  None
+  * @retval None
+  */
+/*void PPP_IRQHandler(void)
+{
+}*/
+
+/**
+  * @}
+  */
+
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web_server_demo/src/stm32f7xx_it.h	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,69 @@
+/**
+  ******************************************************************************
+  * @file    Templates/stm32f7xx_it.h
+  * @author  MCD Application Team
+  * @version V1.0.3
+  * @date    22-April-2016
+  * @brief   This file contains the headers of the interrupt handlers.
+  ******************************************************************************
+  * @attention
+  *
+  * <h2><center>&copy; COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
+  *
+  * Redistribution and use in source and binary forms, with or without modification,
+  * are permitted provided that the following conditions are met:
+  *   1. Redistributions of source code must retain the above copyright notice,
+  *      this list of conditions and the following disclaimer.
+  *   2. Redistributions in binary form must reproduce 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.
+  *
+  ******************************************************************************
+  */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32F7xx_IT_H
+#define __STM32F7xx_IT_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+/* Exported macro ------------------------------------------------------------*/
+/* Exported functions ------------------------------------------------------- */
+
+void NMI_Handler(void);
+void HardFault_Handler(void);
+void MemManage_Handler(void);
+void BusFault_Handler(void);
+void UsageFault_Handler(void);
+void SVC_Handler(void);
+void DebugMon_Handler(void);
+void PendSV_Handler(void);
+void SysTick_Handler(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __STM32F7xx_IT_H */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web_server_demo/src/system_stm32f7xx.c	Sat Feb 04 18:15:49 2017 +0000
@@ -0,0 +1,281 @@
+/**
+  ******************************************************************************
+  * @file    Templates/system_stm32f7xx.c
+  * @author  MCD Application Team
+  * @version V1.0.1
+  * @date    22-April-2016
+  * @brief   - CMSIS Cortex-M7 Device Peripheral Access Layer System Source File.
+  *          - This file is dedicated only for STM32F746 NUCLEO 144 boards.
+  *
+  *   This file provides two functions and one global variable to be called from
+  *   user application:
+  *      - SystemInit(): This function is called at startup just after reset and
+  *                      before branch to main program. This call is made inside
+  *                      the "startup_stm32f7xx.s" file.
+  *
+  *      - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
+  *                                  by the user application to setup the SysTick
+  *                                  timer or configure other parameters.
+  *
+  *      - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
+  *                                 be called whenever the core clock is changed
+  *                                 during program execution.
+  *
+  *
+  ******************************************************************************
+  * @attention
+  *
+  * <h2><center>&copy; COPYRIGHT 2016 STMicroelectronics</center></h2>
+  *
+  * Redistribution and use in source and binary forms, with or without modification,
+  * are permitted provided that the following conditions are met:
+  *   1. Redistributions of source code must retain the above copyright notice,
+  *      this list of conditions and the following disclaimer.
+  *   2. Redistributions in binary form must reproduce 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.
+  *
+  ******************************************************************************
+  */
+
+/** @addtogroup CMSIS
+  * @{
+  */
+
+/** @addtogroup stm32f7xx_system
+  * @{
+  */
+
+/** @addtogroup STM32F7xx_System_Private_Includes
+  * @{
+  */
+
+#include "stm32f7xx.h"
+
+#if !defined  (HSE_VALUE)
+  #define HSE_VALUE    ((uint32_t)8000000) /*!< Default value of the External oscillator in Hz */
+#endif /* HSE_VALUE */
+
+#if !defined  (HSI_VALUE)
+  #define HSI_VALUE    ((uint32_t)16000000) /*!< Value of the Internal oscillator in Hz*/
+#endif /* HSI_VALUE */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F7xx_System_Private_TypesDefinitions
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F7xx_System_Private_Defines
+  * @{
+  */
+
+/************************* Miscellaneous Configuration ************************/
+/*!< Uncomment the following line if you need to relocate your vector Table in
+     Internal SRAM. */
+/* #define VECT_TAB_SRAM */
+#define VECT_TAB_OFFSET  0x00 /*!< Vector Table base offset field.
+                                   This value must be a multiple of 0x200. */
+/******************************************************************************/
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F7xx_System_Private_Macros
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F7xx_System_Private_Variables
+  * @{
+  */
+
+  /* This variable is updated in three ways:
+      1) by calling CMSIS function SystemCoreClockUpdate()
+      2) by calling HAL API function HAL_RCC_GetHCLKFreq()
+      3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency
+         Note: If you use this function to configure the system clock; then there
+               is no need to call the 2 first functions listed above, since SystemCoreClock
+               variable is updated automatically.
+  */
+  uint32_t SystemCoreClock = 16000000;
+  const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
+  const uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4};
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F7xx_System_Private_FunctionPrototypes
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32F7xx_System_Private_Functions
+  * @{
+  */
+
+/**
+  * @brief  Setup the microcontroller system
+  *         Initialize the Embedded Flash Interface, the PLL and update the
+  *         SystemFrequency variable.
+  * @param  None
+  * @retval None
+  */
+void SystemInit(void)
+{
+  /* FPU settings ------------------------------------------------------------*/
+  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
+    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
+  #endif
+  /* Reset the RCC clock configuration to the default reset state ------------*/
+  /* Set HSION bit */
+  RCC->CR |= (uint32_t)0x00000001;
+
+  /* Reset CFGR register */
+  RCC->CFGR = 0x00000000;
+
+  /* Reset HSEON, CSSON and PLLON bits */
+  RCC->CR &= (uint32_t)0xFEF6FFFF;
+
+  /* Reset PLLCFGR register */
+  RCC->PLLCFGR = 0x24003010;
+
+  /* Reset HSEBYP bit */
+  RCC->CR &= (uint32_t)0xFFFBFFFF;
+
+  /* Disable all interrupts */
+  RCC->CIR = 0x00000000;
+
+  /* Configure the Vector Table location add offset address ------------------*/
+#ifdef VECT_TAB_SRAM
+  //SCB->VTOR = SRAM1_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
+#else
+  //SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
+#endif
+}
+
+/**
+   * @brief  Update SystemCoreClock variable according to Clock Register Values.
+  *         The SystemCoreClock variable contains the core clock (HCLK), it can
+  *         be used by the user application to setup the SysTick timer or configure
+  *         other parameters.
+  *
+  * @note   Each time the core clock (HCLK) changes, this function must be called
+  *         to update SystemCoreClock variable value. Otherwise, any configuration
+  *         based on this variable will be incorrect.
+  *
+  * @note   - The system frequency computed by this function is not the real
+  *           frequency in the chip. It is calculated based on the predefined
+  *           constant and the selected clock source:
+  *
+  *           - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
+  *
+  *           - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)
+  *
+  *           - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**)
+  *             or HSI_VALUE(*) multiplied/divided by the PLL factors.
+  *
+  *         (*) HSI_VALUE is a constant defined in stm32f7xx.h file (default value
+  *             16 MHz) but the real value may vary depending on the variations
+  *             in voltage and temperature.
+  *
+  *         (**) HSE_VALUE is a constant defined in stm32f7xx.h file (default value
+  *              25 MHz), user has to ensure that HSE_VALUE is same as the real
+  *              frequency of the crystal used. Otherwise, this function may
+  *              have wrong result.
+  *
+  *         - The result of this function could be not correct when using fractional
+  *           value for HSE crystal.
+  *
+  * @param  None
+  * @retval None
+  */
+void SystemCoreClockUpdate(void)
+{
+  uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2;
+
+  /* Get SYSCLK source -------------------------------------------------------*/
+  tmp = RCC->CFGR & RCC_CFGR_SWS;
+
+  switch (tmp)
+  {
+    case 0x00:  /* HSI used as system clock source */
+      SystemCoreClock = HSI_VALUE;
+      break;
+    case 0x04:  /* HSE used as system clock source */
+      SystemCoreClock = HSE_VALUE;
+      break;
+    case 0x08:  /* PLL used as system clock source */
+
+      /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N
+         SYSCLK = PLL_VCO / PLL_P
+         */
+      pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22;
+      pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;
+
+      if (pllsource != 0)
+      {
+        /* HSE used as PLL clock source */
+        pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
+      }
+      else
+      {
+        /* HSI used as PLL clock source */
+        pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
+      }
+
+      pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2;
+      SystemCoreClock = pllvco/pllp;
+      break;
+    default:
+      SystemCoreClock = HSI_VALUE;
+      break;
+  }
+  /* Compute HCLK frequency --------------------------------------------------*/
+  /* Get HCLK prescaler */
+  tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
+  /* HCLK frequency */
+  SystemCoreClock >>= tmp;
+}
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
+